import { randomUUID } from 'node:crypto' import { state } from '../state.js' import { queueCalendarEventsWrite } from '../data.js' import { requireAdminAuth } from '../auth.js' const VALID_TYPES = ['general', 'recording', 'social', 'task'] const DATE_RE = /^\d{4}-\d{2}-\d{2}$/ function sanitize(ev) { return { id: ev.id, type: VALID_TYPES.includes(ev.type) ? ev.type : 'general', title: typeof ev.title === 'string' ? ev.title.slice(0, 300) : '', date: typeof ev.date === 'string' && DATE_RE.test(ev.date) ? ev.date : '', notes: typeof ev.notes === 'string' ? ev.notes.slice(0, 2000) : '', completed: Boolean(ev.completed), reminderDays: Number.isFinite(ev.reminderDays) && ev.reminderDays > 0 ? ev.reminderDays : 0, reminderSentAt: typeof ev.reminderSentAt === 'string' ? ev.reminderSentAt : undefined, createdAt: typeof ev.createdAt === 'string' ? ev.createdAt : new Date().toISOString(), } } export function register(app) { app.get('/api/admin-calendar-events', requireAdminAuth, (_req, res) => { res.json({ events: state.calendarEvents }) }) app.post('/api/admin-calendar-events', requireAdminAuth, (req, res) => { const { type, title, date, notes, reminderDays } = req.body ?? {} if (!title?.trim()) { res.status(400).json({ message: 'Title is required.' }); return } if (!date || !DATE_RE.test(date)) { res.status(400).json({ message: 'Valid date is required.' }); return } if (type && !VALID_TYPES.includes(type)) { res.status(400).json({ message: 'Invalid type.' }); return } const ev = sanitize({ id: randomUUID(), type: type ?? 'general', title: String(title).trim(), date, notes: notes ?? '', completed: false, reminderDays: reminderDays ?? 0, createdAt: new Date().toISOString(), }) state.calendarEvents.unshift(ev) queueCalendarEventsWrite() res.json({ ok: true, event: ev }) }) app.patch('/api/admin-calendar-events/:id', requireAdminAuth, (req, res) => { const { id } = req.params let found = false state.calendarEvents = state.calendarEvents.map(ev => { if (ev.id !== id) return ev found = true const patch = {} if (typeof req.body?.title === 'string') patch.title = req.body.title.trim().slice(0, 300) if (typeof req.body?.date === 'string' && DATE_RE.test(req.body.date)) patch.date = req.body.date if (typeof req.body?.notes === 'string') patch.notes = req.body.notes.trim().slice(0, 2000) if (typeof req.body?.completed === 'boolean') patch.completed = req.body.completed if (VALID_TYPES.includes(req.body?.type)) patch.type = req.body.type if (Number.isFinite(req.body?.reminderDays)) patch.reminderDays = req.body.reminderDays const dateChanged = patch.date && patch.date !== ev.date const reminderChanged = 'reminderDays' in patch && patch.reminderDays !== ev.reminderDays if (dateChanged || reminderChanged) patch.reminderSentAt = undefined return { ...ev, ...patch } }) if (!found) { res.status(404).json({ message: 'Event not found.' }); return } queueCalendarEventsWrite() res.json({ ok: true }) }) app.delete('/api/admin-calendar-events/:id', requireAdminAuth, (req, res) => { const before = state.calendarEvents.length state.calendarEvents = state.calendarEvents.filter(ev => ev.id !== req.params.id) if (state.calendarEvents.length === before) { res.status(404).json({ message: 'Event not found.' }); return } queueCalendarEventsWrite() res.json({ ok: true }) }) }