feat: remove unused endpoints

This commit is contained in:
Oleg Proskurin 2025-10-09 23:48:25 +07:00
parent 2a4ba8f4ed
commit 83303f8890
8 changed files with 20 additions and 546 deletions

View File

@ -213,9 +213,7 @@ Located at `apps/api-service/.env` - used ONLY when running `pnpm dev:api` local
### Protected Endpoints (API Key Required) ### Protected Endpoints (API Key Required)
- `POST /api/generate` - Generate images from text + optional reference images
- `POST /api/text-to-image` - Generate images from text only (JSON) - `POST /api/text-to-image` - Generate images from text only (JSON)
- `POST /api/enhance` - Enhance and optimize text prompts
- `GET /api/images` - List generated images - `GET /api/images` - List generated images
**Authentication**: All protected endpoints require `X-API-Key` header **Authentication**: All protected endpoints require `X-API-Key` header
@ -245,10 +243,13 @@ Located at `apps/api-service/.env` - used ONLY when running `pnpm dev:api` local
```bash ```bash
# Image generation with project key # Image generation with project key
curl -X POST http://localhost:3000/api/generate \ curl -X POST http://localhost:3000/api/text-to-image \
-H "X-API-Key: YOUR_PROJECT_KEY" \ -H "X-API-Key: YOUR_PROJECT_KEY" \
-F "prompt=a sunset" \ -H "Content-Type: application/json" \
-F "filename=test_image" -d '{
"prompt": "a sunset",
"filename": "test_image"
}'
``` ```
### Key Management ### Key Management

View File

@ -144,15 +144,14 @@ See individual app README files for specific environment variables.
### Generate Image ### Generate Image
```bash ```bash
# Basic text-to-image # Text-to-image
curl -X POST http://localhost:3000/api/generate \ curl -X POST http://localhost:3000/api/text-to-image \
-F "prompt=A magical forest with glowing mushrooms" \ -H "Content-Type: application/json" \
-F "filename=magical_forest" -H "X-API-Key: YOUR_API_KEY" \
-d '{
# With reference images "prompt": "A magical forest with glowing mushrooms",
curl -X POST http://localhost:3000/api/generate \ "filename": "magical_forest"
-F "prompt=Character in medieval armor like the reference" \ }'
-F "referenceImages=@./reference.jpg"
``` ```
See `apps/api-service/README.md` for detailed API documentation. See `apps/api-service/README.md` for detailed API documentation.

View File

@ -2,8 +2,6 @@ import express, { Application } from 'express';
import cors from 'cors'; import cors from 'cors';
import { config } from 'dotenv'; import { config } from 'dotenv';
import { Config } from './types/api'; import { Config } from './types/api';
import { generateRouter } from './routes/generate';
import { enhanceRouter } from './routes/enhance';
import { textToImageRouter } from './routes/textToImage'; import { textToImageRouter } from './routes/textToImage';
import { imagesRouter } from './routes/images'; import { imagesRouter } from './routes/images';
import bootstrapRoutes from './routes/bootstrap'; import bootstrapRoutes from './routes/bootstrap';
@ -72,9 +70,7 @@ export const createApp = (): Application => {
endpoints: { endpoints: {
'GET /health': 'Health check', 'GET /health': 'Health check',
'GET /api/info': 'API information', 'GET /api/info': 'API information',
'POST /api/generate': 'Generate images from text prompt with optional reference images',
'POST /api/text-to-image': 'Generate images from text prompt only (JSON)', 'POST /api/text-to-image': 'Generate images from text prompt only (JSON)',
'POST /api/enhance': 'Enhance and optimize prompts for better image generation',
}, },
limits: { limits: {
maxFileSize: `${appConfig.maxFileSize / (1024 * 1024)}MB`, maxFileSize: `${appConfig.maxFileSize / (1024 * 1024)}MB`,
@ -120,8 +116,6 @@ export const createApp = (): Application => {
app.use('/api/admin/keys', adminKeysRoutes); app.use('/api/admin/keys', adminKeysRoutes);
// Protected API routes (require valid API key) // Protected API routes (require valid API key)
app.use('/api', generateRouter);
app.use('/api', enhanceRouter);
app.use('/api', textToImageRouter); app.use('/api', textToImageRouter);
app.use('/api', imagesRouter); app.use('/api', imagesRouter);

View File

@ -1,179 +0,0 @@
import { Request, Response, Router } from 'express';
import type { Router as RouterType } from 'express';
import { PromptEnhancementService } from '../services/promptEnhancement';
import { asyncHandler } from '../middleware/errorHandler';
import { PromptEnhancementRequest, PromptEnhancementResponse } from '../types/api';
import { body, validationResult } from 'express-validator';
import { validateApiKey } from '../middleware/auth/validateApiKey';
import { rateLimitByApiKey } from '../middleware/auth/rateLimiter';
export const enhanceRouter: RouterType = Router();
let promptEnhancementService: PromptEnhancementService;
const validateEnhanceRequest = [
body('prompt')
.notEmpty()
.withMessage('Prompt is required')
.isLength({ min: 1, max: 5000 })
.withMessage('Prompt must be between 1 and 5000 characters')
.trim(),
body('options.imageStyle')
.optional()
.isIn(['photorealistic', 'illustration', 'minimalist', 'sticker', 'product', 'comic'])
.withMessage('Invalid image style'),
body('options.aspectRatio')
.optional()
.isIn(['square', 'portrait', 'landscape', 'wide', 'ultrawide'])
.withMessage('Invalid aspect ratio'),
body('options.mood')
.optional()
.isLength({ max: 100 })
.withMessage('Mood description too long')
.trim(),
body('options.lighting')
.optional()
.isLength({ max: 100 })
.withMessage('Lighting description too long')
.trim(),
body('options.cameraAngle')
.optional()
.isLength({ max: 100 })
.withMessage('Camera angle description too long')
.trim(),
body('options.outputFormat')
.optional()
.isIn(['text', 'markdown', 'detailed'])
.withMessage('Invalid output format'),
body('options.negativePrompts')
.optional()
.isArray({ max: 10 })
.withMessage('Too many negative prompts (max 10)'),
body('options.negativePrompts.*')
.optional()
.isLength({ max: 100 })
.withMessage('Negative prompt too long')
.trim(),
];
const logEnhanceRequest = (req: Request, _res: Response, next: Function) => {
const timestamp = new Date().toISOString();
const requestId = req.requestId;
const { prompt, options } = req.body as PromptEnhancementRequest;
console.log(`[${timestamp}] [${requestId}] POST /api/enhance`);
console.log(`[${timestamp}] [${requestId}] Prompt length: ${prompt?.length || 0} characters`);
console.log(`[${timestamp}] [${requestId}] Options:`, options || 'none');
next();
};
enhanceRouter.post(
'/enhance',
// Authentication middleware
validateApiKey,
rateLimitByApiKey,
validateEnhanceRequest,
logEnhanceRequest,
asyncHandler(async (req: Request, res: Response) => {
if (!promptEnhancementService) {
const apiKey = process.env['GEMINI_API_KEY'];
if (!apiKey) {
return res.status(500).json({
success: false,
originalPrompt: '',
error: 'Server configuration error: GEMINI_API_KEY not configured',
} as PromptEnhancementResponse);
}
promptEnhancementService = new PromptEnhancementService(apiKey);
}
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log(
`[${new Date().toISOString()}] [${req.requestId}] Validation failed:`,
errors.array(),
);
return res.status(400).json({
success: false,
originalPrompt: req.body.prompt || '',
error: `Validation failed: ${errors
.array()
.map((e) => e.msg)
.join(', ')}`,
} as PromptEnhancementResponse);
}
const timestamp = new Date().toISOString();
const requestId = req.requestId;
const { prompt, options } = req.body as PromptEnhancementRequest;
console.log(`[${timestamp}] [${requestId}] Starting prompt enhancement`);
try {
// Extract orgId and projectId from validated API key
const orgId = req.apiKey?.organizationSlug || 'unknown';
const projectId = req.apiKey?.projectSlug || 'unknown';
const result = await promptEnhancementService.enhancePrompt(prompt, options || {}, {
orgId,
projectId,
});
console.log(`[${timestamp}] [${requestId}] Enhancement completed:`, {
success: result.success,
detectedLanguage: result.detectedLanguage,
appliedTemplate: result.appliedTemplate,
enhancementsCount: result.metadata?.enhancements.length || 0,
hasError: !!result.error,
});
if (result.success) {
const successResponse: PromptEnhancementResponse = {
success: true,
originalPrompt: result.originalPrompt,
enhancedPrompt: result.enhancedPrompt!,
...(result.detectedLanguage && {
detectedLanguage: result.detectedLanguage,
}),
...(result.appliedTemplate && {
appliedTemplate: result.appliedTemplate,
}),
...(result.metadata && { metadata: result.metadata }),
};
console.log(`[${timestamp}] [${requestId}] Sending success response`);
return res.status(200).json(successResponse);
} else {
const errorResponse: PromptEnhancementResponse = {
success: false,
originalPrompt: result.originalPrompt,
error: result.error || 'Unknown error occurred',
};
console.log(`[${timestamp}] [${requestId}] Sending error response: ${result.error}`);
return res.status(500).json(errorResponse);
}
} catch (error) {
console.error(`[${timestamp}] [${requestId}] Unhandled error in enhance endpoint:`, error);
const errorResponse: PromptEnhancementResponse = {
success: false,
originalPrompt: prompt,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
return res.status(500).json(errorResponse);
}
}),
);

View File

@ -1,158 +0,0 @@
import { Response, Router } from 'express';
import type { Router as RouterType } from 'express';
import { ImageGenService } from '../services/ImageGenService';
import { uploadReferenceImages, handleUploadErrors } from '../middleware/upload';
import { validateGenerateRequest, logRequestDetails } from '../middleware/validation';
import { autoEnhancePrompt, logEnhancementResult } 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
export const generateRouter: RouterType = Router();
// Initialize ImageGenService (will be created in the route handler to avoid circular dependency)
let imageGenService: ImageGenService;
/**
* POST /api/generate - Generate image from text prompt with optional reference images
*/
generateRouter.post(
'/generate',
// Authentication middleware
validateApiKey,
requireProjectKey,
rateLimitByApiKey,
// File upload middleware
uploadReferenceImages,
handleUploadErrors,
// Validation middleware
logRequestDetails,
validateGenerateRequest,
// Auto-enhancement middleware (optional)
autoEnhancePrompt,
logEnhancementResult,
// Main handler
asyncHandler(async (req: any, res: Response) => {
// Initialize service if not already done
if (!imageGenService) {
const apiKey = process.env['GEMINI_API_KEY'];
if (!apiKey) {
return res.status(500).json({
success: false,
message: 'Server configuration error',
error: 'GEMINI_API_KEY not configured',
} as GenerateImageResponse);
}
imageGenService = new ImageGenService(apiKey);
}
const timestamp = new Date().toISOString();
const requestId = req.requestId;
const { prompt, filename, aspectRatio, meta } = req.body;
const files = (req.files as Express.Multer.File[]) || [];
// Extract org/project slugs from validated API key
const orgId = req.apiKey?.organizationSlug || undefined;
const projectId = req.apiKey?.projectSlug!; // Guaranteed by requireProjectKey middleware
console.log(
`[${timestamp}] [${requestId}] Starting image generation process for org:${orgId}, project:${projectId}`,
);
try {
// Validate reference images if provided
if (files.length > 0) {
const validation = ImageGenService.validateReferenceImages(files);
if (!validation.valid) {
console.log(
`[${timestamp}] [${requestId}] Reference image validation failed: ${validation.error}`,
);
return res.status(400).json({
success: false,
message: 'Reference image validation failed',
error: validation.error,
} as GenerateImageResponse);
}
console.log(`[${timestamp}] [${requestId}] Reference images validation passed`);
}
// Convert files to reference images
const referenceImages =
files.length > 0 ? ImageGenService.convertFilesToReferenceImages(files) : undefined;
// Generate the image
console.log(`[${timestamp}] [${requestId}] Calling ImageGenService.generateImage()`);
const result = await imageGenService.generateImage({
prompt,
filename,
...(aspectRatio && { aspectRatio }),
orgId,
projectId,
...(referenceImages && { referenceImages }),
...(meta && { meta }),
});
// Log the result
console.log(`[${timestamp}] [${requestId}] Image generation completed:`, {
success: result.success,
model: result.model,
filename: result.filename,
hasError: !!result.error,
});
// Send response
if (result.success) {
const successResponse: GenerateImageResponse = {
success: true,
message: 'Image generated successfully',
data: {
filename: result.filename!,
filepath: result.filepath!,
...(result.description && { description: result.description }),
model: result.model,
generatedAt: timestamp,
...(req.enhancedPrompt && {
promptEnhancement: {
originalPrompt: req.originalPrompt,
enhancedPrompt: req.enhancedPrompt,
detectedLanguage: req.enhancementMetadata?.detectedLanguage,
appliedTemplate: req.enhancementMetadata?.appliedTemplate,
enhancements: req.enhancementMetadata?.enhancements || [],
},
}),
},
};
console.log(`[${timestamp}] [${requestId}] Sending success response`);
return res.status(200).json(successResponse);
} else {
const errorResponse: GenerateImageResponse = {
success: false,
message: 'Image generation failed',
error: result.error || 'Unknown error occurred',
};
console.log(`[${timestamp}] [${requestId}] Sending error response: ${result.error}`);
return res.status(500).json(errorResponse);
}
} catch (error) {
console.error(`[${timestamp}] [${requestId}] Unhandled error in generate endpoint:`, error);
const errorResponse: GenerateImageResponse = {
success: false,
message: 'Image generation failed',
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
return res.status(500).json(errorResponse);
}
}),
);

View File

@ -46,26 +46,6 @@ DELETE {{base}}/api/admin/keys/KEY_ID_HERE
X-API-Key: {{masterKey}} X-API-Key: {{masterKey}}
### Enhance Prompt (Requires API Key)
POST {{base}}/api/enhance
Content-Type: application/json
X-API-Key: {{apiKey}}
{
"prompt": "Два мага сражаются в снежном лесу. У одного из них в руках посох, из которого вырывается молния, а другой маг защищается щитом из льда. Вокруг них падают снежинки, и на заднем плане видны заснеженные деревья и горы.",
"options": {
"imageStyle": "photorealistic",
"aspectRatio": "landscape",
"mood": "serene and peaceful",
"lighting": "golden hour",
"cameraAngle": "wide shot",
"outputFormat": "detailed",
"negativePrompts": ["blurry", "low quality"]
}
}
### Generate Image from Text (Requires API Key) ### Generate Image from Text (Requires API Key)
POST {{base}}/api/text-to-image POST {{base}}/api/text-to-image
@ -78,37 +58,7 @@ X-API-Key: {{apiKey}}
} }
### Generate Image with Files (Requires API Key) ### Generate Image - Text to Image (alternative format)
POST {{base}}/api/generate
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
X-API-Key: {{apiKey}}
------WebKitFormBoundary
Content-Disposition: form-data; name="prompt"
A majestic dragon soaring through a crystal cave filled with glowing blue crystals, sunbeams piercing through cracks in the ceiling creating dramatic lighting, highly detailed fantasy art style
------WebKitFormBoundary
Content-Disposition: form-data; name="filename"
dragon-crystal-cave
------WebKitFormBoundary
Content-Disposition: form-data; name="autoEnhance"
true
------WebKitFormBoundary
Content-Disposition: form-data; name="enhancementOptions"
{"imageStyle":"illustration","aspectRatio":"landscape","mood":"mystical and dramatic","lighting":"magical glow with sunbeams","cameraAngle":"wide shot","negativePrompts":["blurry","low quality","amateur"]}
------WebKitFormBoundary
Content-Disposition: form-data; name="referenceImages"; filename="reference.jpg"
Content-Type: image/jpeg
< ./reference.jpg
------WebKitFormBoundary--
### Generate Image - Text to Image
POST http://localhost:3000/api/text-to-image POST http://localhost:3000/api/text-to-image
Content-Type: application/json Content-Type: application/json
X-API-Key: bnt_61ba018f01474491cbaacec4509220d7154fffcd011f005eece4dba7889fba99 X-API-Key: bnt_61ba018f01474491cbaacec4509220d7154fffcd011f005eece4dba7889fba99

View File

@ -16,17 +16,11 @@ echo "2. Testing API info endpoint..."
curl -s "$BASE_URL/api/info" | jq '.' || echo "API info endpoint failed" curl -s "$BASE_URL/api/info" | jq '.' || echo "API info endpoint failed"
echo -e "\n" echo -e "\n"
# Test 3: Generate endpoint validation (should fail without prompt) # Test 3: Text-to-image endpoint validation (should fail without API key)
echo "3. Testing generate endpoint validation..." echo "3. Testing text-to-image endpoint (should fail without valid API key)..."
curl -s -X POST "$BASE_URL/api/generate" \ curl -s -X POST "$BASE_URL/api/text-to-image" \
-F "filename=test" | jq '.' || echo "Generate endpoint validation test failed" -H "Content-Type: application/json" \
echo -e "\n" -d '{"prompt": "A simple test image", "filename": "test"}' | jq '.' || echo "Text-to-image endpoint test failed"
# Test 4: Generate endpoint validation (should fail without API key)
echo "4. Testing generate endpoint with prompt (should fail without valid API key)..."
curl -s -X POST "$BASE_URL/api/generate" \
-F "prompt=A simple test image" \
-F "filename=test" | jq '.' || echo "Generate endpoint test failed"
echo -e "\n" echo -e "\n"
echo "✅ API tests completed!" echo "✅ API tests completed!"

View File

@ -1,127 +0,0 @@
#!/bin/bash
# Test script for Banatie Prompt Enhancement API endpoints
BASE_URL="http://localhost:3000"
echo "🧪 Testing Banatie Prompt Enhancement API"
echo "========================================="
# Test 1: Health check first
echo "1. Testing health endpoint..."
curl -s "$BASE_URL/health" | jq '.status' || echo "Health endpoint failed"
echo -e "\n"
# Test 2: API info (should now include enhance endpoint)
echo "2. Testing API info endpoint..."
curl -s "$BASE_URL/api/info" | jq '.endpoints' || echo "API info endpoint failed"
echo -e "\n"
# Test 3: Basic prompt enhancement
echo "3. Testing basic prompt enhancement..."
curl -s -X POST "$BASE_URL/api/enhance" \
-H "Content-Type: application/json" \
-d '{
"prompt": "cat"
}' | jq '.' || echo "Basic enhancement test failed"
echo -e "\n"
# Test 4: Prompt enhancement with options
echo "4. Testing prompt enhancement with options..."
curl -s -X POST "$BASE_URL/api/enhance" \
-H "Content-Type: application/json" \
-d '{
"prompt": "beautiful sunset",
"options": {
"imageStyle": "photorealistic",
"aspectRatio": "landscape",
"mood": "serene",
"lighting": "golden hour"
}
}' | jq '.' || echo "Enhancement with options test failed"
echo -e "\n"
# Test 5: Multilingual prompt enhancement (Spanish)
echo "5. Testing Spanish prompt enhancement..."
curl -s -X POST "$BASE_URL/api/enhance" \
-H "Content-Type: application/json" \
-d '{
"prompt": "un gato hermoso con ojos azules",
"options": {
"imageStyle": "illustration"
}
}' | jq '.' || echo "Spanish enhancement test failed"
echo -e "\n"
# Test 6: Multilingual prompt enhancement (Chinese)
echo "6. Testing Chinese prompt enhancement..."
curl -s -X POST "$BASE_URL/api/enhance" \
-H "Content-Type: application/json" \
-d '{
"prompt": "美丽的山景",
"options": {
"imageStyle": "minimalist"
}
}' | jq '.' || echo "Chinese enhancement test failed"
echo -e "\n"
# Test 7: Sticker style prompt
echo "7. Testing sticker style enhancement..."
curl -s -X POST "$BASE_URL/api/enhance" \
-H "Content-Type: application/json" \
-d '{
"prompt": "happy panda",
"options": {
"imageStyle": "sticker",
"negativePrompts": ["dark", "scary"]
}
}' | jq '.' || echo "Sticker style test failed"
echo -e "\n"
# Test 8: Validation error test (empty prompt)
echo "8. Testing validation error (empty prompt)..."
curl -s -X POST "$BASE_URL/api/enhance" \
-H "Content-Type: application/json" \
-d '{
"prompt": ""
}' | jq '.' || echo "Validation error test failed"
echo -e "\n"
# Test 9: Validation error test (invalid image style)
echo "9. Testing validation error (invalid style)..."
curl -s -X POST "$BASE_URL/api/enhance" \
-H "Content-Type: application/json" \
-d '{
"prompt": "test",
"options": {
"imageStyle": "invalid_style"
}
}' | jq '.' || echo "Invalid style test failed"
echo -e "\n"
# Test 10: Auto-enhancement in generate endpoint
echo "10. Testing auto-enhancement in generate endpoint..."
curl -s -X POST "$BASE_URL/api/generate" \
-F "prompt=gato bonito" \
-F "filename=test_autoenhance" \
-F "autoEnhance=true" \
-F "enhancementOptions[imageStyle]=photorealistic" | jq '.' || echo "Auto-enhancement test failed"
echo -e "\n"
# Test 11: Generate without auto-enhancement (normal behavior)
echo "11. Testing generate without auto-enhancement..."
curl -s -X POST "$BASE_URL/api/generate" \
-F "prompt=a simple cat" \
-F "filename=test_normal" | jq '.' || echo "Normal generate test failed"
echo -e "\n"
echo "✅ Enhancement API tests completed!"
echo ""
echo "📋 Test Summary:"
echo "- Basic enhancement functionality"
echo "- Enhancement with styling options"
echo "- Multilingual support (Spanish, Chinese)"
echo "- Different image styles (photorealistic, illustration, minimalist, sticker)"
echo "- Validation error handling"
echo "- Auto-enhancement integration with generate endpoint"
echo ""
echo "💡 To run with API key, ensure GEMINI_API_KEY is set in your environment"