diff --git a/src/db-migrate.js b/src/db-migrate.js index a811e3d..d152cb2 100755 --- a/src/db-migrate.js +++ b/src/db-migrate.js @@ -999,60 +999,97 @@ async function migratePlaceholders() { async function migratePromptStyles() { await query(` CREATE TABLE IF NOT EXISTS prompt_styles ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - type VARCHAR(20) NOT NULL CHECK (type IN ('fix', 'atmosphere', 'setting')), - themenfeld_id UUID, - text_en TEXT NOT NULL + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + type VARCHAR(20) NOT NULL CHECK (type IN ('fix', 'atmosphere', 'setting')), + kategorie_id UUID, + text_en TEXT NOT NULL ) `); - // Seed-Daten aus prompt_styles.csv (idempotent per id) + // Umbenennung themenfeld_id → kategorie_id (idempotent) + await query(`ALTER TABLE prompt_styles RENAME COLUMN themenfeld_id TO kategorie_id`).catch(() => {}); + + // FK auf categories nachrüsten (idempotent) + await query(` + ALTER TABLE prompt_styles + ADD CONSTRAINT prompt_styles_kategorie_fk + FOREIGN KEY (kategorie_id) REFERENCES categories(id) ON DELETE SET NULL + `).catch(() => {}); + + // Seed-Daten aus prompt_styles.csv (idempotent per id, kategorie_id zunächst null) const seeds = [ - { id: 'b0f5c2a4-a95d-426f-a01c-0edc53e719b8', type: 'fix', themenfeld_id: null, text_en: 'hyperrealistic photography, natural unposed moment, shot on Canon EOS R5, ambient natural light, no color grading, razor sharp details, photorealistic textures, each object clearly visible and spatially separated, 8k' }, - { id: '62015070-1fbe-40b8-b293-8c39ae5994c3', type: 'atmosphere', themenfeld_id: null, text_en: 'misty autumn morning, golden hour light breaking through cool gray clouds, frost on the ground, dew on surfaces' }, - { id: 'd644f215-25b9-49be-87ea-629d7d8acb78', type: 'atmosphere', themenfeld_id: null, text_en: 'bright summer midday, harsh direct sunlight, vivid colors, dry warm air' }, - { id: 'da0a5339-37f5-47be-ba63-1fbf6c1e9f90', type: 'atmosphere', themenfeld_id: null, text_en: 'overcast spring day, soft diffused light, fresh green tones, slightly cool atmosphere' }, - { id: '11a8edb4-90a3-48a8-8407-31056644b55a', type: 'atmosphere', themenfeld_id: null, text_en: 'golden winter afternoon, low sun casting long shadows, bare trees, cold crisp air' }, - { id: '97bad727-6555-4f48-9a68-17dd5ce85535', type: 'atmosphere', themenfeld_id: null, text_en: 'early morning blue hour, soft cool light, calm and quiet atmosphere, slight fog' }, - { id: '6de167ef-5a87-4333-9325-cc31ccd9db05', type: 'atmosphere', themenfeld_id: null, text_en: 'warm summer evening, golden orange glow, long shadows, relaxed atmosphere' }, - { id: '082cc098-4c26-4d9a-b3a1-209dd9e507ea', type: 'setting', themenfeld_id: '32114354-08f2-4218-920b-ddac63ad6de7', text_en: 'open green meadow with wooden fence, rolling hills in soft background, natural habitat' }, - { id: 'f0ef007a-c763-4c40-99c0-1bd17901739e', type: 'setting', themenfeld_id: '32114354-08f2-4218-920b-ddac63ad6de7', text_en: 'dense forest edge with dappled light, mossy ground, wild and untouched environment' }, - { id: 'b809f859-2592-4207-8111-7da05e7057c9', type: 'setting', themenfeld_id: '32114354-08f2-4218-920b-ddac63ad6de7', text_en: 'cozy living room corner, warm home environment, soft natural light from window' }, - { id: '28dac228-c335-46d2-9b40-481dc9e2b373', type: 'setting', themenfeld_id: '32114354-08f2-4218-920b-ddac63ad6de7', text_en: 'shallow clear river bank, rocky ground, water reflections, natural wetland' }, - { id: '89cfbdf7-7fbc-439a-9265-73f18124e372', type: 'setting', themenfeld_id: '733bdeee-37a7-4e26-9a74-5e461e7c604d', text_en: 'rustic wooden kitchen counter, natural light from nearby window, linen cloth underneath' }, - { id: 'e7faf2ec-78e1-43bc-b870-c363f7ec2032', type: 'setting', themenfeld_id: '733bdeee-37a7-4e26-9a74-5e461e7c604d', text_en: 'outdoor farmers market stall, weathered wooden crates, morning light, earthy atmosphere' }, - { id: '45dc2aee-d223-4952-943d-cdbe86b7e8c3', type: 'setting', themenfeld_id: '733bdeee-37a7-4e26-9a74-5e461e7c604d', text_en: 'garden harvest scene, soil and greenery visible, freshly picked produce on ground' }, - { id: '5589aa12-ee74-4041-9443-40e9cfa538fd', type: 'setting', themenfeld_id: '733bdeee-37a7-4e26-9a74-5e461e7c604d', text_en: 'simple white kitchen table, clean minimal background, soft indoor daylight' }, - { id: '738365f1-b000-4dde-8e99-9b90f6984b79', type: 'setting', themenfeld_id: '294a30e8-a284-4a4e-a257-c4f11d71ce80', text_en: 'neutral light studio setting, clean background, soft natural sidelight, medical clarity' }, - { id: '98f1c118-b333-43ba-9167-870af883b5ae', type: 'setting', themenfeld_id: '294a30e8-a284-4a4e-a257-c4f11d71ce80', text_en: 'warm bathroom environment, mirror and soft light, everyday personal care setting' }, - { id: '2b81a5c9-7328-41e9-b08e-0d98d9a5c78f', type: 'setting', themenfeld_id: '362e5bfe-4394-4b37-8e3c-5b186bd80384', text_en: 'flat lay on light wooden surface, natural window light, clean and minimal styling' }, - { id: '2a3a4eed-ba32-4b21-8dad-1cf5679b00fb', type: 'setting', themenfeld_id: '362e5bfe-4394-4b37-8e3c-5b186bd80384', text_en: 'cozy bedroom setting, clothes laid out on bed, soft morning light' }, - { id: 'c816e95e-5edc-4ae9-8c0d-9c71a5a4dfb6', type: 'setting', themenfeld_id: '362e5bfe-4394-4b37-8e3c-5b186bd80384', text_en: 'outdoor market rack, hangers visible, casual everyday atmosphere' }, - { id: '33af0241-c19d-4429-91b5-0359c1f973e4', type: 'setting', themenfeld_id: '6f64102f-8405-4438-861a-fdb438a902b7', text_en: 'warm living room, family home atmosphere, soft afternoon light through curtains' }, - { id: '153e70c4-f011-42af-ba0f-8ab82bf920ab', type: 'setting', themenfeld_id: '6f64102f-8405-4438-861a-fdb438a902b7', text_en: 'outdoor garden or backyard, relaxed family setting, natural daylight' }, - { id: '9fe7fc4a-6578-4ee0-8a8e-a885e89e58c1', type: 'setting', themenfeld_id: '7c08ea8d-06c7-4b24-99f7-d282044a62ad', text_en: 'bright kitchen countertop, clean and organized, natural window light' }, - { id: '46dab63b-7b3d-45e7-9ea9-4a4a67e9fabd', type: 'setting', themenfeld_id: '7c08ea8d-06c7-4b24-99f7-d282044a62ad', text_en: 'utility room or bathroom shelf, everyday cleaning supplies visible, practical setting' }, - { id: '28246e90-4ac8-444f-be23-de401365d38d', type: 'setting', themenfeld_id: '3a781e6a-1900-453a-99b0-ddb15962200d', text_en: 'cozy Scandinavian living room, warm tones, natural materials, soft indirect light' }, - { id: '5143c10f-d717-4698-88f5-f1598d0eeef9', type: 'setting', themenfeld_id: '3a781e6a-1900-453a-99b0-ddb15962200d', text_en: 'bright airy bedroom, white walls, minimal furniture, morning sunlight' }, - { id: 'd23d7050-dc22-4226-8a5b-79e75f11de8b', type: 'setting', themenfeld_id: '88b0eb99-88b7-4edd-9d67-496381e5adc7', text_en: 'open countryside landscape, wide sky, natural untouched terrain, peaceful atmosphere' }, - { id: '34c6a784-7a32-4f84-a06d-f546c9c9fbea', type: 'setting', themenfeld_id: '88b0eb99-88b7-4edd-9d67-496381e5adc7', text_en: 'forest floor close-up, mossy rocks, fallen leaves, soft filtered light through canopy' }, - { id: '1fc61dd9-57c6-4eba-8328-37cbf5fc135e', type: 'setting', themenfeld_id: '88b0eb99-88b7-4edd-9d67-496381e5adc7', text_en: 'garden bed with rich dark soil, plants at various growth stages, earthy tones' }, - { id: '3244f090-f2a2-4806-875a-88038598fc5e', type: 'setting', themenfeld_id: '606606e2-5362-4e43-9657-3ad315e323af', text_en: 'quiet suburban street, cobblestone or asphalt road, parked vehicles, everyday scene' }, - { id: '36d80c19-13ea-4672-b2e9-8ceedb4ab178', type: 'setting', themenfeld_id: '606606e2-5362-4e43-9657-3ad315e323af', text_en: 'rural road with open fields, minimal traffic, wide sky, natural light' }, - { id: '98957b0a-f415-4282-9b3d-863a9bf03a77', type: 'setting', themenfeld_id: '5d9e7af6-7866-422e-ac62-b918cfcb2e1a', text_en: 'busy European city street, historic buildings in background, natural daylight' }, - { id: '66fa361a-e062-4adc-9c9a-3e01ac8dbbe0', type: 'setting', themenfeld_id: '5d9e7af6-7866-422e-ac62-b918cfcb2e1a', text_en: 'quiet town square, fountain or bench visible, calm everyday atmosphere' }, - { id: '2dba4303-c743-419f-a7e8-06b6d54ba91d', type: 'setting', themenfeld_id: '852f44a4-7930-459e-9a8d-4a44cd895285', text_en: 'clean modern workspace, desk surface, natural sidelight, organized tools' }, - { id: 'a78df43b-8897-40dd-9ccf-de29ff9bf5da', type: 'setting', themenfeld_id: '852f44a4-7930-459e-9a8d-4a44cd895285', text_en: 'garage or workshop setting, workbench with tools, practical everyday environment' }, - { id: '949774d1-0678-4683-9b8e-e5568f648ba8', type: 'setting', themenfeld_id: '14cad3c8-8194-4a27-8d48-5107c5bd34e4', text_en: 'outdoor park or sports field, open space, natural daylight, active atmosphere' }, - { id: '9b35a717-03dd-41aa-a60e-90dff8bc5aaf', type: 'setting', themenfeld_id: '14cad3c8-8194-4a27-8d48-5107c5bd34e4', text_en: 'cozy indoor hobby room, soft warm light, creative materials visible' }, + { id: 'b0f5c2a4-a95d-426f-a01c-0edc53e719b8', type: 'fix', text_en: 'hyperrealistic photography, natural unposed moment, shot on Canon EOS R5, ambient natural light, no color grading, razor sharp details, photorealistic textures, each object clearly visible and spatially separated, 8k' }, + { id: '62015070-1fbe-40b8-b293-8c39ae5994c3', type: 'atmosphere', text_en: 'misty autumn morning, golden hour light breaking through cool gray clouds, frost on the ground, dew on surfaces' }, + { id: 'd644f215-25b9-49be-87ea-629d7d8acb78', type: 'atmosphere', text_en: 'bright summer midday, harsh direct sunlight, vivid colors, dry warm air' }, + { id: 'da0a5339-37f5-47be-ba63-1fbf6c1e9f90', type: 'atmosphere', text_en: 'overcast spring day, soft diffused light, fresh green tones, slightly cool atmosphere' }, + { id: '11a8edb4-90a3-48a8-8407-31056644b55a', type: 'atmosphere', text_en: 'golden winter afternoon, low sun casting long shadows, bare trees, cold crisp air' }, + { id: '97bad727-6555-4f48-9a68-17dd5ce85535', type: 'atmosphere', text_en: 'early morning blue hour, soft cool light, calm and quiet atmosphere, slight fog' }, + { id: '6de167ef-5a87-4333-9325-cc31ccd9db05', type: 'atmosphere', text_en: 'warm summer evening, golden orange glow, long shadows, relaxed atmosphere' }, + { id: '082cc098-4c26-4d9a-b3a1-209dd9e507ea', type: 'setting', text_en: 'open green meadow with wooden fence, rolling hills in soft background, natural habitat' }, + { id: 'f0ef007a-c763-4c40-99c0-1bd17901739e', type: 'setting', text_en: 'dense forest edge with dappled light, mossy ground, wild and untouched environment' }, + { id: 'b809f859-2592-4207-8111-7da05e7057c9', type: 'setting', text_en: 'cozy living room corner, warm home environment, soft natural light from window' }, + { id: '28dac228-c335-46d2-9b40-481dc9e2b373', type: 'setting', text_en: 'shallow clear river bank, rocky ground, water reflections, natural wetland' }, + { id: '89cfbdf7-7fbc-439a-9265-73f18124e372', type: 'setting', text_en: 'rustic wooden kitchen counter, natural light from nearby window, linen cloth underneath' }, + { id: 'e7faf2ec-78e1-43bc-b870-c363f7ec2032', type: 'setting', text_en: 'outdoor farmers market stall, weathered wooden crates, morning light, earthy atmosphere' }, + { id: '45dc2aee-d223-4952-943d-cdbe86b7e8c3', type: 'setting', text_en: 'garden harvest scene, soil and greenery visible, freshly picked produce on ground' }, + { id: '5589aa12-ee74-4041-9443-40e9cfa538fd', type: 'setting', text_en: 'simple white kitchen table, clean minimal background, soft indoor daylight' }, + { id: '738365f1-b000-4dde-8e99-9b90f6984b79', type: 'setting', text_en: 'neutral light studio setting, clean background, soft natural sidelight, medical clarity' }, + { id: '98f1c118-b333-43ba-9167-870af883b5ae', type: 'setting', text_en: 'warm bathroom environment, mirror and soft light, everyday personal care setting' }, + { id: '2b81a5c9-7328-41e9-b08e-0d98d9a5c78f', type: 'setting', text_en: 'flat lay on light wooden surface, natural window light, clean and minimal styling' }, + { id: '2a3a4eed-ba32-4b21-8dad-1cf5679b00fb', type: 'setting', text_en: 'cozy bedroom setting, clothes laid out on bed, soft morning light' }, + { id: 'c816e95e-5edc-4ae9-8c0d-9c71a5a4dfb6', type: 'setting', text_en: 'outdoor market rack, hangers visible, casual everyday atmosphere' }, + { id: '33af0241-c19d-4429-91b5-0359c1f973e4', type: 'setting', text_en: 'warm living room, family home atmosphere, soft afternoon light through curtains' }, + { id: '153e70c4-f011-42af-ba0f-8ab82bf920ab', type: 'setting', text_en: 'outdoor garden or backyard, relaxed family setting, natural daylight' }, + { id: '9fe7fc4a-6578-4ee0-8a8e-a885e89e58c1', type: 'setting', text_en: 'bright kitchen countertop, clean and organized, natural window light' }, + { id: '46dab63b-7b3d-45e7-9ea9-4a4a67e9fabd', type: 'setting', text_en: 'utility room or bathroom shelf, everyday cleaning supplies visible, practical setting' }, + { id: '28246e90-4ac8-444f-be23-de401365d38d', type: 'setting', text_en: 'cozy Scandinavian living room, warm tones, natural materials, soft indirect light' }, + { id: '5143c10f-d717-4698-88f5-f1598d0eeef9', type: 'setting', text_en: 'bright airy bedroom, white walls, minimal furniture, morning sunlight' }, + { id: 'd23d7050-dc22-4226-8a5b-79e75f11de8b', type: 'setting', text_en: 'open countryside landscape, wide sky, natural untouched terrain, peaceful atmosphere' }, + { id: '34c6a784-7a32-4f84-a06d-f546c9c9fbea', type: 'setting', text_en: 'forest floor close-up, mossy rocks, fallen leaves, soft filtered light through canopy' }, + { id: '1fc61dd9-57c6-4eba-8328-37cbf5fc135e', type: 'setting', text_en: 'garden bed with rich dark soil, plants at various growth stages, earthy tones' }, + { id: '3244f090-f2a2-4806-875a-88038598fc5e', type: 'setting', text_en: 'quiet suburban street, cobblestone or asphalt road, parked vehicles, everyday scene' }, + { id: '36d80c19-13ea-4672-b2e9-8ceedb4ab178', type: 'setting', text_en: 'rural road with open fields, minimal traffic, wide sky, natural light' }, + { id: '98957b0a-f415-4282-9b3d-863a9bf03a77', type: 'setting', text_en: 'busy European city street, historic buildings in background, natural daylight' }, + { id: '66fa361a-e062-4adc-9c9a-3e01ac8dbbe0', type: 'setting', text_en: 'quiet town square, fountain or bench visible, calm everyday atmosphere' }, + { id: '2dba4303-c743-419f-a7e8-06b6d54ba91d', type: 'setting', text_en: 'clean modern workspace, desk surface, natural sidelight, organized tools' }, + { id: 'a78df43b-8897-40dd-9ccf-de29ff9bf5da', type: 'setting', text_en: 'garage or workshop setting, workbench with tools, practical everyday environment' }, + { id: '949774d1-0678-4683-9b8e-e5568f648ba8', type: 'setting', text_en: 'outdoor park or sports field, open space, natural daylight, active atmosphere' }, + { id: '9b35a717-03dd-41aa-a60e-90dff8bc5aaf', type: 'setting', text_en: 'cozy indoor hobby room, soft warm light, creative materials visible' }, ]; for (const s of seeds) { await query( - `INSERT INTO prompt_styles (id, type, themenfeld_id, text_en) - SELECT $1, $2, $3, $4 + `INSERT INTO prompt_styles (id, type, text_en) + SELECT $1, $2, $3 WHERE NOT EXISTS (SELECT 1 FROM prompt_styles WHERE id = $1)`, - [s.id, s.type, s.themenfeld_id, s.text_en] + [s.id, s.type, s.text_en] + ).catch(() => {}); + } + + // kategorie_id per Kategoriename befüllen (idempotent, unabhängig von Category-UUIDs) + const THEME_MAP = [ + { en: 'Animals', ids: ['082cc098-4c26-4d9a-b3a1-209dd9e507ea', 'f0ef007a-c763-4c40-99c0-1bd17901739e', 'b809f859-2592-4207-8111-7da05e7057c9', '28dac228-c335-46d2-9b40-481dc9e2b373'] }, + { en: 'Food', ids: ['89cfbdf7-7fbc-439a-9265-73f18124e372', 'e7faf2ec-78e1-43bc-b870-c363f7ec2032', '45dc2aee-d223-4952-943d-cdbe86b7e8c3', '5589aa12-ee74-4041-9443-40e9cfa538fd'] }, + { en: 'Body', ids: ['738365f1-b000-4dde-8e99-9b90f6984b79', '98f1c118-b333-43ba-9167-870af883b5ae'] }, + { en: 'Clothing', ids: ['2b81a5c9-7328-41e9-b08e-0d98d9a5c78f', '2a3a4eed-ba32-4b21-8dad-1cf5679b00fb', 'c816e95e-5edc-4ae9-8c0d-9c71a5a4dfb6'] }, + { en: 'Family & People', ids: ['33af0241-c19d-4429-91b5-0359c1f973e4', '153e70c4-f011-42af-ba0f-8ab82bf920ab'] }, + { en: 'Household', ids: ['9fe7fc4a-6578-4ee0-8a8e-a885e89e58c1', '46dab63b-7b3d-45e7-9ea9-4a4a67e9fabd'] }, + { en: 'Home & Furniture', ids: ['28246e90-4ac8-444f-be23-de401365d38d', '5143c10f-d717-4698-88f5-f1598d0eeef9'] }, + { en: 'Nature & Plants', ids: ['d23d7050-dc22-4226-8a5b-79e75f11de8b', '34c6a784-7a32-4f84-a06d-f546c9c9fbea', '1fc61dd9-57c6-4eba-8328-37cbf5fc135e'] }, + { en: 'Transport & Travel',ids: ['3244f090-f2a2-4806-875a-88038598fc5e', '36d80c19-13ea-4672-b2e9-8ceedb4ab178'] }, + { en: 'City & Buildings', ids: ['98957b0a-f415-4282-9b3d-863a9bf03a77', '66fa361a-e062-4adc-9c9a-3e01ac8dbbe0'] }, + { en: 'Tools', ids: ['2dba4303-c743-419f-a7e8-06b6d54ba91d', 'a78df43b-8897-40dd-9ccf-de29ff9bf5da'] }, + { en: 'Sports & Leisure', ids: ['949774d1-0678-4683-9b8e-e5568f648ba8', '9b35a717-03dd-41aa-a60e-90dff8bc5aaf'] }, + ]; + + for (const { en, ids } of THEME_MAP) { + await query( + `UPDATE prompt_styles + SET kategorie_id = (SELECT id FROM categories WHERE lower(titel_en) = lower($1) LIMIT 1) + WHERE id = ANY($2::uuid[]) + AND kategorie_id IS DISTINCT FROM + (SELECT id FROM categories WHERE lower(titel_en) = lower($1) LIMIT 1)`, + [en, ids] ).catch(() => {}); } } diff --git a/src/routes/prompt-styles.js b/src/routes/prompt-styles.js index 3df75e4..36b3bd1 100644 --- a/src/routes/prompt-styles.js +++ b/src/routes/prompt-styles.js @@ -31,14 +31,14 @@ router.get('/:id', async (req, res, next) => { // POST /api/prompt-styles router.post('/', async (req, res, next) => { try { - const { type, themenfeld_id, text_en } = req.body; + const { type, kategorie_id, text_en } = req.body; if (!type || !TYPES.includes(type)) return res.status(400).json({ error: `type must be one of: ${TYPES.join(', ')}` }); if (!text_en) return res.status(400).json({ error: 'text_en is required' }); const result = await query( - `INSERT INTO prompt_styles (type, themenfeld_id, text_en) VALUES ($1, $2, $3) RETURNING *`, - [type, themenfeld_id || null, text_en] + `INSERT INTO prompt_styles (type, kategorie_id, text_en) VALUES ($1, $2, $3) RETURNING *`, + [type, kategorie_id || null, text_en] ); res.status(201).json(result.rows[0]); } catch (err) { next(err); } @@ -47,7 +47,7 @@ router.post('/', async (req, res, next) => { // PATCH /api/prompt-styles/:id router.patch('/:id', async (req, res, next) => { try { - const allowed = ['type', 'themenfeld_id', 'text_en']; + const allowed = ['type', 'kategorie_id', 'text_en']; const fields = Object.keys(req.body).filter(k => allowed.includes(k)); if (!fields.length) return res.status(400).json({ error: 'No valid fields provided' }); if (req.body.type && !TYPES.includes(req.body.type))