Admin UX: light/dark theme, gallery pagination, content modal + delete, openai type

- Light/dark theme toggle persisted in localStorage
- Gallery pagination: page size 50/100/150/200 with prev/next
- Fullscreen modal viewer (image/video/audio) with metadata and delete
- Delete content: DELETE /api/generations/:id removes file (media-dir guarded)
  and journal record; added Journal.deleteById
- Provider types now include openai
- HTML output escaping in UI
This commit is contained in:
OpenCode
2026-08-17 18:13:36 +07:00
parent 6ff2b27556
commit 1bc3746594
5 changed files with 216 additions and 31 deletions
+29 -1
View File
@@ -12,7 +12,7 @@ import type { AdminContext } from './context.js';
const log = createLogger('admin:api');
const KNOWN_TYPES = ['nordrouter'];
const KNOWN_TYPES = ['nordrouter', 'openai'];
function errorReply(err: unknown): { statusCode: number; body: { error: string; code: string } } {
const e = NexusError.from(err);
@@ -150,6 +150,34 @@ export function registerRoutes(app: FastifyInstance, ctx: AdminContext): void {
};
});
app.delete('/api/generations/:id', async (req, reply) => {
try {
const { id } = req.params as { id: string };
const rec = ctx.journal.getById(id);
if (!rec) return reply.code(404).send({ error: 'not found', code: 'NOT_FOUND' });
let fileRemoved = false;
if (rec.filePath) {
const mediaDir = path.resolve(ctx.config.mediaDir);
const resolved = path.resolve(rec.filePath);
// Only delete files that live inside the media dir.
if (resolved.startsWith(mediaDir + path.sep) && fs.existsSync(resolved)) {
try {
fs.unlinkSync(resolved);
fileRemoved = true;
} catch (e) {
log.warn('failed to delete file', e instanceof Error ? e.message : String(e));
}
}
}
const recordRemoved = ctx.journal.deleteById(id);
return { ok: true, fileRemoved, recordRemoved };
} catch (err) {
const { statusCode, body } = errorReply(err);
return reply.code(statusCode).send(body);
}
});
log.info('routes registered');
}