97 lines
2.7 KiB
TypeScript
97 lines
2.7 KiB
TypeScript
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<string, unknown>, 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<MediaSessionCreateResult> {
|
|
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<MediaPublishCredentials> {
|
|
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<MediaSubscribeCredentials> {
|
|
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,
|
|
};
|
|
}
|
|
}
|