/** * Event Stream API * GET /api/transparency/events - Get recent events or SSE stream * POST /api/transparency/events - Emit a custom event * * Provides real-time event streaming for transparency. */ import type { NextApiRequest, NextApiResponse } from 'next'; import { getEventStream, TransparencyEventType, EventPriority } from '../../../lib/transparency'; export default async function handler( req: NextApiRequest, res: NextApiResponse ) { const eventStream = getEventStream(); if (req.method === 'GET') { const { stream, types, limit = '50', start, end } = req.query; // Server-Sent Events mode if (stream === 'true') { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); const connectionId = `sse_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; // Send initial connection event res.write(`event: connected\ndata: ${JSON.stringify({ connectionId })}\n\n`); // Register SSE callback eventStream.registerSSE(connectionId, (event) => { try { res.write(eventStream.formatSSE(event)); } catch { // Connection closed eventStream.unregisterSSE(connectionId); } }); // Handle client disconnect req.on('close', () => { eventStream.unregisterSSE(connectionId); }); // Keep connection alive const keepAlive = setInterval(() => { try { res.write(': keepalive\n\n'); } catch { clearInterval(keepAlive); eventStream.unregisterSSE(connectionId); } }, 30000); return; } // Regular GET - return recent events try { const typeList = types ? (types as string).split(',') as TransparencyEventType[] : undefined; let events; if (start && end) { events = eventStream.getByTimeRange( start as string, end as string, { types: typeList, limit: parseInt(limit as string, 10) } ); } else { events = eventStream.getRecent(parseInt(limit as string, 10), typeList); } return res.status(200).json({ success: true, data: { events, count: events.length, availableTypes: eventStream.getAvailableEventTypes() } }); } catch (error) { console.error('[API] Events error:', error); return res.status(500).json({ success: false, error: 'Failed to fetch events' }); } } if (req.method === 'POST') { try { const { type, source, data, priority, metadata } = req.body; if (!type || !source || !data) { return res.status(400).json({ success: false, error: 'Missing required fields: type, source, data' }); } const event = eventStream.emit( type as TransparencyEventType, source, data, { priority: priority as EventPriority, metadata } ); return res.status(201).json({ success: true, data: event }); } catch (error) { console.error('[API] Event emit error:', error); return res.status(500).json({ success: false, error: 'Failed to emit event' }); } } return res.status(405).json({ error: 'Method not allowed' }); }