openvidu-meet/backend/src/controllers/room.controller.ts
juancarmore 0cab67eb65 Implement authentication transport modes for JWT tokens
- Added AuthTransportMode enum to define COOKIE and HEADER modes.
- Updated AuthenticationConfig interface to include authTransportMode.
- Refactored token handling in participant and recording services to support header-based authentication.
- Introduced TokenStorageService for managing JWT tokens in localStorage and sessionStorage.
- Modified middleware and controllers to utilize new token extraction methods based on transport mode.
- Updated frontend services and components to handle token storage and retrieval according to the selected transport mode.
- Enhanced error handling and logging for authentication processes.
2025-10-09 19:13:08 +02:00

265 lines
8.8 KiB
TypeScript

import {
AuthTransportMode,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomDeletionSuccessCode,
MeetRoomFilters,
MeetRoomOptions,
MeetRoomRoleAndPermissions,
ParticipantRole
} from '@typings-ce';
import { Request, Response } from 'express';
import { container } from '../config/index.js';
import INTERNAL_CONFIG from '../config/internal-config.js';
import { handleError } from '../models/error.model.js';
import { LoggerService, ParticipantService, RoomService } from '../services/index.js';
import { getAuthTransportMode, getBaseUrl, getCookieOptions } from '../utils/index.js';
export const createRoom = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const options: MeetRoomOptions = req.body;
try {
logger.verbose(`Creating room with options '${JSON.stringify(options)}'`);
const room = await roomService.createMeetRoom(options);
res.set('Location', `${getBaseUrl()}${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${room.roomId}`);
return res.status(201).json(room);
} catch (error) {
handleError(res, error, 'creating room');
}
};
export const getRooms = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const queryParams = req.query as unknown as MeetRoomFilters;
logger.verbose('Getting all rooms');
try {
const { rooms, isTruncated, nextPageToken } = await roomService.getAllMeetRooms(queryParams);
const maxItems = Number(queryParams.maxItems);
return res.status(200).json({ rooms, pagination: { isTruncated, nextPageToken, maxItems } });
} catch (error) {
handleError(res, error, 'getting rooms');
}
};
export const getRoom = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const { roomId } = req.params;
const fields = req.query.fields as string | undefined;
const role = req.session?.participantRole;
try {
logger.verbose(`Getting room '${roomId}'`);
const roomService = container.get(RoomService);
const room = await roomService.getMeetRoom(roomId, fields, role);
return res.status(200).json(room);
} catch (error) {
handleError(res, error, `getting room '${roomId}'`);
}
};
export const deleteRoom = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const { roomId } = req.params;
const { withMeeting, withRecordings } = req.query as {
withMeeting: MeetRoomDeletionPolicyWithMeeting;
withRecordings: MeetRoomDeletionPolicyWithRecordings;
};
try {
logger.verbose(`Deleting room '${roomId}'`);
const response = await roomService.deleteMeetRoom(roomId, withMeeting, withRecordings);
// Determine the status code based on the success code
// If the room action is scheduled, return 202. Otherwise, return 200.
const scheduledSuccessCodes = [
MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_DELETED,
MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_CLOSED,
MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_AND_RECORDINGS_SCHEDULED_TO_BE_DELETED
];
const statusCode = scheduledSuccessCodes.includes(response.successCode) ? 202 : 200;
logger.info(response.message);
return res.status(statusCode).json(response);
} catch (error) {
handleError(res, error, `deleting room '${roomId}'`);
}
};
export const bulkDeleteRooms = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const { roomIds, withMeeting, withRecordings } = req.query as {
roomIds: string[];
withMeeting: MeetRoomDeletionPolicyWithMeeting;
withRecordings: MeetRoomDeletionPolicyWithRecordings;
};
try {
logger.verbose(`Deleting rooms: ${roomIds}`);
const { successful, failed } = await roomService.bulkDeleteMeetRooms(roomIds, withMeeting, withRecordings);
logger.info(
`Bulk delete operation - Successfully processed rooms: ${successful.length}, failed to process: ${failed.length}`
);
if (failed.length === 0) {
// All rooms were successfully processed
return res.status(200).json({ message: 'All rooms successfully processed for deletion', successful });
} else {
// Some rooms failed to process
return res
.status(400)
.json({ message: `${failed.length} room(s) failed to process while deleting`, successful, failed });
}
} catch (error) {
handleError(res, error, `deleting rooms`);
}
};
export const getRoomConfig = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const { roomId } = req.params;
logger.verbose(`Getting room config for room '${roomId}'`);
try {
const { config } = await roomService.getMeetRoom(roomId);
return res.status(200).json(config);
} catch (error) {
handleError(res, error, `getting room config for room '${roomId}'`);
}
};
export const updateRoomConfig = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const { config } = req.body;
const { roomId } = req.params;
logger.verbose(`Updating room config for room '${roomId}'`);
try {
await roomService.updateMeetRoomConfig(roomId, config);
return res.status(200).json({ message: `Room config for room '${roomId}' updated successfully` });
} catch (error) {
handleError(res, error, `updating room config for room '${roomId}'`);
}
};
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);
const { roomId } = req.params;
const { secret } = req.body;
logger.verbose(`Generating recording token for room '${roomId}'`);
try {
const token = await roomService.generateRecordingToken(roomId, secret);
const authTransportMode = await getAuthTransportMode();
// Send recording token as cookie for cookie mode
if (authTransportMode === AuthTransportMode.COOKIE) {
res.cookie(
INTERNAL_CONFIG.RECORDING_TOKEN_COOKIE_NAME,
token,
getCookieOptions('/', INTERNAL_CONFIG.RECORDING_TOKEN_EXPIRATION)
);
}
return res.status(200).json({ token });
} catch (error) {
handleError(res, error, `generating recording token for room '${roomId}'`);
}
};
export const getRoomRolesAndPermissions = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const participantService = container.get(ParticipantService);
const { roomId } = req.params;
// Check if the room exists
try {
await roomService.getMeetRoom(roomId);
} catch (error) {
return handleError(res, error, `getting room '${roomId}'`);
}
logger.verbose(`Getting roles and associated permissions for room '${roomId}'`);
const moderatorPermissions = participantService.getParticipantPermissions(roomId, ParticipantRole.MODERATOR);
const speakerPermissions = participantService.getParticipantPermissions(roomId, ParticipantRole.SPEAKER);
const rolesAndPermissions = [
{
role: ParticipantRole.MODERATOR,
permissions: moderatorPermissions
},
{
role: ParticipantRole.SPEAKER,
permissions: speakerPermissions
}
];
res.status(200).json(rolesAndPermissions);
};
export const getRoomRoleAndPermissions = async (req: Request, res: Response) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
const participantService = container.get(ParticipantService);
const { roomId, secret } = req.params;
try {
logger.verbose(`Getting room role and associated permissions for room '${roomId}' and secret '${secret}'`);
const role = await roomService.getRoomRoleBySecret(roomId, secret);
const permissions = participantService.getParticipantPermissions(roomId, role);
const roleAndPermissions: MeetRoomRoleAndPermissions = {
role,
permissions
};
return res.status(200).json(roleAndPermissions);
} catch (error) {
handleError(res, error, `getting room role and permissions for room '${roomId}' and secret '${secret}'`);
}
};