Files
ai-florist/src/routes/api/flower-flow/mood-analysis/+server.js
Chaewon Lee 0f102eb289 chore: prepare production deploy with API hardening and Railway adapter
* Harden API routes with rate limits, upload cap, and edit dedupe.

Protect expensive endpoints from abuse, reject oversized mood uploads, dedupe concurrent edit-images calls, and surface Kakao search failures instead of silent mock fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: switch to adapter-node for Railway deploy

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 10:33:57 +09:00

47 lines
1.5 KiB
JavaScript

import { createJob, updateJob } from '$lib/server/flowerFlow/jobStore.js';
import { analyzeImageMood } from '$lib/server/gemini/vision.js';
import { isGeminiConfigured } from '$lib/server/gemini/client.js';
import { RATE_LIMITS } from '$lib/server/rateLimit.js';
import { MAX_MOOD_IMAGE_BYTES, MAX_MOOD_IMAGE_LABEL } from '$lib/server/uploadLimits.js';
import { enforceRateLimit, json, readUserInput, toErrorResponse } from '$lib/server/http.js';
/** @type {import('./$types').RequestHandler} */
export async function POST({ request, getClientAddress }) {
try {
const limited = enforceRateLimit(getClientAddress(), RATE_LIMITS.moodAnalysis, 'mood-analysis');
if (limited) return limited;
const formData = await request.formData();
const image = formData.get('image');
if (!(image instanceof File)) {
return json({ error: 'image file is required', code: 'bad_request' }, 400);
}
if (image.size > MAX_MOOD_IMAGE_BYTES) {
return json(
{
error: `Image must be ${MAX_MOOD_IMAGE_LABEL} or smaller.`,
code: 'bad_request'
},
400
);
}
const userInput = readUserInput(formData);
const job = await createJob(userInput);
const imageBytes = new Uint8Array(await image.arrayBuffer());
const moodAnalysis = await analyzeImageMood(imageBytes, image.type || 'image/jpeg', userInput);
await updateJob(job.id, { moodAnalysis });
return json({
jobId: job.id,
moodAnalysis,
mock: !isGeminiConfigured()
});
} catch (error) {
return toErrorResponse(error);
}
}