fix(documents): hard delete file in storage driver (#266)

This commit is contained in:
Corentin Thomasset
2025-04-27 14:52:05 +02:00
committed by GitHub
parent 332d836d11
commit fd44897bca
2 changed files with 24 additions and 18 deletions

View File

@@ -29,7 +29,7 @@ export function createDocumentsRepository({ db }: { db: Database }) {
getExpiredDeletedDocuments, getExpiredDeletedDocuments,
getOrganizationStats, getOrganizationStats,
getOrganizationDocumentBySha256Hash, getOrganizationDocumentBySha256Hash,
getAllOrganizationTrashDocumentIds, getAllOrganizationTrashDocuments,
updateDocument, updateDocument,
}, },
{ db }, { db },
@@ -258,6 +258,7 @@ async function getExpiredDeletedDocuments({ db, expirationDelayInDays, now = new
const documents = await db.select({ const documents = await db.select({
id: documentsTable.id, id: documentsTable.id,
originalStorageKey: documentsTable.originalStorageKey,
}).from(documentsTable).where( }).from(documentsTable).where(
and( and(
eq(documentsTable.isDeleted, true), eq(documentsTable.isDeleted, true),
@@ -266,7 +267,7 @@ async function getExpiredDeletedDocuments({ db, expirationDelayInDays, now = new
); );
return { return {
documentIds: documents.map(document => document.id), documents,
}; };
} }
@@ -312,9 +313,10 @@ async function getOrganizationStats({ organizationId, db }: { organizationId: st
}; };
} }
async function getAllOrganizationTrashDocumentIds({ organizationId, db }: { organizationId: string; db: Database }) { async function getAllOrganizationTrashDocuments({ organizationId, db }: { organizationId: string; db: Database }) {
const documents = await db.select({ const documents = await db.select({
id: documentsTable.id, id: documentsTable.id,
originalStorageKey: documentsTable.originalStorageKey,
}).from(documentsTable).where( }).from(documentsTable).where(
and( and(
eq(documentsTable.organizationId, organizationId), eq(documentsTable.organizationId, organizationId),
@@ -323,7 +325,7 @@ async function getAllOrganizationTrashDocumentIds({ organizationId, db }: { orga
); );
return { return {
documentIds: documents.map(document => document.id), documents,
}; };
} }

View File

@@ -286,17 +286,19 @@ export async function ensureDocumentExists({
} }
export async function hardDeleteDocument({ export async function hardDeleteDocument({
documentId, document,
documentsRepository, documentsRepository,
documentsStorageService, documentsStorageService,
}: { }: {
documentId: string; document: Pick<Document, 'id' | 'originalStorageKey'>;
documentsRepository: DocumentsRepository; documentsRepository: DocumentsRepository;
documentsStorageService: DocumentStorageService; documentsStorageService: DocumentStorageService;
}) { }) {
await Promise.allSettled([ // TODO: use transaction
documentsRepository.hardDeleteDocument({ documentId }),
documentsStorageService.deleteFile({ storageKey: documentId }), await Promise.all([
documentsRepository.hardDeleteDocument({ documentId: document.id }),
documentsStorageService.deleteFile({ storageKey: document.originalStorageKey }),
]); ]);
} }
@@ -313,23 +315,25 @@ export async function deleteExpiredDocuments({
now?: Date; now?: Date;
logger?: Logger; logger?: Logger;
}) { }) {
const { documentIds } = await documentsRepository.getExpiredDeletedDocuments({ const { documents } = await documentsRepository.getExpiredDeletedDocuments({
expirationDelayInDays: config.documents.deletedDocumentsRetentionDays, expirationDelayInDays: config.documents.deletedDocumentsRetentionDays,
now, now,
}); });
const limit = pLimit(10);
await Promise.all( await Promise.all(
documentIds.map(async (documentId) => { documents.map(document => limit(async () => {
const [, error] = await safely(hardDeleteDocument({ documentId, documentsRepository, documentsStorageService })); const [, error] = await safely(hardDeleteDocument({ document, documentsRepository, documentsStorageService }));
if (error) { if (error) {
logger.error({ documentId, error }, 'Error while deleting expired document'); logger.error({ document, error }, 'Error while deleting expired document');
} }
}), })),
); );
return { return {
deletedDocumentsCount: documentIds.length, deletedDocumentsCount: documents.length,
}; };
} }
@@ -354,7 +358,7 @@ export async function deleteTrashDocument({
throw createDocumentNotDeletedError(); throw createDocumentNotDeletedError();
} }
await hardDeleteDocument({ documentId, documentsRepository, documentsStorageService }); await hardDeleteDocument({ document, documentsRepository, documentsStorageService });
} }
export async function deleteAllTrashDocuments({ export async function deleteAllTrashDocuments({
@@ -366,13 +370,13 @@ export async function deleteAllTrashDocuments({
documentsRepository: DocumentsRepository; documentsRepository: DocumentsRepository;
documentsStorageService: DocumentStorageService; documentsStorageService: DocumentStorageService;
}) { }) {
const { documentIds } = await documentsRepository.getAllOrganizationTrashDocumentIds({ organizationId }); const { documents } = await documentsRepository.getAllOrganizationTrashDocuments({ organizationId });
// TODO: refactor to use batching and transaction // TODO: refactor to use batching and transaction
const limit = pLimit(10); const limit = pLimit(10);
await Promise.all( await Promise.all(
documentIds.map(documentId => limit(() => hardDeleteDocument({ documentId, documentsRepository, documentsStorageService }))), documents.map(document => limit(() => hardDeleteDocument({ document, documentsRepository, documentsStorageService }))),
); );
} }