backend: add timestamps for permissions updates in room member and room schemas

This commit is contained in:
juancarmore 2026-02-02 14:14:29 +01:00
parent 268a6f9709
commit 70d51e21a6
6 changed files with 171 additions and 150 deletions

View File

@ -92,6 +92,10 @@ const MeetRoomMemberSchema = new Schema<MeetRoomMemberDocument>(
type: MeetRoomMemberPermissionsSchema,
required: true
},
permissionsUpdatedAt: {
type: Number,
required: true
},
currentParticipantIdentity: {
type: String,
required: false

View File

@ -285,6 +285,10 @@ const MeetRoomSchema = new Schema<MeetRoomDocument>(
required: true,
default: MeetRoomStatus.OPEN
},
rolesUpdatedAt: {
type: Number,
required: true
},
meetingEndAction: {
type: String,
enum: Object.values(MeetingEndAction),

View File

@ -111,15 +111,17 @@ export class RoomMemberService {
const room = await this.roomService.getMeetRoom(roomId);
const effectivePermissions = this.computeEffectivePermissions(room.roles, baseRole, customPermissions);
const now = Date.now();
const roomMember = {
memberId,
roomId,
name: memberName,
membershipDate: Date.now(),
membershipDate: now,
accessUrl,
baseRole,
customPermissions,
effectivePermissions
effectivePermissions,
permissionsUpdatedAt: now
};
return this.roomMemberRepository.create(roomMember);
}
@ -222,6 +224,7 @@ export class RoomMemberService {
member.baseRole,
member.customPermissions
);
member.permissionsUpdatedAt = Date.now();
const updatedMember = await this.roomMemberRepository.update(member);
@ -299,6 +302,7 @@ export class RoomMemberService {
// Update the member with new effective permissions
member.effectivePermissions = effectivePermissions;
member.permissionsUpdatedAt = Date.now();
await this.roomMemberRepository.update(member);
this.logger.verbose(

View File

@ -142,11 +142,12 @@ export class RoomService {
}
};
const now = Date.now();
const meetRoom: MeetRoom = {
roomId,
roomName: roomName!,
owner: user.userId,
creationDate: Date.now(),
creationDate: now,
// maxParticipants,
autoDeletionDate,
autoDeletionPolicy: autoDeletionDate ? autoDeletionPolicy : undefined,
@ -155,6 +156,7 @@ export class RoomService {
anonymous: anonymousConfig,
accessUrl: `/room/${roomId}`,
status: MeetRoomStatus.OPEN,
rolesUpdatedAt: now,
meetingEndAction: MeetingEndAction.NONE
};
return await this.roomRepository.create(meetRoom);
@ -263,6 +265,7 @@ export class RoomService {
// Merge existing roles with new roles (partial update)
room.roles = merge({}, room.roles, roles);
room.rolesUpdatedAt = Date.now();
await this.roomRepository.update(room);
// Update existing room members with new effective permissions
@ -288,6 +291,7 @@ export class RoomService {
// Merge existing anonymous config with new anonymous config (partial update)
room.anonymous = merge({}, room.anonymous, anonymous);
room.rolesUpdatedAt = Date.now();
await this.roomRepository.update(room);
return room;

View File

@ -24,6 +24,7 @@ export interface MeetRoomMember {
baseRole: MeetRoomMemberRole; // The base role of the member in the room
customPermissions?: Partial<MeetRoomMemberPermissions>; // Custom permissions for the member (if any)
effectivePermissions: MeetRoomMemberPermissions; // Effective permissions for the member (base role + custom permissions)
permissionsUpdatedAt: number; // Timestamp when the effective permissions were last updated
currentParticipantIdentity?: string; // The participant identity if the member is currently in a meeting, undefined otherwise
}

View File

@ -9,74 +9,78 @@ export interface MeetRoomOptions {
/**
* Name of the room
*/
roomName?: string;
roomName?: string;
/**
* Date in milliseconds since epoch when the room will be automatically deleted
*/
autoDeletionDate?: number;
autoDeletionDate?: number;
/**
* Configuration for automatic deletion behavior of the room. See {@link MeetRoomAutoDeletionPolicy} for details.
*/
autoDeletionPolicy?: MeetRoomAutoDeletionPolicy;
autoDeletionPolicy?: MeetRoomAutoDeletionPolicy;
/**
* Configuration of the room. See {@link MeetRoomConfig} for details.
*/
config?: Partial<MeetRoomConfig>;
config?: Partial<MeetRoomConfig>;
/**
* Roles configuration for the room. See {@link MeetRoomRolesConfig} for details.
*/
roles?: MeetRoomRolesConfig;
roles?: MeetRoomRolesConfig;
/**
* Anonymous access configuration for the room. See {@link MeetRoomAnonymousConfig} for details.
*/
anonymous?: MeetRoomAnonymousConfig;
// maxParticipants?: number | null;
anonymous?: MeetRoomAnonymousConfig;
// maxParticipants?: number | null;
}
/**
* Representation of a room
*/
export interface MeetRoom extends MeetRoomOptions {
/**
* Unique identifier of the room
*/
roomId: string;
/**
* Name of the room
*/
roomName: string;
/**
* User ID of the internal Meet user who owns this room
*/
owner: string;
/**
* Timestamp of room creation in milliseconds since epoch
*/
creationDate: number;
/**
* Configuration of the room. See {@link MeetRoomConfig} for details.
*/
config: MeetRoomConfig;
/**
* Roles configuration for the room. See {@link MeetRoomRoles} for details.
*/
roles: MeetRoomRoles;
/**
* Anonymous access configuration for the room. See {@link MeetRoomAnonymous} for details.
*/
anonymous: MeetRoomAnonymous;
/**
* General access URL for authenticated users (owner and internal members)
*/
accessUrl: string;
/**
* Status of the room. See {@link MeetRoomStatus} for details.
*/
status: MeetRoomStatus;
/**
* Action to take on the room when the meeting ends. See {@link MeetingEndAction} for details.
*/
meetingEndAction: MeetingEndAction;
/**
* Unique identifier of the room
*/
roomId: string;
/**
* Name of the room
*/
roomName: string;
/**
* User ID of the internal Meet user who owns this room
*/
owner: string;
/**
* Timestamp of room creation in milliseconds since epoch
*/
creationDate: number;
/**
* Configuration of the room. See {@link MeetRoomConfig} for details.
*/
config: MeetRoomConfig;
/**
* Roles configuration for the room. See {@link MeetRoomRoles} for details.
*/
roles: MeetRoomRoles;
/**
* Anonymous access configuration for the room. See {@link MeetRoomAnonymous} for details.
*/
anonymous: MeetRoomAnonymous;
/**
* General access URL for authenticated users (owner and internal members)
*/
accessUrl: string;
/**
* Status of the room. See {@link MeetRoomStatus} for details.
*/
status: MeetRoomStatus;
/**
* Timestamp in milliseconds of the last time the room's role permissions or anonymous access were updated
*/
rolesUpdatedAt: number;
/**
* Action to take on the room when the meeting ends. See {@link MeetingEndAction} for details.
*/
meetingEndAction: MeetingEndAction;
}
/**
@ -84,12 +88,12 @@ export interface MeetRoom extends MeetRoomOptions {
* Defines the complete permissions for moderator and speaker roles.
*/
export interface MeetRoomRoles {
moderator: {
permissions: MeetRoomMemberPermissions;
};
speaker: {
permissions: MeetRoomMemberPermissions;
};
moderator: {
permissions: MeetRoomMemberPermissions;
};
speaker: {
permissions: MeetRoomMemberPermissions;
};
}
/**
@ -97,12 +101,12 @@ export interface MeetRoomRoles {
* Allows partial permission updates.
*/
export interface MeetRoomRolesConfig {
moderator?: {
permissions: Partial<MeetRoomMemberPermissions>;
};
speaker?: {
permissions: Partial<MeetRoomMemberPermissions>;
};
moderator?: {
permissions: Partial<MeetRoomMemberPermissions>;
};
speaker?: {
permissions: Partial<MeetRoomMemberPermissions>;
};
}
/**
@ -110,14 +114,14 @@ export interface MeetRoomRolesConfig {
* Defines which roles have anonymous access enabled and their access URLs.
*/
export interface MeetRoomAnonymous {
moderator: {
enabled: boolean;
accessUrl: string;
};
speaker: {
enabled: boolean;
accessUrl: string;
};
moderator: {
enabled: boolean;
accessUrl: string;
};
speaker: {
enabled: boolean;
accessUrl: string;
};
}
/**
@ -125,132 +129,132 @@ export interface MeetRoomAnonymous {
* Only includes enabled flags.
*/
export interface MeetRoomAnonymousConfig {
moderator?: {
enabled: boolean;
};
speaker?: {
enabled: boolean;
};
moderator?: {
enabled: boolean;
};
speaker?: {
enabled: boolean;
};
}
/**
* Represents the current status of a meeting room.
*/
export enum MeetRoomStatus {
/**
* Room is open and available to host a meeting.
*/
OPEN = 'open',
/**
* Room is open and available to host a meeting.
*/
OPEN = 'open',
/**
* There is an ongoing meeting in the room.
*/
ACTIVE_MEETING = 'active_meeting',
/**
* There is an ongoing meeting in the room.
*/
ACTIVE_MEETING = 'active_meeting',
/**
* Room is closed to hosting new meetings.
*/
CLOSED = 'closed',
/**
* Room is closed to hosting new meetings.
*/
CLOSED = 'closed'
}
/**
* Defines the action to take when a meeting ends.
*/
export enum MeetingEndAction {
/**
* No action is taken when the meeting ends.
*/
NONE = 'none',
/**
* No action is taken when the meeting ends.
*/
NONE = 'none',
/**
* The room will be closed when the meeting ends.
*/
CLOSE = 'close',
/**
* The room will be closed when the meeting ends.
*/
CLOSE = 'close',
/**
* The room (and its recordings, if any) will be deleted
* when the meeting ends.
*/
DELETE = 'delete',
/**
* The room (and its recordings, if any) will be deleted
* when the meeting ends.
*/
DELETE = 'delete'
}
/**
* Configuration for automatic deletion behavior of a meeting room.
*/
export interface MeetRoomAutoDeletionPolicy {
/**
* Deletion policy when there is an active meeting.
*/
withMeeting: MeetRoomDeletionPolicyWithMeeting;
/**
* Deletion policy when there is an active meeting.
*/
withMeeting: MeetRoomDeletionPolicyWithMeeting;
/**
* Deletion policy when recordings exist.
*/
withRecordings: MeetRoomDeletionPolicyWithRecordings;
/**
* Deletion policy when recordings exist.
*/
withRecordings: MeetRoomDeletionPolicyWithRecordings;
}
/**
* Defines how room deletion behaves when a meeting is active.
*/
export enum MeetRoomDeletionPolicyWithMeeting {
/**
* Force deletion even if there is an active meeting.
*/
FORCE = 'force',
/**
* Force deletion even if there is an active meeting.
*/
FORCE = 'force',
/**
* Delete the room when the meeting ends.
*/
WHEN_MEETING_ENDS = 'when_meeting_ends',
/**
* Delete the room when the meeting ends.
*/
WHEN_MEETING_ENDS = 'when_meeting_ends',
/**
* Fail the deletion if there is an active meeting.
*/
FAIL = 'fail',
/**
* Fail the deletion if there is an active meeting.
*/
FAIL = 'fail'
}
/**
* Defines how room deletion behaves when recordings exist.
*/
export enum MeetRoomDeletionPolicyWithRecordings {
/**
* Force deletion even if there are ongoing or previous recordings.
*/
FORCE = 'force',
/**
* Force deletion even if there are ongoing or previous recordings.
*/
FORCE = 'force',
/**
* Close the room and keep recordings.
*/
CLOSE = 'close',
/**
* Close the room and keep recordings.
*/
CLOSE = 'close',
/**
* Fail the deletion if there are ongoing or previous recordings.
*/
FAIL = 'fail',
/**
* Fail the deletion if there are ongoing or previous recordings.
*/
FAIL = 'fail'
}
export interface MeetRoomFilters extends SortAndPagination {
roomName?: string;
status?: MeetRoomStatus;
fields?: string;
roomName?: string;
status?: MeetRoomStatus;
fields?: string;
}
export enum MeetRoomDeletionSuccessCode {
ROOM_DELETED = 'room_deleted',
ROOM_WITH_ACTIVE_MEETING_DELETED = 'room_with_active_meeting_deleted',
ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_DELETED = 'room_with_active_meeting_scheduled_to_be_deleted',
ROOM_AND_RECORDINGS_DELETED = 'room_and_recordings_deleted',
ROOM_CLOSED = 'room_closed',
ROOM_WITH_ACTIVE_MEETING_AND_RECORDINGS_DELETED = 'room_with_active_meeting_and_recordings_deleted',
ROOM_WITH_ACTIVE_MEETING_CLOSED = 'room_with_active_meeting_closed',
ROOM_WITH_ACTIVE_MEETING_AND_RECORDINGS_SCHEDULED_TO_BE_DELETED = 'room_with_active_meeting_and_recordings_scheduled_to_be_deleted',
ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_CLOSED = 'room_with_active_meeting_scheduled_to_be_closed',
ROOM_DELETED = 'room_deleted',
ROOM_WITH_ACTIVE_MEETING_DELETED = 'room_with_active_meeting_deleted',
ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_DELETED = 'room_with_active_meeting_scheduled_to_be_deleted',
ROOM_AND_RECORDINGS_DELETED = 'room_and_recordings_deleted',
ROOM_CLOSED = 'room_closed',
ROOM_WITH_ACTIVE_MEETING_AND_RECORDINGS_DELETED = 'room_with_active_meeting_and_recordings_deleted',
ROOM_WITH_ACTIVE_MEETING_CLOSED = 'room_with_active_meeting_closed',
ROOM_WITH_ACTIVE_MEETING_AND_RECORDINGS_SCHEDULED_TO_BE_DELETED = 'room_with_active_meeting_and_recordings_scheduled_to_be_deleted',
ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_CLOSED = 'room_with_active_meeting_scheduled_to_be_closed'
}
export enum MeetRoomDeletionErrorCode {
ROOM_HAS_ACTIVE_MEETING = 'room_has_active_meeting',
ROOM_HAS_RECORDINGS = 'room_has_recordings',
ROOM_WITH_ACTIVE_MEETING_HAS_RECORDINGS = 'room_with_active_meeting_has_recordings',
ROOM_WITH_ACTIVE_MEETING_HAS_RECORDINGS_CANNOT_SCHEDULE_DELETION = 'room_with_active_meeting_has_recordings_cannot_schedule_deletion',
ROOM_WITH_RECORDINGS_HAS_ACTIVE_MEETING = 'room_with_recordings_has_active_meeting',
ROOM_HAS_ACTIVE_MEETING = 'room_has_active_meeting',
ROOM_HAS_RECORDINGS = 'room_has_recordings',
ROOM_WITH_ACTIVE_MEETING_HAS_RECORDINGS = 'room_with_active_meeting_has_recordings',
ROOM_WITH_ACTIVE_MEETING_HAS_RECORDINGS_CANNOT_SCHEDULE_DELETION = 'room_with_active_meeting_has_recordings_cannot_schedule_deletion',
ROOM_WITH_RECORDINGS_HAS_ACTIVE_MEETING = 'room_with_recordings_has_active_meeting'
}