import crypto from 'node:crypto'; import { db } from '../core/db.js'; import { bus } from '../core/bus.js'; import { requireMember } from '../core/guards.js'; function sendError(reply, status, code, message) { return reply.code(status).send({ error: { code, message } }); } export default async function calendarRoutes(fastify) { fastify.get('/spaces/:id/events', async (request) => { await requireMember(request, request.params.id); const events = db .prepare('SELECT * FROM events WHERE space_id = ? ORDER BY start_at ASC') .all(request.params.id); return { events }; }); fastify.post('/spaces/:id/events', async (request, reply) => { await requireMember(request, request.params.id); const spaceId = request.params.id; const { title, start_at } = request.body || {}; if (!title || typeof title !== 'string') { return sendError(reply, 400, 'VALIDATION', 'title обязателен'); } if (!Number.isInteger(start_at)) { return sendError(reply, 400, 'VALIDATION', 'start_at должен быть unix ms'); } const event = { id: crypto.randomUUID(), space_id: spaceId, title, start_at, created_at: Date.now(), }; db.prepare('INSERT INTO events (id, space_id, title, start_at, created_at) VALUES (?,?,?,?,?)') .run(event.id, event.space_id, event.title, event.start_at, event.created_at); bus.emit(spaceId, { type: 'event.created', space_id: spaceId, payload: event }); return reply.code(201).send({ event }); }); }