import { randomString } from '@/lib/client-utils'; import { getLiveKitURL } from '@/lib/getLiveKitURL'; import { ConnectionDetails } from '@/lib/types'; import { AccessToken, AccessTokenOptions, VideoGrant } from 'livekit-server-sdk'; import { NextRequest, NextResponse } from 'next/server'; const API_KEY = process.env.LIVEKIT_API_KEY; const API_SECRET = process.env.LIVEKIT_API_SECRET; const LIVEKIT_URL = process.env.LIVEKIT_URL; const COOKIE_KEY = 'random-participant-postfix'; export async function GET(request: NextRequest) { try { // Parse query parameters const roomName = request.nextUrl.searchParams.get('roomName'); const participantName = request.nextUrl.searchParams.get('participantName'); const metadata = request.nextUrl.searchParams.get('metadata') ?? ''; const region = request.nextUrl.searchParams.get('region'); if (!LIVEKIT_URL) { throw new Error('LIVEKIT_URL is not defined'); } const livekitServerUrl = region ? getLiveKitURL(LIVEKIT_URL, region) : LIVEKIT_URL; let randomParticipantPostfix = request.cookies.get(COOKIE_KEY)?.value; if (livekitServerUrl === undefined) { throw new Error('Invalid region'); } if (typeof roomName !== 'string') { return new NextResponse('Missing required query parameter: roomName', { status: 400 }); } if (participantName === null) { return new NextResponse('Missing required query parameter: participantName', { status: 400 }); } // Generate participant token if (!randomParticipantPostfix) { randomParticipantPostfix = randomString(4); } const participantToken = await createParticipantToken( { identity: `${participantName}__${randomParticipantPostfix}`, name: participantName, metadata, }, roomName, ); // Return connection details const data: ConnectionDetails = { serverUrl: livekitServerUrl, roomName: roomName, participantToken: participantToken, participantName: participantName, }; return new NextResponse(JSON.stringify(data), { headers: { 'Content-Type': 'application/json', 'Set-Cookie': `${COOKIE_KEY}=${randomParticipantPostfix}; Path=/; HttpOnly; SameSite=Strict; Secure; Expires=${getCookieExpirationTime()}`, }, }); } catch (error) { if (error instanceof Error) { return new NextResponse(error.message, { status: 500 }); } } } function createParticipantToken(userInfo: AccessTokenOptions, roomName: string) { const at = new AccessToken(API_KEY, API_SECRET, userInfo); at.ttl = '5m'; const grant: VideoGrant = { room: roomName, roomJoin: true, canPublish: true, canPublishData: true, canSubscribe: true, }; at.addGrant(grant); return at.toJwt(); } function getCookieExpirationTime(): string { var now = new Date(); var time = now.getTime(); var expireTime = time + 60 * 120 * 1000; now.setTime(expireTime); return now.toUTCString(); }