backend: add autoDeletionPolicy and related schemas to room validation and add new fileds when creating Meet room

This commit is contained in:
juancarmore 2025-08-28 10:34:12 +02:00
parent 04f828f82e
commit e7fae2b3be
2 changed files with 57 additions and 35 deletions

View File

@ -2,6 +2,9 @@ import {
MeetChatPreferences, MeetChatPreferences,
MeetRecordingAccess, MeetRecordingAccess,
MeetRecordingPreferences, MeetRecordingPreferences,
MeetRoomAutoDeletionPolicy,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomFilters, MeetRoomFilters,
MeetRoomOptions, MeetRoomOptions,
MeetRoomPreferences, MeetRoomPreferences,
@ -55,17 +58,6 @@ export const nonEmptySanitizedRoomId = (fieldName: string) =>
message: `${fieldName} cannot be empty after sanitization` message: `${fieldName} cannot be empty after sanitization`
}); });
const validForceQueryParam = () =>
z
.preprocess((val) => {
if (typeof val === 'string') {
return val.toLowerCase() === 'true';
}
return val;
}, z.boolean())
.default(false);
const RecordingAccessSchema: z.ZodType<MeetRecordingAccess> = z.enum([ const RecordingAccessSchema: z.ZodType<MeetRecordingAccess> = z.enum([
MeetRecordingAccess.ADMIN, MeetRecordingAccess.ADMIN,
MeetRecordingAccess.ADMIN_MODERATOR, MeetRecordingAccess.ADMIN_MODERATOR,
@ -102,6 +94,23 @@ const RoomPreferencesSchema: z.ZodType<MeetRoomPreferences> = z.object({
virtualBackgroundPreferences: VirtualBackgroundPreferencesSchema virtualBackgroundPreferences: VirtualBackgroundPreferencesSchema
}); });
const RoomDeletionPolicyWithMeetingSchema: z.ZodType<MeetRoomDeletionPolicyWithMeeting> = z.enum([
MeetRoomDeletionPolicyWithMeeting.FORCE,
MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
MeetRoomDeletionPolicyWithMeeting.FAIL
]);
const RoomDeletionPolicyWithRecordingsSchema: z.ZodType<MeetRoomDeletionPolicyWithRecordings> = z.enum([
MeetRoomDeletionPolicyWithRecordings.FORCE,
MeetRoomDeletionPolicyWithRecordings.CLOSE,
MeetRoomDeletionPolicyWithRecordings.FAIL
]);
const RoomAutoDeletionPolicySchema: z.ZodType<MeetRoomAutoDeletionPolicy> = z.object({
withMeeting: RoomDeletionPolicyWithMeetingSchema,
withRecordings: RoomDeletionPolicyWithRecordingsSchema
});
const RoomRequestOptionsSchema: z.ZodType<MeetRoomOptions> = z.object({ const RoomRequestOptionsSchema: z.ZodType<MeetRoomOptions> = z.object({
roomName: z roomName: z
.string() .string()
@ -117,6 +126,10 @@ const RoomRequestOptionsSchema: z.ZodType<MeetRoomOptions> = z.object({
`autoDeletionDate must be at least ${INTERNAL_CONFIG.MIN_FUTURE_TIME_FOR_ROOM_AUTODELETION_DATE} in the future` `autoDeletionDate must be at least ${INTERNAL_CONFIG.MIN_FUTURE_TIME_FOR_ROOM_AUTODELETION_DATE} in the future`
) )
.optional(), .optional(),
autoDeletionPolicy: RoomAutoDeletionPolicySchema.optional().default({
withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
}),
preferences: RoomPreferencesSchema.optional().default({ preferences: RoomPreferencesSchema.optional().default({
recordingPreferences: { enabled: true, allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER }, recordingPreferences: { enabled: true, allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER },
chatPreferences: { enabled: true }, chatPreferences: { enabled: true },
@ -146,6 +159,11 @@ const GetRoomFiltersSchema: z.ZodType<MeetRoomFilters> = z.object({
fields: z.string().optional() fields: z.string().optional()
}); });
const DeleteRoomQueryParamsSchema = z.object({
withMeeting: RoomDeletionPolicyWithMeetingSchema.optional().default(MeetRoomDeletionPolicyWithMeeting.FAIL),
withRecordings: RoomDeletionPolicyWithRecordingsSchema.optional().default(MeetRoomDeletionPolicyWithRecordings.FAIL)
});
const BulkDeleteRoomsSchema = z.object({ const BulkDeleteRoomsSchema = z.object({
roomIds: z.preprocess( roomIds: z.preprocess(
(arg) => { (arg) => {
@ -181,7 +199,8 @@ const BulkDeleteRoomsSchema = z.object({
message: 'At least one valid roomId is required after sanitization' message: 'At least one valid roomId is required after sanitization'
}) })
), ),
force: validForceQueryParam() withMeeting: RoomDeletionPolicyWithMeetingSchema.optional().default(MeetRoomDeletionPolicyWithMeeting.FAIL),
withRecordings: RoomDeletionPolicyWithRecordingsSchema.optional().default(MeetRoomDeletionPolicyWithRecordings.FAIL)
}); });
const UpdateRoomPreferencesSchema = z.object({ const UpdateRoomPreferencesSchema = z.object({
@ -250,18 +269,6 @@ export const withValidRoomId = (req: Request, res: Response, next: NextFunction)
next(); next();
}; };
export const withValidRoomBulkDeleteRequest = (req: Request, res: Response, next: NextFunction) => {
const { success, error, data } = BulkDeleteRoomsSchema.safeParse(req.query);
if (!success) {
return rejectUnprocessableRequest(res, error);
}
req.query.roomIds = data.roomIds as any;
req.query.force = data.force ? 'true' : 'false';
next();
};
export const withValidRoomDeleteRequest = (req: Request, res: Response, next: NextFunction) => { export const withValidRoomDeleteRequest = (req: Request, res: Response, next: NextFunction) => {
const roomIdResult = nonEmptySanitizedRoomId('roomId').safeParse(req.params.roomId); const roomIdResult = nonEmptySanitizedRoomId('roomId').safeParse(req.params.roomId);
@ -271,13 +278,24 @@ export const withValidRoomDeleteRequest = (req: Request, res: Response, next: Ne
req.params.roomId = roomIdResult.data; req.params.roomId = roomIdResult.data;
const forceResult = validForceQueryParam().safeParse(req.query.force); const queryParamsResult = DeleteRoomQueryParamsSchema.safeParse(req.query);
if (!forceResult.success) { if (!queryParamsResult.success) {
return rejectUnprocessableRequest(res, forceResult.error); return rejectUnprocessableRequest(res, queryParamsResult.error);
} }
req.query.force = forceResult.data ? 'true' : 'false'; req.query = queryParamsResult.data;
next();
};
export const withValidRoomBulkDeleteRequest = (req: Request, res: Response, next: NextFunction) => {
const { success, error, data } = BulkDeleteRoomsSchema.safeParse(req.query);
if (!success) {
return rejectUnprocessableRequest(res, error);
}
req.query = data;
next(); next();
}; };

View File

@ -1,9 +1,11 @@
import { import {
MeetingEndAction,
MeetRecordingAccess, MeetRecordingAccess,
MeetRoom, MeetRoom,
MeetRoomFilters, MeetRoomFilters,
MeetRoomOptions, MeetRoomOptions,
MeetRoomPreferences, MeetRoomPreferences,
MeetRoomStatus,
ParticipantRole, ParticipantRole,
RecordingPermissions RecordingPermissions
} from '@typings-ce'; } from '@typings-ce';
@ -15,6 +17,7 @@ import { uid } from 'uid/single';
import INTERNAL_CONFIG from '../config/internal-config.js'; import INTERNAL_CONFIG from '../config/internal-config.js';
import { MEET_NAME_ID } from '../environment.js'; import { MEET_NAME_ID } from '../environment.js';
import { MeetRoomHelper, UtilsHelper } from '../helpers/index.js'; import { MeetRoomHelper, UtilsHelper } from '../helpers/index.js';
import { validateRecordingTokenMetadata } from '../middlewares/index.js';
import { import {
errorInvalidRoomSecret, errorInvalidRoomSecret,
errorRoomMetadataNotFound, errorRoomMetadataNotFound,
@ -23,15 +26,14 @@ import {
} from '../models/error.model.js'; } from '../models/error.model.js';
import { import {
DistributedEventService, DistributedEventService,
FrontendEventService,
IScheduledTask, IScheduledTask,
LiveKitService, LiveKitService,
LoggerService, LoggerService,
MeetStorageService, MeetStorageService,
TaskSchedulerService, TaskSchedulerService,
TokenService, TokenService
FrontendEventService
} from './index.js'; } from './index.js';
import { validateRecordingTokenMetadata } from '../middlewares/index.js';
/** /**
* Service for managing OpenVidu Meet rooms. * Service for managing OpenVidu Meet rooms.
@ -70,7 +72,7 @@ export class RoomService {
* *
*/ */
async createMeetRoom(baseUrl: string, roomOptions: MeetRoomOptions): Promise<MeetRoom> { async createMeetRoom(baseUrl: string, roomOptions: MeetRoomOptions): Promise<MeetRoom> {
const { roomName, autoDeletionDate, preferences } = roomOptions; const { roomName, autoDeletionDate, autoDeletionPolicy, preferences } = roomOptions;
const roomIdPrefix = roomName!.replace(/\s+/g, ''); // Remove all spaces const roomIdPrefix = roomName!.replace(/\s+/g, ''); // Remove all spaces
const roomId = `${roomIdPrefix}-${uid(15)}`; // Generate a unique room ID based on the room name const roomId = `${roomIdPrefix}-${uid(15)}`; // Generate a unique room ID based on the room name
@ -80,13 +82,15 @@ export class RoomService {
creationDate: Date.now(), creationDate: Date.now(),
// maxParticipants, // maxParticipants,
autoDeletionDate, autoDeletionDate,
preferences, autoDeletionPolicy,
preferences: preferences!,
moderatorUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`, moderatorUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`,
speakerUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}` speakerUrl: `${baseUrl}/room/${roomId}?secret=${secureUid(10)}`,
status: MeetRoomStatus.OPEN,
meetingEndAction: MeetingEndAction.NONE
}; };
await this.storageService.saveMeetRoom(meetRoom); await this.storageService.saveMeetRoom(meetRoom);
return meetRoom; return meetRoom;
} }