diff --git a/server/src/lib/imports/imports.ts b/server/src/lib/imports/imports.ts index e7f49d263..c7cc5bd33 100644 --- a/server/src/lib/imports/imports.ts +++ b/server/src/lib/imports/imports.ts @@ -1,10 +1,18 @@ import db from "external/mongo/db"; import CreateLogCtx from "lib/logger/logger"; +import { + CheckAndSetOngoingImportLock, + UnsetOngoingImportLock, +} from "lib/score-import/framework/import-locks/lock"; import { DeleteMultipleScores } from "lib/score-mutation/delete-scores"; import type { ImportDocument } from "tachi-common"; const logger = CreateLogCtx(__filename); +interface OngoingImportError { + tag: "ONGOING_IMPORT"; +} + /** * Given an importDocument, undo it. This will remove all of the scores inside the import. * @@ -12,28 +20,46 @@ const logger = CreateLogCtx(__filename); * * If this results in sessions being deleted, it will delete them. */ -export async function RevertImport(importDoc: ImportDocument) { +export async function RevertImport(importDoc: ImportDocument): Promise { logger.info(`Received revert-import request for import '${importDoc.importID}'`, { importDoc }); const scores = await GetImportScores(importDoc); - await DeleteMultipleScores(scores); + const hasNoOngoingImport = await CheckAndSetOngoingImportLock(importDoc.userID); - logger.info( - `Deleted ${scores.length} scores as part of reverting import '${importDoc.importID}'.`, - { importDoc } - ); + if (hasNoOngoingImport) { + logger.info( + `User ${importDoc.userID} tried to revert an import while they had one ongoing.` + ); + + return { + tag: "ONGOING_IMPORT", + }; + } try { - await db.imports.remove({ importID: importDoc.importID }); + await DeleteMultipleScores(scores); - logger.info(`Reverted and deleted import '${importDoc.importID}'.`); - } catch (err) { - logger.severe( - `Deleted scores that were part of import, but failed to remove the actual import? There is a stale import with ID '${importDoc.importID}', which must be removed manually.`, - { importDoc, err } + logger.info( + `Deleted ${scores.length} scores as part of reverting import '${importDoc.importID}'.`, + { importDoc } ); + + try { + await db.imports.remove({ importID: importDoc.importID }); + + logger.info(`Reverted and deleted import '${importDoc.importID}'.`); + } catch (err) { + logger.severe( + `Deleted scores that were part of import, but failed to remove the actual import? There is a stale import with ID '${importDoc.importID}', which must be removed manually.`, + { importDoc, err } + ); + } + } finally { + await UnsetOngoingImportLock(importDoc.userID); } + + return null; } /** diff --git a/server/src/server/router/api/v1/imports/router.ts b/server/src/server/router/api/v1/imports/router.ts index f2fb7a680..c9cba2b3d 100644 --- a/server/src/server/router/api/v1/imports/router.ts +++ b/server/src/server/router/api/v1/imports/router.ts @@ -183,7 +183,14 @@ router.post( async (req, res) => { const importDoc = GetTachiData(req, "importDoc"); - await RevertImport(importDoc); + const k = await RevertImport(importDoc); + + if (k !== null) { + return res.status(409).json({ + success: false, + description: `You already have an import or a revert ongoing.`, + }); + } return res.status(200).json({ success: true,