* Migrate Home Page to App Router * Update themeColor from layout.tsx * port room page to app router * small changes * port custom page to app router * port token and url api routes * port start stop routes * Refactor error handling in GET function * delete pages folder * remove unused function * remove deprecated field from docs: @deprecated — will be enabled by default and removed in Next.js 15 * wrap useSearchParams in Suspense * split up custom page into server and client component * update imports * simplify * Refactor error handling in GET function * refactor to use props for components * Refactor video codec validation and handling * Refactor LiveKitRoom component to handle null liveKitUrl * refactor: improve video codec validation and handling * add video codec typeguard * fix isVideoCodec
68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
import { AccessToken } from 'livekit-server-sdk';
|
|
import type { AccessTokenOptions, VideoGrant } from 'livekit-server-sdk';
|
|
import { TokenResult } from '@/lib/types';
|
|
import { NextResponse } from 'next/server';
|
|
|
|
const apiKey = process.env.LIVEKIT_API_KEY;
|
|
const apiSecret = process.env.LIVEKIT_API_SECRET;
|
|
|
|
const createToken = (userInfo: AccessTokenOptions, grant: VideoGrant) => {
|
|
const at = new AccessToken(apiKey, apiSecret, userInfo);
|
|
at.ttl = '5m';
|
|
at.addGrant(grant);
|
|
return at.toJwt();
|
|
};
|
|
|
|
const roomPattern = /\w{4}\-\w{4}/;
|
|
|
|
export async function GET(req: Request) {
|
|
try {
|
|
const url = new URL(req.url);
|
|
const searchParams = url.searchParams;
|
|
const roomName = searchParams.get('roomName');
|
|
const identity = searchParams.get('identity');
|
|
const name = searchParams.get('name');
|
|
const metadata = searchParams.get('metadata') ?? '';
|
|
|
|
if (typeof identity !== 'string' || typeof roomName !== 'string') {
|
|
return new NextResponse('Forbidden', { status: 401 });
|
|
}
|
|
if (name === null) {
|
|
return new NextResponse('Provide a name.', { status: 400 });
|
|
}
|
|
if (Array.isArray(name)) {
|
|
return new NextResponse('Provide only one room name.', { status: 400 });
|
|
}
|
|
if (Array.isArray(metadata)) {
|
|
return new NextResponse('Provide only one metadata string.', { status: 400 });
|
|
}
|
|
|
|
// enforce room name to be xxxx-xxxx
|
|
// this is simple & naive way to prevent user from guessing room names
|
|
// please use your own authentication mechanisms in your own app
|
|
if (!roomName.match(roomPattern)) {
|
|
return new NextResponse('Invalid room name format.', { status: 400 });
|
|
}
|
|
|
|
const grant: VideoGrant = {
|
|
room: roomName,
|
|
roomJoin: true,
|
|
canPublish: true,
|
|
canPublishData: true,
|
|
canSubscribe: true,
|
|
};
|
|
|
|
const token = await createToken({ identity, name, metadata }, grant);
|
|
const result: TokenResult = {
|
|
identity,
|
|
accessToken: token,
|
|
};
|
|
|
|
return NextResponse.json(result);
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
return new NextResponse(error.message, { status: 500 });
|
|
}
|
|
}
|
|
}
|