Use formidable instead of multer

This commit is contained in:
Alice Gaudon 2020-06-14 21:47:18 +02:00
parent adf380c19d
commit a15d496c53
9 changed files with 64 additions and 39 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "wms-core", "name": "wms-core",
"version": "0.7.12", "version": "0.8.2",
"description": "Node web framework", "description": "Node web framework",
"repository": "git@gitlab.com:ArisuOngaku/wms-core.git", "repository": "git@gitlab.com:ArisuOngaku/wms-core.git",
"author": "Alice Gaudon <alice@gaudon.pro>", "author": "Alice Gaudon <alice@gaudon.pro>",
@ -22,6 +22,7 @@
"@types/connect-redis": "^0.0.13", "@types/connect-redis": "^0.0.13",
"@types/cookie": "^0.3.3", "@types/cookie": "^0.3.3",
"@types/cookie-parser": "^1.4.2", "@types/cookie-parser": "^1.4.2",
"@types/formidable": "^1.0.31",
"@types/geoip-lite": "^1.1.31", "@types/geoip-lite": "^1.1.31",
"@types/jest": "^25.2.1", "@types/jest": "^25.2.1",
"@types/mjml": "^4.0.4", "@types/mjml": "^4.0.4",
@ -34,7 +35,6 @@
"dependencies": { "dependencies": {
"@types/express": "^4.17.6", "@types/express": "^4.17.6",
"@types/express-session": "^1.17.0", "@types/express-session": "^1.17.0",
"@types/multer": "^1.4.3",
"@types/mysql": "^2.15.10", "@types/mysql": "^2.15.10",
"@types/nodemailer": "^6.4.0", "@types/nodemailer": "^6.4.0",
"@types/nunjucks": "^3.1.3", "@types/nunjucks": "^3.1.3",
@ -48,6 +48,7 @@
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.5",
"express": "^4.17.1", "express": "^4.17.1",
"express-session": "^1.17.1", "express-session": "^1.17.1",
"formidable": "^1.2.2",
"geoip-lite": "^1.4.2", "geoip-lite": "^1.4.2",
"mjml": "^4.6.2", "mjml": "^4.6.2",
"mysql": "^2.18.1", "mysql": "^2.18.1",

View File

@ -170,12 +170,6 @@ export default abstract class Application {
}); });
} }
public setupRequestParsingMiddlewares(router: Router) {
for (const controller of this.controllers) {
controller.setupRequestParsingMiddlewares(router);
}
}
public getWebSocketListeners(): { [p: string]: WebSocketListener } { public getWebSocketListeners(): { [p: string]: WebSocketListener } {
return this.webSocketListeners; return this.webSocketListeners;
} }

View File

@ -39,9 +39,6 @@ export default abstract class Controller {
return []; return [];
} }
public setupRequestParsingMiddlewares(router: Router): void {
}
public hasGlobalHandlers(): boolean { public hasGlobalHandlers(): boolean {
return this.getGlobalHandlers().length > 0; return this.getGlobalHandlers().length > 0;
} }

View File

@ -24,6 +24,16 @@ export default class AuthComponent extends ApplicationComponent<void> {
} }
} }
export const REQUIRE_REQUEST_AUTH_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
if (!await req.authGuard.isAuthenticatedViaRequest(req)) {
req.flash('error', `You must be logged in to access ${req.url}.`);
res.redirect(Controller.route('auth') || '/');
return;
}
req.models.user = await req.authGuard.getUserForRequest(req);
next();
};
export const REQUIRE_AUTH_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => { export const REQUIRE_AUTH_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
if (await req.authGuard.isAuthenticatedViaRequest(req)) { if (await req.authGuard.isAuthenticatedViaRequest(req)) {

View File

@ -17,7 +17,7 @@ export default class CsrfProtectionComponent extends ApplicationComponent<void>
return req.session!.csrf; return req.session!.csrf;
}; };
if (!['GET', 'HEAD', 'OPTIONS'].find(s => s === req.method)) { if (!['GET', 'HEAD', 'OPTIONS'].find(s => s === req.method) && !req.authGuard.isAuthenticatedViaRequest(req)) {
if (req.session.csrf === undefined) { if (req.session.csrf === undefined) {
throw new InvalidCsrfTokenError(req.baseUrl, `You weren't assigned any CSRF token.`); throw new InvalidCsrfTokenError(req.baseUrl, `You weren't assigned any CSRF token.`);
} else if (req.body.csrf === undefined) { } else if (req.body.csrf === undefined) {

View File

@ -1,8 +1,8 @@
import ApplicationComponent from "../ApplicationComponent"; import ApplicationComponent from "../ApplicationComponent";
import express, {Express, NextFunction, Request, Response, Router} from "express"; import express, {Express, RequestHandler, Router} from "express";
import Logger from "../Logger"; import Logger from "../Logger";
import {Server} from "http"; import {Server} from "http";
import {MulterError} from "multer"; import {IncomingForm} from "formidable";
import {FileError, ValidationBag} from "../db/Validator"; import {FileError, ValidationBag} from "../db/Validator";
export default class ExpressAppComponent extends ApplicationComponent<void> { export default class ExpressAppComponent extends ApplicationComponent<void> {
@ -19,22 +19,6 @@ export default class ExpressAppComponent extends ApplicationComponent<void> {
Logger.info(`Web server running on localhost:${this.port}.`); Logger.info(`Web server running on localhost:${this.port}.`);
}); });
this.app?.setupRequestParsingMiddlewares(router);
// Multer error handler
router.use((err: any, req: Request, res: Response, next: NextFunction) => {
if (err instanceof MulterError) {
const bag = new ValidationBag();
const validationError = new FileError(err.message);
validationError.thingName = err.field;
bag.addMessage(validationError);
req.flash('validation', bag.getMessages());
res.redirectBack();
} else {
next(err);
}
});
router.use(express.json()); router.use(express.json());
router.use(express.urlencoded({ router.use(express.urlencoded({
extended: true, extended: true,
@ -58,3 +42,30 @@ export default class ExpressAppComponent extends ApplicationComponent<void> {
return this.server; return this.server;
} }
} }
export const FILE_UPLOAD_MIDDLEWARE: (formFactory: () => IncomingForm, defaultField: string) => RequestHandler = (formFactory: () => IncomingForm, defaultField: string) => {
return async (req, res, next) => {
const form = formFactory();
try {
await new Promise<any>((resolve, reject) => {
form.parse(req, (err, fields, files) => {
if (err) {
reject(err);
return;
}
req.body = fields;
req.files = files;
resolve();
});
});
} catch (e) {
const bag = new ValidationBag();
const fileError = new FileError(e);
fileError.thingName = defaultField;
bag.addMessage(fileError);
next(bag);
return;
}
next();
};
};

View File

@ -34,7 +34,6 @@ export default class LogRequestsComponent extends ApplicationComponent<void> {
query: req.query, query: req.query,
params: req.params, params: req.params,
body: req.body, body: req.body,
file: req.file,
files: req.files, files: req.files,
cookies: req.cookies, cookies: req.cookies,
sessionId: req.sessionID, sessionId: req.sessionID,

View File

@ -1,6 +1,7 @@
import {Environment} from "nunjucks"; import {Environment} from "nunjucks";
import Model from "../db/Model"; import Model from "../db/Model";
import AuthGuard from "../auth/AuthGuard"; import AuthGuard from "../auth/AuthGuard";
import {Files} from "formidable";
declare global { declare global {
namespace Express { namespace Express {
@ -9,6 +10,7 @@ declare global {
models: { [p: string]: Model | null }; models: { [p: string]: Model | null };
modelCollections: { [p: string]: Model[] | null }; modelCollections: { [p: string]: Model[] | null };
authGuard: AuthGuard<any>; authGuard: AuthGuard<any>;
files: Files;
flash(): { [key: string]: string[] }; flash(): { [key: string]: string[] };

View File

@ -557,6 +557,11 @@
resolved "https://registry.toot.party/@types%2fcookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" resolved "https://registry.toot.party/@types%2fcookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803"
integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==
"@types/events@*":
version "3.0.0"
resolved "https://registry.toot.party/@types%2fevents/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
"@types/express-serve-static-core@*": "@types/express-serve-static-core@*":
version "4.17.7" version "4.17.7"
resolved "https://registry.toot.party/@types%2fexpress-serve-static-core/-/express-serve-static-core-4.17.7.tgz#dfe61f870eb549dc6d7e12050901847c7d7e915b" resolved "https://registry.toot.party/@types%2fexpress-serve-static-core/-/express-serve-static-core-4.17.7.tgz#dfe61f870eb549dc6d7e12050901847c7d7e915b"
@ -584,6 +589,14 @@
"@types/qs" "*" "@types/qs" "*"
"@types/serve-static" "*" "@types/serve-static" "*"
"@types/formidable@^1.0.31":
version "1.0.31"
resolved "https://registry.toot.party/@types%2fformidable/-/formidable-1.0.31.tgz#274f9dc2d0a1a9ce1feef48c24ca0859e7ec947b"
integrity sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==
dependencies:
"@types/events" "*"
"@types/node" "*"
"@types/geoip-lite@^1.1.31": "@types/geoip-lite@^1.1.31":
version "1.1.31" version "1.1.31"
resolved "https://registry.toot.party/@types%2fgeoip-lite/-/geoip-lite-1.1.31.tgz#0063e47916ea982fa913a8c825f429e66b59ad10" resolved "https://registry.toot.party/@types%2fgeoip-lite/-/geoip-lite-1.1.31.tgz#0063e47916ea982fa913a8c825f429e66b59ad10"
@ -641,13 +654,6 @@
resolved "https://registry.toot.party/@types%2fmjml/-/mjml-4.0.4.tgz#af6075d29f64d47186d76125504daf544dfb2b42" resolved "https://registry.toot.party/@types%2fmjml/-/mjml-4.0.4.tgz#af6075d29f64d47186d76125504daf544dfb2b42"
integrity sha512-4PhI6iZ1zGXZ9X9W0bbmI7mS2xdxITURueqSWJ/cTeS5+tbAtOUDG1ww/fPbfcffWwR4NeOjyNcZiczafH/yfw== integrity sha512-4PhI6iZ1zGXZ9X9W0bbmI7mS2xdxITURueqSWJ/cTeS5+tbAtOUDG1ww/fPbfcffWwR4NeOjyNcZiczafH/yfw==
"@types/multer@^1.4.3":
version "1.4.3"
resolved "https://registry.toot.party/@types%2fmulter/-/multer-1.4.3.tgz#bdff74b334c38a8ee1de9fbedb5d1d3dbc377422"
integrity sha512-tWsKbF5LYtXrJ7eOfI0aLBgEv9B7fnJe1JRXTj5+Z6EMfX0yHVsRFsNGnKyN8Bs0gtDv+JR37xAqsPnALyVTqg==
dependencies:
"@types/express" "*"
"@types/mysql@^2.15.10": "@types/mysql@^2.15.10":
version "2.15.13" version "2.15.13"
resolved "https://registry.toot.party/@types%2fmysql/-/mysql-2.15.13.tgz#153dc2e2f8dffd39f7bba556c2679f14bdbecde1" resolved "https://registry.toot.party/@types%2fmysql/-/mysql-2.15.13.tgz#153dc2e2f8dffd39f7bba556c2679f14bdbecde1"
@ -2115,6 +2121,11 @@ form-data@~2.3.2:
combined-stream "^1.0.6" combined-stream "^1.0.6"
mime-types "^2.1.12" mime-types "^2.1.12"
formidable@^1.2.2:
version "1.2.2"
resolved "https://registry.toot.party/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9"
integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==
forwarded@~0.1.2: forwarded@~0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.toot.party/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" resolved "https://registry.toot.party/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"