import { auth } from '@/lib/auth' import { db } from '@/lib/db' import { scores, roundPlayers } from '@/lib/db/schema' import { eq, and } from 'drizzle-orm' import { headers } from 'next/headers' export async function GET( request: Request, { params }: { params: Promise<{ id: string }> }, ) { const { id: roundId } = await params const session = await auth.api.getSession({ headers: await headers() }) if (!session) return new Response('Unauthorized', { status: 401 }) // Verify caller is a member of this round const [membership] = await db .select() .from(roundPlayers) .where(and(eq(roundPlayers.roundId, roundId), eq(roundPlayers.userId, session.user.id))) .limit(1) if (!membership) return new Response('Forbidden', { status: 403 }) const encoder = new TextEncoder() const stream = new ReadableStream({ async start(controller) { async function send() { const rows = await db .select({ holeId: scores.holeId, playerId: scores.playerId, strokes: scores.strokes }) .from(scores) .where(eq(scores.roundId, roundId)) controller.enqueue(encoder.encode(`data: ${JSON.stringify(rows)}\n\n`)) } // Initial payload await send() // Poll every 3 seconds const interval = setInterval(async () => { try { await send() } catch { clearInterval(interval); controller.close() } }, 3000) // Heartbeat every 30s to keep connection alive through proxies const heartbeat = setInterval(() => { try { controller.enqueue(encoder.encode(': heartbeat\n\n')) } catch { /* closed */ } }, 30_000) // Clean up on disconnect or 4-hour timeout const timeout = setTimeout(() => { clearInterval(interval) clearInterval(heartbeat) controller.close() }, 4 * 60 * 60 * 1000) request.signal.addEventListener('abort', () => { clearInterval(interval) clearInterval(heartbeat) clearTimeout(timeout) controller.close() }) }, }) return new Response(stream, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache, no-transform', Connection: 'keep-alive', 'X-Accel-Buffering': 'no', // disable nginx buffering }, }) }