diff --git a/backend/src/services/recording.service.ts b/backend/src/services/recording.service.ts index 678a302..eeb1ef8 100644 --- a/backend/src/services/recording.service.ts +++ b/backend/src/services/recording.service.ts @@ -32,7 +32,6 @@ import { MutexService, RedisLock, RoomService, - S3Service, SystemEventService, TaskSchedulerService } from './index.js'; @@ -40,7 +39,6 @@ import { @injectable() export class RecordingService { constructor( - @inject(S3Service) protected s3Service: S3Service, @inject(LiveKitService) protected livekitService: LiveKitService, @inject(RoomService) protected roomService: RoomService, @inject(MutexService) protected mutexService: MutexService, @@ -306,7 +304,7 @@ export class RecordingService { protected async shouldDeleteRoomMetadata(roomId: string): Promise { try { const metadataPrefix = `${INTERNAL_CONFIG.S3_RECORDINGS_PREFIX}/.metadata/${roomId}`; - const { Contents } = await this.s3Service.listObjectsPaginated(metadataPrefix); + const { Contents } = await this.storageService.listObjects(metadataPrefix, 1); // If no metadata files exist or the list is empty, the room metadata should be deleted return !Contents || Contents.length === 0; @@ -346,10 +344,11 @@ export class RecordingService { try { // Construct the room prefix if a room ID is provided const roomPrefix = roomId ? `/${roomId}` : ''; + const recordingPrefix = `${INTERNAL_CONFIG.S3_RECORDINGS_PREFIX}/.metadata${roomPrefix}`; // Retrieve the recordings from the S3 bucket - const { Contents, IsTruncated, NextContinuationToken } = await this.s3Service.listObjectsPaginated( - `${INTERNAL_CONFIG.S3_RECORDINGS_PREFIX}/.metadata${roomPrefix}`, + const { Contents, IsTruncated, NextContinuationToken } = await this.storageService.listObjects( + recordingPrefix, maxItems, nextPageToken ); diff --git a/backend/src/services/s3.service.ts b/backend/src/services/s3.service.ts index 4a8d023..bfe79fb 100644 --- a/backend/src/services/s3.service.ts +++ b/backend/src/services/s3.service.ts @@ -128,7 +128,6 @@ export class S3Service { additionalPrefix = '', maxKeys = 50, continuationToken?: string, - searchPattern = '', bucket: string = MEET_S3_BUCKET ): Promise { // The complete prefix is constructed by combining the subbucket and the additionalPrefix. @@ -145,16 +144,7 @@ export class S3Service { }); try { - const response: ListObjectsV2CommandOutput = await this.s3.send(command); - - // If searchPattern is provided, filter the results. - - if (searchPattern) { - const regex = new RegExp(searchPattern); - response.Contents = (response.Contents || []).filter((item) => item.Key && regex.test(item.Key)); - } - - return response; + return await this.s3.send(command); } catch (error: any) { this.logger.error(`S3 listObjectsPaginated: error listing objects with prefix "${basePrefix}": ${error}`); throw internalError('listing objects from S3'); diff --git a/backend/src/services/storage/providers/s3-storage.provider.ts b/backend/src/services/storage/providers/s3-storage.provider.ts index 460c1f1..df54083 100644 --- a/backend/src/services/storage/providers/s3-storage.provider.ts +++ b/backend/src/services/storage/providers/s3-storage.provider.ts @@ -58,6 +58,39 @@ export class S3StorageProvider< } } + /** + * Lists objects in the storage with optional pagination support. + * + * @param prefix - The prefix to filter objects by (acts as a folder path) + * @param maxItems - Maximum number of items to return (optional) + * @param nextPageToken - Token for pagination to get the next page (optional) + * @returns Promise resolving to paginated list of objects with metadata + */ + async listObjects( + prefix: string, + maxItems?: number, + nextPageToken?: string + ): Promise<{ + Contents?: Array<{ + Key?: string; + LastModified?: Date; + Size?: number; + ETag?: string; + }>; + IsTruncated?: boolean; + NextContinuationToken?: string; + }> { + try { + this.logger.debug( + `Listing objects with prefix: ${prefix}, maxItems: ${maxItems}, nextPageToken: ${nextPageToken}` + ); + return await this.s3Service.listObjectsPaginated(prefix, maxItems, nextPageToken); + } catch (error) { + this.handleError(error, `Error listing objects with prefix ${prefix}`); + throw error; + } + } + /** * Initializes global preferences. If no preferences exist, persists the provided defaults. * If preferences exist but belong to a different project, they are replaced. diff --git a/backend/src/services/storage/storage.interface.ts b/backend/src/services/storage/storage.interface.ts index 3ea4318..596afe7 100644 --- a/backend/src/services/storage/storage.interface.ts +++ b/backend/src/services/storage/storage.interface.ts @@ -33,6 +33,29 @@ export interface StorageProvider< */ getObjectHeaders(filePath: string): Promise<{ contentLength?: number; contentType?: string }>; + /** + * Lists objects in the storage with optional pagination support. + * + * @param prefix - The prefix to filter objects by (acts as a folder path) + * @param maxItems - Maximum number of items to return (optional) + * @param nextPageToken - Token for pagination to get the next page (optional) + * @returns Promise resolving to paginated list of objects with metadata + */ + listObjects( + prefix: string, + maxItems?: number, + nextPageToken?: string + ): Promise<{ + Contents?: Array<{ + Key?: string; + LastModified?: Date; + Size?: number; + ETag?: string; + }>; + IsTruncated?: boolean; + NextContinuationToken?: string; + }>; + /** * Retrieves the global preferences of Openvidu Meet. * diff --git a/backend/src/services/storage/storage.service.ts b/backend/src/services/storage/storage.service.ts index 53ce5a2..c0d4c5a 100644 --- a/backend/src/services/storage/storage.service.ts +++ b/backend/src/services/storage/storage.service.ts @@ -42,6 +42,31 @@ export class MeetStorageService< } } + /** + * Lists objects in the storage with optional pagination support. + * + * @param prefix - The prefix to filter objects by (acts as a folder path) + * @param maxItems - Maximum number of items to return (optional) + * @param nextPageToken - Token for pagination to get the next page (optional) + * @returns Promise resolving to paginated list of objects with metadata + */ + listObjects( + prefix: string, + maxItems?: number, + nextPageToken?: string + ): Promise<{ + Contents?: Array<{ + Key?: string; + LastModified?: Date; + Size?: number; + ETag?: string; + }>; + IsTruncated?: boolean; + NextContinuationToken?: string; + }> { + return this.storageProvider.listObjects(prefix, maxItems, nextPageToken); + } + /** * Initializes default preferences if not already initialized. * @returns {Promise} Default global preferences.