Derive region url from project url (#441)
* Derive region url from project url * add tests * test workflow * fix workflow * ugh * fix * fix staging/prod
This commit is contained in:
parent
5230af4fb6
commit
b650fecdd4
32
.github/workflows/test.yaml
vendored
Normal file
32
.github/workflows/test.yaml
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
name: Test
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- name: Use Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: ESLint
|
||||||
|
run: pnpm lint
|
||||||
|
|
||||||
|
- name: Prettier
|
||||||
|
run: pnpm format:check
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
run: pnpm test
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { randomString } from '@/lib/client-utils';
|
import { randomString } from '@/lib/client-utils';
|
||||||
|
import { getLiveKitURL } from '@/lib/getLiveKitURL';
|
||||||
import { ConnectionDetails } from '@/lib/types';
|
import { ConnectionDetails } from '@/lib/types';
|
||||||
import { AccessToken, AccessTokenOptions, VideoGrant } from 'livekit-server-sdk';
|
import { AccessToken, AccessTokenOptions, VideoGrant } from 'livekit-server-sdk';
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
@ -6,6 +7,7 @@ import { NextRequest, NextResponse } from 'next/server';
|
|||||||
const API_KEY = process.env.LIVEKIT_API_KEY;
|
const API_KEY = process.env.LIVEKIT_API_KEY;
|
||||||
const API_SECRET = process.env.LIVEKIT_API_SECRET;
|
const API_SECRET = process.env.LIVEKIT_API_SECRET;
|
||||||
const LIVEKIT_URL = process.env.LIVEKIT_URL;
|
const LIVEKIT_URL = process.env.LIVEKIT_URL;
|
||||||
|
|
||||||
const COOKIE_KEY = 'random-participant-postfix';
|
const COOKIE_KEY = 'random-participant-postfix';
|
||||||
|
|
||||||
export async function GET(request: NextRequest) {
|
export async function GET(request: NextRequest) {
|
||||||
@ -15,7 +17,10 @@ export async function GET(request: NextRequest) {
|
|||||||
const participantName = request.nextUrl.searchParams.get('participantName');
|
const participantName = request.nextUrl.searchParams.get('participantName');
|
||||||
const metadata = request.nextUrl.searchParams.get('metadata') ?? '';
|
const metadata = request.nextUrl.searchParams.get('metadata') ?? '';
|
||||||
const region = request.nextUrl.searchParams.get('region');
|
const region = request.nextUrl.searchParams.get('region');
|
||||||
const livekitServerUrl = region ? getLiveKitURL(region) : LIVEKIT_URL;
|
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;
|
let randomParticipantPostfix = request.cookies.get(COOKIE_KEY)?.value;
|
||||||
if (livekitServerUrl === undefined) {
|
if (livekitServerUrl === undefined) {
|
||||||
throw new Error('Invalid region');
|
throw new Error('Invalid region');
|
||||||
@ -75,21 +80,6 @@ function createParticipantToken(userInfo: AccessTokenOptions, roomName: string)
|
|||||||
return at.toJwt();
|
return at.toJwt();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the LiveKit server URL for the given region.
|
|
||||||
*/
|
|
||||||
function getLiveKitURL(region: string | null): string {
|
|
||||||
let targetKey = 'LIVEKIT_URL';
|
|
||||||
if (region) {
|
|
||||||
targetKey = `LIVEKIT_URL_${region}`.toUpperCase();
|
|
||||||
}
|
|
||||||
const url = process.env[targetKey];
|
|
||||||
if (!url) {
|
|
||||||
throw new Error(`${targetKey} is not defined`);
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCookieExpirationTime(): string {
|
function getCookieExpirationTime(): string {
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
var time = now.getTime();
|
var time = now.getTime();
|
||||||
|
|||||||
35
lib/getLiveKitURL.test.ts
Normal file
35
lib/getLiveKitURL.test.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { getLiveKitURL } from './getLiveKitURL';
|
||||||
|
|
||||||
|
describe('getLiveKitURL', () => {
|
||||||
|
it('returns the original URL if no region is provided', () => {
|
||||||
|
const url = 'https://myproject.livekit.cloud';
|
||||||
|
expect(getLiveKitURL(url, null)).toBe(url + '/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inserts the region into livekit.cloud URLs', () => {
|
||||||
|
const url = 'https://myproject.livekit.cloud';
|
||||||
|
const region = 'eu';
|
||||||
|
expect(getLiveKitURL(url, region)).toBe('https://myproject.eu.production.livekit.cloud/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inserts the region into livekit.cloud URLs and preserves the staging environment', () => {
|
||||||
|
const url = 'https://myproject.staging.livekit.cloud';
|
||||||
|
const region = 'eu';
|
||||||
|
expect(getLiveKitURL(url, region)).toBe('https://myproject.eu.staging.livekit.cloud/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the original URL for non-livekit.cloud hosts, even with region', () => {
|
||||||
|
const url = 'https://example.com';
|
||||||
|
const region = 'us';
|
||||||
|
expect(getLiveKitURL(url, region)).toBe(url + '/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles URLs with paths and query params', () => {
|
||||||
|
const url = 'https://myproject.livekit.cloud/room?foo=bar';
|
||||||
|
const region = 'ap';
|
||||||
|
expect(getLiveKitURL(url, region)).toBe(
|
||||||
|
'https://myproject.ap.production.livekit.cloud/room?foo=bar',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
12
lib/getLiveKitURL.ts
Normal file
12
lib/getLiveKitURL.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export function getLiveKitURL(projectUrl: string, region: string | null): string {
|
||||||
|
const url = new URL(projectUrl);
|
||||||
|
if (region && url.hostname.includes('livekit.cloud')) {
|
||||||
|
let [projectId, ...hostParts] = url.hostname.split('.');
|
||||||
|
if (hostParts[0] !== 'staging') {
|
||||||
|
hostParts = ['production', ...hostParts];
|
||||||
|
}
|
||||||
|
const regionURL = [projectId, region, ...hostParts].join('.');
|
||||||
|
url.hostname = regionURL;
|
||||||
|
}
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@
|
|||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"lint:fix": "next lint --fix",
|
"lint:fix": "next lint --fix",
|
||||||
|
"test": "vitest run",
|
||||||
"format:check": "prettier --check \"**/*.{ts,tsx,md,json}\"",
|
"format:check": "prettier --check \"**/*.{ts,tsx,md,json}\"",
|
||||||
"format:write": "prettier --write \"**/*.{ts,tsx,md,json}\""
|
"format:write": "prettier --write \"**/*.{ts,tsx,md,json}\""
|
||||||
},
|
},
|
||||||
@ -31,9 +32,10 @@
|
|||||||
"@types/react-dom": "18.3.7",
|
"@types/react-dom": "18.3.7",
|
||||||
"eslint": "9.29.0",
|
"eslint": "9.29.0",
|
||||||
"eslint-config-next": "15.3.3",
|
"eslint-config-next": "15.3.3",
|
||||||
|
"prettier": "3.5.3",
|
||||||
"source-map-loader": "^5.0.0",
|
"source-map-loader": "^5.0.0",
|
||||||
"typescript": "5.8.3",
|
"typescript": "5.8.3",
|
||||||
"prettier": "3.5.3"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
|||||||
907
pnpm-lock.yaml
generated
907
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user