backend: update bulk deletion recording logic to improve error handling and response structure

This commit is contained in:
juancarmore 2025-09-03 18:31:17 +02:00
parent 01ffd49803
commit 7295c45d07
2 changed files with 19 additions and 16 deletions

View File

@ -82,18 +82,18 @@ export const bulkDeleteRecordings = async (req: Request, res: Response) => {
try { try {
const recordingIdsArray = (recordingIds as string).split(','); const recordingIdsArray = (recordingIds as string).split(',');
const { deleted, notDeleted } = await recordingService.bulkDeleteRecordingsAndAssociatedFiles( const { deleted, failed } = await recordingService.bulkDeleteRecordingsAndAssociatedFiles(
recordingIdsArray, recordingIdsArray,
roomId roomId
); );
// All recordings were successfully deleted // All recordings were successfully deleted
if (deleted.length > 0 && notDeleted.length === 0) { if (deleted.length > 0 && failed.length === 0) {
return res.sendStatus(204); return res.status(200).json({ message: 'All recordings deleted successfully', deleted });
} }
// Some or all recordings could not be deleted // Some or all recordings could not be deleted
return res.status(200).json({ deleted, notDeleted }); return res.status(400).json({ message: `${failed.length} recording(s) could not be deleted`, deleted, failed });
} catch (error) { } catch (error) {
handleError(res, error, 'deleting recordings'); handleError(res, error, 'deleting recordings');
} }

View File

@ -322,19 +322,22 @@ export class RecordingService {
await new Promise((resolve) => setTimeout(resolve, retryDelayMs * retryCount)); await new Promise((resolve) => setTimeout(resolve, retryDelayMs * retryCount));
} }
const { notDeleted } = await this.bulkDeleteRecordingsAndAssociatedFiles(remainingRecordings, roomId); const { failed } = await this.bulkDeleteRecordingsAndAssociatedFiles(
remainingRecordings,
roomId
);
if (notDeleted.length === 0) { if (failed.length === 0) {
this.logger.info(`Successfully deleted all recordings for room '${roomId}'`); this.logger.info(`Successfully deleted all recordings for room '${roomId}'`);
return; return;
} }
// Prepare for retry with failed recordings // Prepare for retry with failed recordings
remainingRecordings = notDeleted.map((failed) => failed.recordingId); remainingRecordings = failed.map((failed) => failed.recordingId);
retryCount++; retryCount++;
this.logger.warn( this.logger.warn(
`${notDeleted.length} recordings failed to delete for room '${roomId}': ${remainingRecordings.join(', ')}` `${failed.length} recordings failed to delete for room '${roomId}': ${remainingRecordings.join(', ')}`
); );
if (retryCount < maxRetries) { if (retryCount < maxRetries) {
@ -391,10 +394,10 @@ export class RecordingService {
async bulkDeleteRecordingsAndAssociatedFiles( async bulkDeleteRecordingsAndAssociatedFiles(
recordingIds: string[], recordingIds: string[],
roomId?: string roomId?: string
): Promise<{ deleted: string[]; notDeleted: { recordingId: string; error: string }[] }> { ): Promise<{ deleted: string[]; failed: { recordingId: string; error: string }[] }> {
const validRecordingIds: Set<string> = new Set<string>(); const validRecordingIds: Set<string> = new Set<string>();
const deletedRecordings: Set<string> = new Set<string>(); const deletedRecordings: Set<string> = new Set<string>();
const notDeletedRecordings: Set<{ recordingId: string; error: string }> = new Set(); const failedRecordings: Set<{ recordingId: string; error: string }> = new Set();
const roomsToCheck: Set<string> = new Set(); const roomsToCheck: Set<string> = new Set();
for (const recordingId of recordingIds) { for (const recordingId of recordingIds) {
@ -404,7 +407,7 @@ export class RecordingService {
if (recRoomId !== roomId) { if (recRoomId !== roomId) {
this.logger.warn(`Skipping recording '${recordingId}' as it does not belong to room '${roomId}'`); this.logger.warn(`Skipping recording '${recordingId}' as it does not belong to room '${roomId}'`);
notDeletedRecordings.add({ failedRecordings.add({
recordingId, recordingId,
error: `Recording '${recordingId}' does not belong to room '${roomId}'` error: `Recording '${recordingId}' does not belong to room '${roomId}'`
}); });
@ -426,14 +429,14 @@ export class RecordingService {
// Track room for metadata cleanup // Track room for metadata cleanup
roomsToCheck.add(recordingInfo.roomId); roomsToCheck.add(recordingInfo.roomId);
} catch (error) { } catch (error) {
this.logger.error(`BulkDelete: Error processing recording ${recordingId}: ${error}`); this.logger.error(`BulkDelete: Error processing recording '${recordingId}': ${error}`);
notDeletedRecordings.add({ recordingId, error: (error as OpenViduMeetError).message }); failedRecordings.add({ recordingId, error: (error as OpenViduMeetError).message });
} }
} }
if (validRecordingIds.size === 0) { if (validRecordingIds.size === 0) {
this.logger.warn(`BulkDelete: No eligible recordings found for deletion.`); this.logger.warn(`BulkDelete: No eligible recordings found for deletion.`);
return { deleted: Array.from(deletedRecordings), notDeleted: Array.from(notDeletedRecordings) }; return { deleted: Array.from(deletedRecordings), failed: Array.from(failedRecordings) };
} }
// Delete recordings and its metadata from S3 // Delete recordings and its metadata from S3
@ -458,7 +461,7 @@ export class RecordingService {
if (roomMetadataToDelete.length === 0) { if (roomMetadataToDelete.length === 0) {
this.logger.verbose(`BulkDelete: No room metadata files to delete.`); this.logger.verbose(`BulkDelete: No room metadata files to delete.`);
return { deleted: Array.from(deletedRecordings), notDeleted: Array.from(notDeletedRecordings) }; return { deleted: Array.from(deletedRecordings), failed: Array.from(failedRecordings) };
} }
// Perform bulk deletion of room metadata files // Perform bulk deletion of room metadata files
@ -474,7 +477,7 @@ export class RecordingService {
return { return {
deleted: Array.from(deletedRecordings), deleted: Array.from(deletedRecordings),
notDeleted: Array.from(notDeletedRecordings) failed: Array.from(failedRecordings)
}; };
} }