diff --git a/migrations/006_variants.sql b/migrations/006_variants.sql new file mode 100644 index 0000000..8d44013 --- /dev/null +++ b/migrations/006_variants.sql @@ -0,0 +1,4 @@ +-- Alternativen/Regenerierung: mehrere Ergebnisse zum selben Ursprung gruppieren. +-- variant_of zeigt auf das „Wurzel"-Item (die erste Fassung); Alternativen teilen es. +ALTER TABLE items ADD COLUMN IF NOT EXISTS variant_of uuid REFERENCES items(id) ON DELETE SET NULL; +CREATE INDEX IF NOT EXISTS items_variant_idx ON items(variant_of); diff --git a/src/pages/api/items/[id]/alternative.ts b/src/pages/api/items/[id]/alternative.ts new file mode 100644 index 0000000..b7c90f2 --- /dev/null +++ b/src/pages/api/items/[id]/alternative.ts @@ -0,0 +1,29 @@ +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' } }); + +/** Erzeugt eine weitere Fassung (Alternative) zum selben Ursprung, gleiches Rezept. */ +export const POST: APIRoute = async ({ params, locals }) => { + if (!locals.user) return new Response('Unauthorized', { status: 401 }); + const item = await one( + `SELECT id, job_id, source_path, source_paths, filename, variant_of FROM items WHERE id=$1`, [params.id]); + if (!item) return json({ error: 'Nicht gefunden.' }, 404); + // Ohne Quelle & ohne Modellaufruf gibt es nichts zu variieren. + const root = item.variant_of || item.id; + + const it = await one<{ id: string }>( + `INSERT INTO items (job_id, position, status, source_path, source_paths, filename, source_quality, variant_of) + VALUES ($1, (SELECT COALESCE(max(position),0)+1 FROM items WHERE job_id=$1), 'queued', $2, $3, $4, 'original', $5) + RETURNING id`, + [item.job_id, item.source_path, item.source_paths ? JSON.stringify(item.source_paths) : null, + item.filename, root]); + // Auftrag zählt eine Position mehr und läuft erneut an. + await query(`UPDATE jobs SET total=total+1, status='queued', finished_at=NULL + WHERE id=$1 AND status IN ('done','paused')`, [item.job_id]); + await enqueue({ itemId: it!.id, jobId: item.job_id }); + return json({ ok: true, itemId: it!.id }); +}; diff --git a/src/pages/api/items/index.ts b/src/pages/api/items/index.ts index 3bcb732..2c51e63 100644 --- a/src/pages/api/items/index.ts +++ b/src/pages/api/items/index.ts @@ -23,13 +23,13 @@ export const GET: APIRoute = async ({ url, locals }) => { } const rows = await query( - `SELECT i.id, i.filename, i.output_px, i.has_alpha, i.result_path, i.folder_id, - i.delivery_status, i.created_at, i.model_used, u.display_name AS by_name, + `SELECT i.id, i.filename, i.output_px, i.has_alpha, i.result_path, i.thumb_path, i.folder_id, + i.delivery_status, i.created_at, i.model_used, i.variant_of, j.mode, u.display_name AS by_name, j.recipe_snapshot->'tasks' AS tasks FROM items i JOIN jobs j ON j.id = i.job_id LEFT JOIN users u ON u.id = j.created_by WHERE ${where.join(' AND ')} - ORDER BY i.created_at DESC LIMIT 300`, args); + ORDER BY i.created_at DESC LIMIT 400`, args); return json({ items: rows }); };