import { createHmac } from 'crypto'; import type { MediaProvider, MediaPublishCredentials, MediaSessionCreateInput, MediaSessionCreateResult, MediaSubscribeCredentials, } from '../types'; const secret = process.env.BETTER_AUTH_SECRET; if (!secret) { throw new Error('BETTER_AUTH_SECRET is required for mock media provider token signing'); } const DEFAULT_TTL_SECONDS = 60 * 10; const signToken = (payload: Record, ttlSeconds = DEFAULT_TTL_SECONDS): { token: string; expiresInSeconds: number } => { const body = { ...payload, exp: Math.floor(Date.now() / 1000) + ttlSeconds, }; const encoded = Buffer.from(JSON.stringify(body), 'utf8').toString('base64url'); const signature = createHmac('sha256', secret).update(encoded).digest('base64url'); return { token: `${encoded}.${signature}`, expiresInSeconds: ttlSeconds, }; }; const getBaseUrl = (): string => process.env.MEDIA_MOCK_BASE_URL ?? process.env.BETTER_AUTH_URL ?? 'http://localhost:3000'; export class MockMediaProvider implements MediaProvider { name = 'mock'; async createSession(input: MediaSessionCreateInput): Promise { // SIMPLE_STREAMING bypasses provider-backed transport at runtime. This metadata // path is kept only for legacy endpoints and backwards compatibility. const mediaSessionId = `mock_${input.streamSessionId}`; const baseUrl = getBaseUrl(); return { provider: this.name, mediaSessionId, publishUrl: `${baseUrl}/media/mock/publish/${mediaSessionId}`, subscribeUrl: `${baseUrl}/media/mock/subscribe/${mediaSessionId}`, }; } async issuePublishCredentials(input: { mediaSessionId: string; cameraDeviceId: string; ownerUserId: string; }): Promise { const baseUrl = getBaseUrl(); const { token, expiresInSeconds } = signToken({ typ: 'publish', provider: this.name, mediaSessionId: input.mediaSessionId, cameraDeviceId: input.cameraDeviceId, ownerUserId: input.ownerUserId, }); return { provider: this.name, mediaSessionId: input.mediaSessionId, publishToken: token, publishUrl: `${baseUrl}/media/mock/publish/${input.mediaSessionId}`, expiresInSeconds, }; } async issueSubscribeCredentials(input: { mediaSessionId: string; viewerDeviceId: string; ownerUserId: string; }): Promise { const baseUrl = getBaseUrl(); const { token, expiresInSeconds } = signToken({ typ: 'subscribe', provider: this.name, mediaSessionId: input.mediaSessionId, viewerDeviceId: input.viewerDeviceId, ownerUserId: input.ownerUserId, }); return { provider: this.name, mediaSessionId: input.mediaSessionId, subscribeToken: token, subscribeUrl: `${baseUrl}/media/mock/subscribe/${input.mediaSessionId}`, expiresInSeconds, }; } }