import { auth } from '@/auth' import { db } from '@/lib/db' import { sessions } from '@/lib/db/schema' import { and, eq } from 'drizzle-orm' import { NextRequest, NextResponse } from 'next/server' // In-memory signaling store (use Redis for multi-instance deployments) const signalingStore = new Map() function cleanupOldSessions() { const now = Date.now() for (const [key, value] of signalingStore.entries()) { if (now - value.createdAt > 5 * 60 * 1000) signalingStore.delete(key) } } export async function POST(request: NextRequest) { try { const authSession = await auth() if (!authSession?.user?.id) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const userId = authSession.user.id const { action, sessionId, data } = await request.json() cleanupOldSessions() switch (action) { case 'create-session': { // Verify the sessionId corresponds to a real session owned by this user const dbSession = await db .select({ id: sessions.id }) .from(sessions) .where(and(eq(sessions.id, sessionId), eq(sessions.viewerUserId, userId))) .limit(1) if (!dbSession[0]) { return NextResponse.json({ error: 'Session not found' }, { status: 404 }) } signalingStore.set(sessionId, { viewerCandidates: [], hostCandidates: [], createdAt: Date.now(), ownerUserId: userId }) return NextResponse.json({ success: true }) } case 'send-offer': { const s = signalingStore.get(sessionId) if (!s || s.ownerUserId !== userId) return NextResponse.json({ error: 'Session not found' }, { status: 404 }) s.offer = data.offer return NextResponse.json({ success: true }) } case 'get-offer': { const s = signalingStore.get(sessionId) if (!s || s.ownerUserId !== userId) return NextResponse.json({ error: 'Session not found' }, { status: 404 }) return NextResponse.json({ offer: s.offer ?? null }) } case 'send-answer': { const s = signalingStore.get(sessionId) if (!s || s.ownerUserId !== userId) return NextResponse.json({ error: 'Session not found' }, { status: 404 }) s.answer = data.answer return NextResponse.json({ success: true }) } case 'get-answer': { const s = signalingStore.get(sessionId) if (!s || s.ownerUserId !== userId) return NextResponse.json({ error: 'Session not found' }, { status: 404 }) return NextResponse.json({ answer: s.answer ?? null }) } case 'send-ice-candidate': { const s = signalingStore.get(sessionId) if (!s || s.ownerUserId !== userId) return NextResponse.json({ error: 'Session not found' }, { status: 404 }) if (data.role === 'viewer') s.viewerCandidates.push(data.candidate) else s.hostCandidates.push(data.candidate) return NextResponse.json({ success: true }) } case 'get-ice-candidates': { const s = signalingStore.get(sessionId) if (!s || s.ownerUserId !== userId) return NextResponse.json({ error: 'Session not found' }, { status: 404 }) const candidates = data.role === 'viewer' ? s.hostCandidates : s.viewerCandidates if (data.role === 'viewer') s.hostCandidates = [] else s.viewerCandidates = [] return NextResponse.json({ candidates }) } case 'close-session': { const s = signalingStore.get(sessionId) if (s && s.ownerUserId !== userId) return NextResponse.json({ error: 'Session not found' }, { status: 404 }) signalingStore.delete(sessionId) return NextResponse.json({ success: true }) } default: return NextResponse.json({ error: 'Unknown action' }, { status: 400 }) } } catch (error) { console.error('[Signal] Error:', error) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } }