import { Router } from 'express'; import { randomBytes } from 'node:crypto'; import { listRecipients, createRecipient, deleteRecipient, deleteGrant, getAccessLog, logAccess } from './db.js'; import { requireAdminAuth } from './auth.js'; export const router = Router(); router.use(requireAdminAuth); function newRecipientId(label: string): string { const slug = label.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 24); const suffix = randomBytes(3).toString('hex'); return slug ? `${slug}-${suffix}` : suffix; } router.get('/recipients', (_req, res) => { res.json(listRecipients().map(r => ({ recipientId: r.recipient_id, label: r.label, publicKey: r.public_key, createdAt: r.created_at }))); }); router.post('/recipients', (req, res) => { const body = req.body as { label?: string; publicKey?: string }; if (!body.label || !body.publicKey) { res.status(400).json({ error: 'label and publicKey are required' }); return; } const recipientId = newRecipientId(body.label); createRecipient(recipientId, body.label, body.publicKey); res.json({ recipientId }); }); router.delete('/recipients/:id', (req, res) => { deleteRecipient(req.params.id); res.json({ ok: true }); }); // Revocation is pure metadata deletion — no crypto material is touched, // which is exactly why admin alone (no vault access of their own // required) can perform it, unlike `grant`. router.delete('/vaults/:key/grants/:recipientId', (req, res) => { deleteGrant(req.params.key, req.params.recipientId); logAccess(req.params.key, req.params.recipientId, 'revoke'); res.json({ ok: true }); }); router.get('/vaults/:key/log', (req, res) => { res.json(getAccessLog(req.params.key)); });