diff --git a/packages/server/src/api/controllers/Accounts.ts b/packages/server/src/api/controllers/Accounts.ts index 001a5edb7..9c1b12e9f 100644 --- a/packages/server/src/api/controllers/Accounts.ts +++ b/packages/server/src/api/controllers/Accounts.ts @@ -207,7 +207,6 @@ export default class AccountsController extends BaseController { tenantId, accountDTO ); - return res.status(200).send({ id: account.id, message: 'The account has been created successfully.', diff --git a/packages/server/src/api/controllers/Export/ExportController.ts b/packages/server/src/api/controllers/Export/ExportController.ts index 632c84932..8ef90ca53 100644 --- a/packages/server/src/api/controllers/Export/ExportController.ts +++ b/packages/server/src/api/controllers/Export/ExportController.ts @@ -14,7 +14,7 @@ export class ExportController extends BaseController { /** * Router constructor method. */ - router() { + public router() { const router = Router(); router.get( @@ -53,6 +53,7 @@ export class ExportController extends BaseController { query.resource, acceptType === ACCEPT_TYPE.APPLICATION_XLSX ? 'xlsx' : 'csv' ); + // Retrieves the csv format. if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) { res.setHeader('Content-Disposition', 'attachment; filename=output.csv'); res.setHeader('Content-Type', 'text/csv'); diff --git a/packages/server/src/api/controllers/Import/ImportController.ts b/packages/server/src/api/controllers/Import/ImportController.ts index 5567aca78..17b60bc95 100644 --- a/packages/server/src/api/controllers/Import/ImportController.ts +++ b/packages/server/src/api/controllers/Import/ImportController.ts @@ -16,7 +16,7 @@ export class ImportController extends BaseController { /** * Router constructor method. */ - router() { + public router() { const router = Router(); router.post( @@ -240,11 +240,7 @@ export class ImportController extends BaseController { errors: [{ type: 'IMPORTED_FILE_EXTENSION_INVALID' }], }); } - return res.status(400).send({ - errors: [{ type: error.errorType }], - }); } - next(error); } } diff --git a/packages/server/src/api/controllers/ManualJournals.ts b/packages/server/src/api/controllers/ManualJournals.ts index 8af8c13b5..520400658 100644 --- a/packages/server/src/api/controllers/ManualJournals.ts +++ b/packages/server/src/api/controllers/ManualJournals.ts @@ -77,14 +77,14 @@ export default class ManualJournalsController extends BaseController { /** * Specific manual journal id param validation schema. */ - get manualJournalParamSchema() { + private get manualJournalParamSchema() { return [param('id').exists().isNumeric().toInt()]; } /** * Manual journal DTO schema. */ - get manualJournalValidationSchema() { + private get manualJournalValidationSchema() { return [ check('date').exists().isISO8601(), check('currency_code').optional(), @@ -154,7 +154,7 @@ export default class ManualJournalsController extends BaseController { /** * Manual journals list validation schema. */ - get manualJournalsListSchema() { + private get manualJournalsListSchema() { return [ query('page').optional().isNumeric().toInt(), query('page_size').optional().isNumeric().toInt(), @@ -320,7 +320,7 @@ export default class ManualJournalsController extends BaseController { * @param {Response} res * @param {NextFunction} next */ - getManualJournalsList = async ( + private getManualJournalsList = async ( req: Request, res: Response, next: NextFunction diff --git a/packages/server/src/api/controllers/OrganizationDashboard.ts b/packages/server/src/api/controllers/OrganizationDashboard.ts index a374645c7..6b38b8e0b 100644 --- a/packages/server/src/api/controllers/OrganizationDashboard.ts +++ b/packages/server/src/api/controllers/OrganizationDashboard.ts @@ -33,17 +33,17 @@ export default class OrganizationDashboardController extends BaseController { } /** - * - * @param req - * @param res - * @param next - * @returns + * Detarmines whether the current authed organization to able to change its currency/. + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + * @returns {Response|void} */ private async baseCurrencyMutateAbility( req: Request, res: Response, next: Function - ) { + ): Promise { const { tenantId } = req; try { diff --git a/packages/server/src/api/controllers/Projects/Projects.ts b/packages/server/src/api/controllers/Projects/Projects.ts index 33155fa81..6953e322d 100644 --- a/packages/server/src/api/controllers/Projects/Projects.ts +++ b/packages/server/src/api/controllers/Projects/Projects.ts @@ -29,8 +29,7 @@ export class ProjectsController extends BaseController { check('cost_estimate').exists().isDecimal(), ], this.validationResult, - asyncMiddleware(this.createProject.bind(this)), - this.catchServiceErrors + asyncMiddleware(this.createProject.bind(this)) ); router.post( '/:id', @@ -43,8 +42,7 @@ export class ProjectsController extends BaseController { check('cost_estimate').exists().isDecimal(), ], this.validationResult, - asyncMiddleware(this.editProject.bind(this)), - this.catchServiceErrors + asyncMiddleware(this.editProject.bind(this)) ); router.patch( '/:projectId/status', @@ -56,16 +54,14 @@ export class ProjectsController extends BaseController { .isIn([IProjectStatus.InProgress, IProjectStatus.Closed]), ], this.validationResult, - asyncMiddleware(this.editProject.bind(this)), - this.catchServiceErrors + asyncMiddleware(this.editProject.bind(this)) ); router.get( '/:id', CheckPolicies(ProjectAction.VIEW, AbilitySubject.Project), [param('id').exists().isInt().toInt()], this.validationResult, - asyncMiddleware(this.getProject.bind(this)), - this.catchServiceErrors + asyncMiddleware(this.getProject.bind(this)) ); router.get( '/:projectId/billable/entries', @@ -76,24 +72,21 @@ export class ProjectsController extends BaseController { query('to_date').optional().isISO8601(), ], this.validationResult, - asyncMiddleware(this.projectBillableEntries.bind(this)), - this.catchServiceErrors + asyncMiddleware(this.projectBillableEntries.bind(this)) ); router.get( '/', CheckPolicies(ProjectAction.VIEW, AbilitySubject.Project), [], this.validationResult, - asyncMiddleware(this.getProjects.bind(this)), - this.catchServiceErrors + asyncMiddleware(this.getProjects.bind(this)) ); router.delete( '/:id', CheckPolicies(ProjectAction.DELETE, AbilitySubject.Project), [param('id').exists().isInt().toInt()], this.validationResult, - asyncMiddleware(this.deleteProject.bind(this)), - this.catchServiceErrors + asyncMiddleware(this.deleteProject.bind(this)) ); return router; } @@ -252,22 +245,4 @@ export class ProjectsController extends BaseController { next(error); } }; - - /** - * Transforms service errors to response. - * @param {Error} - * @param {Request} req - * @param {Response} res - * @param {ServiceError} error - */ - private catchServiceErrors( - error, - req: Request, - res: Response, - next: NextFunction - ) { - if (error instanceof ServiceError) { - } - next(error); - } } diff --git a/packages/server/src/api/controllers/Users.ts b/packages/server/src/api/controllers/Users.ts index f888b90c0..39f438cdb 100644 --- a/packages/server/src/api/controllers/Users.ts +++ b/packages/server/src/api/controllers/Users.ts @@ -155,6 +155,7 @@ export default class UsersController extends BaseController { try { const user = await this.usersService.getUser(tenantId, userId); + return res.status(200).send({ user }); } catch (error) { next(error); @@ -229,7 +230,7 @@ export default class UsersController extends BaseController { * @param {Response} res * @param {NextFunction} next */ - catchServiceErrors( + private catchServiceErrors( error: Error, req: Request, res: Response, diff --git a/packages/server/src/api/controllers/Warehouses/index.ts b/packages/server/src/api/controllers/Warehouses/index.ts index 0d61f80bc..c704f44e0 100644 --- a/packages/server/src/api/controllers/Warehouses/index.ts +++ b/packages/server/src/api/controllers/Warehouses/index.ts @@ -17,7 +17,7 @@ export class WarehousesController extends BaseController { * * @returns */ - router() { + public router() { const router = Router(); router.post( diff --git a/packages/server/src/api/controllers/Webhooks/Webhooks.ts b/packages/server/src/api/controllers/Webhooks/Webhooks.ts index 4ff3acc76..acfdc8bd6 100644 --- a/packages/server/src/api/controllers/Webhooks/Webhooks.ts +++ b/packages/server/src/api/controllers/Webhooks/Webhooks.ts @@ -34,14 +34,21 @@ export class Webhooks extends BaseController { * @param {Response} res * @returns {Response} */ - public async lemonWebhooks(req: Request, res: Response) { + public async lemonWebhooks(req: Request, res: Response, next: any) { const data = req.body; const signature = req.headers['x-signature'] ?? ''; const rawBody = req.rawBody; - await this.lemonWebhooksService.handlePostWebhook(rawBody, data, signature); - - return res.status(200).send(); + try { + await this.lemonWebhooksService.handlePostWebhook( + rawBody, + data, + signature + ); + return res.status(200).send(); + } catch (error) { + next(error); + } } /** diff --git a/packages/server/src/api/exceptions/GlobalErrorException.ts b/packages/server/src/api/exceptions/GlobalErrorException.ts new file mode 100644 index 000000000..85f433437 --- /dev/null +++ b/packages/server/src/api/exceptions/GlobalErrorException.ts @@ -0,0 +1,20 @@ +import { Request, Response, NextFunction } from 'express'; + +/** + * Global error handler. + * @param {Error} err + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + */ +export function GlobalErrorException( + err: Error, + req: Request, + res: Response, + next: NextFunction +) { + console.error(err.stack); + + res.status(500); + res.boom.badImplementation('', { stack: err.stack }); +} diff --git a/packages/server/src/api/middleware/ObjectionErrorHandlerMiddleware.ts b/packages/server/src/api/exceptions/ObjectionErrorException.ts similarity index 92% rename from packages/server/src/api/middleware/ObjectionErrorHandlerMiddleware.ts rename to packages/server/src/api/exceptions/ObjectionErrorException.ts index 424b86501..31ddc64e9 100644 --- a/packages/server/src/api/middleware/ObjectionErrorHandlerMiddleware.ts +++ b/packages/server/src/api/exceptions/ObjectionErrorException.ts @@ -10,8 +10,14 @@ import { DataError, } from 'objection'; -// In this example `res` is an express response object. -export default function ObjectionErrorHandlerMiddleware( +/** + * Handles the Objection error exception. + * @param {Error} err + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + */ +export function ObjectionErrorException( err: Error, req: Request, res: Response, @@ -108,6 +114,7 @@ export default function ObjectionErrorHandlerMiddleware( type: 'UnknownDatabaseError', data: {}, }); + } else { + next(err); } - next(err); } diff --git a/packages/server/src/api/exceptions/ServiceErrorException.ts b/packages/server/src/api/exceptions/ServiceErrorException.ts new file mode 100644 index 000000000..fd1990e59 --- /dev/null +++ b/packages/server/src/api/exceptions/ServiceErrorException.ts @@ -0,0 +1,25 @@ +import { NextFunction, Request, Response } from 'express'; +import { ServiceError } from '@/exceptions'; + +/** + * Handles service error exception. + * @param {Error | ServiceError} err + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + */ +export function ServiceErrorException( + err: Error | ServiceError, + req: Request, + res: Response, + next: NextFunction +) { + if (err instanceof ServiceError) { + res.boom.badRequest('', { + errors: [{ type: err.errorType, message: err.message }], + type: 'ServiceError', + }); + } else { + next(err); + } +} diff --git a/packages/server/src/loaders/express.ts b/packages/server/src/loaders/express.ts index 92589f70f..e3afad411 100644 --- a/packages/server/src/loaders/express.ts +++ b/packages/server/src/loaders/express.ts @@ -17,7 +17,9 @@ import { } from '@/api/middleware/JSONResponseTransformer'; import config from '@/config'; import path from 'path'; -import ObjectionErrorHandlerMiddleware from '@/api/middleware/ObjectionErrorHandlerMiddleware'; +import { ObjectionErrorException } from '@/api/exceptions/ObjectionErrorException'; +import { ServiceErrorException } from '@/api/exceptions/ServiceErrorException'; +import { GlobalErrorException } from '@/api/exceptions/GlobalErrorException'; export default ({ app }) => { // Express configuration. @@ -30,9 +32,6 @@ export default ({ app }) => { // Helmet helps you secure your Express apps by setting various HTTP headers. app.use(helmet()); - // Allow to full error stack traces and internal details - app.use(errorHandler()); - // Boom response objects. app.use(boom()); @@ -65,8 +64,10 @@ export default ({ app }) => { // Agendash application load. app.use('/agendash', AgendashController.router()); - // Handles objectionjs errors. - app.use(ObjectionErrorHandlerMiddleware); + // Handles errors. + app.use(ObjectionErrorException); + app.use(ServiceErrorException); + app.use(GlobalErrorException); // catch 404 and forward to error handler app.use((req: Request, res: Response, next: NextFunction) => { diff --git a/packages/server/src/services/Subscription/LemonSqueezyWebhooks.ts b/packages/server/src/services/Subscription/LemonSqueezyWebhooks.ts index adec8b028..6a7c3966d 100644 --- a/packages/server/src/services/Subscription/LemonSqueezyWebhooks.ts +++ b/packages/server/src/services/Subscription/LemonSqueezyWebhooks.ts @@ -10,6 +10,7 @@ import { } from './utils'; import { Plan } from '@/system/models'; import { Subscription } from './Subscription'; +import { isEmpty } from 'lodash'; @Service() export class LemonSqueezyWebhooks { @@ -32,6 +33,9 @@ export class LemonSqueezyWebhooks { if (!config.lemonSqueezy.webhookSecret) { throw new Error('Lemon Squeezy Webhook Secret not set in .env'); } + if (!signature) { + throw new Error('Request signature is required.'); + } const secret = config.lemonSqueezy.webhookSecret; const hmacSignature = createHmacSignature(secret, rawData); @@ -49,7 +53,7 @@ export class LemonSqueezyWebhooks { /** * This action will process a webhook event in the database. - * @param {unknown} eventBody - + * @param {unknown} eventBody - * @returns {Promise} */ private async processWebhookEvent(eventBody): Promise { @@ -96,7 +100,7 @@ export class LemonSqueezyWebhooks { if (webhookEvent === 'subscription_created') { await this.subscriptionService.newSubscribtion( tenantId, - 'early-adaptor', + 'early-adaptor' ); } }