diff --git a/examples/room-manager/src/config.ts b/examples/room-manager/src/config.ts index 9414871..574b0f9 100644 --- a/examples/room-manager/src/config.ts +++ b/examples/room-manager/src/config.ts @@ -1,3 +1,17 @@ +declare module 'fastify' { + interface FastifyInstance { + config: { + PORT: number; + WEBHOOK_URL?: string; + PEERLESS_PURGE_TIMEOUT?: number; + ENABLE_SIMULCAST: boolean; + MAX_PEERS?: number; + FISHJAM_URL: string; + FISHJAM_SERVER_TOKEN: string; + }; + } +} + export const configSchema = { type: 'object', required: ['PORT', 'ENABLE_SIMULCAST', 'FISHJAM_URL', 'FISHJAM_SERVER_TOKEN'], @@ -31,17 +45,3 @@ export const configSchema = { }, }, }; - -declare module 'fastify' { - interface FastifyInstance { - config: { - PORT: number; - WEBHOOK_URL?: string; - PEERLESS_PURGE_TIMEOUT?: number; - ENABLE_SIMULCAST: boolean; - MAX_PEERS?: number; - FISHJAM_URL: string; - FISHJAM_SERVER_TOKEN: string; - }; - } -} diff --git a/examples/room-manager/src/index.ts b/examples/room-manager/src/index.ts index f72201b..e79435a 100644 --- a/examples/room-manager/src/index.ts +++ b/examples/room-manager/src/index.ts @@ -1,11 +1,12 @@ import Fastify, { FastifyRequest } from 'fastify'; import cors from '@fastify/cors'; -import { configSchema } from './config'; import fastifyEnv from '@fastify/env'; -import { roomsEndpoints } from './routes/rooms'; +import fastifySwagger from '@fastify/swagger'; import { ServerMessage } from '@fishjam-cloud/js-server-sdk/proto'; import healthcheck from 'fastify-healthcheck'; -import fastifySwagger from '@fastify/swagger'; +import { configSchema } from './config'; + +import { rooms } from './routes/rooms'; import openapi from './openapi'; const envToLogger = { @@ -40,7 +41,7 @@ async function setupServer() { await fastify.register(fastifySwagger, { openapi }); await fastify.register(healthcheck); - await fastify.register(roomsEndpoints, { prefix: '/api/rooms' }); + await fastify.register(rooms, { prefix: '/api/rooms' }); fastify.listen({ port: fastify.config.PORT, host: '0.0.0.0' }, (err) => { if (err) { diff --git a/examples/room-manager/src/roomService.ts b/examples/room-manager/src/plugins/fishjam.ts similarity index 51% rename from examples/room-manager/src/roomService.ts rename to examples/room-manager/src/plugins/fishjam.ts index 4fdea7f..193d979 100644 --- a/examples/room-manager/src/roomService.ts +++ b/examples/room-manager/src/plugins/fishjam.ts @@ -1,24 +1,35 @@ +import fp from 'fastify-plugin'; +import { type FastifyInstance } from 'fastify'; import { FishjamClient, Room, RoomNotFoundException } from '@fishjam-cloud/js-server-sdk'; import { ServerMessage } from '@fishjam-cloud/js-server-sdk/proto'; -import { fastify } from './index'; -import type { PeerAccessData } from './schema'; -import { RoomManagerError } from './errors'; - -export class RoomService { - private readonly peerNameToAccessMap = new Map(); - private readonly roomNameToRoomIdMap = new Map(); - private readonly fishjamClient: FishjamClient; - - constructor(fishjamUrl: string, managementToken: string) { - this.fishjamClient = new FishjamClient({ - fishjamUrl, - managementToken, - }); +import { RoomManagerError } from '../errors'; +import { PeerAccessData } from '../schema'; + +declare module 'fastify' { + interface FastifyInstance { + fishjam: { + getPeerAccess: (roomName: string, username: string) => Promise; + handleFishjamMessage: (notification: ServerMessage) => Promise; + }; + } +} + +export const fishjamPlugin = fp(async (fastify: FastifyInstance): Promise => { + if (fastify.hasDecorator('fishjam')) { + throw new Error('The `fishjamPlugin` plugin has already been registered.'); } - async getPeerAccess(roomName: string, username: string): Promise { - const room = await this.findOrCreateRoomInFishjam(roomName); - const peerAccess = this.peerNameToAccessMap.get(username); + const fishjamClient = new FishjamClient({ + fishjamUrl: fastify.config.FISHJAM_URL, + managementToken: fastify.config.FISHJAM_SERVER_TOKEN, + }); + + const peerNameToAccessMap = new Map(); + const roomNameToRoomIdMap = new Map(); + + async function getPeerAccess(roomName: string, username: string): Promise { + const room = await findOrCreateRoomInFishjam(roomName); + const peerAccess = peerNameToAccessMap.get(username); const peer = room.peers.find((peer) => peer.id === peerAccess?.peer.id); @@ -31,7 +42,7 @@ export class RoomService { if (!peer) { fastify.log.info({ name: 'Creating peer' }); - return await this.createPeer(roomName, username); + return await createPeer(roomName, username); } if (!peerAccess?.peerToken) throw new RoomManagerError('Missing peer token in room'); @@ -41,7 +52,7 @@ export class RoomService { return peerAccess; } - async handleJellyfishMessage(notification: ServerMessage): Promise { + async function handleFishjamMessage(notification: ServerMessage): Promise { Object.entries(notification) .filter(([_, value]) => value) .forEach(([name, value]) => { @@ -53,7 +64,7 @@ export class RoomService { if (peerToBeRemoved) { const { roomId, peerId } = peerToBeRemoved; - const userAccess = [...this.peerNameToAccessMap.values()].find( + const userAccess = [...peerNameToAccessMap.values()].find( ({ room, peer }) => room.id === roomId && peer.id === peerId ); @@ -62,7 +73,7 @@ export class RoomService { return; } - this.peerNameToAccessMap.delete(userAccess.peer.name); + peerNameToAccessMap.delete(userAccess.peer.name); fastify.log.info({ name: 'Peer deleted from cache', roomId, peerId: peerId }); } @@ -70,13 +81,13 @@ export class RoomService { const roomToBeRemovedId = (notification.roomDeleted ?? notification.roomCrashed)?.roomId; if (roomToBeRemovedId) { - const roomName = this.roomNameToRoomIdMap.get(roomToBeRemovedId); - if (roomName) this.roomNameToRoomIdMap.delete(roomName); + const roomName = roomNameToRoomIdMap.get(roomToBeRemovedId); + if (roomName) roomNameToRoomIdMap.delete(roomName); - const usersToRemove = [...this.peerNameToAccessMap.values()].filter((user) => user.room.id === roomToBeRemovedId); + const usersToRemove = [...peerNameToAccessMap.values()].filter((user) => user.room.id === roomToBeRemovedId); usersToRemove.forEach(({ peer }) => { - this.peerNameToAccessMap.delete(peer.name); + peerNameToAccessMap.delete(peer.name); }); fastify.log.info({ @@ -86,12 +97,12 @@ export class RoomService { } } - private async createPeer(roomName: string, peerName: string): Promise { - const roomId = this.roomNameToRoomIdMap.get(roomName); + async function createPeer(roomName: string, peerName: string): Promise { + const roomId = roomNameToRoomIdMap.get(roomName); if (!roomId) throw new RoomManagerError('Room not found'); - const { peer, peerToken } = await this.fishjamClient.createPeer(roomId, { + const { peer, peerToken } = await fishjamClient.createPeer(roomId, { enableSimulcast: fastify.config.ENABLE_SIMULCAST, metadata: { username: peerName }, }); @@ -102,20 +113,20 @@ export class RoomService { peerToken, }; - this.peerNameToAccessMap.set(peerName, peerAccess); + peerNameToAccessMap.set(peerName, peerAccess); fastify.log.info('Created peer', { peerName, ...peerAccess }); return peerAccess; } - private async findOrCreateRoomInFishjam(roomName: string): Promise { - const roomId = this.roomNameToRoomIdMap.get(roomName); + async function findOrCreateRoomInFishjam(roomName: string): Promise { + const roomId = roomNameToRoomIdMap.get(roomName); if (roomId) { try { - const room = await this.fishjamClient.getRoom(roomId); - fastify.log.info({ name: 'Room already exist in the Fishjam', room }); + const room = await fishjamClient.getRoom(roomId); + fastify.log.info({ name: 'Room already exist in Fishjam', room }); return room; } catch (err) { @@ -126,21 +137,23 @@ export class RoomService { } fastify.log.info({ - name: 'Creating room in the Fishjam', + name: 'Creating room in Fishjam', roomId, roomName, }); - const newRoom = await this.fishjamClient.createRoom({ + const newRoom = await fishjamClient.createRoom({ maxPeers: fastify.config.MAX_PEERS, webhookUrl: fastify.config.WEBHOOK_URL, peerlessPurgeTimeout: fastify.config.PEERLESS_PURGE_TIMEOUT, }); - this.roomNameToRoomIdMap.set(roomName, newRoom.id); + roomNameToRoomIdMap.set(roomName, newRoom.id); fastify.log.info({ name: 'Room created', newRoom }); return newRoom; } -} + + fastify.decorate('fishjam', { getPeerAccess, handleFishjamMessage }); +}); diff --git a/examples/room-manager/src/plugins/roomService.ts b/examples/room-manager/src/plugins/roomService.ts deleted file mode 100644 index 3318b96..0000000 --- a/examples/room-manager/src/plugins/roomService.ts +++ /dev/null @@ -1,19 +0,0 @@ -import fp from 'fastify-plugin'; -import type { FastifyInstance } from 'fastify'; -import { RoomService } from '../roomService'; - -declare module 'fastify' { - interface FastifyInstance { - roomService: RoomService; - } -} - -export const roomServicePlugin = fp(async (fastify: FastifyInstance): Promise => { - if (fastify.hasDecorator('roomService')) { - throw new Error( - 'A `roomService` decorator has already been registered, please ensure you are not registering multiple instances of this plugin' - ); - } - const roomService = new RoomService(fastify.config.FISHJAM_URL, fastify.config.FISHJAM_SERVER_TOKEN); - fastify.decorate('roomService', roomService); -}); diff --git a/examples/room-manager/src/routes/rooms.ts b/examples/room-manager/src/routes/rooms.ts index 1cfaec5..e9377b6 100644 --- a/examples/room-manager/src/routes/rooms.ts +++ b/examples/room-manager/src/routes/rooms.ts @@ -2,15 +2,15 @@ import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; import { ServerMessage } from '@fishjam-cloud/js-server-sdk/proto'; import { GetPeerAccessQueryParams, startRecordingSchema, queryStringPeerEndpointSchema } from '../schema'; import { parseError } from '../errors'; -import { roomServicePlugin } from '../plugins/roomService'; +import { fishjamPlugin } from '../plugins/fishjam'; import { httpToWebsocket, removeTrailingSlash } from '../utils'; -export async function roomsEndpoints(fastify: FastifyInstance) { - await fastify.register(roomServicePlugin); +export async function rooms(fastify: FastifyInstance) { + await fastify.register(fishjamPlugin); const getRoomAccessHandler = async (roomName: string, peerName: string, res: FastifyReply) => { try { - const accessData = await fastify.roomService.getPeerAccess(roomName, peerName); + const accessData = await fastify.fishjam.getPeerAccess(roomName, peerName); const url = httpToWebsocket(fastify.config.FISHJAM_URL); // When creating a URL object from a URL without a path (e.g., `http://localhost:5002`), @@ -25,7 +25,7 @@ export async function roomsEndpoints(fastify: FastifyInstance) { }; const webhookHandler = async (req: FastifyRequest<{ Body: ServerMessage }>, res: FastifyReply) => { - await fastify.roomService.handleJellyfishMessage(req.body); + await fastify.fishjam.handleFishjamMessage(req.body); return res.status(200).send(); };