feat: stored under org/project path
This commit is contained in:
parent
e2cfd6e27f
commit
bdf2c80782
|
|
@ -4,7 +4,8 @@
|
||||||
"description": "Nano Banana Image Generation Service - REST API for AI-powered image generation using Gemini Flash Image model",
|
"description": "Nano Banana Image Generation Service - REST API for AI-powered image generation using Gemini Flash Image model",
|
||||||
"main": "dist/server.js",
|
"main": "dist/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "echo 'Logs will be saved to api-dev.log' && tsx --watch src/server.ts 2>&1 | tee api-dev.log",
|
"infra:up": "cd ../.. && docker compose up -d postgres minio storage-init",
|
||||||
|
"dev": "npm run infra:up && echo 'Logs will be saved to api-dev.log' && tsx --watch src/server.ts 2>&1 | tee api-dev.log",
|
||||||
"start": "node dist/server.js",
|
"start": "node dist/server.js",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware to ensure only project keys can access generation endpoints
|
||||||
|
* Master keys are for admin purposes only
|
||||||
|
*/
|
||||||
|
export function requireProjectKey(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): void {
|
||||||
|
// This middleware assumes validateApiKey has already run and attached req.apiKey
|
||||||
|
if (!req.apiKey) {
|
||||||
|
res.status(401).json({
|
||||||
|
error: 'Authentication required',
|
||||||
|
message: 'API key validation must be performed first',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block master keys from generation endpoints
|
||||||
|
if (req.apiKey.keyType === 'master') {
|
||||||
|
res.status(403).json({
|
||||||
|
error: 'Forbidden',
|
||||||
|
message: 'Master keys cannot be used for image generation. Please use a project-specific API key.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure project key has required IDs
|
||||||
|
if (!req.apiKey.projectId) {
|
||||||
|
res.status(400).json({
|
||||||
|
error: 'Invalid API key',
|
||||||
|
message: 'Project key must be associated with a project',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[${new Date().toISOString()}] Project key validated for generation: ${req.apiKey.id}`);
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
} from "../middleware/promptEnhancement";
|
} from "../middleware/promptEnhancement";
|
||||||
import { asyncHandler } from "../middleware/errorHandler";
|
import { asyncHandler } from "../middleware/errorHandler";
|
||||||
import { validateApiKey } from "../middleware/auth/validateApiKey";
|
import { validateApiKey } from "../middleware/auth/validateApiKey";
|
||||||
|
import { requireProjectKey } from "../middleware/auth/requireProjectKey";
|
||||||
import { rateLimitByApiKey } from "../middleware/auth/rateLimiter";
|
import { rateLimitByApiKey } from "../middleware/auth/rateLimiter";
|
||||||
import { GenerateImageResponse } from "../types/api";
|
import { GenerateImageResponse } from "../types/api";
|
||||||
// Create router
|
// Create router
|
||||||
|
|
@ -30,6 +31,7 @@ generateRouter.post(
|
||||||
"/generate",
|
"/generate",
|
||||||
// Authentication middleware
|
// Authentication middleware
|
||||||
validateApiKey,
|
validateApiKey,
|
||||||
|
requireProjectKey,
|
||||||
rateLimitByApiKey,
|
rateLimitByApiKey,
|
||||||
|
|
||||||
// File upload middleware
|
// File upload middleware
|
||||||
|
|
@ -64,8 +66,12 @@ generateRouter.post(
|
||||||
const { prompt, filename } = req.body;
|
const { prompt, filename } = req.body;
|
||||||
const files = (req.files as Express.Multer.File[]) || [];
|
const files = (req.files as Express.Multer.File[]) || [];
|
||||||
|
|
||||||
|
// Extract org/project IDs from validated API key
|
||||||
|
const orgId = req.apiKey?.organizationId || undefined;
|
||||||
|
const projectId = req.apiKey?.projectId!; // Guaranteed by requireProjectKey middleware
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`[${timestamp}] [${requestId}] Starting image generation process`,
|
`[${timestamp}] [${requestId}] Starting image generation process for org:${orgId}, project:${projectId}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -102,6 +108,8 @@ generateRouter.post(
|
||||||
const result = await imageGenService.generateImage({
|
const result = await imageGenService.generateImage({
|
||||||
prompt,
|
prompt,
|
||||||
filename,
|
filename,
|
||||||
|
orgId,
|
||||||
|
projectId,
|
||||||
...(referenceImages && { referenceImages }),
|
...(referenceImages && { referenceImages }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
} from "../middleware/promptEnhancement";
|
} from "../middleware/promptEnhancement";
|
||||||
import { asyncHandler } from "../middleware/errorHandler";
|
import { asyncHandler } from "../middleware/errorHandler";
|
||||||
import { validateApiKey } from "../middleware/auth/validateApiKey";
|
import { validateApiKey } from "../middleware/auth/validateApiKey";
|
||||||
|
import { requireProjectKey } from "../middleware/auth/requireProjectKey";
|
||||||
import { rateLimitByApiKey } from "../middleware/auth/rateLimiter";
|
import { rateLimitByApiKey } from "../middleware/auth/rateLimiter";
|
||||||
import { GenerateImageResponse } from "../types/api";
|
import { GenerateImageResponse } from "../types/api";
|
||||||
|
|
||||||
|
|
@ -25,6 +26,7 @@ textToImageRouter.post(
|
||||||
"/text-to-image",
|
"/text-to-image",
|
||||||
// Authentication middleware
|
// Authentication middleware
|
||||||
validateApiKey,
|
validateApiKey,
|
||||||
|
requireProjectKey,
|
||||||
rateLimitByApiKey,
|
rateLimitByApiKey,
|
||||||
|
|
||||||
// JSON validation middleware
|
// JSON validation middleware
|
||||||
|
|
@ -54,8 +56,12 @@ textToImageRouter.post(
|
||||||
const requestId = req.requestId;
|
const requestId = req.requestId;
|
||||||
const { prompt, filename } = req.body;
|
const { prompt, filename } = req.body;
|
||||||
|
|
||||||
|
// Extract org/project IDs from validated API key
|
||||||
|
const orgId = req.apiKey?.organizationId || undefined;
|
||||||
|
const projectId = req.apiKey?.projectId!; // Guaranteed by requireProjectKey middleware
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`[${timestamp}] [${requestId}] Starting text-to-image generation process`,
|
`[${timestamp}] [${requestId}] Starting text-to-image generation process for org:${orgId}, project:${projectId}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -67,6 +73,8 @@ textToImageRouter.post(
|
||||||
const result = await imageGenService.generateImage({
|
const result = await imageGenService.generateImage({
|
||||||
prompt,
|
prompt,
|
||||||
filename,
|
filename,
|
||||||
|
orgId,
|
||||||
|
projectId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Log the result
|
// Log the result
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue