diff --git a/meet-ce/backend/src/repositories/api-key.repository.ts b/meet-ce/backend/src/repositories/api-key.repository.ts index a54a8f20..bcd5ce7c 100644 --- a/meet-ce/backend/src/repositories/api-key.repository.ts +++ b/meet-ce/backend/src/repositories/api-key.repository.ts @@ -1,39 +1,36 @@ import { MeetApiKey } from '@openvidu-meet/typings'; import { inject, injectable } from 'inversify'; +import { Require_id } from 'mongoose'; import { MeetApiKeyDocument, MeetApiKeyModel } from '../models/mongoose-schemas/api-key.schema.js'; import { LoggerService } from '../services/logger.service.js'; import { BaseRepository } from './base.repository.js'; /** * Repository for managing MeetApiKey entities in MongoDB. - * - * @template TApiKey - The domain type extending MeetApiKey (default: MeetApiKey) */ @injectable() -export class ApiKeyRepository extends BaseRepository< - TApiKey, - MeetApiKeyDocument -> { +export class ApiKeyRepository extends BaseRepository { constructor(@inject(LoggerService) logger: LoggerService) { super(logger, MeetApiKeyModel); } - protected toDomain(document: MeetApiKeyDocument): TApiKey { - return document.toObject() as TApiKey; + protected toDomain(dbObject: Require_id & { __v: number }): MeetApiKey { + const { _id, __v, schemaVersion, ...apiKey } = dbObject; + (void _id, __v, schemaVersion); + return apiKey as MeetApiKey; } /** * Creates a new API key. */ - async create(apiKey: TApiKey): Promise { - const doc = await this.createDocument(apiKey); - return this.toDomain(doc); + async create(apiKey: MeetApiKey): Promise { + return this.createDocument(apiKey); } /** * Returns all API keys. */ - async findAll(): Promise { + async findAll(): Promise { return await super.findAll(); } diff --git a/meet-ce/backend/src/repositories/global-config.repository.ts b/meet-ce/backend/src/repositories/global-config.repository.ts index 44f93b75..79581bf7 100644 --- a/meet-ce/backend/src/repositories/global-config.repository.ts +++ b/meet-ce/backend/src/repositories/global-config.repository.ts @@ -1,5 +1,6 @@ import { GlobalConfig } from '@openvidu-meet/typings'; import { inject, injectable } from 'inversify'; +import { Require_id } from 'mongoose'; import { MeetGlobalConfigDocument, MeetGlobalConfigModel } from '../models/mongoose-schemas/global-config.schema.js'; import { LoggerService } from '../services/logger.service.js'; import { BaseRepository } from './base.repository.js'; @@ -9,26 +10,17 @@ import { BaseRepository } from './base.repository.js'; * * IMPORTANT: This collection should only contain ONE document representing the * system-wide global configuration. Methods are designed to work with this singleton pattern. - * - * @template TGlobalConfig - The domain type extending GlobalConfig (default: GlobalConfig) */ @injectable() -export class GlobalConfigRepository extends BaseRepository< - TGlobalConfig, - MeetGlobalConfigDocument -> { +export class GlobalConfigRepository extends BaseRepository { constructor(@inject(LoggerService) logger: LoggerService) { super(logger, MeetGlobalConfigModel); } - /** - * Transforms a MongoDB document into a domain GlobalConfig object. - * - * @param document - The MongoDB document - * @returns GlobalConfig domain object - */ - protected toDomain(document: MeetGlobalConfigDocument): TGlobalConfig { - return document.toObject() as TGlobalConfig; + protected toDomain(dbObject: Require_id & { __v: number }): GlobalConfig { + const { _id, __v, schemaVersion, ...globalConfig } = dbObject; + (void _id, __v, schemaVersion); + return globalConfig as GlobalConfig; } /** @@ -40,9 +32,8 @@ export class GlobalConfigRepository { - const document = await this.createDocument(config); - return this.toDomain(document); + async create(config: GlobalConfig): Promise { + return this.createDocument(config); } /** @@ -54,10 +45,9 @@ export class GlobalConfigRepository { + async update(config: GlobalConfig): Promise { // Update the first document in the collection (there should only be one) - const document = await this.updateOne({}, config); - return this.toDomain(document); + return this.updateOne({}, config); } /** @@ -65,10 +55,9 @@ export class GlobalConfigRepository { + async get(): Promise { // Get the first (and only) document from the collection - const document = await this.findOne({}); - return document ? this.toDomain(document) : null; + return this.findOne({}); } /** diff --git a/meet-ce/backend/src/repositories/migration.repository.ts b/meet-ce/backend/src/repositories/migration.repository.ts index 80f88801..4acb4f6e 100644 --- a/meet-ce/backend/src/repositories/migration.repository.ts +++ b/meet-ce/backend/src/repositories/migration.repository.ts @@ -1,4 +1,5 @@ import { inject, injectable } from 'inversify'; +import { Require_id } from 'mongoose'; import { MeetMigration, MigrationName, MigrationStatus } from '../models/migration.model.js'; import { MeetMigrationDocument, MeetMigrationModel } from '../models/mongoose-schemas/migration.schema.js'; import { LoggerService } from '../services/logger.service.js'; @@ -10,14 +11,10 @@ export class MigrationRepository extends BaseRepository & { __v: number }): MeetMigration { + const { _id, __v, ...migration } = dbObject; + (void _id, __v); + return migration as MeetMigration; } /** @@ -34,7 +31,7 @@ export class MigrationRepository extends BaseRepository): Promise { - const document = await this.updateOne( + return this.updateOne( { name }, { $set: { @@ -78,7 +73,6 @@ export class MigrationRepository extends BaseRepository { - const document = await this.updateOne( + return this.updateOne( { name }, { $set: { @@ -100,7 +94,6 @@ export class MigrationRepository extends BaseRepository extends BaseRepository< - TRecording, - MeetRecordingDocument -> { +export class RecordingRepository extends BaseRepository { constructor(@inject(LoggerService) logger: LoggerService) { super(logger, MeetRecordingModel); } - /** - * Transforms a Mongoose document to a domain object. - * Removes access secrets before returning. - */ - protected toDomain(document: MeetRecordingDocument): TRecording { - return document.toObject() as TRecording; + protected toDomain(dbObject: Require_id & { __v: number }): MeetRecordingInfo { + const { _id, __v, schemaVersion, accessSecrets, ...recording } = dbObject; + (void _id, __v, schemaVersion, accessSecrets); + return recording as MeetRecordingInfo; } /** - * Creates a new recording document in the database. - * Automatically generates access secrets if not provided. + * Creates a new recording with generated access secrets. * - * @param recording - The recording information to create (optionally includes accessSecrets) + * @param recording - The recording information to create (excluding access secrets) * @returns The created recording (without access secrets) */ - async create(recording: TRecording): Promise { - // Check if recording already includes accessSecrets - const hasAccessSecrets = - 'accessSecrets' in recording && - recording.accessSecrets && - typeof recording.accessSecrets === 'object' && - 'public' in recording.accessSecrets && - 'private' in recording.accessSecrets; - - // Generate access secrets only if not provided - const recordingDoc = { + async create(recording: MeetRecordingInfo): Promise { + // Generate access secrets + const recordingDoc: Omit = { ...recording, - accessSecrets: hasAccessSecrets - ? recording.accessSecrets - : { - public: secureUid(10), - private: secureUid(10) - } + accessSecrets: { + public: secureUid(10), + private: secureUid(10) + } }; - - const result = await this.createDocument(recordingDoc); - return this.toDomain(result); + return this.createDocument(recordingDoc); } /** @@ -70,9 +53,8 @@ export class RecordingRepository { - const document = await this.updateOne({ recordingId: recording.recordingId }, { $set: recording }); - return this.toDomain(document); + async update(recording: MeetRecordingInfo): Promise { + return this.updateOne({ recordingId: recording.recordingId }, { $set: recording }); } /** @@ -82,9 +64,8 @@ export class RecordingRepository { - const document = await this.findOne({ recordingId }, fields); - return document ? this.toDomain(document) : null; + async findByRecordingId(recordingId: string, fields?: MeetRecordingField[]): Promise { + return this.findOne({ recordingId }, fields); } /** @@ -107,7 +88,7 @@ export class RecordingRepository { @@ -124,7 +105,7 @@ export class RecordingRepository = {}; + const filter: FilterQuery = {}; if (roomIds) { // Filter by multiple room IDs @@ -171,7 +152,7 @@ export class RecordingRepository { + async findAllByRoomId(roomId: string): Promise { return await this.findAll({ roomId }); } @@ -203,8 +184,8 @@ export class RecordingRepository { - return await this.findAll({ + async findActiveRecordings(): Promise { + return this.findAll({ status: { $in: [MeetRecordingStatus.ACTIVE, MeetRecordingStatus.ENDING] } }); } @@ -216,7 +197,7 @@ export class RecordingRepository { - await this.deleteOne({ recordingId }); + this.deleteOne({ recordingId }); } /** @@ -226,14 +207,14 @@ export class RecordingRepository { - await this.deleteMany({ recordingId: { $in: recordingIds } }); + this.deleteMany({ recordingId: { $in: recordingIds } }); } /** * Counts the total number of recordings. */ async countTotal(): Promise { - return await this.count(); + return this.count(); } /** diff --git a/meet-ce/backend/src/repositories/room-member.repository.ts b/meet-ce/backend/src/repositories/room-member.repository.ts index 940d76af..d6cd10cb 100644 --- a/meet-ce/backend/src/repositories/room-member.repository.ts +++ b/meet-ce/backend/src/repositories/room-member.repository.ts @@ -1,30 +1,24 @@ import { MeetRoomMember, MeetRoomMemberFilters, MeetRoomMemberPermissions, SortOrder } from '@openvidu-meet/typings'; import { inject, injectable } from 'inversify'; +import { FilterQuery, Require_id } from 'mongoose'; import { MeetRoomMemberDocument, MeetRoomMemberModel } from '../models/mongoose-schemas/room-member.schema.js'; import { LoggerService } from '../services/logger.service.js'; import { BaseRepository } from './base.repository.js'; /** * Repository for managing MeetRoomMember entities in MongoDB. - * Handles the storage and retrieval of room members. + * Provides CRUD operations and specialized queries for room member data. */ @injectable() -export class RoomMemberRepository extends BaseRepository< - TRoomMember, - MeetRoomMemberDocument -> { +export class RoomMemberRepository extends BaseRepository { constructor(@inject(LoggerService) logger: LoggerService) { super(logger, MeetRoomMemberModel); } - /** - * Transforms a MongoDB document into a domain room member object. - * - * @param document - The MongoDB document - * @returns Room member with computed permissions - */ - protected toDomain(document: MeetRoomMemberDocument): TRoomMember { - return document.toObject() as TRoomMember; + protected toDomain(dbObject: Require_id & { __v: number }): MeetRoomMember { + const { _id, __v, schemaVersion, ...member } = dbObject; + (void _id, __v, schemaVersion); + return member as MeetRoomMember; } /** @@ -33,9 +27,8 @@ export class RoomMemberRepository { - const document = await this.createDocument(member as TRoomMember); - return this.toDomain(document); + async create(member: MeetRoomMember): Promise { + return this.createDocument(member); } /** @@ -45,9 +38,8 @@ export class RoomMemberRepository { - const document = await this.updateOne({ roomId: member.roomId, memberId: member.memberId }, member); - return this.toDomain(document); + async update(member: MeetRoomMember): Promise { + return this.updateOne({ roomId: member.roomId, memberId: member.memberId }, member); } /** @@ -57,9 +49,8 @@ export class RoomMemberRepository { - const document = await this.findOne({ roomId, memberId }); - return document ? this.toDomain(document) : null; + async findByRoomAndMemberId(roomId: string, memberId: string): Promise { + return this.findOne({ roomId, memberId }); } /** @@ -70,8 +61,8 @@ export class RoomMemberRepository { - return await this.findAll({ roomId, memberId: { $in: memberIds } }, fields); + async findByRoomAndMemberIds(roomId: string, memberIds: string[], fields?: string[]): Promise { + return this.findAll({ roomId, memberId: { $in: memberIds } }, fields); } /** @@ -123,7 +114,7 @@ export class RoomMemberRepository { @@ -137,7 +128,7 @@ export class RoomMemberRepository = { roomId }; + const filter: FilterQuery = { roomId }; if (name) { filter.name = new RegExp(name, 'i'); diff --git a/meet-ce/backend/src/repositories/room.repository.ts b/meet-ce/backend/src/repositories/room.repository.ts index 065f3e5e..1baf4546 100644 --- a/meet-ce/backend/src/repositories/room.repository.ts +++ b/meet-ce/backend/src/repositories/room.repository.ts @@ -1,5 +1,6 @@ import { MeetRoom, MeetRoomField, MeetRoomFilters, MeetRoomStatus, SortOrder } from '@openvidu-meet/typings'; import { inject, injectable } from 'inversify'; +import { FilterQuery, Require_id } from 'mongoose'; import { MeetRoomDocument, MeetRoomModel } from '../models/mongoose-schemas/room.schema.js'; import { LoggerService } from '../services/logger.service.js'; import { getBasePath } from '../utils/html-dynamic-base-path.utils.js'; @@ -9,24 +10,24 @@ import { BaseRepository } from './base.repository.js'; /** * Repository for managing MeetRoom entities in MongoDB. * Provides CRUD operations and specialized queries for room data. - * - * @template TRoom - The domain type extending MeetRoom (default: MeetRoom) */ @injectable() -export class RoomRepository extends BaseRepository { +export class RoomRepository extends BaseRepository { constructor(@inject(LoggerService) logger: LoggerService) { super(logger, MeetRoomModel); } /** - * Transforms a MongoDB document into a domain room object. - * Enriches URLs with the base URL. + * Transforms a persisted MeetRoom document into a domain MeetRoom object. + * Enriches access URLs with the base URL. * - * @param document - The MongoDB document + * @param dbObject - The MongoDB document representing a room * @returns Room with complete URLs */ - protected toDomain(document: MeetRoomDocument): TRoom { - return this.enrichRoomWithBaseUrls(document); + protected toDomain(dbObject: Require_id & { __v: number }): MeetRoom { + const { _id, __v, schemaVersion, ...room } = dbObject; + (void _id, __v, schemaVersion); + return this.enrichRoomWithBaseUrls(room as MeetRoom); } /** @@ -36,10 +37,9 @@ export class RoomRepository extends BaseRepos * @param room - The room data to create * @returns The created room with enriched URLs */ - async create(room: TRoom): Promise { + async create(room: MeetRoom): Promise { const normalizedRoom = this.normalizeRoomForStorage(room); - const document = await this.createDocument(normalizedRoom); - return this.enrichRoomWithBaseUrls(document); + return this.createDocument(normalizedRoom); } /** @@ -52,10 +52,9 @@ export class RoomRepository extends BaseRepos * * TODO: This method should be renamed to replace or updateFull to better reflect that it replaces the entire document */ - async update(room: TRoom): Promise { + async update(room: MeetRoom): Promise { const normalizedRoom = this.normalizeRoomForStorage(room); - const document = await this.updateOne({ roomId: room.roomId }, normalizedRoom); - return this.enrichRoomWithBaseUrls(document); + return this.updateOne({ roomId: room.roomId }, normalizedRoom); } /** @@ -64,7 +63,7 @@ export class RoomRepository extends BaseRepos * @param roomId * @param fieldsToUpdate */ - async updatePartial(roomId: string, fieldsToUpdate: Partial): Promise { + async updatePartial(roomId: string, fieldsToUpdate: Partial): Promise { await this.updateOne({ roomId }, fieldsToUpdate); } @@ -76,9 +75,8 @@ export class RoomRepository extends BaseRepos * @param fields - Array of field names to include in the result * @returns The room or null if not found */ - async findByRoomId(roomId: string, fields?: MeetRoomField[]): Promise { - const document = await this.findOne({ roomId }, fields); - return document ? this.enrichRoomWithBaseUrls(document) : null; + async findByRoomId(roomId: string, fields?: MeetRoomField[]): Promise { + return this.findOne({ roomId }, fields); } /** @@ -89,8 +87,8 @@ export class RoomRepository extends BaseRepos * @param fields - Array of field names to include in the result * @returns Array of rooms owned by the user */ - async findByOwner(owner: string, fields?: MeetRoomField[]): Promise { - return await this.findAll({ owner }, fields); + async findByOwner(owner: string, fields?: MeetRoomField[]): Promise { + return this.findAll({ owner }, fields); } /** @@ -113,7 +111,7 @@ export class RoomRepository extends BaseRepos * @returns Object containing rooms array, pagination info, and optional next page token */ async find(options: MeetRoomFilters & { owner?: string; roomIds?: string[] } = {}): Promise<{ - rooms: TRoom[]; + rooms: MeetRoom[]; isTruncated: boolean; nextPageToken?: string; }> { @@ -130,7 +128,7 @@ export class RoomRepository extends BaseRepos } = options; // Build base filter - const filter: Record = {}; + const filter: FilterQuery = {}; // Handle owner and roomIds with $or when both are present if (owner && roomIds) { @@ -174,11 +172,11 @@ export class RoomRepository extends BaseRepos * * @returns Array of expired rooms with enriched URLs */ - async findExpiredRooms(): Promise { + async findExpiredRooms(): Promise { const now = Date.now(); // Find all rooms where autoDeletionDate exists and is less than now - return await this.findAll({ + return this.findAll({ autoDeletionDate: { $exists: true, $lt: now } }); } @@ -189,8 +187,8 @@ export class RoomRepository extends BaseRepos * * @returns Array of active rooms with enriched URLs */ - async findActiveRooms(): Promise { - return await this.findAll({ + async findActiveRooms(): Promise { + return this.findAll({ status: MeetRoomStatus.ACTIVE_MEETING }); } @@ -219,14 +217,14 @@ export class RoomRepository extends BaseRepos * Counts the total number of rooms. */ async countTotal(): Promise { - return await this.count(); + return this.count(); } /** * Counts the number of rooms with active meetings. */ async countActiveRooms(): Promise { - return await this.count({ status: MeetRoomStatus.ACTIVE_MEETING }); + return this.count({ status: MeetRoomStatus.ACTIVE_MEETING }); } // ========================================== @@ -240,7 +238,7 @@ export class RoomRepository extends BaseRepos * @param room - The room data to normalize * @returns Normalized room data */ - private normalizeRoomForStorage(room: TRoom): TRoom { + private normalizeRoomForStorage(room: MeetRoom): MeetRoom { return { ...room, accessUrl: this.extractPathFromUrl(room.accessUrl), @@ -307,9 +305,8 @@ export class RoomRepository extends BaseRepos * @param document - The MongoDB document * @returns Room data with complete URLs */ - private enrichRoomWithBaseUrls(document: MeetRoomDocument): TRoom { + private enrichRoomWithBaseUrls(room: MeetRoom): MeetRoom { const baseUrl = getBaseUrl(); - const room = document.toObject() as TRoom; return { ...room, diff --git a/meet-ce/backend/src/repositories/user.repository.ts b/meet-ce/backend/src/repositories/user.repository.ts index 8f0cee90..8033b0ce 100644 --- a/meet-ce/backend/src/repositories/user.repository.ts +++ b/meet-ce/backend/src/repositories/user.repository.ts @@ -1,5 +1,6 @@ import { MeetUser, MeetUserFilters, SortOrder } from '@openvidu-meet/typings'; import { inject, injectable } from 'inversify'; +import { FilterQuery, Require_id } from 'mongoose'; import { MeetUserDocument, MeetUserModel } from '../models/mongoose-schemas/user.schema.js'; import { LoggerService } from '../services/logger.service.js'; import { BaseRepository } from './base.repository.js'; @@ -7,23 +8,17 @@ import { BaseRepository } from './base.repository.js'; /** * Repository for managing MeetUser entities in MongoDB. * Provides CRUD operations and specialized queries for user data. - * - * @template TUser - The domain type extending MeetUser (default: MeetUser) */ @injectable() -export class UserRepository extends BaseRepository { +export class UserRepository extends BaseRepository { constructor(@inject(LoggerService) logger: LoggerService) { super(logger, MeetUserModel); } - /** - * Transforms a MongoDB document into a domain user object. - * - * @param document - The MongoDB document - * @returns User domain object - */ - protected toDomain(document: MeetUserDocument): TUser { - return document.toObject() as TUser; + protected toDomain(dbObject: Require_id & { __v: number }): MeetUser { + const { _id, __v, schemaVersion, ...user } = dbObject; + (void _id, __v, schemaVersion); + return user as MeetUser; } /** @@ -32,9 +27,8 @@ export class UserRepository extends BaseRepos * @param user - The user data to create * @returns The created user */ - async create(user: TUser): Promise { - const document = await this.createDocument(user); - return this.toDomain(document); + async create(user: MeetUser): Promise { + return this.createDocument(user); } /** @@ -44,9 +38,8 @@ export class UserRepository extends BaseRepos * @returns The updated user * @throws Error if user not found */ - async update(user: TUser): Promise { - const document = await this.updateOne({ userId: user.userId }, user); - return this.toDomain(document); + async update(user: MeetUser): Promise { + return this.updateOne({ userId: user.userId }, user); } /** @@ -55,9 +48,8 @@ export class UserRepository extends BaseRepos * @param userId - The unique user identifier * @returns The user or null if not found */ - async findByUserId(userId: string): Promise { - const document = await this.findOne({ userId }); - return document ? this.toDomain(document) : null; + async findByUserId(userId: string): Promise { + return this.findOne({ userId }); } /** @@ -66,7 +58,7 @@ export class UserRepository extends BaseRepos * @param userIds - Array of user identifiers * @returns Array of found users */ - async findByUserIds(userIds: string[]): Promise { + async findByUserIds(userIds: string[]): Promise { return await this.findAll({ userId: { $in: userIds } }); } @@ -84,7 +76,7 @@ export class UserRepository extends BaseRepos * @returns Object containing users array, pagination info, and optional next page token */ async find(options: MeetUserFilters = {}): Promise<{ - users: TUser[]; + users: MeetUser[]; isTruncated: boolean; nextPageToken?: string; }> { @@ -99,7 +91,7 @@ export class UserRepository extends BaseRepos } = options; // Build base filter - const filter: Record = {}; + const filter: FilterQuery = {}; if (userId && name) { // Both defined: OR filter with regex userId match and regex name match