feat: add text endpoint
This commit is contained in:
parent
d55eba8817
commit
1c6dfc4f8b
|
|
@ -14,7 +14,7 @@ API key required via `GEMINI_API_KEY` environment variable (server-side configur
|
||||||
|
|
||||||
## Content Types
|
## Content Types
|
||||||
|
|
||||||
- **Request**: `multipart/form-data` for file uploads, `application/json` for other endpoints
|
- **Request**: `multipart/form-data` for file uploads, `application/json` for JSON endpoints
|
||||||
- **Response**: `application/json`
|
- **Response**: `application/json`
|
||||||
|
|
||||||
## Rate Limits
|
## Rate Limits
|
||||||
|
|
@ -60,6 +60,7 @@ Returns API metadata and configuration limits.
|
||||||
"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/generate": "Generate images from text prompt with optional reference images",
|
||||||
|
"POST /api/text-to-image": "Generate images from text prompt only (JSON)",
|
||||||
"POST /api/enhance": "Enhance and optimize prompts for better image generation"
|
"POST /api/enhance": "Enhance and optimize prompts for better image generation"
|
||||||
},
|
},
|
||||||
"limits": {
|
"limits": {
|
||||||
|
|
@ -144,6 +145,92 @@ curl -X POST http://localhost:3000/api/generate \
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Text-to-Image (JSON)
|
||||||
|
|
||||||
|
#### `POST /api/text-to-image`
|
||||||
|
|
||||||
|
Generate images from text prompts only using JSON payload. Simplified endpoint for text-only requests without file uploads.
|
||||||
|
|
||||||
|
**Content-Type:** `application/json`
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"prompt": "A beautiful sunset over mountains",
|
||||||
|
"filename": "sunset_image",
|
||||||
|
"autoEnhance": true,
|
||||||
|
"enhancementOptions": {
|
||||||
|
"imageStyle": "photorealistic",
|
||||||
|
"aspectRatio": "landscape",
|
||||||
|
"mood": "peaceful",
|
||||||
|
"lighting": "golden hour"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
| Field | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `prompt` | string | Yes | Text description of the image to generate (3-2000 chars) |
|
||||||
|
| `filename` | string | Yes | Desired filename for the generated image (alphanumeric, underscore, hyphen only) |
|
||||||
|
| `autoEnhance` | boolean | No | Enable automatic prompt enhancement |
|
||||||
|
| `enhancementOptions` | object | No | Enhancement configuration options (same as /api/generate) |
|
||||||
|
|
||||||
|
**Example Request:**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3000/api/text-to-image \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"prompt": "A beautiful sunset over mountains with golden clouds",
|
||||||
|
"filename": "test_sunset",
|
||||||
|
"autoEnhance": true,
|
||||||
|
"enhancementOptions": {
|
||||||
|
"imageStyle": "photorealistic",
|
||||||
|
"aspectRatio": "landscape"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Success Response (200):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Image generated successfully",
|
||||||
|
"data": {
|
||||||
|
"filename": "test_sunset.png",
|
||||||
|
"filepath": "results/test_sunset.png",
|
||||||
|
"description": "Here's a beautiful sunset over mountains with golden clouds for you!",
|
||||||
|
"model": "Nano Banana",
|
||||||
|
"generatedAt": "2025-09-26T15:04:27.705Z",
|
||||||
|
"promptEnhancement": {
|
||||||
|
"originalPrompt": "A beautiful sunset over mountains",
|
||||||
|
"enhancedPrompt": "A breathtaking photorealistic sunset over majestic mountains...",
|
||||||
|
"detectedLanguage": "English",
|
||||||
|
"appliedTemplate": "landscape",
|
||||||
|
"enhancements": ["lighting_enhancement", "composition_improvement"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error Response (400/500):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "Validation failed",
|
||||||
|
"error": "Prompt is required"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Differences from /api/generate:**
|
||||||
|
- **JSON only**: No file upload support
|
||||||
|
- **Faster**: No multipart parsing overhead
|
||||||
|
- **Simpler testing**: Easy to use with curl or API clients
|
||||||
|
- **Same features**: Supports all enhancement options
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Enhance Prompt
|
### Enhance Prompt
|
||||||
|
|
||||||
#### `POST /api/enhance`
|
#### `POST /api/enhance`
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,25 @@ Content-Type: application/json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### Generate image from text
|
||||||
|
|
||||||
|
POST {{base}}/api/text-to-image
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"prompt": "банановый стимпанк. много стимпанк машин и меаханизмов посвященных бананм и работающих на бананах. банановая феерия",
|
||||||
|
"filename": "banatie-party",
|
||||||
|
"autoEnhance": true,
|
||||||
|
"enhancementOptions": {
|
||||||
|
"imageStyle": "photorealistic",
|
||||||
|
"aspectRatio": "landscape",
|
||||||
|
"mood": "peaceful",
|
||||||
|
"lighting": "golden hour"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
### Generate Image with Files
|
### Generate Image with Files
|
||||||
POST {{base}}/api/generate
|
POST {{base}}/api/generate
|
||||||
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
|
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { config } from 'dotenv';
|
||||||
import { Config } from './types/api';
|
import { Config } from './types/api';
|
||||||
import { generateRouter } from './routes/generate';
|
import { generateRouter } from './routes/generate';
|
||||||
import { enhanceRouter } from './routes/enhance';
|
import { enhanceRouter } from './routes/enhance';
|
||||||
|
import { textToImageRouter } from './routes/textToImage';
|
||||||
import { errorHandler, notFoundHandler } from './middleware/errorHandler';
|
import { errorHandler, notFoundHandler } from './middleware/errorHandler';
|
||||||
|
|
||||||
// Load environment variables
|
// Load environment variables
|
||||||
|
|
@ -63,6 +64,7 @@ export const createApp = (): Application => {
|
||||||
'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/generate': 'Generate images from text prompt with optional reference images',
|
||||||
|
'POST /api/text-to-image': 'Generate images from text prompt only (JSON)',
|
||||||
'POST /api/enhance': 'Enhance and optimize prompts for better image generation'
|
'POST /api/enhance': 'Enhance and optimize prompts for better image generation'
|
||||||
},
|
},
|
||||||
limits: {
|
limits: {
|
||||||
|
|
@ -79,6 +81,7 @@ export const createApp = (): Application => {
|
||||||
// Mount API routes
|
// Mount API routes
|
||||||
app.use('/api', generateRouter);
|
app.use('/api', generateRouter);
|
||||||
app.use('/api', enhanceRouter);
|
app.use('/api', enhanceRouter);
|
||||||
|
app.use('/api', textToImageRouter);
|
||||||
|
|
||||||
// Error handling middleware (must be last)
|
// Error handling middleware (must be last)
|
||||||
app.use(notFoundHandler);
|
app.use(notFoundHandler);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
import { Response, NextFunction } from "express";
|
||||||
|
import { sanitizeFilename } from "./validation";
|
||||||
|
|
||||||
|
// Validation rules (same as existing validation but for JSON)
|
||||||
|
const VALIDATION_RULES = {
|
||||||
|
prompt: {
|
||||||
|
minLength: 3,
|
||||||
|
maxLength: 2000,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
filename: {
|
||||||
|
minLength: 1,
|
||||||
|
maxLength: 100,
|
||||||
|
required: true,
|
||||||
|
pattern: /^[a-zA-Z0-9_-]+$/, // Only alphanumeric, underscore, hyphen
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the text-to-image JSON request
|
||||||
|
*/
|
||||||
|
export const validateTextToImageRequest = (
|
||||||
|
req: any,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction,
|
||||||
|
): void | Response => {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const { prompt, filename, autoEnhance, enhancementOptions } = req.body;
|
||||||
|
const errors: string[] = [];
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] Validating text-to-image JSON request`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Validate that request body exists
|
||||||
|
if (!req.body || typeof req.body !== "object") {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: "Request body must be valid JSON",
|
||||||
|
message: "Invalid request format",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate prompt
|
||||||
|
if (!prompt) {
|
||||||
|
errors.push("Prompt is required");
|
||||||
|
} else if (typeof prompt !== "string") {
|
||||||
|
errors.push("Prompt must be a string");
|
||||||
|
} else if (prompt.trim().length < VALIDATION_RULES.prompt.minLength) {
|
||||||
|
errors.push(
|
||||||
|
`Prompt must be at least ${VALIDATION_RULES.prompt.minLength} characters`,
|
||||||
|
);
|
||||||
|
} else if (prompt.length > VALIDATION_RULES.prompt.maxLength) {
|
||||||
|
errors.push(
|
||||||
|
`Prompt must be less than ${VALIDATION_RULES.prompt.maxLength} characters`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate filename
|
||||||
|
if (!filename) {
|
||||||
|
errors.push("Filename is required");
|
||||||
|
} else if (typeof filename !== "string") {
|
||||||
|
errors.push("Filename must be a string");
|
||||||
|
} else if (filename.trim().length < VALIDATION_RULES.filename.minLength) {
|
||||||
|
errors.push("Filename cannot be empty");
|
||||||
|
} else if (filename.length > VALIDATION_RULES.filename.maxLength) {
|
||||||
|
errors.push(
|
||||||
|
`Filename must be less than ${VALIDATION_RULES.filename.maxLength} characters`,
|
||||||
|
);
|
||||||
|
} else if (!VALIDATION_RULES.filename.pattern.test(filename)) {
|
||||||
|
errors.push(
|
||||||
|
"Filename can only contain letters, numbers, underscores, and hyphens",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate autoEnhance (optional boolean)
|
||||||
|
if (autoEnhance !== undefined && typeof autoEnhance !== "boolean") {
|
||||||
|
errors.push("autoEnhance must be a boolean");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate enhancementOptions (optional object)
|
||||||
|
if (enhancementOptions !== undefined) {
|
||||||
|
if (
|
||||||
|
typeof enhancementOptions !== "object" ||
|
||||||
|
Array.isArray(enhancementOptions)
|
||||||
|
) {
|
||||||
|
errors.push("enhancementOptions must be an object");
|
||||||
|
} else {
|
||||||
|
const {
|
||||||
|
imageStyle,
|
||||||
|
aspectRatio,
|
||||||
|
mood,
|
||||||
|
lighting,
|
||||||
|
cameraAngle,
|
||||||
|
negativePrompts,
|
||||||
|
} = enhancementOptions;
|
||||||
|
|
||||||
|
if (
|
||||||
|
imageStyle !== undefined &&
|
||||||
|
![
|
||||||
|
"photorealistic",
|
||||||
|
"illustration",
|
||||||
|
"minimalist",
|
||||||
|
"sticker",
|
||||||
|
"product",
|
||||||
|
"comic",
|
||||||
|
].includes(imageStyle)
|
||||||
|
) {
|
||||||
|
errors.push("Invalid imageStyle in enhancementOptions");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
aspectRatio !== undefined &&
|
||||||
|
!["square", "portrait", "landscape", "wide", "ultrawide"].includes(
|
||||||
|
aspectRatio,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
errors.push("Invalid aspectRatio in enhancementOptions");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
mood !== undefined &&
|
||||||
|
(typeof mood !== "string" || mood.length > 100)
|
||||||
|
) {
|
||||||
|
errors.push("mood must be a string with max 100 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
lighting !== undefined &&
|
||||||
|
(typeof lighting !== "string" || lighting.length > 100)
|
||||||
|
) {
|
||||||
|
errors.push("lighting must be a string with max 100 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
cameraAngle !== undefined &&
|
||||||
|
(typeof cameraAngle !== "string" || cameraAngle.length > 100)
|
||||||
|
) {
|
||||||
|
errors.push("cameraAngle must be a string with max 100 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (negativePrompts !== undefined) {
|
||||||
|
if (!Array.isArray(negativePrompts) || negativePrompts.length > 10) {
|
||||||
|
errors.push("negativePrompts must be an array with max 10 items");
|
||||||
|
} else {
|
||||||
|
for (const item of negativePrompts) {
|
||||||
|
if (typeof item !== "string" || item.length > 100) {
|
||||||
|
errors.push(
|
||||||
|
"Each negative prompt must be a string with max 100 characters",
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for XSS attempts in prompt
|
||||||
|
const xssPatterns = [
|
||||||
|
/<script/i,
|
||||||
|
/javascript:/i,
|
||||||
|
/on\w+\s*=/i,
|
||||||
|
/<iframe/i,
|
||||||
|
/<object/i,
|
||||||
|
/<embed/i,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (prompt && xssPatterns.some((pattern) => pattern.test(prompt))) {
|
||||||
|
errors.push("Invalid characters detected in prompt");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log validation results
|
||||||
|
if (errors.length > 0) {
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] Validation failed: ${errors.join(", ")}`,
|
||||||
|
);
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: "Validation failed",
|
||||||
|
message: errors.join(", "),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize filename
|
||||||
|
if (filename) {
|
||||||
|
req.body.filename = sanitizeFilename(filename.trim());
|
||||||
|
if (req.body.filename !== filename.trim()) {
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] Filename sanitized: "${filename}" -> "${req.body.filename}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim and clean prompt
|
||||||
|
if (prompt) {
|
||||||
|
req.body.prompt = prompt.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[${timestamp}] [${req.requestId}] JSON validation passed`);
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log text-to-image request details for debugging
|
||||||
|
*/
|
||||||
|
export const logTextToImageRequest = (
|
||||||
|
req: any,
|
||||||
|
_res: Response,
|
||||||
|
next: NextFunction,
|
||||||
|
): void => {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const { prompt, filename, autoEnhance, enhancementOptions } = req.body;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] === TEXT-TO-IMAGE REQUEST ===`,
|
||||||
|
);
|
||||||
|
console.log(`[${timestamp}] [${req.requestId}] Method: ${req.method}`);
|
||||||
|
console.log(`[${timestamp}] [${req.requestId}] Path: ${req.path}`);
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] Content-Type: ${req.get("Content-Type")}`,
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] Prompt: "${prompt?.substring(0, 100)}${prompt?.length > 100 ? "..." : ""}"`,
|
||||||
|
);
|
||||||
|
console.log(`[${timestamp}] [${req.requestId}] Filename: "${filename}"`);
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] Auto-enhance: ${autoEnhance || false}`,
|
||||||
|
);
|
||||||
|
if (enhancementOptions) {
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] Enhancement options:`,
|
||||||
|
enhancementOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] Reference files: 0 (text-only endpoint)`,
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${req.requestId}] =====================================`,
|
||||||
|
);
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
import { Response, Router } from "express";
|
||||||
|
import type { Router as RouterType } from "express";
|
||||||
|
import { ImageGenService } from "../services/ImageGenService";
|
||||||
|
import {
|
||||||
|
validateTextToImageRequest,
|
||||||
|
logTextToImageRequest,
|
||||||
|
} from "../middleware/jsonValidation";
|
||||||
|
import {
|
||||||
|
autoEnhancePrompt,
|
||||||
|
logEnhancementResult,
|
||||||
|
} from "../middleware/promptEnhancement";
|
||||||
|
import { asyncHandler } from "../middleware/errorHandler";
|
||||||
|
import { GenerateImageResponse } from "../types/api";
|
||||||
|
|
||||||
|
export const textToImageRouter: RouterType = Router();
|
||||||
|
|
||||||
|
let imageGenService: ImageGenService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/text-to-image - Generate image from text prompt only (JSON)
|
||||||
|
*/
|
||||||
|
textToImageRouter.post(
|
||||||
|
"/text-to-image",
|
||||||
|
// JSON validation middleware
|
||||||
|
logTextToImageRequest,
|
||||||
|
validateTextToImageRequest,
|
||||||
|
|
||||||
|
// 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 } = req.body;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${requestId}] Starting text-to-image generation process`,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Generate the image (no reference images for this endpoint)
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${requestId}] Calling ImageGenService.generateImage() (text-only)`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await imageGenService.generateImage({
|
||||||
|
prompt,
|
||||||
|
filename,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log the result
|
||||||
|
console.log(
|
||||||
|
`[${timestamp}] [${requestId}] Text-to-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 text-to-image 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);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
@ -6,6 +6,26 @@ export interface GenerateImageRequest {
|
||||||
filename: string;
|
filename: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TextToImageRequest {
|
||||||
|
prompt: string;
|
||||||
|
filename: string;
|
||||||
|
autoEnhance?: boolean;
|
||||||
|
enhancementOptions?: {
|
||||||
|
imageStyle?:
|
||||||
|
| "photorealistic"
|
||||||
|
| "illustration"
|
||||||
|
| "minimalist"
|
||||||
|
| "sticker"
|
||||||
|
| "product"
|
||||||
|
| "comic";
|
||||||
|
aspectRatio?: "square" | "portrait" | "landscape" | "wide" | "ultrawide";
|
||||||
|
mood?: string;
|
||||||
|
lighting?: string;
|
||||||
|
cameraAngle?: string;
|
||||||
|
negativePrompts?: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface GenerateImageResponse {
|
export interface GenerateImageResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue