import type { APIRoute } from 'astro'; import { one, query } from '../../../lib/db'; import { enqueue } from '../../../lib/queue'; export const prerender = false; const json = (b: unknown, s = 200) => new Response(JSON.stringify(b), { status: s, headers: { 'Content-Type': 'application/json' } }); export const GET: APIRoute = async ({ locals }) => { if (!locals.user) return new Response('Unauthorized', { status: 401 }); const rows = await query( `SELECT j.*, u.display_name AS by_name FROM jobs j LEFT JOIN users u ON u.id=j.created_by ORDER BY j.created_at DESC LIMIT 50`); return json({ jobs: rows }); }; // Body: { recipeId?, recipe?, sources:[{source_path, filename, source_quality?}], // mode?: 'each'|'compose'|'generate', prompt_text?, origin? } export const POST: APIRoute = async ({ request, locals }) => { if (!locals.user) return new Response('Unauthorized', { status: 401 }); const b = await request.json(); const mode: 'each' | 'compose' | 'generate' = ['each', 'compose', 'generate'].includes(b.mode) ? b.mode : 'each'; const sources: any[] = b.sources || []; const promptText: string = (b.prompt_text || '').trim(); if (mode === 'each' && !sources.length) return json({ error: 'Keine Bilder.' }, 400); if (mode === 'compose' && sources.length < 2) return json({ error: 'Zum Kombinieren mindestens 2 Bilder.' }, 400); if (mode === 'compose' && !promptText) return json({ error: 'Bitte beschreiben, was entstehen soll.' }, 400); if (mode === 'generate' && !promptText) return json({ error: 'Bitte einen Text eingeben.' }, 400); let snapshot: any = b.recipe; if (b.recipeId) { const r = await one('SELECT * FROM recipes WHERE id=$1', [b.recipeId]); if (!r) return json({ error: 'Rezept nicht gefunden.' }, 404); snapshot = r; } snapshot = snapshot || {}; // Rezept-Snapshot einfrieren const snap = { tasks: snapshot.tasks || (mode === 'each' ? [] : []), output_format: snapshot.output_format, orientation: snapshot.orientation, crop_mode: snapshot.crop_mode || 'crop', dpi: snapshot.dpi || 300, contour_mm: snapshot.contour_mm ?? null, model_key: snapshot.model_key ?? null, custom_instruction: snapshot.custom_instruction ?? null, prompt_text: promptText || null, delivery: snapshot.delivery || 'library', picdrop_gallery: snapshot.picdrop_gallery ?? null, }; const total = (mode === 'each') ? sources.length : 1; const job = await one<{ id: string }>( `INSERT INTO jobs (created_by, origin, mode, recipe_snapshot, status, total) VALUES ($1,$2,$3,$4,'queued',$5) RETURNING id`, [locals.user.uid, b.origin || 'web', mode, JSON.stringify(snap), total]); if (mode === 'each') { for (let i = 0; i < sources.length; i++) { const s = sources[i]; const item = await one<{ id: string }>( `INSERT INTO items (job_id, position, status, source_path, filename, source_quality) VALUES ($1,$2,'queued',$3,$4,$5) RETURNING id`, [job!.id, i, s.source_path, s.filename || null, s.source_quality || 'original']); await enqueue({ itemId: item!.id, jobId: job!.id }); } } else { const srcPaths = sources.map((s) => s.source_path).filter(Boolean); const base = mode === 'generate' ? 'neu' : 'kombiniert'; const item = await one<{ id: string }>( `INSERT INTO items (job_id, position, status, source_paths, filename, source_quality) VALUES ($1,0,'queued',$2,$3,'original') RETURNING id`, [job!.id, srcPaths.length ? JSON.stringify(srcPaths) : null, base]); await enqueue({ itemId: item!.id, jobId: job!.id }); } return json({ jobId: job!.id }); };