feat: stored under org/project path

This commit is contained in:
Oleg Proskurin 2025-10-05 17:36:01 +07:00
parent e2cfd6e27f
commit bdf2c80782
4 changed files with 62 additions and 3 deletions

View File

@ -4,7 +4,8 @@
"description": "Nano Banana Image Generation Service - REST API for AI-powered image generation using Gemini Flash Image model",
"main": "dist/server.js",
"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",
"build": "tsc",
"typecheck": "tsc --noEmit",

View File

@ -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();
}

View File

@ -15,6 +15,7 @@ import {
} from "../middleware/promptEnhancement";
import { asyncHandler } from "../middleware/errorHandler";
import { validateApiKey } from "../middleware/auth/validateApiKey";
import { requireProjectKey } from "../middleware/auth/requireProjectKey";
import { rateLimitByApiKey } from "../middleware/auth/rateLimiter";
import { GenerateImageResponse } from "../types/api";
// Create router
@ -30,6 +31,7 @@ generateRouter.post(
"/generate",
// Authentication middleware
validateApiKey,
requireProjectKey,
rateLimitByApiKey,
// File upload middleware
@ -64,8 +66,12 @@ generateRouter.post(
const { prompt, filename } = req.body;
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(
`[${timestamp}] [${requestId}] Starting image generation process`,
`[${timestamp}] [${requestId}] Starting image generation process for org:${orgId}, project:${projectId}`,
);
try {
@ -102,6 +108,8 @@ generateRouter.post(
const result = await imageGenService.generateImage({
prompt,
filename,
orgId,
projectId,
...(referenceImages && { referenceImages }),
});

View File

@ -11,6 +11,7 @@ import {
} from "../middleware/promptEnhancement";
import { asyncHandler } from "../middleware/errorHandler";
import { validateApiKey } from "../middleware/auth/validateApiKey";
import { requireProjectKey } from "../middleware/auth/requireProjectKey";
import { rateLimitByApiKey } from "../middleware/auth/rateLimiter";
import { GenerateImageResponse } from "../types/api";
@ -25,6 +26,7 @@ textToImageRouter.post(
"/text-to-image",
// Authentication middleware
validateApiKey,
requireProjectKey,
rateLimitByApiKey,
// JSON validation middleware
@ -54,8 +56,12 @@ textToImageRouter.post(
const requestId = req.requestId;
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(
`[${timestamp}] [${requestId}] Starting text-to-image generation process`,
`[${timestamp}] [${requestId}] Starting text-to-image generation process for org:${orgId}, project:${projectId}`,
);
try {
@ -67,6 +73,8 @@ textToImageRouter.post(
const result = await imageGenService.generateImage({
prompt,
filename,
orgId,
projectId,
});
// Log the result