From 2c8c7a4f3d18dfeb737467153316e8db6f8f9b29 Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Sun, 21 Feb 2021 22:49:03 +0800 Subject: [PATCH] server: drop ajaxUtil and handle responses in each endpoint --- server/routes/api/auth.ts | 228 ++++---- server/routes/api/client.ts | 52 +- server/routes/api/feed-monitor.ts | 183 +++---- server/routes/api/index.ts | 73 ++- server/routes/api/torrents.ts | 833 +++++++++++++++--------------- server/util/ajaxUtil.ts | 28 - 6 files changed, 647 insertions(+), 750 deletions(-) delete mode 100644 server/util/ajaxUtil.ts diff --git a/server/routes/api/auth.ts b/server/routes/api/auth.ts index 8d8e3a85..99477e6f 100644 --- a/server/routes/api/auth.ts +++ b/server/routes/api/auth.ts @@ -12,7 +12,6 @@ import { } from '../../../shared/schema/api/auth'; import config from '../../../config'; import {getAuthToken, getCookieOptions} from '../../util/authUtil'; -import {getResponseFn, validationError} from '../../util/ajaxUtil'; import requireAdmin from '../../middleware/requireAdmin'; import services from '../../services'; import Users from '../../models/Users'; @@ -43,7 +42,7 @@ router.use( const sendAuthenticationResponse = ( res: Response, credentials: Required>, -): void => { +): Response => { const {username, level} = credentials; res.cookie('jwt', getAuthToken(username), getCookieOptions()); @@ -54,7 +53,7 @@ const sendAuthenticationResponse = ( level, }; - res.json(response); + return res.json(response); }; const preloadConfigs: AuthVerificationPreloadConfigs = { @@ -74,36 +73,34 @@ router.use('/users', passport.authenticate('jwt', {session: false}), requireAdmi * @return {object} 401 - incorrect username or password - application/json * @return {AuthAuthenticationResponse} 200 - success response - application/json */ -router.post('/authenticate', (req, res) => { - if (config.authMethod === 'none') { - sendAuthenticationResponse(res, Users.getConfigUser()); - return; - } +router.post( + '/authenticate', + async (req, res): Promise => { + if (config.authMethod === 'none') { + return sendAuthenticationResponse(res, Users.getConfigUser()); + } - const parsedResult = authAuthenticationSchema.safeParse(req.body); + const parsedResult = authAuthenticationSchema.safeParse(req.body); - if (!parsedResult.success) { - validationError(res, parsedResult.error); - return; - } + if (!parsedResult.success) { + return res.status(422).json({message: 'Validation error.'}); + } - const credentials = parsedResult.data; + const credentials = parsedResult.data; - Users.comparePassword(credentials).then( - (level) => { - sendAuthenticationResponse(res, { - ...credentials, - level, - }); - }, - () => { - // Incorrect username or password. - res.status(401).json({ - message: failedLoginResponse, - }); - }, - ); -}); + return Users.comparePassword(credentials).then( + (level) => + sendAuthenticationResponse(res, { + ...credentials, + level, + }), + () => + res.status(401).json({ + message: failedLoginResponse, + }), + ); + }, +); // Allow unauthenticated registration if no users are currently registered. router.use('/register', (req, res, next) => { @@ -114,8 +111,7 @@ router.use('/register', (req, res, next) => { handleSubsequentUser: () => { passport.authenticate('jwt', {session: false}, (err, user: UserInDatabase) => { if (err || !user) { - res.status(401).send('Unauthorized'); - return; + return res.status(401).send('Unauthorized'); } req.user = user; // Only admin users can create users @@ -139,40 +135,38 @@ router.use('/register', (req, res, next) => { * @return {{username: string}} 200 - success response if cookie=false - application/json * @return {AuthAuthenticationResponse} 200 - success response - application/json */ -router.post('/register', (req, res) => { - // No user can be registered when authMethod is none - if (config.authMethod === 'none') { - // Return 404 - res.status(404).send('Not found'); - return; - } +router.post( + '/register', + async (req, res): Promise => { + // No user can be registered when authMethod is none + if (config.authMethod === 'none') { + // Return 404 + return res.status(404).send('Not found'); + } - const parsedResult = authRegistrationSchema.safeParse(req.body); + const parsedResult = authRegistrationSchema.safeParse(req.body); - if (!parsedResult.success) { - validationError(res, parsedResult.error); - return; - } + if (!parsedResult.success) { + return res.status(422).json({message: 'Validation error.'}); + } - const credentials = parsedResult.data; + const credentials = parsedResult.data; - // Attempt to save the user - Users.createUser(credentials).then( - (user) => { - services.bootstrapServicesForUser(user); + // Attempt to save the user + return Users.createUser(credentials).then( + (user) => { + services.bootstrapServicesForUser(user); - if (req.query.cookie === 'false') { - getResponseFn(res)({username: user.username}); - return; - } + if (req.query.cookie === 'false') { + return res.status(200).json({username: user.username}); + } - sendAuthenticationResponse(res, credentials); - }, - (err) => { - getResponseFn(res)({username: credentials.username}, err); - }, - ); -}); + return sendAuthenticationResponse(res, credentials); + }, + ({message}) => res.status(500).json({message}), + ); + }, +); // Allow unauthenticated verification if no users are currently registered. router.use('/verify', (req, res, next) => { @@ -226,21 +220,23 @@ router.use('/verify', (req, res, next) => { * @return {string} 500 - authenticated succeeded but user is unattached (this should NOT happen) * @return {AuthVerificationResponse} 200 - success response - application/json */ -router.get('/verify', (req, res) => { - if (req.user == null) { - res.status(500).send('Unattached user.'); - return; - } +router.get( + '/verify', + (req, res): Response => { + if (req.user == null) { + return res.status(500).send('Unattached user.'); + } - const response: AuthVerificationResponse = { - initialUser: false, - username: req.user.username, - level: req.user.level, - configs: preloadConfigs, - }; + const response: AuthVerificationResponse = { + initialUser: false, + username: req.user.username, + level: req.user.level, + configs: preloadConfigs, + }; - res.json(response); -}); + return res.json(response); + }, +); // All subsequent routes are protected. router.use('/', passport.authenticate('jwt', {session: false})); @@ -279,18 +275,21 @@ router.use('/users', (_req, res, next) => { * @return {string} 403 - user is not authorized to list users * @return {Array>} 200 - success response - application/json */ -router.get('/users', (_req, res) => { - Users.listUsers().then( - (users) => - res.json( - users.map((user) => ({ - username: user.username, - level: user.level, - })), - ), - () => res.status(500).json(new Error()), - ); -}); +router.get( + '/users', + async (_req, res): Promise => { + return Users.listUsers().then( + (users) => + res.json( + users.map((user) => ({ + username: user.username, + level: user.level, + })), + ), + ({code, message}) => res.status(500).json({code, message}), + ); + }, +); /** * DELETE /api/auth/users/{username} @@ -302,16 +301,17 @@ router.get('/users', (_req, res) => { * @return {string} 403 - user is not authorized to delete user * @return {{username: string}} 200 - success response - application/json */ -router.delete('/users/:username', (req, res) => { - Users.removeUser(req.params.username) - .then((id) => { - services.destroyUserServices(id); - res.json({username: req.params.username}); - }) - .catch((err) => { - res.status(500).json(err); - }); -}); +router.delete( + '/users/:username', + async (req, res): Promise => { + return Users.removeUser(req.params.username) + .then((id) => { + services.destroyUserServices(id); + return res.json({username: req.params.username}); + }) + .catch(({code, message}) => res.status(500).json({code, message})); + }, +); /** * PATCH /api/auth/users/{username} @@ -325,29 +325,29 @@ router.delete('/users/:username', (req, res) => { * @return {object} 422 - request validation error - application/json * @return {} 200 - success response */ -router.patch<{username: Credentials['username']}, unknown, AuthUpdateUserOptions>('/users/:username', (req, res) => { - const {username} = req.params; +router.patch<{username: Credentials['username']}, unknown, AuthUpdateUserOptions>( + '/users/:username', + async (req, res): Promise => { + const {username} = req.params; - const parsedResult = authUpdateUserSchema.safeParse(req.body); + const parsedResult = authUpdateUserSchema.safeParse(req.body); - if (!parsedResult.success) { - validationError(res, parsedResult.error); - return; - } + if (!parsedResult.success) { + return res.status(422).json({message: 'Validation error.'}); + } - const patch = parsedResult.data; + const patch = parsedResult.data; - Users.updateUser(username, patch) - .then((newUsername) => { - return Users.lookupUser(newUsername).then((user) => { - services.destroyUserServices(user._id); - services.bootstrapServicesForUser(user); - res.send(); - }); - }) - .catch((err) => { - res.status(500).json(err); - }); -}); + return Users.updateUser(username, patch) + .then((newUsername) => { + return Users.lookupUser(newUsername).then((user) => { + services.destroyUserServices(user._id); + services.bootstrapServicesForUser(user); + return res.status(200).json({}); + }); + }) + .catch(({code, message}) => res.status(500).json({code, message})); + }, +); export default router; diff --git a/server/routes/api/client.ts b/server/routes/api/client.ts index 8567c133..f6f4086d 100644 --- a/server/routes/api/client.ts +++ b/server/routes/api/client.ts @@ -1,9 +1,8 @@ -import express from 'express'; +import express, {Response} from 'express'; import type {ClientSettings} from '@shared/types/ClientSettings'; import type {SetClientSettingsOptions} from '@shared/types/api/client'; -import {getResponseFn} from '../../util/ajaxUtil'; import requireAdmin from '../../middleware/requireAdmin'; // Those settings don't require administrator access. @@ -19,16 +18,14 @@ const router = express.Router(); * @return {{isConnected: true}} 200 - success response - application/json * @return {{isConnected: false}} 500 - failure response - application/json */ -router.get('/connection-test', (req, res) => { - req.services.clientGatewayService - .testGateway() - .then(() => { - res.status(200).json({isConnected: true}); - }) - .catch(() => { - res.status(500).json({isConnected: false}); - }); -}); +router.get( + '/connection-test', + async (req, res): Promise => + req.services.clientGatewayService.testGateway().then( + () => res.status(200).json({isConnected: true}), + () => res.status(500).json({isConnected: false}), + ), +); /** * GET /api/client/settings @@ -38,14 +35,14 @@ router.get('/connection-test', (req, res) => { * @return {ClientSettings} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.get('/settings', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .getClientSettings() - .then(callback) - .catch((e) => callback(null, e)); -}); +router.get( + '/settings', + async (req, res): Promise => + req.services.clientGatewayService.getClientSettings().then( + (settings) => res.status(200).json(settings), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PATCH /api/client/settings @@ -70,13 +67,14 @@ router.patch('/settings', (req, res, next) => { next(); } }); -router.patch('/settings', (req, res) => { - const callback = getResponseFn(res); - req.services.clientGatewayService - .setClientSettings(req.body) - .then(callback) - .catch((e) => callback(null, e)); -}); +router.patch( + '/settings', + async (req, res): Promise => + req.services.clientGatewayService.setClientSettings(req.body).then( + (response) => res.status(200).json(response), + ({code, message}) => res.status(500).json({code, message}), + ), +); export default router; diff --git a/server/routes/api/feed-monitor.ts b/server/routes/api/feed-monitor.ts index 2b9fece7..1d1330f6 100644 --- a/server/routes/api/feed-monitor.ts +++ b/server/routes/api/feed-monitor.ts @@ -1,9 +1,8 @@ -import express from 'express'; +import express, {Response} from 'express'; import type {AddFeedOptions, AddRuleOptions, ModifyFeedOptions} from '@shared/types/api/feed-monitor'; import {accessDeniedError, isAllowedPath, sanitizePath} from '../../util/fileUtil'; -import {getResponseFn} from '../../util/ajaxUtil'; const router = express.Router(); @@ -15,18 +14,14 @@ const router = express.Router(); * @return {{feeds: Array; rules: Array}} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.get('/', (req, res) => { - const callback = getResponseFn(res); - - req.services.feedService - .getAll() - .then((feedsAndRules) => { - callback(feedsAndRules); - }) - .catch((error) => { - callback(null, error); - }); -}); +router.get( + '/', + async (req, res): Promise => + req.services.feedService.getAll().then( + (feedsAndRules) => res.status(200).json(feedsAndRules), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * DELETE /api/feed-monitor/{id} @@ -37,18 +32,14 @@ router.get('/', (req, res) => { * @return {} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.delete<{id: string}>('/:id', (req, res) => { - const callback = getResponseFn(res); - - req.services.feedService - .removeItem(req.params.id) - .then(() => { - callback(null); - }) - .catch((error) => { - callback(null, error); - }); -}); +router.delete<{id: string}>( + '/:id', + async (req, res): Promise => + req.services.feedService.removeItem(req.params.id).then( + (response) => res.status(200).json(response), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * GET /api/feed-monitor/feeds/{id?} @@ -59,18 +50,14 @@ router.delete<{id: string}>('/:id', (req, res) => { * @return {Array}} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.get<{id?: string}>('/feeds/:id?', (req, res) => { - const callback = getResponseFn(res); - - req.services.feedService - .getFeeds(req.params.id) - .then((feeds) => { - callback(feeds); - }) - .catch((error) => { - callback(null, error); - }); -}); +router.get<{id?: string}>( + '/feeds/:id?', + async (req, res): Promise => + req.services.feedService.getFeeds(req.params.id).then( + (feeds) => res.status(200).json(feeds), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PUT /api/feed-monitor/feeds @@ -81,18 +68,14 @@ router.get<{id?: string}>('/feeds/:id?', (req, res) => { * @return {Feed} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.put('/feeds', (req, res) => { - const callback = getResponseFn(res); - - req.services.feedService - .addFeed(req.body) - .then((feed) => { - callback(feed); - }) - .catch((error) => { - callback(null, error); - }); -}); +router.put( + '/feeds', + async (req, res): Promise => + req.services.feedService.addFeed(req.body).then( + (feed) => res.status(200).json(feed), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PATCH /api/feed-monitor/feeds/{id} @@ -104,18 +87,14 @@ router.put('/feeds', (req, res) => { * @return {} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.patch<{id: string}, unknown, ModifyFeedOptions>('/feeds/:id', (req, res) => { - const callback = getResponseFn(res); - - req.services.feedService - .modifyFeed(req.params.id, req.body) - .then(() => { - callback(null); - }) - .catch((error) => { - callback(null, error); - }); -}); +router.patch<{id: string}, unknown, ModifyFeedOptions>( + '/feeds/:id', + async (req, res): Promise => + req.services.feedService.modifyFeed(req.params.id, req.body).then( + (response) => res.status(200).json(response), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * GET /api/feed-monitor/feeds/{id}/items?search= @@ -127,18 +106,14 @@ router.patch<{id: string}, unknown, ModifyFeedOptions>('/feeds/:id', (req, res) * @return {Array} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.get<{id: string}, unknown, ModifyFeedOptions, {search: string}>('/feeds/:id/items', (req, res) => { - const callback = getResponseFn(res); - - req.services.feedService - .getItems(req.params.id, req.query.search) - .then((items) => { - callback(items); - }) - .catch((error) => { - callback(null, error); - }); -}); +router.get<{id: string}, unknown, ModifyFeedOptions, {search: string}>( + '/feeds/:id/items', + async (req, res): Promise => + req.services.feedService.getItems(req.params.id, req.query.search).then( + (items) => res.status(200).json(items), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * GET /api/feed-monitor/rules @@ -148,18 +123,14 @@ router.get<{id: string}, unknown, ModifyFeedOptions, {search: string}>('/feeds/: * @return {Array}} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.get('/rules', (req, res) => { - const callback = getResponseFn(res); - - req.services.feedService - .getRules() - .then((rules) => { - callback(rules); - }) - .catch((error) => { - callback(null, error); - }); -}); +router.get( + '/rules', + async (req, res): Promise => + req.services.feedService.getRules().then( + (rules) => res.status(200).json(rules), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PUT /api/feed-monitor/rules @@ -170,29 +141,25 @@ router.get('/rules', (req, res) => { * @return {Rule} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.put('/rules', (req, res) => { - const callback = getResponseFn(res); - - let sanitizedPath: string | null = null; - try { - sanitizedPath = sanitizePath(req.body.destination); - if (!isAllowedPath(sanitizedPath)) { - callback(null, accessDeniedError()); - return; +router.put( + '/rules', + async (req, res): Promise => { + let sanitizedPath: string | null = null; + try { + sanitizedPath = sanitizePath(req.body.destination); + if (!isAllowedPath(sanitizedPath)) { + const {code, message} = accessDeniedError(); + return res.status(403).json({code, message}); + } + } catch ({code, message}) { + return res.status(403).json({code, message}); } - } catch (e) { - callback(null, e); - return; - } - req.services.feedService - .addRule({...req.body, destination: sanitizedPath}) - .then((rule) => { - callback(rule); - }) - .catch((error) => { - callback(null, error); - }); -}); + return req.services.feedService.addRule({...req.body, destination: sanitizedPath}).then( + (rule) => res.status(200).json(rule), + ({code, message}) => res.status(500).json({code, message}), + ); + }, +); export default router; diff --git a/server/routes/api/index.ts b/server/routes/api/index.ts index 03d37a94..9c1bddf2 100644 --- a/server/routes/api/index.ts +++ b/server/routes/api/index.ts @@ -19,7 +19,6 @@ import clientActivityStream from '../../middleware/clientActivityStream'; import eventStream from '../../middleware/eventStream'; import feedMonitorRoutes from './feed-monitor'; import {getAuthToken, verifyToken} from '../../util/authUtil'; -import {getResponseFn} from '../../util/ajaxUtil'; import torrentsRoutes from './torrents'; const router = express.Router(); @@ -172,8 +171,8 @@ router.get('/history', ( (snapshot) => { res.json(snapshot); }, - (err) => { - res.status(500).json(err); + ({code, message}) => { + res.status(500).json({code, message}); }, ); }); @@ -192,8 +191,8 @@ router.get('/notifications' (notifications) => { res.status(200).json(notifications); }, - (err: Error) => { - res.status(500).json({message: err.message}); + ({code, message}) => { + res.status(500).json({code, message}); }, ); }); @@ -211,8 +210,8 @@ router.delete('/notifications', (req, res) => { () => { res.status(200).send(); }, - (err: Error) => { - res.status(500).json({message: err.message}); + ({code, message}) => { + res.status(500).json({code, message}); }, ); }); @@ -225,18 +224,14 @@ router.delete('/notifications', (req, res) => { * @return {FloodSettings} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.get('/settings', (req, res) => { - const callback = getResponseFn(res); - - req.services.settingService - .get(null) - .then((settings) => { - callback(settings as FloodSettings); - }) - .catch((err) => { - callback(null, err); - }); -}); +router.get( + '/settings', + async (req, res): Promise => + req.services.settingService.get(null).then( + (settings) => res.status(200).json(settings), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * GET /api/settings/{property} @@ -247,18 +242,14 @@ router.get('/settings', (req, res) => { * @return {Partial} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.get<{property: keyof FloodSettings}>('/settings/:property', (req, res) => { - const callback = getResponseFn(res); - - req.services.settingService - .get(req.params.property) - .then((settings) => { - callback(settings); - }) - .catch((err) => { - callback(null, err); - }); -}); +router.get<{property: keyof FloodSettings}>( + '/settings/:property', + async (req, res): Promise => + req.services.settingService.get(req.params.property).then( + (setting) => res.status(200).json(setting), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PATCH /api/settings @@ -269,17 +260,13 @@ router.get<{property: keyof FloodSettings}>('/settings/:property', (req, res) => * @return {Partial} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.patch('/settings', (req, res) => { - const callback = getResponseFn(res); - - req.services.settingService - .set(req.body) - .then((savedSettings) => { - callback(savedSettings); - }) - .catch((err) => { - callback(null, err); - }); -}); +router.patch( + '/settings', + async (req, res): Promise => + req.services.settingService.set(req.body).then( + (savedSettings) => res.status(200).json(savedSettings), + ({code, message}) => res.status(500).json({code, message}), + ), +); export default router; diff --git a/server/routes/api/torrents.ts b/server/routes/api/torrents.ts index bde2126b..57411acf 100644 --- a/server/routes/api/torrents.ts +++ b/server/routes/api/torrents.ts @@ -1,7 +1,7 @@ import childProcess from 'child_process'; import contentDisposition from 'content-disposition'; import createTorrent from 'create-torrent'; -import express from 'express'; +import express, {Response} from 'express'; import fs from 'fs'; import path from 'path'; import rateLimit from 'express-rate-limit'; @@ -34,7 +34,6 @@ import { setTorrentsTagsSchema, } from '../../../shared/schema/api/torrents'; import {accessDeniedError, fileNotFoundError, isAllowedPath, sanitizePath} from '../../util/fileUtil'; -import {getResponseFn, validationError} from '../../util/ajaxUtil'; import {getTempPath} from '../../models/TemporaryStorage'; import {getToken} from '../../util/authUtil'; @@ -88,22 +87,19 @@ const router = express.Router(); * @return {TorrentListSummary} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.get('/', (req, res) => { - const callback = getResponseFn(res); - - req.services.torrentService - .fetchTorrentList() - .then((data) => { - if (data == null) { - callback(null, new Error()); - } else { - callback(data); - } - }) - .catch((err) => { - callback(null, err); - }); -}); +router.get( + '/', + async (req, res): Promise => + req.services.torrentService + .fetchTorrentList() + .then((data) => { + if (data == null) { + throw new Error(); + } + return res.status(200).json(data); + }) + .catch(({code, message}) => res.status(500).json({code, message})), +); /** * POST /api/torrents/add-urls @@ -117,65 +113,64 @@ router.get('/', (req, res) => { * @return {Error} 403 - illegal destination - application/json * @return {Error} 500 - failure response - application/json */ -router.post('/add-urls', async (req, res) => { - const parsedResult = addTorrentByURLSchema.safeParse(req.body); +router.post( + '/add-urls', + async (req, res): Promise => { + const parsedResult = addTorrentByURLSchema.safeParse(req.body); - if (!parsedResult.success) { - validationError(res, parsedResult.error); - return; - } + if (!parsedResult.success) { + return res.status(422).json({message: 'Validation error.'}); + } - const { - urls, - cookies, - destination, - tags, - isBasePath, - isCompleted, - isSequential, - isInitialSeeding, - start, - } = parsedResult.data; - - const finalDestination = await getDestination(req.services, { - destination, - tags, - }); - - if (finalDestination == null) { - const {code, message} = accessDeniedError(); - res.status(403).json({code, message}); - return; - } - - req.services.clientGatewayService - .addTorrentsByURL({ + const { urls, - cookies: cookies != null ? cookies : {}, - destination: finalDestination, - tags: tags ?? [], - isBasePath: isBasePath ?? false, - isCompleted: isCompleted ?? false, - isSequential: isSequential ?? false, - isInitialSeeding: isInitialSeeding ?? false, - start: start ?? false, - }) - .then( - (response) => { - req.services.torrentService.fetchTorrentList(); - if (response.length === 0) { - res.status(202).json(response); - } else if (response.length < urls.length) { - res.status(207).json(response); - } else { - res.status(200).json(response); - } - }, - ({code, message}) => { - res.status(500).json({code, message}); - }, - ); -}); + cookies, + destination, + tags, + isBasePath, + isCompleted, + isSequential, + isInitialSeeding, + start, + } = parsedResult.data; + + const finalDestination = await getDestination(req.services, { + destination, + tags, + }); + + if (finalDestination == null) { + const {code, message} = accessDeniedError(); + return res.status(403).json({code, message}); + } + + return req.services.clientGatewayService + .addTorrentsByURL({ + urls, + cookies: cookies != null ? cookies : {}, + destination: finalDestination, + tags: tags ?? [], + isBasePath: isBasePath ?? false, + isCompleted: isCompleted ?? false, + isSequential: isSequential ?? false, + isInitialSeeding: isInitialSeeding ?? false, + start: start ?? false, + }) + .then( + (response) => { + req.services.torrentService.fetchTorrentList(); + if (response.length === 0) { + return res.status(202).json(response); + } else if (response.length < urls.length) { + return res.status(207).json(response); + } else { + return res.status(200).json(response); + } + }, + ({code, message}) => res.status(500).json({code, message}), + ); + }, +); /** * POST /api/torrents/add-files @@ -189,54 +184,62 @@ router.post('/add-urls', async (req, r * @return {Error} 403 - illegal destination - application/json * @return {Error} 500 - failure response - application/json */ -router.post('/add-files', async (req, res) => { - const parsedResult = addTorrentByFileSchema.safeParse(req.body); +router.post( + '/add-files', + async (req, res): Promise => { + const parsedResult = addTorrentByFileSchema.safeParse(req.body); - if (!parsedResult.success) { - validationError(res, parsedResult.error); - return; - } + if (!parsedResult.success) { + return res.status(422).json({message: 'Validation error.'}); + } - const {files, destination, tags, isBasePath, isCompleted, isSequential, isInitialSeeding, start} = parsedResult.data; - - const finalDestination = await getDestination(req.services, { - destination, - tags, - }); - - if (finalDestination == null) { - const {code, message} = accessDeniedError(); - res.status(403).json({code, message}); - return; - } - - req.services.clientGatewayService - .addTorrentsByFile({ + const { files, - destination: finalDestination, - tags: tags ?? [], - isBasePath: isBasePath ?? false, - isCompleted: isCompleted ?? false, - isSequential: isSequential ?? false, - isInitialSeeding: isInitialSeeding ?? false, - start: start ?? false, - }) - .then( - (response) => { - req.services.torrentService.fetchTorrentList(); - if (response.length === 0) { - res.status(202).json(response); - } else if (response.length < files.length) { - res.status(207).json(response); - } else { - res.status(200).json(response); - } - }, - ({code, message}) => { - res.status(500).json({code, message}); - }, - ); -}); + destination, + tags, + isBasePath, + isCompleted, + isSequential, + isInitialSeeding, + start, + } = parsedResult.data; + + const finalDestination = await getDestination(req.services, { + destination, + tags, + }); + + if (finalDestination == null) { + const {code, message} = accessDeniedError(); + return res.status(403).json({code, message}); + } + + return req.services.clientGatewayService + .addTorrentsByFile({ + files, + destination: finalDestination, + tags: tags ?? [], + isBasePath: isBasePath ?? false, + isCompleted: isCompleted ?? false, + isSequential: isSequential ?? false, + isInitialSeeding: isInitialSeeding ?? false, + start: start ?? false, + }) + .then( + (response) => { + req.services.torrentService.fetchTorrentList(); + if (response.length === 0) { + return res.status(202).json(response); + } else if (response.length < files.length) { + return res.status(207).json(response); + } else { + return res.status(200).json(response); + } + }, + ({code, message}) => res.status(500).json({code, message}), + ); + }, +); /** * POST /api/torrents/create @@ -247,71 +250,74 @@ router.post('/add-files', async (req, * @return {object} 200 - success response - application/x-bittorrent * @return {Error} 500 - failure response - application/json */ -router.post('/create', async (req, res) => { - const {name, sourcePath, trackers, comment, infoSource, isPrivate, isInitialSeeding, tags, start} = req.body; - const callback = getResponseFn(res); +router.post( + '/create', + async (req, res): Promise => { + const {name, sourcePath, trackers, comment, infoSource, isPrivate, isInitialSeeding, tags, start} = req.body; - if (typeof sourcePath !== 'string') { - callback(null, accessDeniedError()); - return; - } + if (typeof sourcePath !== 'string') { + return res.status(422).json({message: 'Validation error.'}); + } - const sanitizedPath = sanitizePath(sourcePath); - if (!isAllowedPath(sanitizedPath)) { - callback(null, accessDeniedError()); - return; - } + const sanitizedPath = sanitizePath(sourcePath); + if (!isAllowedPath(sanitizedPath)) { + const {code, message} = accessDeniedError(); + return res.status(403).json({code, message}); + } - const torrentFileName = sanitize(name ?? sanitizedPath.split(path.sep).pop() ?? `${Date.now()}`).concat('.torrent'); - const torrentPath = getTempPath(torrentFileName); + const torrentFileName = sanitize(name ?? sanitizedPath.split(path.sep).pop() ?? `${Date.now()}`).concat('.torrent'); + const torrentPath = getTempPath(torrentFileName); - createTorrent( - sanitizedPath, - { - name, - comment, - createdBy: 'Flood - flood.js.org', - private: isPrivate, - announceList: [trackers], - info: infoSource - ? { - source: infoSource, - } - : undefined, - }, - (err, torrent) => { - if (err) { - callback(null, err); - return; - } - - fs.promises.writeFile(torrentPath, torrent).then( - () => { - res.attachment(torrentFileName); - res.download(torrentPath); - - req.services.clientGatewayService - .addTorrentsByFile({ - files: [torrent.toString('base64')], - destination: fs.lstatSync(sanitizedPath).isDirectory() ? sanitizedPath : path.dirname(sanitizedPath), - tags: tags ?? [], - isBasePath: true, - isCompleted: true, - isSequential: false, - isInitialSeeding: isInitialSeeding ?? false, - start: start ?? false, - }) - .catch(() => { - // do nothing. - }); + return new Promise((resolve) => { + createTorrent( + sanitizedPath, + { + name, + comment, + createdBy: 'Flood - flood.js.org', + private: isPrivate, + announceList: [trackers], + info: infoSource + ? { + source: infoSource, + } + : undefined, }, - (writeErr) => { - callback(null, writeErr); + (err, torrent) => { + if (err) { + const {message} = err; + return resolve(res.status(500).json({message})); + } + + fs.promises.writeFile(torrentPath, torrent).then( + () => { + res.attachment(torrentFileName); + res.download(torrentPath); + + req.services.clientGatewayService + .addTorrentsByFile({ + files: [torrent.toString('base64')], + destination: fs.lstatSync(sanitizedPath).isDirectory() ? sanitizedPath : path.dirname(sanitizedPath), + tags: tags ?? [], + isBasePath: true, + isCompleted: true, + isSequential: false, + isInitialSeeding: isInitialSeeding ?? false, + start: start ?? false, + }) + .catch(() => { + // do nothing. + }); + + resolve(res); + }, + ({code, message}) => resolve(res.status(500).json({code, message})), + ); }, ); - }, - ); -}); + }); + }, +); /** * POST /api/torrents/start @@ -322,20 +328,17 @@ router.post('/create', async (req, res) * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.post('/start', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .startTorrents(req.body) - .then((response) => { - req.services.torrentService.fetchTorrentList(); - return response; - }) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.post( + '/start', + async (req, res): Promise => + req.services.clientGatewayService.startTorrents(req.body).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * POST /api/torrents/stop @@ -346,20 +349,17 @@ router.post('/start', (req, res) => { * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.post('/stop', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .stopTorrents(req.body) - .then((response) => { - req.services.torrentService.fetchTorrentList(); - return response; - }) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.post( + '/stop', + async (req, res): Promise => + req.services.clientGatewayService.stopTorrents(req.body).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * POST /api/torrents/check-hash @@ -370,20 +370,17 @@ router.post('/stop', (req, res) => { * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.post('/check-hash', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .checkTorrents(req.body) - .then((response) => { - req.services.torrentService.fetchTorrentList(); - return response; - }) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.post( + '/check-hash', + async (req, res): Promise => + req.services.clientGatewayService.checkTorrents(req.body).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * POST /api/torrents/move @@ -394,32 +391,29 @@ router.post('/check-hash', (req, res) => * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.post('/move', (req, res) => { - const callback = getResponseFn(res); - - let sanitizedPath: string | null = null; - try { - sanitizedPath = sanitizePath(req.body.destination); - if (!isAllowedPath(sanitizedPath)) { - callback(null, accessDeniedError()); - return; +router.post( + '/move', + async (req, res): Promise => { + let sanitizedPath: string | null = null; + try { + sanitizedPath = sanitizePath(req.body.destination); + if (!isAllowedPath(sanitizedPath)) { + const {code, message} = accessDeniedError(); + return res.status(403).json({code, message}); + } + } catch ({code, message}) { + return res.status(403).json({code, message}); } - } catch (e) { - callback(null, e); - return; - } - req.services.clientGatewayService - .moveTorrents({...req.body, destination: sanitizedPath}) - .then((response) => { - req.services.torrentService.fetchTorrentList(); - return response; - }) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); + return req.services.clientGatewayService.moveTorrents({...req.body, destination: sanitizedPath}).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ); + }, +); /** * POST /api/torrents/delete @@ -430,20 +424,17 @@ router.post('/move', (req, res) => { * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.post('/delete', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .removeTorrents(req.body) - .then((response) => { - req.services.torrentService.fetchTorrentList(); - return response; - }) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.post( + '/delete', + async (req, res): Promise => + req.services.clientGatewayService.removeTorrents(req.body).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PATCH /api/torrents/initial-seeding @@ -454,17 +445,17 @@ router.post('/delete', (req, res) => { * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.patch('/initial-seeding', (req, res) => { - req.services.clientGatewayService.setTorrentsInitialSeeding(req.body).then( - (response) => { - req.services.torrentService.fetchTorrentList(); - res.status(200).json(response); - }, - (err) => { - res.status(500).json(err); - }, - ); -}); +router.patch( + '/initial-seeding', + async (req, res): Promise => + req.services.clientGatewayService.setTorrentsInitialSeeding(req.body).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PATCH /api/torrents/priority @@ -475,20 +466,17 @@ router.patch('/initial-seedi * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.patch('/priority', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .setTorrentsPriority(req.body) - .then((response) => { - req.services.torrentService.fetchTorrentList(); - return response; - }) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.patch( + '/priority', + async (req, res): Promise => + req.services.clientGatewayService.setTorrentsPriority(req.body).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PATCH /api/torrents/sequential @@ -499,17 +487,17 @@ router.patch('/priority', (req, re * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.patch('/sequential', (req, res) => { - req.services.clientGatewayService.setTorrentsSequential(req.body).then( - (response) => { - req.services.torrentService.fetchTorrentList(); - res.status(200).json(response); - }, - (err) => { - res.status(500).json(err); - }, - ); -}); +router.patch( + '/sequential', + async (req, res): Promise => + req.services.clientGatewayService.setTorrentsSequential(req.body).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PATCH /api/torrents/tags @@ -520,24 +508,24 @@ router.patch('/sequential', (req * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.patch('/tags', (req, res) => { - const parsedResult = setTorrentsTagsSchema.safeParse(req.body); +router.patch( + '/tags', + async (req, res): Promise => { + const parsedResult = setTorrentsTagsSchema.safeParse(req.body); - if (!parsedResult.success) { - validationError(res, parsedResult.error); - return; - } + if (!parsedResult.success) { + return res.status(422).json({message: 'Validation error.'}); + } - req.services.clientGatewayService.setTorrentsTags(parsedResult.data).then( - (response) => { - req.services.torrentService.fetchTorrentList(); - res.status(200).json(response); - }, - (err) => { - res.status(500).json(err); - }, - ); -}); + return req.services.clientGatewayService.setTorrentsTags(parsedResult.data).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ); + }, +); /** * PATCH /api/torrents/trackers @@ -548,20 +536,17 @@ router.patch('/tags', (req, res) => { * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.patch('/trackers', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .setTorrentsTrackers(req.body) - .then((response) => { - req.services.torrentService.fetchTorrentList(); - return response; - }) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.patch( + '/trackers', + async (req, res): Promise => + req.services.clientGatewayService.setTorrentsTrackers(req.body).then( + (response) => { + req.services.torrentService.fetchTorrentList(); + return res.status(200).json(response); + }, + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * GET /api/torrents/{hash(, hash2, ...)}/metainfo @@ -582,19 +567,17 @@ router.get<{hashes: string}>( windowMs: 5 * 60 * 1000, max: 60, }), - async (req, res) => { + async (req, res): Promise => { const hashes: Array = req.params.hashes?.split(',').map((hash) => sanitize(hash)); if (!Array.isArray(hashes) || hashes?.length < 1) { - res.status(422).json(new Error('Hash not provided.')); - return; + return res.status(422).json(new Error('Hash not provided.')); } const {path: sessionDirectory, case: torrentCase} = (await req.services.clientGatewayService.getClientSessionDirectory().catch(() => undefined)) || {}; if (sessionDirectory == null || !fs.existsSync(sessionDirectory)) { - res.status(500).json(new Error('Failed to get session directory.')); - return; + return res.status(500).json(new Error('Failed to get session directory.')); } const torrentFileNames = hashes.map( @@ -604,7 +587,7 @@ router.get<{hashes: string}>( if (hashes.length < 2) { res.attachment(torrentFileNames[0]); res.download(path.join(sessionDirectory, torrentFileNames[0])); - return; + return res; } try { @@ -612,12 +595,12 @@ router.get<{hashes: string}>( fs.accessSync(path.join(sessionDirectory, torrentFileName), fs.constants.R_OK), ); } catch { - res.status(404).json('Failed to access torrent files.'); - return; + return res.status(404).json('Failed to access torrent files.'); } res.attachment(`torrents-${Date.now()}.tar`); - tar + + return tar .pack(sessionDirectory, { entries: torrentFileNames, strict: true, @@ -648,17 +631,17 @@ router.get<{hashes: string}>( * @tags Torrent * @security User * @param {string} hash.path + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json */ -router.get('/:hash/contents', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .getTorrentContents(req.params.hash) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.get( + '/:hash/contents', + async (req, res): Promise => + req.services.clientGatewayService.getTorrentContents(req.params.hash).then( + (contents) => res.status(200).json(contents), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * PATCH /api/torrents/{hash}/contents @@ -670,16 +653,14 @@ router.get('/:hash/contents', (req, res) => { * @return {object} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.patch<{hash: string}, unknown, SetTorrentContentsPropertiesOptions>('/:hash/contents', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .setTorrentContentsPriority(req.params.hash, req.body) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.patch<{hash: string}, unknown, SetTorrentContentsPropertiesOptions>( + '/:hash/contents', + async (req, res): Promise => + req.services.clientGatewayService.setTorrentContentsPriority(req.params.hash, req.body).then( + (response) => res.status(200).json(response), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * GET /api/torrents/{hash}/contents/{indices}/token @@ -697,17 +678,19 @@ router.get<{hash: string; indices: string}, unknown, unknown, {token: string}>( windowMs: 5 * 60 * 1000, max: 200, }), - (req, res) => { - if (req.user) { - const {hash, indices} = req.params; - res.status(200).send( - getToken({ - username: req.user.username, - hash, - indices, - }), - ); + async (req, res): Promise => { + if (!req.user) { + return res.status(500).json({message: 'User is not attached.'}); } + + const {hash, indices} = req.params; + return res.status(200).send( + getToken({ + username: req.user.username, + hash, + indices, + }), + ); }, ); @@ -728,7 +711,7 @@ router.get<{hash: string; indices: string}, unknown, unknown, {token: string}>( windowMs: 5 * 60 * 1000, max: 200, }), - (req, res) => { + async (req, res): Promise => { const {hash, indices: stringIndices} = req.params; if (req.user != null && req.query.token == null) { @@ -741,22 +724,20 @@ router.get<{hash: string; indices: string}, unknown, unknown, {token: string}>( indices: stringIndices, })}`, ); - return; + return res; } } const selectedTorrent = req.services.torrentService.getTorrent(hash); if (!selectedTorrent) { - res.status(404).json({error: 'Torrent not found.'}); - return; + return res.status(404).json({error: 'Torrent not found.'}); } - req.services.clientGatewayService + return req.services.clientGatewayService .getTorrentContents(hash) .then((contents) => { if (!contents || contents.length < 1) { - res.status(404).json({error: 'Torrent contents not found'}); - return; + return res.status(404).json({error: 'Torrent contents not found'}); } let indices: Array; @@ -774,16 +755,14 @@ router.get<{hash: string; indices: string}, unknown, unknown, {token: string}>( if (filePathsToDownload.length !== indices.length) { const {code, message} = accessDeniedError(); - res.status(403).json({code, message}); - return; + return res.status(403).json({code, message}); } filePathsToDownload = filePathsToDownload.filter((filePath) => fs.existsSync(filePath)); if (filePathsToDownload.length < 1 || filePathsToDownload.length !== indices.length) { const {code, message} = fileNotFoundError(); - res.status(404).json({code, message}); - return; + return res.status(404).json({code, message}); } if (filePathsToDownload.length === 1) { @@ -815,7 +794,7 @@ router.get<{hash: string; indices: string}, unknown, unknown, {token: string}>( res.sendFile(file); - return; + return res; } const archiveRootFolder = sanitizePath(selectedTorrent.directory); @@ -858,10 +837,10 @@ router.get<{hash: string; indices: string}, unknown, unknown, {token: string}>( tarStream.unpipe(res); res.destroy(); }); + + return res; }) - .catch((err) => { - res.status(500).json(err); - }); + .catch(({code, message}) => res.status(500).json({code, message})); }, ); @@ -872,25 +851,26 @@ router.get<{hash: string; indices: string}, unknown, unknown, {token: string}>( * @security User * @param {string} hash.path */ -router.get('/:hash/details', async (req, res) => { - const callback = getResponseFn(res); +router.get( + '/:hash/details', + async (req, res): Promise => { + try { + const contents = req.services.clientGatewayService.getTorrentContents(req.params.hash); + const peers = req.services.clientGatewayService.getTorrentPeers(req.params.hash); + const trackers = req.services.clientGatewayService.getTorrentTrackers(req.params.hash); - try { - const contents = req.services.clientGatewayService.getTorrentContents(req.params.hash); - const peers = req.services.clientGatewayService.getTorrentPeers(req.params.hash); - const trackers = req.services.clientGatewayService.getTorrentTrackers(req.params.hash); + await Promise.all([contents, peers, trackers]); - await Promise.all([contents, peers, trackers]); - - callback({ - contents: await contents, - peers: await peers, - trackers: await trackers, - }); - } catch (e) { - callback(null, e); - } -}); + return res.status(200).json({ + contents: await contents, + peers: await peers, + trackers: await trackers, + }); + } catch ({code, message}) { + return res.status(500).json({code, message}); + } + }, +); /** * GET /api/torrents/{hash}/mediainfo @@ -908,17 +888,14 @@ router.get<{hash: string}>( windowMs: 5 * 60 * 1000, max: 30, }), - async (req, res) => { - const callback = getResponseFn(res); - + async (req, res): Promise => { const torrentDirectory = req.services.torrentService.getTorrent(req.params.hash)?.directory; const torrentContents = await req.services.clientGatewayService .getTorrentContents(req.params.hash) .catch(() => undefined); if (torrentDirectory == null || torrentContents == null || torrentContents.length < 1) { - callback(null, new Error('Failed to fetch info of torrent.')); - return; + return res.status(404).json({message: 'Failed to fetch info of torrent.'}); } try { @@ -928,14 +905,14 @@ router.get<{hash: string}>( torrentContentPaths = torrentContentPaths.filter((contentPath) => isAllowedPath(contentPath)); if (torrentContentPaths.length < 1) { - callback(null, accessDeniedError()); - return; + const {code, message} = accessDeniedError(); + return res.status(403).json({code, message}); } torrentContentPaths = torrentContentPaths.filter((contentPath) => fs.existsSync(contentPath)); if (torrentContentPaths.length < 1) { - callback(null, fileNotFoundError()); - return; + const {code, message} = fileNotFoundError(); + return res.status(404).json({code, message}); } const mediainfoProcess = childProcess.execFile( @@ -944,22 +921,22 @@ router.get<{hash: string}>( {maxBuffer: 1024 * 2000, timeout: 1000 * 10}, (error, stdout, stderr) => { if (error) { - callback(null, error); - return; + return res.status(500).json({message: error.message}); } if (stderr) { - callback(null, stderr); - return; + return res.status(500).json({message: stderr}); } - callback({output: stdout}); + return res.status(200).json({output: stdout}); }, ); req.on('close', () => mediainfoProcess.kill('SIGTERM')); - } catch (error) { - callback(null, error); + + return res; + } catch ({code, message}) { + return res.status(500).json({code, message}); } }, ); @@ -971,16 +948,14 @@ router.get<{hash: string}>( * @security User * @param {string} hash.path */ -router.get('/:hash/peers', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .getTorrentPeers(req.params.hash) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.get( + '/:hash/peers', + async (req, res): Promise => + req.services.clientGatewayService.getTorrentPeers(req.params.hash).then( + (peers) => res.status(200).json(peers), + ({code, message}) => res.status(500).json({code, message}), + ), +); /** * GET /api/torrents/{hash}/trackers @@ -991,15 +966,13 @@ router.get('/:hash/peers', (req, res) => { * @return {Array} 200 - success response - application/json * @return {Error} 500 - failure response - application/json */ -router.get('/:hash/trackers', (req, res) => { - const callback = getResponseFn(res); - - req.services.clientGatewayService - .getTorrentTrackers(req.params.hash) - .then(callback) - .catch((err) => { - callback(null, err); - }); -}); +router.get( + '/:hash/trackers', + async (req, res): Promise => + req.services.clientGatewayService.getTorrentTrackers(req.params.hash).then( + (trackers) => res.status(200).json(trackers), + ({code, message}) => res.status(500).json({code, message}), + ), +); export default router; diff --git a/server/util/ajaxUtil.ts b/server/util/ajaxUtil.ts deleted file mode 100644 index dcb9a68e..00000000 --- a/server/util/ajaxUtil.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type {Response} from 'express'; - -export const validationError = (res: Response, err: Error) => { - res.status(422).json({ - message: 'Validation error.', - error: err, - }); -}; - -export const getResponseFn = (res: Response) => (data: D, error?: Error | string) => { - if (error) { - if (process.env.NODE_ENV === 'development') { - console.trace(error); - } - - if (typeof error === 'string') { - res.status(500).json(Error(error)); - return; - } - - res.status(500).json({ - message: error.message, - code: (error as NodeJS.ErrnoException).code, - }); - } else { - res.json(data); - } -};