// app/api/live/telemetry/route.ts // Server-Sent Events endpoint streaming from C++ Unix socket import { getTelemetryBridge } from '@/lib/telemetryBridge'; export const dynamic = 'force-dynamic'; export const runtime = 'nodejs'; export async function GET(request: Request) { const { searchParams } = new URL(request.url); const serverId = searchParams.get('serverId'); const filterServerId = serverId ? parseInt(serverId) : null; const encoder = new TextEncoder(); const bridge = getTelemetryBridge(); const stream = new ReadableStream({ start(controller) { console.log('[SSE] Client connected', filterServerId ? `(server ${filterServerId})` : '(all servers)'); // Send initial connection message controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'connected', bridge_status: bridge.isConnected() ? 'connected' : 'connecting' })}\n\n`)); // Subscribe to telemetry updates const unsubscribe = bridge.subscribe((packet) => { // Filter by server if specified if (filterServerId && packet.server_id !== filterServerId) { return; } // Transform to client format const telemetry = { type: 'update', timestamp: Date.now(), server_id: packet.server_id, cars: packet.cars.map((car, index) => ({ carID: car.carID, driver_guid: car.driver_guid, driver_name: car.driver_name, car_model: car.car_model, position: car.position || index + 1, current_lap: car.current_lap, normalizedSplinePos: car.normalizedSplinePos, speed: Math.round(car.speed_kmh), gear: car.gear, rpm: car.rpm, last_lap_time: car.last_lap_time || null, best_lap_time: car.best_lap_time || null, })), }; try { controller.enqueue(encoder.encode(`data: ${JSON.stringify(telemetry)}\n\n`)); } catch (error) { console.error('[SSE] Error sending data:', error); } }); // Heartbeat every 30 seconds const heartbeat = setInterval(() => { try { controller.enqueue(encoder.encode(`: heartbeat\n\n`)); } catch (error) { clearInterval(heartbeat); } }, 30000); // Cleanup on connection close request.signal.addEventListener('abort', () => { console.log('[SSE] Client disconnected'); unsubscribe(); clearInterval(heartbeat); controller.close(); }); }, }); return new Response(stream, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'X-Accel-Buffering': 'no', // Disable nginx buffering }, }); }