diff --git a/backend/src/controllers/participant.controller.ts b/backend/src/controllers/participant.controller.ts index 2b15871..fb5da91 100644 --- a/backend/src/controllers/participant.controller.ts +++ b/backend/src/controllers/participant.controller.ts @@ -4,34 +4,20 @@ import { LoggerService } from '../services/logger.service.js'; import { TokenOptions } from '@typings-ce'; import { OpenViduMeetError } from '../models/index.js'; import { ParticipantService } from '../services/participant.service.js'; -import { RoomService } from '../services/room.service.js'; import { MEET_PARTICIPANT_TOKEN_EXPIRATION, PARTICIPANT_TOKEN_COOKIE_NAME } from '../environment.js'; import { getCookieOptions } from '../utils/cookie-utils.js'; export const generateParticipantToken = async (req: Request, res: Response) => { const logger = container.get(LoggerService); const tokenOptions: TokenOptions = req.body; - const { roomName, secret, participantName } = tokenOptions; + const { roomName } = tokenOptions; + const participantService = container.get(ParticipantService); try { - const roomService = container.get(RoomService); - const participantService = container.get(ParticipantService); - - // Check if participant with same participantName exists in the room - const participantExists = await participantService.participantExists(roomName, participantName); - - if (participantExists) { - logger.verbose(`Participant ${participantName} already exists in room ${roomName}`); - return res.status(409).json({ message: 'Participant already exists' }); - } - logger.verbose(`Generating participant token for room ${roomName}`); - - const secretRole = await roomService.getRoomSecretRole(roomName, secret); - const token = await participantService.generateParticipantToken(secretRole, tokenOptions); + const token = await participantService.generateOrRefreshParticipantToken(tokenOptions); res.cookie(PARTICIPANT_TOKEN_COOKIE_NAME, token, getCookieOptions('/', MEET_PARTICIPANT_TOKEN_EXPIRATION)); - logger.verbose(`Participant token generated for room ${roomName}`); return res.status(200).json({ token }); } catch (error) { @@ -40,6 +26,25 @@ export const generateParticipantToken = async (req: Request, res: Response) => { } }; +export const refreshParticipantToken = async (req: Request, res: Response) => { + const logger = container.get(LoggerService); + const tokenOptions: TokenOptions = req.body; + const { roomName } = tokenOptions; + const participantService = container.get(ParticipantService); + + try { + logger.verbose(`Refreshing participant token for room ${roomName}`); + const token = await participantService.generateOrRefreshParticipantToken(tokenOptions, true); + + res.cookie(PARTICIPANT_TOKEN_COOKIE_NAME, token, getCookieOptions('/', MEET_PARTICIPANT_TOKEN_EXPIRATION)); + logger.verbose(`Participant token refreshed for room ${roomName}`); + return res.status(200).json({ token }); + } catch (error) { + logger.error(`Error refreshing participant token for room: ${roomName}`); + return handleError(res, error); + } +}; + export const deleteParticipant = async (req: Request, res: Response) => { const logger = container.get(LoggerService); const participantService = container.get(ParticipantService); diff --git a/backend/src/routes/participants.routes.ts b/backend/src/routes/participants.routes.ts index 7472b32..2a80306 100644 --- a/backend/src/routes/participants.routes.ts +++ b/backend/src/routes/participants.routes.ts @@ -11,6 +11,7 @@ participantsInternalRouter.use(bodyParser.urlencoded({ extended: true })); participantsInternalRouter.use(bodyParser.json()); participantsInternalRouter.post('/token', validateParticipantTokenRequest, participantCtrl.generateParticipantToken); +participantsInternalRouter.post('/token/refresh', validateParticipantTokenRequest, participantCtrl.refreshParticipantToken); export const participantsRouter = Router(); participantsRouter.use(bodyParser.urlencoded({ extended: true })); diff --git a/backend/src/services/livekit.service.ts b/backend/src/services/livekit.service.ts index e8466f8..e63892f 100644 --- a/backend/src/services/livekit.service.ts +++ b/backend/src/services/livekit.service.ts @@ -24,7 +24,6 @@ import { import { LoggerService } from './logger.service.js'; import { errorLivekitIsNotAvailable, - errorParticipantAlreadyExists, errorParticipantNotFound, errorRoomNotFound, internalError @@ -132,17 +131,6 @@ export class LiveKitService { role: ParticipantRole ): Promise { const { roomName, participantName } = options; - - try { - if (await this.participantExists(roomName, participantName)) { - this.logger.error(`Participant ${participantName} already exists in room ${roomName}`); - throw errorParticipantAlreadyExists(participantName, roomName); - } - } catch (error) { - this.logger.error(`Error checking participant existence, ${JSON.stringify(error)}`); - throw error; - } - this.logger.info(`Generating token for ${participantName} in room ${roomName}`); const at = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET, { diff --git a/backend/src/services/participant.service.ts b/backend/src/services/participant.service.ts index 666831b..6de9ebd 100644 --- a/backend/src/services/participant.service.ts +++ b/backend/src/services/participant.service.ts @@ -3,17 +3,39 @@ import { LiveKitService } from './livekit.service.js'; import { LoggerService } from './logger.service.js'; import { ParticipantPermissions, ParticipantRole, TokenOptions } from '@typings-ce'; import { ParticipantInfo } from 'livekit-server-sdk'; +import { RoomService } from './room.service.js'; +import { errorParticipantAlreadyExists, errorParticipantNotFound } from '../models/index.js'; @injectable() export class ParticipantService { constructor( @inject(LoggerService) protected logger: LoggerService, + @inject(RoomService) protected roomService: RoomService, @inject(LiveKitService) protected livekitService: LiveKitService ) {} - async generateParticipantToken(role: ParticipantRole, options: TokenOptions): Promise { - const permissions = this.getParticipantPermissions(role, options.roomName); + async generateOrRefreshParticipantToken(options: TokenOptions, refresh = false): Promise { + const { roomName, participantName, secret } = options; + // Check if participant with same participantName exists in the room + const participantExists = await this.participantExists(roomName, participantName); + + if (!refresh && participantExists) { + this.logger.verbose(`Participant ${participantName} already exists in room ${roomName}`); + throw errorParticipantAlreadyExists(participantName, roomName); + } + + if (refresh && !participantExists) { + this.logger.verbose(`Participant ${participantName} does not exist in room ${roomName}`); + throw errorParticipantNotFound(participantName, roomName); + } + + const role = await this.roomService.getRoomSecretRole(roomName, secret); + return this.generateParticipantToken(role, options); + } + + protected async generateParticipantToken(role: ParticipantRole, options: TokenOptions): Promise { + const permissions = this.getParticipantPermissions(role, options.roomName); return this.livekitService.generateToken(options, permissions, role); } diff --git a/backend/src/services/room.service.ts b/backend/src/services/room.service.ts index 472a818..b449668 100644 --- a/backend/src/services/room.service.ts +++ b/backend/src/services/room.service.ts @@ -23,7 +23,6 @@ export class RoomService { @inject(LoggerService) protected logger: LoggerService, @inject(GlobalPreferencesService) protected globalPrefService: GlobalPreferencesService, @inject(LiveKitService) protected livekitService: LiveKitService, - @inject(ParticipantService) protected participantService: ParticipantService, @inject(SystemEventService) protected systemEventService: SystemEventService, @inject(TaskSchedulerService) protected taskSchedulerService: TaskSchedulerService ) {}