From 982247736ef19898baa941a628672cfb2bde84ca Mon Sep 17 00:00:00 2001 From: juancarmore Date: Fri, 29 Aug 2025 17:40:26 +0200 Subject: [PATCH] backend: add updateRoomStatus endpoint and validation for room status updates --- backend/src/controllers/room.controller.ts | 24 +++++++++++++++++++ .../room-validator.middleware.ts | 16 +++++++++++++ backend/src/routes/room.routes.ts | 13 +++++++++- backend/src/services/room.service.ts | 24 +++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/backend/src/controllers/room.controller.ts b/backend/src/controllers/room.controller.ts index 03db2d1..d467d37 100644 --- a/backend/src/controllers/room.controller.ts +++ b/backend/src/controllers/room.controller.ts @@ -150,6 +150,30 @@ export const updateRoomPreferences = async (req: Request, res: Response) => { } }; +export const updateRoomStatus = async (req: Request, res: Response) => { + const logger = container.get(LoggerService); + const roomService = container.get(RoomService); + const { status } = req.body; + const { roomId } = req.params; + + logger.verbose(`Updating room status for room '${roomId}' to '${status}'`); + + try { + const { room, updated } = await roomService.updateMeetRoomStatus(roomId, status); + let message: string; + + if (updated) { + message = `Room '${roomId}' ${status} successfully`; + } else { + message = `Room '${roomId}' scheduled to be closed when the meeting ends`; + } + + return res.status(updated ? 200 : 202).json({ message, room }); + } catch (error) { + handleError(res, error, `updating room status for room '${roomId}'`); + } +}; + export const generateRecordingToken = async (req: Request, res: Response) => { const logger = container.get(LoggerService); const roomService = container.get(RoomService); diff --git a/backend/src/middlewares/request-validators/room-validator.middleware.ts b/backend/src/middlewares/request-validators/room-validator.middleware.ts index 30e9453..490804e 100644 --- a/backend/src/middlewares/request-validators/room-validator.middleware.ts +++ b/backend/src/middlewares/request-validators/room-validator.middleware.ts @@ -8,6 +8,7 @@ import { MeetRoomFilters, MeetRoomOptions, MeetRoomPreferences, + MeetRoomStatus, MeetVirtualBackgroundPreferences, ParticipantRole, RecordingPermissions @@ -207,6 +208,10 @@ const UpdateRoomPreferencesSchema = z.object({ preferences: RoomPreferencesSchema }); +const UpdateRoomStatusSchema = z.object({ + status: z.enum([MeetRoomStatus.OPEN, MeetRoomStatus.CLOSED]) +}); + const RecordingTokenRequestSchema = z.object({ secret: z.string().nonempty('Secret is required') }); @@ -299,6 +304,17 @@ export const withValidRoomBulkDeleteRequest = (req: Request, res: Response, next next(); }; +export const withValidRoomStatus = (req: Request, res: Response, next: NextFunction) => { + const { success, error, data } = UpdateRoomStatusSchema.safeParse(req.body); + + if (!success) { + return rejectUnprocessableRequest(res, error); + } + + req.body = data; + next(); +}; + export const withValidRoomSecret = (req: Request, res: Response, next: NextFunction) => { const { success, error, data } = RecordingTokenRequestSchema.safeParse(req.body); diff --git a/backend/src/routes/room.routes.ts b/backend/src/routes/room.routes.ts index 4135e2c..1ea3fd6 100644 --- a/backend/src/routes/room.routes.ts +++ b/backend/src/routes/room.routes.ts @@ -16,7 +16,8 @@ import { withValidRoomId, withValidRoomOptions, withValidRoomPreferences, - withValidRoomSecret + withValidRoomSecret, + withValidRoomStatus } from '../middlewares/index.js'; export const roomRouter = Router(); @@ -42,6 +43,7 @@ roomRouter.delete( withValidRoomBulkDeleteRequest, roomCtrl.bulkDeleteRooms ); + roomRouter.get( '/:roomId', withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN), participantTokenValidator), @@ -55,6 +57,7 @@ roomRouter.delete( withValidRoomDeleteRequest, roomCtrl.deleteRoom ); + roomRouter.get( '/:roomId/preferences', withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN), participantTokenValidator), @@ -70,6 +73,14 @@ roomRouter.put( roomCtrl.updateRoomPreferences ); +roomRouter.put( + '/:roomId/status', + withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)), + withValidRoomId, + withValidRoomStatus, + roomCtrl.updateRoomStatus +); + // Internal room routes export const internalRoomRouter = Router(); internalRoomRouter.use(bodyParser.urlencoded({ extended: true })); diff --git a/backend/src/services/room.service.ts b/backend/src/services/room.service.ts index 9798393..f594117 100644 --- a/backend/src/services/room.service.ts +++ b/backend/src/services/room.service.ts @@ -146,6 +146,30 @@ export class RoomService { return room; } + /** + * Updates the status of a specific meeting room. + * + * @param roomId - The unique identifier of the meeting room to update + * @param status - The new status to apply to the meeting room + * @returns A Promise that resolves to an object containing the updated room + * and a boolean indicating if the update was immediate or scheduled + */ + async updateMeetRoomStatus(roomId: string, status: MeetRoomStatus): Promise<{ room: MeetRoom; updated: boolean }> { + const room = await this.getMeetRoom(roomId); + + // If closing the room while a meeting is active, mark it to be closed when the meeting ends + if (status === MeetRoomStatus.CLOSED && room.status === MeetRoomStatus.ACTIVE_MEETING) { + room.meetingEndAction = MeetingEndAction.CLOSE; + return { room, updated: false }; + } else { + room.status = status; + room.meetingEndAction = MeetingEndAction.NONE; + } + + await this.storageService.saveMeetRoom(room); + return { room, updated: true }; + } + /** * Checks if a meeting room with the specified name exists *