mirror of
https://github.com/zkldi/Tachi.git
synced 2026-09-29 10:28:04 +03:00
138 lines
4.9 KiB
TypeScript
138 lines
4.9 KiB
TypeScript
import { Router, NextFunction, Request, Response } from "express";
|
|
import { FileUploadImportTypes } from "kamaitachi-common";
|
|
import { fileImportTypes } from "kamaitachi-common/js/config";
|
|
import multer, { MulterError } from "multer";
|
|
import Prudence from "prudence";
|
|
import { Logger } from "winston";
|
|
import { GetUserWithID } from "../../core/user-core";
|
|
import CreateLogCtx from "../../logger";
|
|
import prValidate from "../../middleware/prudence-validate";
|
|
import { RequireLoggedIn } from "../../middleware/require-logged-in";
|
|
import ScoreImportFatalError from "../../score-import/framework/core/score-import-error";
|
|
import ScoreImportMain from "../../score-import/framework/score-import-main";
|
|
import ParseEamusementCSV from "../../score-import/import-types/file/csv-eamusement-iidx/parser";
|
|
|
|
const logger = CreateLogCtx("import.ts");
|
|
|
|
const router: Router = Router({ mergeParams: true });
|
|
|
|
// multer config
|
|
const upload = multer({ limits: { fileSize: 1024 * 1024 * 16 } }); // basically 16mb
|
|
const uploadMW = upload.single("scoreData");
|
|
|
|
const parseMultipartScoredata = (req: Request, res: Response, next: NextFunction) => {
|
|
uploadMW(req, res, (err: unknown) => {
|
|
if (err instanceof MulterError) {
|
|
logger.info(`Multer Error.`, { err });
|
|
return res.status(400).json({
|
|
success: false,
|
|
description:
|
|
"File provided was too large, corrupt, or provided in the wrong field.",
|
|
});
|
|
} else if (err) {
|
|
logger.error(`Unknown file import error: ${err}`, { err });
|
|
return res.status(500).json({
|
|
success: false,
|
|
description: `An internal server error has occured.`,
|
|
});
|
|
}
|
|
|
|
next();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Import scores from a file. Expects the post request to be multipart, and to provide a scoreData file.
|
|
* @name /internal-api/import/file
|
|
*/
|
|
router.post(
|
|
"/file",
|
|
RequireLoggedIn,
|
|
parseMultipartScoredata,
|
|
prValidate(
|
|
{
|
|
importType: Prudence.isIn(fileImportTypes),
|
|
},
|
|
{},
|
|
{ allowExcessKeys: true }
|
|
),
|
|
async (req, res) => {
|
|
if (!req.file) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
description: `No file provided.`,
|
|
});
|
|
}
|
|
|
|
try {
|
|
let importType = req.body.importType as FileUploadImportTypes;
|
|
|
|
const inputParser = async (logger: Logger) =>
|
|
await ResolveFileUploadData(importType, req.file, req.body, logger);
|
|
|
|
const userDoc = await GetUserWithID(req.session.ktchi!.userID);
|
|
|
|
if (!userDoc) {
|
|
logger.severe(
|
|
`User ${req.session.ktchi!.userID} does not have an associated user document.`
|
|
);
|
|
return res.status(500).json({
|
|
success: false,
|
|
description: "An internal error has occured.",
|
|
});
|
|
}
|
|
|
|
let importDocument = await ScoreImportMain(userDoc, true, importType, inputParser);
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
description: "Import successful.",
|
|
body: importDocument,
|
|
});
|
|
} catch (err) {
|
|
if (err instanceof ScoreImportFatalError) {
|
|
logger.info(err.message);
|
|
return res.status(err.statusCode).json({
|
|
success: false,
|
|
description: err.message,
|
|
});
|
|
} else {
|
|
logger.error(err);
|
|
return res.status(500).json({
|
|
success: false,
|
|
description: `An internal service error has occured.`,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
/**
|
|
* Resolves the data from a file upload into an iterable,
|
|
* The appropriate processing function to map that iterable over,
|
|
* and and any context the processing may need (such as playtype)
|
|
*
|
|
* This also performs validation on the type of file uploaded.
|
|
* @param importType - The type of import request this was.
|
|
* @param fileData - The data sent by the user.
|
|
* @param body - Other data passed by the user in the request body.
|
|
*/
|
|
export function ResolveFileUploadData(
|
|
importType: FileUploadImportTypes,
|
|
fileData: Express.Multer.File,
|
|
body: Record<string, unknown>,
|
|
logger: Logger
|
|
) {
|
|
switch (importType) {
|
|
case "file/csv:eamusement-iidx":
|
|
return ParseEamusementCSV(fileData, body, logger);
|
|
default:
|
|
logger.error(
|
|
`importType ${importType} made it into ResolveFileUploadData, but should have been rejected by Prudence.`
|
|
);
|
|
throw new ScoreImportFatalError(400, `Invalid importType of ${importType}.`);
|
|
}
|
|
}
|
|
|
|
export default router;
|