import { Router } from 'express'; import { mediaMode } from '../../media/config'; import { sfuService } from '../../media/sfu/service'; import { requireDeviceAuth } from '../../middleware/device-auth'; import { sfuTransportRequestSchema, streamParamSchema } from './schemas'; import { ensureStreamDeviceAuth, getOwnedStreamSession, isStreamParticipant } from './shared'; const router = Router(); router.get('/:streamSessionId/sfu/session', requireDeviceAuth, async (req, res) => { const parsedParams = streamParamSchema.safeParse(req.params); if (!parsedParams.success) { res.status(400).json({ message: 'Invalid streamSessionId', errors: parsedParams.error.flatten() }); return; } const deviceAuth = ensureStreamDeviceAuth(req, res); if (!deviceAuth) return; if (!sfuService) { res.status(409).json({ message: `SFU service disabled (MEDIA_MODE=${mediaMode})` }); return; } const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId); if (!session) { res.status(404).json({ message: 'Stream session not found' }); return; } if (!isStreamParticipant(session, deviceAuth.deviceId)) { res.status(403).json({ message: 'Device cannot access SFU session details for this stream' }); return; } const sfuSession = await sfuService.getSession(session.id); res.json({ streamSessionId: session.id, mediaMode, sfuSession, }); }); router.post('/:streamSessionId/sfu/publish-transport', requireDeviceAuth, async (req, res) => { const parsedParams = streamParamSchema.safeParse(req.params); if (!parsedParams.success) { res.status(400).json({ message: 'Invalid streamSessionId', errors: parsedParams.error.flatten() }); return; } const parsedBody = sfuTransportRequestSchema.safeParse(req.body ?? {}); if (!parsedBody.success) { res.status(400).json({ message: 'Invalid request body', errors: parsedBody.error.flatten() }); return; } const deviceAuth = ensureStreamDeviceAuth(req, res); if (!deviceAuth) return; if (!sfuService) { res.status(409).json({ message: `SFU service disabled (MEDIA_MODE=${mediaMode})` }); return; } const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId); if (!session) { res.status(404).json({ message: 'Stream session not found' }); return; } if (session.status !== 'streaming') { res.status(409).json({ message: 'Stream must be active before creating publish transport' }); return; } if (session.cameraDeviceId !== deviceAuth.deviceId) { res.status(403).json({ message: 'Only camera device can create publish transport' }); return; } const transport = await sfuService.createPublishTransport({ streamSessionId: session.id, cameraDeviceId: deviceAuth.deviceId, }); await sfuService.setSessionState(session.id, 'live'); res.json({ streamSessionId: session.id, mediaMode, transport, }); }); router.post('/:streamSessionId/sfu/subscribe-transport', requireDeviceAuth, async (req, res) => { const parsedParams = streamParamSchema.safeParse(req.params); if (!parsedParams.success) { res.status(400).json({ message: 'Invalid streamSessionId', errors: parsedParams.error.flatten() }); return; } const parsedBody = sfuTransportRequestSchema.safeParse(req.body ?? {}); if (!parsedBody.success) { res.status(400).json({ message: 'Invalid request body', errors: parsedBody.error.flatten() }); return; } const deviceAuth = ensureStreamDeviceAuth(req, res); if (!deviceAuth) return; if (!sfuService) { res.status(409).json({ message: `SFU service disabled (MEDIA_MODE=${mediaMode})` }); return; } const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId); if (!session) { res.status(404).json({ message: 'Stream session not found' }); return; } if (session.status !== 'streaming') { res.status(409).json({ message: 'Stream must be active before creating subscribe transport' }); return; } if (!isStreamParticipant(session, deviceAuth.deviceId)) { res.status(403).json({ message: 'Device cannot create subscribe transport for this stream' }); return; } const transport = await sfuService.createSubscribeTransport({ streamSessionId: session.id, viewerDeviceId: deviceAuth.deviceId, }); await sfuService.setSessionState(session.id, 'live'); res.json({ streamSessionId: session.id, mediaMode, transport, }); }); export default router;