import { rmSync } from 'fs'; import { copyFile, mkdir, readdir, rm } from 'fs/promises'; import { Collection, Db, ObjectId } from 'mongodb'; import multer from 'multer'; const dataPath = process.env.JEOPARDY_CDN_DATA_PATH; const upload = multer({ dest: dataPath }); /** * @type {Collection} */ let ressources; function buildPath(userid, path) { return `${dataPath}/${userid}${path.length === '/' ? '' : path}`; } function ressourcePath(res) { return `${res.fullpath}/${res.filename}`; } /** * * @param {*} app * @param {Db} db */ export function initCdn(app, db) { ressources = db.collection('ressources'); app.post('/upload', upload.single('file'), uploadFile); app.get('/cdn/:userid/:resid', fetchFile); app.put('/cdn/:userid/:resid', renameFile); app.delete('/cdn/:userid/:resid', deleteFile); app.post('/directory', fetchDirectory); app.put('/directory', addDirectory); app.delete('/directory', deleteDirectory); } /** * * @param {import('express').Request} req * @param {import('express').Response} res * @param {import('express').NextFunction} next */ function uploadFile(req, res, next) { console.log(req.file, req.body); /** * @type {string | undefined} */ const path = req.body.path; if (path !== undefined && path.startsWith('/') && !path.includes('.')) { let destinationPath = buildPath(req.user._id, req.body.path); mkdir(destinationPath, { recursive: true, }) .then(() => { return copyFile( req.file.path, `${destinationPath}/${req.file.filename}`, ); }) .then(async () => { rmSync(req.file.path); await ressources.insertOne({ fullpath: destinationPath, path: path, user: req.user._id, mimetype: req.file.mimetype, name: req.file.originalname, filename: req.file.filename, }); res.sendStatus(200); }) .catch((err) => { console.error(err); rmSync(req.file.path); res.sendStatus(500); }); } else { rmSync(req.file.path); res.sendStatus(400); } } /** * * @param {import('express').Request} req * @param {import('express').Response} res */ async function fetchFile(req, res) { let ressource = await ressources.findOne({ user: new ObjectId(req.params.userid), filename: req.params.resid, }); if (ressource) { res.sendFile(ressourcePath(ressource)); } else { res.sendStatus(404); } } /** * * @param {import('express').Request} req * @param {import('express').Response} res */ async function renameFile(req, res) { if (req.user._id.toString() !== req.params.userid) { res.sendStatus(403); return; } if ( req.body === undefined || req.body.name === undefined || req.body.name.length <= 0 || /\.{2}|\/|\\/.test(req.body.name) ) { res.sendStatus(400); return; } const name = req.body.name; let ressource = await ressources.findOne({ user: new ObjectId(req.params.userid), filename: req.params.resid, }); if (!ressource) { console.log('Ressource could not be found'); res.sendStatus(400); return; } ressources .updateOne( { _id: ressource._id, }, { $set: { name, }, }, ) .then(() => { res.sendStatus(200); }) .catch((err) => { console.error(err); res.sendStatus(500); }); } /** * * @param {import('express').Request} req * @param {import('express').Response} res */ async function deleteFile(req, res) { if (req.user._id.toString() !== req.params.userid) { res.sendStatus(403); return; } let ressource = await ressources.findOne({ user: new ObjectId(req.params.userid), filename: req.params.resid, }); if (!ressource) { console.log('Ressource could not be found'); res.sendStatus(400); return; } rm(ressourcePath(ressource)) .then(() => { return ressources.deleteOne({ _id: ressource._id }); }) .then(() => { res.sendStatus(200); }) .catch((err) => { console.error(err); res.sendStatus(500); }); } /** * * @param {import('express').Request} req * @param {import('express').Response} res */ async function fetchDirectory(req, res) { if (!req.body) { res.sendStatus(400); return; } const path = req.body.path; if (!path) { res.sendStatus(400); return; } const files = ressources.find({ path, }); readdir(buildPath(req.user._id, path), { withFileTypes: true, }) .then(async (value) => { let directories = value .filter((dir) => dir.isDirectory()) .map((dir) => { return { name: dir.name, isDir: true, }; }); res.status(200).send([...directories, ...(await files.toArray())]); }) .catch(async (err) => { console.error(err); res.status(200).send(await files.toArray()); }); } /** * * @param {import('express').Request} req * @param {import('express').Response} res */ async function addDirectory(req, res) { if (!req.body) { res.sendStatus(400); return; } const name = req.body.name; const path = req.body.path; if (!name || !path || !/^[a-zA-Z0-9-_]+$/.test(name)) { res.sendStatus(400); return; } mkdir(buildPath(req.user._id, path + '/' + name)) .then(() => { res.sendStatus(200); }) .catch((err) => { console.error(err); res.sendStatus(500); }); } /** * * @param {import('express').Request} req * @param {import('express').Response} res */ async function deleteDirectory(req, res) { console.log(req.body); if ( req.body === undefined || req.body.path === undefined || req.body.path.length <= 0 ) { res.sendStatus(400); return; } console.log(req.body); const path = req.body.path; rm(buildPath(req.user._id, path), { force: true, recursive: true, }) .then(() => { return ressources.deleteMany({ user: req.user._id, path: { $regex: '^' + path + '($|\\/.*)', }, }); }) .then(() => { res.sendStatus(200); }) .catch((err) => { console.error(err); res.sendStatus(500); }); }