diff --git a/package-lock.json b/package-lock.json index 19d310ed..b7bba5b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -141,6 +141,12 @@ "integrity": "sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A==", "dev": true }, + "@types/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.6.tgz", + "integrity": "sha512-ZkrXnZLC1mc4b9QLKaSrsxV4oxTRs10OI2kgSApT8G0v1jrmqppSHUVQ15kLorzsFBTjvf7OKF4kAibuuNQ+xA==", + "dev": true + }, "@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -836,8 +842,7 @@ "async": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, "async-each": { "version": "1.0.3", diff --git a/package.json b/package.json index 14f11d50..3210e42f 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "assets/style.css" ], "dependencies": { + "async": "^3.2.0", "body-parser": "^1.19.0", "decache": "^4.6.0", "express-http-proxy": "^1.6.2" @@ -29,6 +30,7 @@ "devDependencies": { "@purtuga/esm-webpack-plugin": "^1.1.1", "@tsconfig/node12": "^1.0.7", + "@types/async": "^3.2.6", "@types/express": "^4.17.11", "@types/express-http-proxy": "^1.6.1", "@types/node": "^14.14.37", diff --git a/server/lib/middlewares/async.ts b/server/lib/middlewares/async.ts new file mode 100644 index 00000000..c6adbd44 --- /dev/null +++ b/server/lib/middlewares/async.ts @@ -0,0 +1,26 @@ +import { eachSeries } from 'async' +import type { NextFunction, Request, RequestHandler, Response } from 'express' + +// Syntactic sugar to avoid try/catch in express controllers +// Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 + +export type RequestPromiseHandler = ((req: Request, res: Response, next: NextFunction) => Promise) + +function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]): RequestHandler { + return (req: Request, res: Response, next: NextFunction) => { + if (Array.isArray(fun)) { + eachSeries(fun as RequestHandler[], (f, cb) => { + Promise.resolve(f(req, res, (err: any) => cb(err))) + .catch(err => next(err)) + }, next) + return + } + + Promise.resolve((fun as RequestHandler)(req, res, next)) + .catch(err => next(err)) + } +} + +export { + asyncMiddleware +} diff --git a/server/lib/routers/api.ts b/server/lib/routers/api.ts index 7806debd..173e7062 100644 --- a/server/lib/routers/api.ts +++ b/server/lib/routers/api.ts @@ -1,5 +1,6 @@ import type { Router, Request, Response, NextFunction } from 'express' import { videoHasWebchat } from '../../../shared/lib/video' +import { asyncMiddleware } from '../middlewares/async' // See here for description: https://modules.prosody.im/mod_muc_http_defaults.html interface RoomDefaults { @@ -30,8 +31,8 @@ async function initApiRouter (options: RegisterServerOptions): Promise { const router = getRouter() const logger = peertubeHelpers.logger - router.get('/room', async (req: Request, res: Response, next: NextFunction) => { - try { + router.get('/room', asyncMiddleware( + async (req: Request, res: Response, _next: NextFunction) => { const jid: string = req.query.jid as string || '' logger.info(`Requesting room information for room '${jid}'.`) @@ -69,10 +70,8 @@ async function initApiRouter (options: RegisterServerOptions): Promise { affiliations: [] // so that the first user will not be moderator/admin } res.json(roomDefaults) - } catch (error) { - next(error) } - }) + )) return router } diff --git a/server/lib/routers/settings.ts b/server/lib/routers/settings.ts index 3b49d4ed..e0bb1efb 100644 --- a/server/lib/routers/settings.ts +++ b/server/lib/routers/settings.ts @@ -1,31 +1,32 @@ import type { Router, Request, Response, NextFunction } from 'express' import { diag } from '../diagnostic' import { getBaseStaticRoute, isUserAdmin } from '../helpers' +import { asyncMiddleware } from '../middlewares/async' async function initSettingsRouter (options: RegisterServerOptions): Promise { const { peertubeHelpers, getRouter } = options const router = getRouter() const logger = peertubeHelpers.logger - router.get('/diagnostic', async (req: Request, res: Response, next: NextFunction) => { - try { + router.get('/diagnostic', asyncMiddleware( + async (req: Request, res: Response, _next: NextFunction) => { logger.info('Accessing peertube-plugin-livechat diagnostic tool.') const src = getBaseStaticRoute() + 'settings/settings.js' res.status(200) res.type('html') res.send('
Loading...
') - } catch (error) { - return next(error) } - }) + )) - router.post('/diagnostic/test', async (req: Request, res: Response, next: NextFunction) => { - try { + router.post('/diagnostic/test', asyncMiddleware( + async (req: Request, res: Response, _next: NextFunction) => { if (!res.locals.authenticated) { - return res.sendStatus(403) + res.sendStatus(403) + return } if (!isUserAdmin(res)) { - return res.sendStatus(403) + res.sendStatus(403) + return } const test: string = req.body.test || '' @@ -35,10 +36,8 @@ async function initSettingsRouter (options: RegisterServerOptions): Promise