diff --git a/src/index.js b/src/index.js old mode 100644 new mode 100755 index b66d902..dec4c2c --- a/src/index.js +++ b/src/index.js @@ -45,6 +45,8 @@ app.use('/api/tts-settings', auth, require('./routes/tts-settings')); app.use('/api/claude', auth, require('./routes/claude')); app.use('/api/pipeline', auth, require('./routes/pipeline')); app.use('/api/word-generative', auth, require('./routes/wordGenerative')); +app.use('/api/prompt-styles', auth, require('./routes/prompt-styles')); +app.use('/api/picture-jobs', auth, require('./routes/picture-jobs')); // 404 app.use((req, res) => { diff --git a/src/routes/picture-jobs.js b/src/routes/picture-jobs.js new file mode 100644 index 0000000..de763c1 --- /dev/null +++ b/src/routes/picture-jobs.js @@ -0,0 +1,130 @@ +const router = require('express').Router(); +const { query } = require('../db'); + +const STATUSES = ['pending', 'generating', 'done', 'failed']; + +// GET /api/picture-jobs +router.get('/', async (req, res, next) => { + try { + const { status, limit = 50, offset = 0 } = req.query; + const params = [Math.min(parseInt(limit), 500), parseInt(offset)]; + const conditions = []; + if (status) { conditions.push(`pj.status = $${params.length + 1}`); params.push(status); } + const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : ''; + const result = await query( + `SELECT pj.*, + COALESCE(json_agg(DISTINCT pjw.word_id) FILTER (WHERE pjw.word_id IS NOT NULL), '[]') AS word_ids + FROM picture_jobs pj + LEFT JOIN picture_job_words pjw ON pjw.picture_job_id = pj.id + ${where} + GROUP BY pj.id + ORDER BY pj.created_at DESC + LIMIT $1 OFFSET $2`, + params + ); + res.json(result.rows); + } catch (err) { next(err); } +}); + +// GET /api/picture-jobs/:id +router.get('/:id', async (req, res, next) => { + try { + const result = await query( + `SELECT pj.*, + COALESCE(json_agg(DISTINCT pjw.word_id) FILTER (WHERE pjw.word_id IS NOT NULL), '[]') AS word_ids + FROM picture_jobs pj + LEFT JOIN picture_job_words pjw ON pjw.picture_job_id = pj.id + WHERE pj.id = $1 + GROUP BY pj.id`, + [req.params.id] + ); + if (!result.rows.length) return res.status(404).json({ error: 'Not found' }); + res.json(result.rows[0]); + } catch (err) { next(err); } +}); + +// GET /api/picture-jobs/:id/words +router.get('/:id/words', async (req, res, next) => { + try { + const result = await query( + `SELECT w.* FROM words w + JOIN picture_job_words pjw ON pjw.word_id = w.id + WHERE pjw.picture_job_id = $1`, + [req.params.id] + ); + res.json(result.rows); + } catch (err) { next(err); } +}); + +// POST /api/picture-jobs +router.post('/', async (req, res, next) => { + try { + const { kategorie_id, prompt_fix, prompt_atmosphere, prompt_setting, prompt_final, word_ids } = req.body; + const result = await query( + `INSERT INTO picture_jobs (kategorie_id, prompt_fix, prompt_atmosphere, prompt_setting, prompt_final) + VALUES ($1, $2, $3, $4, $5) RETURNING *`, + [kategorie_id || null, prompt_fix || null, prompt_atmosphere || null, prompt_setting || null, prompt_final || null] + ); + const job = result.rows[0]; + if (Array.isArray(word_ids) && word_ids.length) { + for (const wid of word_ids) { + await query( + `INSERT INTO picture_job_words (picture_job_id, word_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`, + [job.id, wid] + ).catch(() => {}); + } + } + res.status(201).json({ ...job, word_ids: word_ids || [] }); + } catch (err) { next(err); } +}); + +// PATCH /api/picture-jobs/:id +router.patch('/:id', async (req, res, next) => { + try { + const allowed = ['kategorie_id', 'prompt_fix', 'prompt_atmosphere', 'prompt_setting', 'prompt_final', 'status', 'picture_id']; + 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.status && !STATUSES.includes(req.body.status)) + return res.status(400).json({ error: `status must be one of: ${STATUSES.join(', ')}` }); + const setClauses = fields.map((f, i) => `${f} = $${i + 1}`).join(', '); + const result = await query( + `UPDATE picture_jobs SET ${setClauses} WHERE id = $${fields.length + 1} RETURNING *`, + [...fields.map(f => req.body[f]), req.params.id] + ); + if (!result.rows.length) return res.status(404).json({ error: 'Not found' }); + res.json(result.rows[0]); + } catch (err) { next(err); } +}); + +// PUT /api/picture-jobs/:id/words/:wordId +router.put('/:id/words/:wordId', async (req, res, next) => { + try { + await query( + `INSERT INTO picture_job_words (picture_job_id, word_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`, + [req.params.id, req.params.wordId] + ); + res.status(204).end(); + } catch (err) { next(err); } +}); + +// DELETE /api/picture-jobs/:id/words/:wordId +router.delete('/:id/words/:wordId', async (req, res, next) => { + try { + await query( + `DELETE FROM picture_job_words WHERE picture_job_id = $1 AND word_id = $2`, + [req.params.id, req.params.wordId] + ); + res.status(204).end(); + } catch (err) { next(err); } +}); + +// DELETE /api/picture-jobs/:id +router.delete('/:id', async (req, res, next) => { + try { + const result = await query('DELETE FROM picture_jobs WHERE id = $1 RETURNING id', [req.params.id]); + if (!result.rows.length) return res.status(404).json({ error: 'Not found' }); + res.status(204).end(); + } catch (err) { next(err); } +}); + +module.exports = router; diff --git a/src/routes/prompt-styles.js b/src/routes/prompt-styles.js new file mode 100644 index 0000000..3df75e4 --- /dev/null +++ b/src/routes/prompt-styles.js @@ -0,0 +1,74 @@ +const router = require('express').Router(); +const { query } = require('../db'); + +const TYPES = ['fix', 'atmosphere', 'setting']; + +// GET /api/prompt-styles +router.get('/', async (req, res, next) => { + try { + const { type, limit = 100, offset = 0 } = req.query; + const params = [Math.min(parseInt(limit), 500), parseInt(offset)]; + const conditions = []; + if (type) { conditions.push(`type = $${params.length + 1}`); params.push(type); } + const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : ''; + const result = await query( + `SELECT * FROM prompt_styles ${where} ORDER BY type, id LIMIT $1 OFFSET $2`, + params + ); + res.json(result.rows); + } catch (err) { next(err); } +}); + +// GET /api/prompt-styles/:id +router.get('/:id', async (req, res, next) => { + try { + const result = await query('SELECT * FROM prompt_styles WHERE id = $1', [req.params.id]); + if (!result.rows.length) return res.status(404).json({ error: 'Not found' }); + res.json(result.rows[0]); + } catch (err) { next(err); } +}); + +// POST /api/prompt-styles +router.post('/', async (req, res, next) => { + try { + const { type, themenfeld_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] + ); + res.status(201).json(result.rows[0]); + } catch (err) { next(err); } +}); + +// PATCH /api/prompt-styles/:id +router.patch('/:id', async (req, res, next) => { + try { + const allowed = ['type', 'themenfeld_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)) + return res.status(400).json({ error: `type must be one of: ${TYPES.join(', ')}` }); + const setClauses = fields.map((f, i) => `${f} = $${i + 1}`).join(', '); + const result = await query( + `UPDATE prompt_styles SET ${setClauses} WHERE id = $${fields.length + 1} RETURNING *`, + [...fields.map(f => req.body[f]), req.params.id] + ); + if (!result.rows.length) return res.status(404).json({ error: 'Not found' }); + res.json(result.rows[0]); + } catch (err) { next(err); } +}); + +// DELETE /api/prompt-styles/:id +router.delete('/:id', async (req, res, next) => { + try { + const result = await query('DELETE FROM prompt_styles WHERE id = $1 RETURNING id', [req.params.id]); + if (!result.rows.length) return res.status(404).json({ error: 'Not found' }); + res.status(204).end(); + } catch (err) { next(err); } +}); + +module.exports = router;