diff --git a/server/src/server/router/api/v1/imports/middleware.ts b/server/src/server/router/api/v1/imports/middleware.ts new file mode 100644 index 000000000..5a53872ef --- /dev/null +++ b/server/src/server/router/api/v1/imports/middleware.ts @@ -0,0 +1,33 @@ +import { RequestHandler } from "express"; +import db from "external/mongo/db"; +import { SYMBOL_TachiData, SYMBOL_TachiAPIAuth } from "lib/constants/tachi"; +import { AssignToReqTachiData } from "utils/req-tachi-data"; + +export const GetImportFromParam: RequestHandler = async (req, res, next) => { + const importDoc = await db.imports.findOne({ importID: req.params.importID }); + + if (!importDoc) { + return res.status(404).json({ + success: false, + description: `This import does not exist.`, + }); + } + + AssignToReqTachiData(req, { importDoc }); + + return next(); +}; + +export const RequireOwnershipOfImport: RequestHandler = (req, res, next) => { + const importDoc = req[SYMBOL_TachiData]!.importDoc!; + const userID = req[SYMBOL_TachiAPIAuth].userID!; + + if (importDoc.userID !== userID) { + return res.status(403).json({ + success: false, + description: `You are not authorised to perform this action.`, + }); + } + + return next(); +}; diff --git a/server/src/server/router/api/v1/imports/router.ts b/server/src/server/router/api/v1/imports/router.ts index a0f0e4f26..a24b55ff6 100644 --- a/server/src/server/router/api/v1/imports/router.ts +++ b/server/src/server/router/api/v1/imports/router.ts @@ -1,11 +1,13 @@ import { Router } from "express"; import db from "external/mongo/db"; -import { JOB_RETRY_COUNT } from "lib/constants/tachi"; +import { JOB_RETRY_COUNT, SYMBOL_TachiData } from "lib/constants/tachi"; +import { RevertImport } from "lib/imports/imports"; import CreateLogCtx from "lib/logger/logger"; import ScoreImportQueue, { ScoreImportQueueEvents } from "lib/score-import/worker/queue"; import { ServerConfig, TachiConfig } from "lib/setup/config"; import { GetRelevantSongsAndCharts } from "utils/db"; import { GetUserWithID } from "utils/user"; +import { GetImportFromParam, RequireOwnershipOfImport } from "./middleware"; const router: Router = Router({ mergeParams: true }); @@ -16,17 +18,8 @@ const logger = CreateLogCtx(__filename); * * @name GET /api/v1/imports/:importID */ -router.get("/:importID", async (req, res) => { - const importDoc = await db.imports.findOne({ - importID: req.params.importID, - }); - - if (!importDoc) { - return res.status(404).json({ - success: false, - description: `This import does not exist.`, - }); - } +router.get("/:importID", GetImportFromParam, async (req, res) => { + const importDoc = req[SYMBOL_TachiData]!.importDoc!; const scores = await db.scores.find({ scoreID: { $in: importDoc.scoreIDs }, @@ -62,7 +55,27 @@ router.get("/:importID", async (req, res) => { }); }); -// Finding jobs is slightly harder than just doing a key lookup, because of +/** + * Delete this import and revert it from having ever happened. This un-imports all + * of the scores that were imported. + * + * Must be a request from the owner of this import. + * + * @name POST /api/v1/imports/:importID/revert + */ +router.post("/:importID/revert", GetImportFromParam, RequireOwnershipOfImport, async (req, res) => { + const importDoc = req[SYMBOL_TachiData]!.importDoc!; + + await RevertImport(importDoc); + + return res.status(200).json({ + success: true, + description: `Reverted import.`, + body: {}, + }); +}); + +// Finding jobs is slightly harder than just doing a key lookup, because of retrying. async function FindImportJob(importID: string) { const possibleImportIDs = []; diff --git a/server/src/utils/types.ts b/server/src/utils/types.ts index 2192e69a5..893334f39 100644 --- a/server/src/utils/types.ts +++ b/server/src/utils/types.ts @@ -17,6 +17,7 @@ import { MilestoneDocument, MilestoneSetDocument, integer, + ImportDocument, } from "tachi-common"; declare module "express-session" { @@ -73,6 +74,7 @@ export interface TachiRequestData { goalSubDoc?: GoalSubscriptionDocument; milestoneSubDoc?: MilestoneSubscriptionDocument; milestoneSetDoc?: MilestoneSetDocument; + importDoc?: ImportDocument; apiClientDoc: Omit; }