diff --git a/__tests__/call.test.ts b/__tests__/call.test.ts index 7623d29..4e17a42 100644 --- a/__tests__/call.test.ts +++ b/__tests__/call.test.ts @@ -204,6 +204,14 @@ describe('call API', () => { expect(response.call.settings.backstage.enabled).toBe(true); }); + it('generate SRT credentials', () => { + const creds = call.createSRTCredetials('john'); + + expect(creds).toBeDefined(); + expect(creds.address).toBeDefined(); + expect(creds.address).not.toBe(''); + }); + it('go live', async () => { const response = await call.goLive(); diff --git a/src/StreamCall.ts b/src/StreamCall.ts index f66c035..eb80e9a 100644 --- a/src/StreamCall.ts +++ b/src/StreamCall.ts @@ -1,8 +1,25 @@ -import { GetOrCreateCallRequest, QueryCallMembersRequest } from './gen/models'; +import { VideoApi } from './gen-imports'; +import { + CallResponse, + GetOrCreateCallRequest, + QueryCallMembersRequest, +} from './gen/models'; import { CallApi } from './gen/video/CallApi'; +import { StreamClient } from './StreamClient'; import { OmitTypeId } from './types'; export class StreamCall extends CallApi { + data?: CallResponse; + + constructor( + videoApi: VideoApi, + readonly type: string, + readonly id: string, + private readonly streamClient: StreamClient, + ) { + super(videoApi, type, id); + } + get cid() { return `${this.type}:${this.id}`; } @@ -16,4 +33,42 @@ export class StreamCall extends CallApi { ...(request ?? {}), }); }; + + getOrCreate = async (request?: GetOrCreateCallRequest) => { + const response = await super.getOrCreate(request); + this.data = response.call; + return response; + }; + + get = async () => { + const response = await super.get(); + this.data = response.call; + return response; + }; + + createSRTCredetials = ( + userID: string, + ): { + address: string; + } => { + if (!this.data) { + throw new Error( + 'Object is not initialized, call get() or getOrCreate() first', + ); + } + + const token = this.streamClient.generatePermanentUserToken({ + user_id: userID, + }); + const segments = token.split('.'); + if (segments.length !== 3) { + throw new Error('Invalid token format'); + } + + return { + address: this.data.ingress.srt.address + .replace('{passphrase}', segments[2]) + .replace('{token}', token), + }; + }; } diff --git a/src/StreamClient.ts b/src/StreamClient.ts index 373cdf0..a4f2cde 100644 --- a/src/StreamClient.ts +++ b/src/StreamClient.ts @@ -145,6 +145,24 @@ export class StreamClient extends CommonApi { return JWTUserToken(this.secret, payload as UserTokenPayload); }; + /** + * + * @param payload + * - user_id - the id of the user the token is for + * - iat - issued at date of the token, unix timestamp in seconds, by default it's now + */ + generatePermanentUserToken = ( + payload: { + user_id: string; + iat?: number; + } & Record, + ) => { + const defaultIat = Math.floor((Date.now() - 1000) / 1000); + payload.iat = payload.iat ?? defaultIat; + + return JWTUserToken(this.secret, payload as UserTokenPayload); + }; + /** * * @param payload diff --git a/src/StreamVideoClient.ts b/src/StreamVideoClient.ts index ac1e6c4..08f7243 100644 --- a/src/StreamVideoClient.ts +++ b/src/StreamVideoClient.ts @@ -25,7 +25,7 @@ export class StreamVideoClient extends VideoApi { } call = (type: string, id: string) => { - return new StreamCall(this, type, id); + return new StreamCall(this, type, id, this.streamClient); }; connectOpenAi = async (options: { diff --git a/src/types.ts b/src/types.ts index 7e93e14..e3bbeee 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,6 +7,7 @@ export interface ApiConfig { /** The timeout for requests in milliseconds. The default is 3000. */ timeout: number; agent?: RequestInit['dispatcher']; + secret?: string; } export interface RequestMetadata { @@ -39,7 +40,7 @@ export interface RateLimit { interface BaseTokenPayload { user_id: string; - exp: number; + exp?: number; iat: number; call_cids?: string[]; } diff --git a/src/utils/create-token.ts b/src/utils/create-token.ts index f3adcc0..d889f9a 100644 --- a/src/utils/create-token.ts +++ b/src/utils/create-token.ts @@ -4,7 +4,7 @@ export function JWTUserToken( apiSecret: Secret, payload: { user_id: string; - exp: number; + exp?: number; iat: number; call_cids?: string[]; } & { [key: string]: any },