mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-13 03:10:31 +00:00
Compare commits
6 Commits
attachment
...
v0.16.16
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c2b9fba29 | ||
|
|
7208b8fab5 | ||
|
|
0836fe14e0 | ||
|
|
1227111fae | ||
|
|
2ada57a2b4 | ||
|
|
e380c598d3 |
@@ -82,6 +82,9 @@ services:
|
||||
- SIGNUP_ALLOWED_DOMAINS=${SIGNUP_ALLOWED_DOMAINS}
|
||||
- SIGNUP_ALLOWED_EMAILS=${SIGNUP_ALLOWED_EMAILS}
|
||||
|
||||
# Sign-up email confirmation
|
||||
- SIGNUP_EMAIL_CONFIRMATION=${SIGNUP_EMAIL_CONFIRMATION}
|
||||
|
||||
# Gotenberg (Pdf generator)
|
||||
- GOTENBERG_URL=${GOTENBERG_URL}
|
||||
- GOTENBERG_DOCS_URL=${GOTENBERG_DOCS_URL}
|
||||
@@ -108,7 +111,8 @@ services:
|
||||
- NEW_RELIC_AI_MONITORING_ENABLED=${NEW_RELIC_AI_MONITORING_ENABLED}
|
||||
- NEW_RELIC_CUSTOM_INSIGHTS_EVENTS_MAX_SAMPLES_STORED=${NEW_RELIC_CUSTOM_INSIGHTS_EVENTS_MAX_SAMPLES_STORED}
|
||||
- NEW_RELIC_SPAN_EVENTS_MAX_SAMPLES_STORED=${NEW_RELIC_SPAN_EVENTS_MAX_SAMPLES_STORED}
|
||||
|
||||
- NEW_RELIC_LICENSE_KEY=${NEW_RELIC_LICENSE_KEY}
|
||||
- NEW_RELIC_APP_NAME=${NEW_RELIC_APP_NAME}
|
||||
|
||||
database_migration:
|
||||
container_name: bigcapital-database-migration
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4846,"time":"2024-05-15T09:09:09.492Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4846,"time":"2024-05-15T09:09:09.514Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4846,"time":"2024-05-15T09:09:09.791Z","msg":"Using AsyncLocalContextManager"}
|
||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4846,"time":"2024-05-15T09:09:09.793Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4883,"time":"2024-05-15T09:09:27.758Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4883,"time":"2024-05-15T09:09:27.771Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4883,"time":"2024-05-15T09:09:27.927Z","msg":"Using AsyncLocalContextManager"}
|
||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4883,"time":"2024-05-15T09:09:27.929Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":5411,"time":"2024-05-15T09:15:10.318Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":5411,"time":"2024-05-15T09:15:10.326Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":5411,"time":"2024-05-15T09:15:10.420Z","msg":"Using AsyncLocalContextManager"}
|
||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":5411,"time":"2024-05-15T09:15:10.421Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":8856,"time":"2024-05-15T11:47:03.324Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":8856,"time":"2024-05-15T11:47:03.335Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":8856,"time":"2024-05-15T11:47:03.504Z","msg":"Using AsyncLocalContextManager"}
|
||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":8856,"time":"2024-05-15T11:47:03.505Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":12076,"time":"2024-05-15T14:30:36.984Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":12076,"time":"2024-05-15T14:30:36.990Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":12076,"time":"2024-05-15T14:30:37.126Z","msg":"Using AsyncLocalContextManager"}
|
||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":12076,"time":"2024-05-15T14:30:37.127Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":29079,"time":"2024-05-17T10:24:22.393Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":29079,"time":"2024-05-17T10:24:22.405Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":29079,"time":"2024-05-17T10:24:22.536Z","msg":"Using AsyncLocalContextManager"}
|
||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":29079,"time":"2024-05-17T10:24:22.537Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
||||
@@ -20,7 +20,6 @@
|
||||
"bigcapital": "./bin/bigcapital.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.576.0",
|
||||
"@casl/ability": "^5.4.3",
|
||||
"@hapi/boom": "^7.4.3",
|
||||
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Router, Response } from 'express';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import { Request } from 'express-validator/src/base';
|
||||
import { AttachmentsApplication } from '@/services/Attachments/AttachmentsApplication';
|
||||
|
||||
@Service()
|
||||
export class AttachmentsController extends BaseController {
|
||||
@Inject()
|
||||
private attachmentsApplication: AttachmentsApplication;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.delete('/:id', this.deleteAttachment.bind(this));
|
||||
router.post('/', this.uploadAttachment.bind(this));
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
private async uploadAttachment(req: Request, res: Response, next: Function) {
|
||||
try {
|
||||
await this.attachmentsApplication.upload();
|
||||
|
||||
return res.status(200).send({});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
private deleteAttachment(req: Request, res: Response, next: Function) {}
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Response|void> {
|
||||
const { tenantId } = req;
|
||||
|
||||
try {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -17,7 +17,7 @@ export class WarehousesController extends BaseController {
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
router() {
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
20
packages/server/src/api/exceptions/GlobalErrorException.ts
Normal file
20
packages/server/src/api/exceptions/GlobalErrorException.ts
Normal file
@@ -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 });
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
25
packages/server/src/api/exceptions/ServiceErrorException.ts
Normal file
25
packages/server/src/api/exceptions/ServiceErrorException.ts
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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) => {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Inject, Service } from "typedi";
|
||||
import { UploadDocument } from "./UploadDocument";
|
||||
|
||||
|
||||
@Service()
|
||||
export class AttachmentsApplication {
|
||||
|
||||
@Inject()
|
||||
private uploadDocumentService: UploadDocument;
|
||||
|
||||
upload(data: any) {
|
||||
return this.uploadDocumentService.upload(data);
|
||||
}
|
||||
|
||||
delete(attachmentId: string) {}
|
||||
|
||||
get(attachmentId: string) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Service } from 'typedi';
|
||||
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
|
||||
|
||||
@Service()
|
||||
export class UploadDocument {
|
||||
// setting up s3 client
|
||||
public s3 = new S3Client({
|
||||
region: process.env.AWS_REGION,
|
||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
||||
});
|
||||
|
||||
async upload(data) {
|
||||
const putObjectCommand = new PutObjectCommand({
|
||||
Bucket: process.env.AWS_BUCKET,
|
||||
Key: data.filename,
|
||||
Body: await data.toBuffer(),
|
||||
ContentType: data.mimetype,
|
||||
});
|
||||
await this.s3.send(putObjectCommand);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { difference } from 'lodash';
|
||||
import { difference, isEmpty } from 'lodash';
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import {
|
||||
@@ -244,16 +244,12 @@ export class CommandManualJournalValidators {
|
||||
/**
|
||||
* Validates the manual journal number require.
|
||||
* @param {string} journalNumber
|
||||
* @throws {ServiceError(ERRORS.MANUAL_JOURNAL_NO_REQUIRED)}
|
||||
*/
|
||||
public validateJournalNoRequireWhenAutoNotEnabled = (
|
||||
tenantId: number,
|
||||
journalNumber: string
|
||||
) => {
|
||||
// Retrieve the next manual journal number.
|
||||
const autoIncrmenetEnabled =
|
||||
this.autoIncrement.autoIncrementEnabled(tenantId);
|
||||
|
||||
if (!journalNumber || !autoIncrmenetEnabled) {
|
||||
if (isEmpty(journalNumber)) {
|
||||
throw new ServiceError(ERRORS.MANUAL_JOURNAL_NO_REQUIRED);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,7 +99,6 @@ export class CreateManualJournalService {
|
||||
|
||||
// Validate manual journal number require when auto-increment not enabled.
|
||||
this.validator.validateJournalNoRequireWhenAutoNotEnabled(
|
||||
tenantId,
|
||||
manualJournalDTO.journalNumber
|
||||
);
|
||||
// Validate manual journal uniquiness on the storage.
|
||||
|
||||
@@ -15,10 +15,8 @@ export default class AutoIncrementOrdersService {
|
||||
const group = settingsGroup;
|
||||
|
||||
// Settings service transaction number and prefix.
|
||||
const autoIncrement = settings.get({ group, key: 'auto_increment' }, false);
|
||||
|
||||
return parseBoolean(autoIncrement, false);
|
||||
}
|
||||
return settings.get({ group, key: 'auto_increment' }, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the next service transaction number.
|
||||
@@ -27,17 +25,16 @@ export default class AutoIncrementOrdersService {
|
||||
* @param {Function} getMaxTransactionNo
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
getNextTransactionNumber(tenantId: number, settingsGroup: string): string {
|
||||
getNextTransactionNumber(tenantId: number, group: string): string {
|
||||
const settings = this.tenancy.settings(tenantId);
|
||||
const group = settingsGroup;
|
||||
|
||||
// Settings service transaction number and prefix.
|
||||
const autoIncrement = settings.get({ group, key: 'auto_increment' }, false);
|
||||
const autoIncrement = this.autoIncrementEnabled(tenantId, group);
|
||||
|
||||
const settingNo = settings.get({ group, key: 'next_number' }, '');
|
||||
const settingPrefix = settings.get({ group, key: 'number_prefix' }, '');
|
||||
|
||||
return parseBoolean(autoIncrement, false) ? `${settingPrefix}${settingNo}` : '';
|
||||
return autoIncrement ? `${settingPrefix}${settingNo}` : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,7 +50,9 @@ export default class AutoIncrementOrdersService {
|
||||
const autoIncrement = settings.get({ group, key: 'auto_increment' });
|
||||
|
||||
// Can't continue if the auto-increment of the service was disabled.
|
||||
if (!autoIncrement) { return; }
|
||||
if (!autoIncrement) {
|
||||
return;
|
||||
}
|
||||
|
||||
settings.set(
|
||||
{ group, key: 'next_number' },
|
||||
|
||||
@@ -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<void>}
|
||||
*/
|
||||
private async processWebhookEvent(eventBody): Promise<void> {
|
||||
@@ -96,7 +100,7 @@ export class LemonSqueezyWebhooks {
|
||||
if (webhookEvent === 'subscription_created') {
|
||||
await this.subscriptionService.newSubscribtion(
|
||||
tenantId,
|
||||
'early-adaptor',
|
||||
'early-adaptor'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +337,9 @@ const booleanValues: string[] = [
|
||||
].map((value) => normalizeValue(value));
|
||||
|
||||
export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => {
|
||||
if (typeof value === 'boolean') {
|
||||
return value; // Retrun early we have nothing to parse.
|
||||
}
|
||||
const normalizedValue = normalizeValue(value);
|
||||
if (isEmpty(value) || booleanValues.indexOf(normalizedValue) === -1) {
|
||||
return defaultValue;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// @ts-nocheck
|
||||
import { T } from '@/components';
|
||||
|
||||
import { Callout } from '@blueprintjs/core';
|
||||
import { SubscriptionPlans } from './SubscriptionPlan';
|
||||
import withPlans from '../../Subscriptions/withPlans';
|
||||
import { compose } from '@/utils';
|
||||
import { Callout, Intent } from '@blueprintjs/core';
|
||||
|
||||
/**
|
||||
* Billing plans.
|
||||
@@ -15,10 +13,11 @@ function SubscriptionPlansSectionRoot({ plans }) {
|
||||
<Callout
|
||||
style={{ marginBottom: '1.5rem' }}
|
||||
icon={null}
|
||||
title={'Early Adaptors Plan'}
|
||||
title={'Early Adopter Plan'}
|
||||
>
|
||||
We're looking for 200 early adaptors, when you subscribe you'll get
|
||||
the full features and unlimited users for a year regardless of the subscribed plan.
|
||||
We're looking for 200 early adopters, when you subscribe you'll get the
|
||||
full features and unlimited users for a year regardless of the
|
||||
subscribed plan.
|
||||
</Callout>
|
||||
<SubscriptionPlans plans={plans} />
|
||||
</section>
|
||||
|
||||
1065
pnpm-lock.yaml
generated
1065
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user