Admin: create/delete mysteries, asset library, cutscene component picker
- Mysteries tab: "+ New" (slug + title) and per-row delete; opens the graph editor. - Assets tab: upload images/audio/PDFs to the shared osint.assets store, grid with thumbnails/icons, copy id/url, delete (blocked with 409 when the asset is in use). - Cutscene component_key is now a datalist of registered keys with a "not registered" warning, instead of free text. Backend: DELETE /api/admin/mysteries/:id and POST/GET/DELETE /api/admin/assets (reusing the deduplicated, MinIO-backed asset store). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -145,6 +145,33 @@ function requireEditing(res: express.Response) {
|
||||
app.get('/api/admin/mysteries', requireAdmin, async (_req, res, next) => {
|
||||
try { res.json(await narrative.listMysteries()) } catch (error) { next(error) }
|
||||
})
|
||||
app.delete('/api/admin/mysteries/:id', requireAdmin, async (req, res, next) => {
|
||||
try {
|
||||
if (!requireEditing(res)) return
|
||||
const ok = await narrative.deleteMystery(String(req.params.id))
|
||||
ok ? res.json({ ok: true }) : res.status(404).json({ error: 'Mystery not found' })
|
||||
} catch (error) { next(error) }
|
||||
})
|
||||
// Shared asset library (images, audio, PDFs) — reuses the immutable, deduplicated
|
||||
// osint.assets store; bytes served via GET /api/assets/:id.
|
||||
app.get('/api/admin/assets', requireAdmin, async (_req, res, next) => {
|
||||
try { res.json(await narrative.listAssets()) } catch (error) { next(error) }
|
||||
})
|
||||
app.post('/api/admin/assets', requireAdmin, upload.single('file'), async (req, res, next) => {
|
||||
try {
|
||||
if (!requireEditing(res)) return
|
||||
if (!req.file) return res.status(400).json({ error: 'A file is required' })
|
||||
res.status(201).json(await narrative.uploadAsset(req.file))
|
||||
} catch (error) { next(error) }
|
||||
})
|
||||
app.delete('/api/admin/assets/:id', requireAdmin, async (req, res, next) => {
|
||||
try {
|
||||
if (!requireEditing(res)) return
|
||||
const outcome = await narrative.deleteAsset(String(req.params.id))
|
||||
if (outcome === 'deleted') return res.json({ ok: true })
|
||||
res.status(outcome === 'in_use' ? 409 : 404).json({ error: outcome === 'in_use' ? 'Asset is in use' : 'Asset not found' })
|
||||
} catch (error) { next(error) }
|
||||
})
|
||||
app.get('/api/admin/npcs', requireAdmin, async (_req, res, next) => {
|
||||
try { res.json(await narrative.listNpcs()) } catch (error) { next(error) }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user