import { Router, Request, Response } from 'express'; import { prisma } from '../db/prisma'; import { requireAuth, requires } from '../middleware/auth'; import { importPostFromNostr, resolveSubmissionImport } from '../services/postImport'; const router = Router(); router.post( '/', requireAuth, async (req: Request, res: Response) => { try { const { eventId, naddr, title } = req.body; if (!title || (!eventId && !naddr)) { res.status(400).json({ error: 'title and either eventId or naddr are required' }); return; } const submission = await prisma.submission.create({ data: { eventId: eventId || null, naddr: naddr || null, title, authorPubkey: req.user!.pubkey, }, }); res.status(201).json(submission); } catch (err) { console.error('Create submission error:', err); res.status(500).json({ error: 'Internal server error' }); } } ); router.get( '/mine', requireAuth, async (req: Request, res: Response) => { try { const submissions = await prisma.submission.findMany({ where: { authorPubkey: req.user!.pubkey }, orderBy: { createdAt: 'desc' }, }); res.json(submissions); } catch (err) { console.error('List own submissions error:', err); res.status(500).json({ error: 'Internal server error' }); } } ); router.get( '/', requireAuth, requires('submissions.review'), async (req: Request, res: Response) => { try { const status = req.query.status as string | undefined; const where: any = {}; if (status) where.status = status; const submissions = await prisma.submission.findMany({ where, orderBy: { createdAt: 'desc' }, }); res.json(submissions); } catch (err) { console.error('List submissions error:', err); res.status(500).json({ error: 'Internal server error' }); } } ); router.patch( '/:id', requireAuth, requires('submissions.review'), async (req: Request, res: Response) => { try { const { status, reviewNote } = req.body; if (!status || !['APPROVED', 'REJECTED'].includes(status)) { res.status(400).json({ error: 'status must be APPROVED or REJECTED' }); return; } const submission = await prisma.submission.findUnique({ where: { id: req.params.id as string }, }); if (!submission) { res.status(404).json({ error: 'Submission not found' }); return; } // Approval publishes the referenced Nostr article to the blog. Do this // before flipping the status so a failed import doesn't leave a submission // marked APPROVED without a corresponding live post. let post = null; if (status === 'APPROVED') { const importInput = await resolveSubmissionImport(submission); if (!importInput) { res.status(422).json({ error: 'Could not resolve the submitted Nostr event from relays. The post was not published, so the submission was left unchanged.', }); return; } post = await importPostFromNostr(importInput); } const updated = await prisma.submission.update({ where: { id: req.params.id as string }, data: { status, reviewedBy: req.user!.pubkey, reviewNote: reviewNote || null, }, }); res.json({ submission: updated, post }); } catch (err) { console.error('Review submission error:', err); res.status(500).json({ error: 'Internal server error' }); } } ); export default router;