@flusys/nestjs-shared 1.1.0-beta → 1.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -1330
- package/cjs/classes/api-controller.class.js +325 -49
- package/cjs/classes/api-service.class.js +271 -149
- package/cjs/classes/handle-custome-db.js +37 -0
- package/cjs/classes/index.js +2 -2
- package/cjs/classes/winston.logger.class.js +103 -70
- package/cjs/constants/index.js +15 -4
- package/cjs/constants/message-keys.js +68 -0
- package/cjs/constants/permissions.js +287 -0
- package/cjs/decorators/api-response.decorator.js +2 -3
- package/cjs/decorators/index.js +1 -0
- package/cjs/decorators/log-action.decorator.js +149 -0
- package/cjs/decorators/require-permission.decorator.js +5 -5
- package/cjs/dtos/delete.dto.js +10 -0
- package/cjs/dtos/filter-and-pagination.dto.js +24 -34
- package/cjs/dtos/get-by-ids.dto.js +65 -0
- package/cjs/dtos/identity-response.dto.js +4 -8
- package/cjs/dtos/index.js +1 -0
- package/cjs/dtos/pagination.dto.js +4 -8
- package/cjs/dtos/response-payload.dto.js +68 -112
- package/cjs/entities/identity.js +12 -6
- package/cjs/entities/index.js +1 -0
- package/cjs/entities/raw-type.js +4 -0
- package/cjs/entities/user-root.js +15 -15
- package/cjs/enums/index.js +20 -0
- package/cjs/enums/notification-type.enum.js +17 -0
- package/cjs/enums/participant-status.enum.js +17 -0
- package/cjs/enums/recurrence-type.enum.js +18 -0
- package/cjs/exceptions/base-app.exception.js +151 -0
- package/cjs/exceptions/index.js +1 -0
- package/cjs/exceptions/permission.exception.js +13 -9
- package/cjs/filters/global-exception.filter.js +279 -0
- package/cjs/filters/index.js +18 -0
- package/cjs/guards/jwt-auth.guard.js +4 -1
- package/cjs/guards/permission.guard.js +88 -148
- package/cjs/index.js +2 -0
- package/cjs/interceptors/idempotency.interceptor.js +1 -1
- package/cjs/interceptors/index.js +1 -4
- package/cjs/interceptors/set-user-field-on-body.interceptor.js +60 -0
- package/cjs/interceptors/slug.interceptor.js +30 -9
- package/cjs/interfaces/datasource.interface.js +4 -0
- package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
- package/cjs/interfaces/index.js +4 -2
- package/cjs/interfaces/module-config.interface.js +4 -0
- package/cjs/interfaces/notification-adapter.interface.js +11 -0
- package/cjs/interfaces/permission.interface.js +1 -1
- package/cjs/middlewares/logger.middleware.js +133 -115
- package/cjs/modules/cache/cache.module.js +3 -3
- package/cjs/modules/datasource/index.js +0 -1
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +60 -183
- package/cjs/modules/utils/utils.service.js +27 -206
- package/cjs/utils/date-time.util.js +94 -0
- package/cjs/utils/html-sanitizer.util.js +64 -0
- package/cjs/utils/index.js +5 -1
- package/cjs/utils/query-helpers.util.js +60 -0
- package/cjs/utils/request.util.js +96 -0
- package/cjs/utils/string.util.js +63 -0
- package/classes/api-controller.class.d.ts +16 -8
- package/classes/api-service.class.d.ts +30 -25
- package/classes/handle-custome-db.d.ts +3 -0
- package/classes/index.d.ts +2 -2
- package/classes/winston.logger.class.d.ts +1 -0
- package/constants/index.d.ts +2 -1
- package/constants/message-keys.d.ts +45 -0
- package/constants/permissions.d.ts +306 -0
- package/decorators/api-response.decorator.d.ts +1 -1
- package/decorators/current-user.decorator.d.ts +1 -1
- package/decorators/index.d.ts +1 -0
- package/decorators/log-action.decorator.d.ts +8 -0
- package/decorators/require-permission.decorator.d.ts +2 -2
- package/dtos/delete.dto.d.ts +1 -0
- package/dtos/filter-and-pagination.dto.d.ts +0 -2
- package/dtos/get-by-ids.dto.d.ts +4 -0
- package/dtos/index.d.ts +1 -0
- package/dtos/response-payload.dto.d.ts +8 -20
- package/entities/index.d.ts +1 -0
- package/entities/raw-type.d.ts +1 -0
- package/enums/index.d.ts +3 -0
- package/enums/notification-type.enum.d.ts +6 -0
- package/enums/participant-status.enum.d.ts +6 -0
- package/enums/recurrence-type.enum.d.ts +7 -0
- package/exceptions/base-app.exception.d.ts +41 -0
- package/exceptions/index.d.ts +1 -0
- package/exceptions/permission.exception.d.ts +2 -2
- package/fesm/classes/api-controller.class.js +326 -50
- package/fesm/classes/api-service.class.js +274 -152
- package/fesm/classes/handle-custome-db.js +19 -0
- package/fesm/classes/index.js +3 -2
- package/fesm/classes/winston.logger.class.js +100 -68
- package/fesm/constants/index.js +4 -1
- package/fesm/constants/message-keys.js +49 -0
- package/fesm/constants/permissions.js +200 -0
- package/fesm/decorators/api-response.decorator.js +3 -21
- package/fesm/decorators/index.js +1 -0
- package/fesm/decorators/log-action.decorator.js +139 -0
- package/fesm/decorators/require-permission.decorator.js +17 -65
- package/fesm/dtos/delete.dto.js +12 -2
- package/fesm/dtos/filter-and-pagination.dto.js +26 -47
- package/fesm/dtos/get-by-ids.dto.js +55 -0
- package/fesm/dtos/identity-response.dto.js +4 -8
- package/fesm/dtos/index.js +1 -0
- package/fesm/dtos/pagination.dto.js +4 -8
- package/fesm/dtos/response-payload.dto.js +68 -103
- package/fesm/entities/identity.js +12 -6
- package/fesm/entities/index.js +1 -0
- package/fesm/entities/raw-type.js +1 -0
- package/fesm/entities/user-root.js +15 -15
- package/fesm/enums/index.js +3 -0
- package/fesm/enums/notification-type.enum.js +7 -0
- package/fesm/enums/participant-status.enum.js +7 -0
- package/fesm/enums/recurrence-type.enum.js +8 -0
- package/fesm/exceptions/base-app.exception.js +115 -0
- package/fesm/exceptions/index.js +1 -0
- package/fesm/exceptions/permission.exception.js +16 -18
- package/fesm/filters/global-exception.filter.js +269 -0
- package/fesm/filters/index.js +1 -0
- package/fesm/guards/jwt-auth.guard.js +5 -2
- package/fesm/guards/permission.guard.js +90 -150
- package/fesm/index.js +2 -0
- package/fesm/interceptors/idempotency.interceptor.js +2 -2
- package/fesm/interceptors/index.js +1 -4
- package/fesm/interceptors/set-user-field-on-body.interceptor.js +39 -0
- package/fesm/interceptors/slug.interceptor.js +31 -10
- package/fesm/interfaces/datasource.interface.js +1 -0
- package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
- package/fesm/interfaces/index.js +4 -2
- package/fesm/interfaces/module-config.interface.js +5 -0
- package/fesm/interfaces/notification-adapter.interface.js +1 -0
- package/fesm/interfaces/permission.interface.js +1 -3
- package/fesm/middlewares/logger.middleware.js +133 -109
- package/fesm/modules/cache/cache.module.js +2 -2
- package/fesm/modules/datasource/index.js +0 -1
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +61 -184
- package/fesm/modules/utils/utils.service.js +27 -206
- package/fesm/utils/date-time.util.js +70 -0
- package/fesm/utils/html-sanitizer.util.js +69 -0
- package/fesm/utils/index.js +5 -1
- package/fesm/utils/query-helpers.util.js +85 -0
- package/fesm/utils/request.util.js +81 -0
- package/fesm/utils/string.util.js +71 -0
- package/filters/global-exception.filter.d.ts +9 -0
- package/filters/index.d.ts +1 -0
- package/guards/permission.guard.d.ts +4 -6
- package/index.d.ts +2 -0
- package/interceptors/idempotency.interceptor.d.ts +1 -1
- package/interceptors/index.d.ts +1 -4
- package/interceptors/set-user-field-on-body.interceptor.d.ts +5 -0
- package/interceptors/slug.interceptor.d.ts +2 -1
- package/interfaces/api.interface.d.ts +4 -2
- package/interfaces/datasource.interface.d.ts +5 -0
- package/interfaces/event-manager-adapter.interface.d.ts +43 -0
- package/interfaces/identity.interface.d.ts +4 -4
- package/interfaces/index.d.ts +4 -2
- package/interfaces/logged-user-info.interface.d.ts +0 -2
- package/interfaces/module-config.interface.d.ts +6 -0
- package/interfaces/notification-adapter.interface.d.ts +21 -0
- package/interfaces/permission.interface.d.ts +13 -9
- package/middlewares/logger.middleware.d.ts +2 -2
- package/modules/datasource/index.d.ts +0 -1
- package/modules/datasource/multi-tenant-datasource.service.d.ts +5 -16
- package/modules/utils/utils.service.d.ts +3 -14
- package/package.json +21 -14
- package/utils/date-time.util.d.ts +8 -0
- package/utils/html-sanitizer.util.d.ts +2 -0
- package/utils/index.d.ts +5 -1
- package/utils/query-helpers.util.d.ts +16 -0
- package/utils/request.util.d.ts +6 -0
- package/utils/string.util.d.ts +2 -0
- package/cjs/classes/request-scoped-api.service.js +0 -61
- package/cjs/classes/winston-logger-adapter.class.js +0 -121
- package/cjs/interceptors/query-performance.interceptor.js +0 -66
- package/cjs/interceptors/set-create-by-on-body.interceptor.js +0 -40
- package/cjs/interceptors/set-delete-by-on-body.interceptor.js +0 -40
- package/cjs/interceptors/set-update-by-on-body.interceptor.js +0 -40
- package/cjs/interfaces/base-query.interface.js +0 -6
- package/cjs/interfaces/logger.interface.js +0 -7
- package/cjs/modules/datasource/datasource.module.js +0 -120
- package/cjs/utils/error-handler.util.js +0 -67
- package/classes/request-scoped-api.service.d.ts +0 -13
- package/classes/winston-logger-adapter.class.d.ts +0 -20
- package/fesm/classes/request-scoped-api.service.js +0 -51
- package/fesm/classes/winston-logger-adapter.class.js +0 -124
- package/fesm/interceptors/query-performance.interceptor.js +0 -56
- package/fesm/interceptors/set-create-by-on-body.interceptor.js +0 -30
- package/fesm/interceptors/set-delete-by-on-body.interceptor.js +0 -30
- package/fesm/interceptors/set-update-by-on-body.interceptor.js +0 -30
- package/fesm/interfaces/base-query.interface.js +0 -3
- package/fesm/interfaces/logger.interface.js +0 -4
- package/fesm/modules/datasource/datasource.module.js +0 -110
- package/fesm/utils/error-handler.util.js +0 -59
- package/interceptors/query-performance.interceptor.d.ts +0 -8
- package/interceptors/set-create-by-on-body.interceptor.d.ts +0 -5
- package/interceptors/set-delete-by-on-body.interceptor.d.ts +0 -5
- package/interceptors/set-update-by-on-body.interceptor.d.ts +0 -5
- package/interfaces/base-query.interface.d.ts +0 -7
- package/interfaces/logger.interface.d.ts +0 -7
- package/modules/datasource/datasource.module.d.ts +0 -18
- package/utils/error-handler.util.d.ts +0 -24
|
@@ -26,10 +26,10 @@ export class Identity {
|
|
|
26
26
|
_define_property(this, "id", void 0);
|
|
27
27
|
_define_property(this, "createdAt", void 0);
|
|
28
28
|
_define_property(this, "updatedAt", void 0);
|
|
29
|
-
_define_property(this, "deletedAt",
|
|
30
|
-
_define_property(this, "createdById",
|
|
31
|
-
_define_property(this, "updatedById",
|
|
32
|
-
_define_property(this, "deletedById",
|
|
29
|
+
_define_property(this, "deletedAt", null);
|
|
30
|
+
_define_property(this, "createdById", null);
|
|
31
|
+
_define_property(this, "updatedById", null);
|
|
32
|
+
_define_property(this, "deletedById", null);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
_ts_decorate([
|
|
@@ -39,14 +39,16 @@ _ts_decorate([
|
|
|
39
39
|
_ts_decorate([
|
|
40
40
|
CreateDateColumn({
|
|
41
41
|
name: 'created_at',
|
|
42
|
-
type: 'timestamp'
|
|
42
|
+
type: 'timestamp',
|
|
43
|
+
select: false
|
|
43
44
|
}),
|
|
44
45
|
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
45
46
|
], Identity.prototype, "createdAt", void 0);
|
|
46
47
|
_ts_decorate([
|
|
47
48
|
UpdateDateColumn({
|
|
48
49
|
name: 'updated_at',
|
|
49
|
-
type: 'timestamp'
|
|
50
|
+
type: 'timestamp',
|
|
51
|
+
select: false
|
|
50
52
|
}),
|
|
51
53
|
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
52
54
|
], Identity.prototype, "updatedAt", void 0);
|
|
@@ -54,6 +56,7 @@ _ts_decorate([
|
|
|
54
56
|
DeleteDateColumn({
|
|
55
57
|
name: 'deleted_at',
|
|
56
58
|
type: 'timestamp',
|
|
59
|
+
select: false,
|
|
57
60
|
nullable: true
|
|
58
61
|
}),
|
|
59
62
|
_ts_metadata("design:type", Object)
|
|
@@ -62,6 +65,7 @@ _ts_decorate([
|
|
|
62
65
|
Column({
|
|
63
66
|
name: 'created_by_id',
|
|
64
67
|
type: 'uuid',
|
|
68
|
+
select: false,
|
|
65
69
|
nullable: true
|
|
66
70
|
}),
|
|
67
71
|
_ts_metadata("design:type", Object)
|
|
@@ -70,6 +74,7 @@ _ts_decorate([
|
|
|
70
74
|
Column({
|
|
71
75
|
name: 'updated_by_id',
|
|
72
76
|
type: 'uuid',
|
|
77
|
+
select: false,
|
|
73
78
|
nullable: true
|
|
74
79
|
}),
|
|
75
80
|
_ts_metadata("design:type", Object)
|
|
@@ -78,6 +83,7 @@ _ts_decorate([
|
|
|
78
83
|
Column({
|
|
79
84
|
name: 'deleted_by_id',
|
|
80
85
|
type: 'uuid',
|
|
86
|
+
select: false,
|
|
81
87
|
nullable: true
|
|
82
88
|
}),
|
|
83
89
|
_ts_metadata("design:type", Object)
|
package/fesm/entities/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -20,27 +20,27 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
20
20
|
function _ts_metadata(k, v) {
|
|
21
21
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
22
|
}
|
|
23
|
+
import { getJsonColumnType } from '@flusys/nestjs-shared';
|
|
23
24
|
import { Column, CreateDateColumn, DeleteDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
|
24
25
|
export class UserRoot {
|
|
25
26
|
constructor(){
|
|
26
27
|
_define_property(this, "id", void 0);
|
|
27
|
-
_define_property(this, "name",
|
|
28
|
-
_define_property(this, "password",
|
|
28
|
+
_define_property(this, "name", null);
|
|
29
|
+
_define_property(this, "password", null);
|
|
29
30
|
_define_property(this, "email", void 0);
|
|
30
|
-
_define_property(this, "phone",
|
|
31
|
-
_define_property(this, "isActive",
|
|
32
|
-
_define_property(this, "emailVerified",
|
|
33
|
-
_define_property(this, "phoneVerified",
|
|
34
|
-
_define_property(this, "profilePictureId",
|
|
35
|
-
_define_property(this, "lastLoginAt",
|
|
36
|
-
_define_property(this, "additionalFields",
|
|
31
|
+
_define_property(this, "phone", null);
|
|
32
|
+
_define_property(this, "isActive", true);
|
|
33
|
+
_define_property(this, "emailVerified", false);
|
|
34
|
+
_define_property(this, "phoneVerified", false);
|
|
35
|
+
_define_property(this, "profilePictureId", null);
|
|
36
|
+
_define_property(this, "lastLoginAt", null);
|
|
37
|
+
_define_property(this, "additionalFields", null);
|
|
37
38
|
_define_property(this, "createdAt", void 0);
|
|
38
39
|
_define_property(this, "updatedAt", void 0);
|
|
39
|
-
_define_property(this, "deletedAt",
|
|
40
|
-
|
|
41
|
-
_define_property(this, "
|
|
42
|
-
_define_property(this, "
|
|
43
|
-
_define_property(this, "deletedById", void 0);
|
|
40
|
+
_define_property(this, "deletedAt", null);
|
|
41
|
+
_define_property(this, "createdById", null);
|
|
42
|
+
_define_property(this, "updatedById", null);
|
|
43
|
+
_define_property(this, "deletedById", null);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
_ts_decorate([
|
|
@@ -125,7 +125,7 @@ _ts_decorate([
|
|
|
125
125
|
], UserRoot.prototype, "lastLoginAt", void 0);
|
|
126
126
|
_ts_decorate([
|
|
127
127
|
Column({
|
|
128
|
-
type:
|
|
128
|
+
type: getJsonColumnType(),
|
|
129
129
|
nullable: true,
|
|
130
130
|
name: 'additional_fields'
|
|
131
131
|
}),
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export var NotificationType = /*#__PURE__*/ function(NotificationType) {
|
|
2
|
+
NotificationType["INFO"] = "info";
|
|
3
|
+
NotificationType["WARNING"] = "warning";
|
|
4
|
+
NotificationType["SUCCESS"] = "success";
|
|
5
|
+
NotificationType["ERROR"] = "error";
|
|
6
|
+
return NotificationType;
|
|
7
|
+
}({});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export var ParticipantStatus = /*#__PURE__*/ function(ParticipantStatus) {
|
|
2
|
+
ParticipantStatus["PENDING"] = "pending";
|
|
3
|
+
ParticipantStatus["ACCEPTED"] = "accepted";
|
|
4
|
+
ParticipantStatus["DECLINED"] = "declined";
|
|
5
|
+
ParticipantStatus["TENTATIVE"] = "tentative";
|
|
6
|
+
return ParticipantStatus;
|
|
7
|
+
}({});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export var RecurrenceType = /*#__PURE__*/ function(RecurrenceType) {
|
|
2
|
+
RecurrenceType["NONE"] = "none";
|
|
3
|
+
RecurrenceType["DAILY"] = "daily";
|
|
4
|
+
RecurrenceType["WEEKLY"] = "weekly";
|
|
5
|
+
RecurrenceType["BIWEEKLY"] = "biweekly";
|
|
6
|
+
RecurrenceType["MONTHLY"] = "monthly";
|
|
7
|
+
return RecurrenceType;
|
|
8
|
+
}({});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
import { HttpException, HttpStatus } from '@nestjs/common';
|
|
15
|
+
import { ERROR_MESSAGES } from '../constants/message-keys';
|
|
16
|
+
export class BaseAppException extends HttpException {
|
|
17
|
+
constructor(options){
|
|
18
|
+
const status = options.status || HttpStatus.BAD_REQUEST;
|
|
19
|
+
super({
|
|
20
|
+
success: false,
|
|
21
|
+
message: options.message,
|
|
22
|
+
messageKey: options.messageKey || ERROR_MESSAGES.GENERIC,
|
|
23
|
+
messageVariables: options.messageVariables,
|
|
24
|
+
errors: options.errors
|
|
25
|
+
}, status), _define_property(this, "messageKey", void 0), _define_property(this, "messageVariables", void 0), _define_property(this, "errors", void 0), _define_property(this, "metadata", void 0);
|
|
26
|
+
this.messageKey = options.messageKey || ERROR_MESSAGES.GENERIC;
|
|
27
|
+
this.messageVariables = options.messageVariables;
|
|
28
|
+
this.errors = options.errors;
|
|
29
|
+
this.metadata = options.metadata;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class NotFoundException extends BaseAppException {
|
|
33
|
+
constructor(entity, id){
|
|
34
|
+
super({
|
|
35
|
+
message: id ? `${entity} with id "${id}" not found` : `${entity} not found`,
|
|
36
|
+
messageKey: ERROR_MESSAGES.NOT_FOUND,
|
|
37
|
+
status: HttpStatus.NOT_FOUND,
|
|
38
|
+
metadata: {
|
|
39
|
+
entity,
|
|
40
|
+
id
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export class ValidationException extends BaseAppException {
|
|
46
|
+
constructor(errors){
|
|
47
|
+
super({
|
|
48
|
+
message: 'Validation failed',
|
|
49
|
+
messageKey: ERROR_MESSAGES.VALIDATION,
|
|
50
|
+
status: HttpStatus.BAD_REQUEST,
|
|
51
|
+
errors
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export class UnauthorizedException extends BaseAppException {
|
|
56
|
+
constructor(message = 'Unauthorized access'){
|
|
57
|
+
super({
|
|
58
|
+
message,
|
|
59
|
+
messageKey: ERROR_MESSAGES.UNAUTHORIZED,
|
|
60
|
+
status: HttpStatus.UNAUTHORIZED
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export class ForbiddenException extends BaseAppException {
|
|
65
|
+
constructor(message = 'Access forbidden'){
|
|
66
|
+
super({
|
|
67
|
+
message,
|
|
68
|
+
messageKey: ERROR_MESSAGES.FORBIDDEN,
|
|
69
|
+
status: HttpStatus.FORBIDDEN
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export class ConflictException extends BaseAppException {
|
|
74
|
+
constructor(entity, field){
|
|
75
|
+
super({
|
|
76
|
+
message: field ? `${entity} with this ${field} already exists` : `${entity} already exists`,
|
|
77
|
+
messageKey: ERROR_MESSAGES.CONFLICT,
|
|
78
|
+
messageVariables: {
|
|
79
|
+
entity,
|
|
80
|
+
...field !== undefined && {
|
|
81
|
+
field
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
status: HttpStatus.CONFLICT,
|
|
85
|
+
metadata: {
|
|
86
|
+
entity,
|
|
87
|
+
field
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export class InternalServerException extends BaseAppException {
|
|
93
|
+
constructor(message = 'Internal server error'){
|
|
94
|
+
super({
|
|
95
|
+
message,
|
|
96
|
+
messageKey: ERROR_MESSAGES.INTERNAL,
|
|
97
|
+
status: HttpStatus.INTERNAL_SERVER_ERROR
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export class ServiceUnavailableException extends BaseAppException {
|
|
102
|
+
constructor(service){
|
|
103
|
+
super({
|
|
104
|
+
message: `${service} is temporarily unavailable`,
|
|
105
|
+
messageKey: ERROR_MESSAGES.SERVICE_UNAVAILABLE,
|
|
106
|
+
messageVariables: {
|
|
107
|
+
service
|
|
108
|
+
},
|
|
109
|
+
status: HttpStatus.SERVICE_UNAVAILABLE,
|
|
110
|
+
metadata: {
|
|
111
|
+
service
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
package/fesm/exceptions/index.js
CHANGED
|
@@ -1,37 +1,35 @@
|
|
|
1
1
|
import { ForbiddenException, InternalServerErrorException } from '@nestjs/common';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
constructor(message = 'Permission system temporarily unavailable. Please try again later.'){
|
|
2
|
+
import { ERROR_MESSAGES } from '../constants/message-keys';
|
|
3
|
+
export class PermissionSystemUnavailableException extends InternalServerErrorException {
|
|
4
|
+
constructor(){
|
|
6
5
|
super({
|
|
7
6
|
success: false,
|
|
8
|
-
message,
|
|
9
|
-
|
|
7
|
+
message: 'Permission system temporarily unavailable',
|
|
8
|
+
messageKey: ERROR_MESSAGES.PERMISSION_SYSTEM_UNAVAILABLE
|
|
10
9
|
});
|
|
11
10
|
}
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
constructor(missingPermissions, operator = 'and'){
|
|
17
|
-
const message = operator === 'or' ? `Requires at least one of: ${missingPermissions.join(', ')}` : `Missing required permissions: ${missingPermissions.join(', ')}`;
|
|
12
|
+
export class InsufficientPermissionsException extends ForbiddenException {
|
|
13
|
+
constructor(missingPermissions, operator = 'AND'){
|
|
14
|
+
const messageKey = operator === 'OR' ? ERROR_MESSAGES.INSUFFICIENT_PERMISSIONS_OR : ERROR_MESSAGES.INSUFFICIENT_PERMISSIONS;
|
|
18
15
|
super({
|
|
19
16
|
success: false,
|
|
20
|
-
message,
|
|
21
|
-
|
|
17
|
+
message: 'Insufficient permissions',
|
|
18
|
+
messageKey,
|
|
19
|
+
messageVariables: {
|
|
20
|
+
permissions: missingPermissions.join(', ')
|
|
21
|
+
},
|
|
22
22
|
missingPermissions,
|
|
23
23
|
operator
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
* Exception thrown when no permissions found for user
|
|
29
|
-
*/ export class NoPermissionsFoundException extends ForbiddenException {
|
|
27
|
+
export class NoPermissionsFoundException extends ForbiddenException {
|
|
30
28
|
constructor(){
|
|
31
29
|
super({
|
|
32
30
|
success: false,
|
|
33
|
-
message: 'No permissions found
|
|
34
|
-
|
|
31
|
+
message: 'No permissions found',
|
|
32
|
+
messageKey: ERROR_MESSAGES.NO_PERMISSIONS_FOUND
|
|
35
33
|
});
|
|
36
34
|
}
|
|
37
35
|
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
import { Catch, HttpException, HttpStatus, Logger } from '@nestjs/common';
|
|
21
|
+
import { QueryFailedError } from 'typeorm';
|
|
22
|
+
import { ERROR_MESSAGES } from '../constants/message-keys';
|
|
23
|
+
import { BaseAppException, ConflictException } from '../exceptions/base-app.exception';
|
|
24
|
+
import { getCompanyId, getRequestId, getTenantId, getUserId } from '../middlewares/logger.middleware';
|
|
25
|
+
export class GlobalExceptionFilter {
|
|
26
|
+
catch(exception, host) {
|
|
27
|
+
const ctx = host.switchToHttp();
|
|
28
|
+
const request = ctx.getRequest();
|
|
29
|
+
const response = ctx.getResponse();
|
|
30
|
+
const responseTime = Date.now() - (request._startTime || Date.now());
|
|
31
|
+
const requestId = getRequestId() || request.headers['x-request-id'] || 'no-request-id';
|
|
32
|
+
const { statusCode, errorResponse } = this.resolveException(exception, requestId, responseTime);
|
|
33
|
+
const logContext = {
|
|
34
|
+
requestId,
|
|
35
|
+
userId: getUserId(),
|
|
36
|
+
tenantId: getTenantId(),
|
|
37
|
+
companyId: getCompanyId(),
|
|
38
|
+
method: request.method,
|
|
39
|
+
url: request.originalUrl,
|
|
40
|
+
path: request.path,
|
|
41
|
+
statusCode,
|
|
42
|
+
duration: `${responseTime}ms`,
|
|
43
|
+
messageKey: errorResponse.messageKey
|
|
44
|
+
};
|
|
45
|
+
const logMessage = `Exception caught "HTTP/1.1" "${request.method}" "${request.originalUrl}" - ${statusCode} "${errorResponse.messageKey}" ${responseTime}ms | ${errorResponse.message}`;
|
|
46
|
+
if (statusCode >= 500) {
|
|
47
|
+
this.logger.error(logMessage, exception instanceof Error ? exception.stack : undefined, logContext);
|
|
48
|
+
} else if (statusCode >= 400) {
|
|
49
|
+
this.logger.warn(logMessage, logContext);
|
|
50
|
+
}
|
|
51
|
+
response.status(statusCode).json(errorResponse);
|
|
52
|
+
}
|
|
53
|
+
resolveException(exception, requestId, responseTime) {
|
|
54
|
+
const meta = {
|
|
55
|
+
requestId,
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
responseTime
|
|
58
|
+
};
|
|
59
|
+
if (exception instanceof BaseAppException) {
|
|
60
|
+
return {
|
|
61
|
+
statusCode: exception.getStatus(),
|
|
62
|
+
errorResponse: {
|
|
63
|
+
success: false,
|
|
64
|
+
message: exception.message,
|
|
65
|
+
messageKey: exception.messageKey,
|
|
66
|
+
messageVariables: exception.messageVariables,
|
|
67
|
+
errors: exception.errors,
|
|
68
|
+
_meta: meta
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (exception instanceof HttpException) {
|
|
73
|
+
const resolved = this.resolveHttpException(exception);
|
|
74
|
+
return {
|
|
75
|
+
statusCode: resolved.statusCode,
|
|
76
|
+
errorResponse: {
|
|
77
|
+
success: false,
|
|
78
|
+
message: resolved.message,
|
|
79
|
+
messageKey: resolved.messageKey,
|
|
80
|
+
messageVariables: resolved.messageVariables,
|
|
81
|
+
errors: resolved.errors,
|
|
82
|
+
_meta: meta
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (exception instanceof QueryFailedError) {
|
|
87
|
+
const resolved = this.resolveQueryFailedError(exception);
|
|
88
|
+
if (resolved) {
|
|
89
|
+
return {
|
|
90
|
+
statusCode: resolved.getStatus(),
|
|
91
|
+
errorResponse: {
|
|
92
|
+
success: false,
|
|
93
|
+
message: resolved.message,
|
|
94
|
+
messageKey: resolved.messageKey,
|
|
95
|
+
messageVariables: resolved.messageVariables,
|
|
96
|
+
_meta: meta
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
|
|
103
|
+
errorResponse: {
|
|
104
|
+
success: false,
|
|
105
|
+
message: 'Internal server error',
|
|
106
|
+
messageKey: ERROR_MESSAGES.INTERNAL,
|
|
107
|
+
_meta: meta
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
resolveHttpException(exception) {
|
|
112
|
+
const statusCode = exception.getStatus();
|
|
113
|
+
const response = exception.getResponse();
|
|
114
|
+
if (typeof response === 'string') {
|
|
115
|
+
return {
|
|
116
|
+
statusCode,
|
|
117
|
+
message: response,
|
|
118
|
+
messageKey: ERROR_MESSAGES.HTTP
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (typeof response === 'object' && response !== null) {
|
|
122
|
+
const obj = response;
|
|
123
|
+
if (Array.isArray(obj.message)) {
|
|
124
|
+
const errors = obj.message.map((msg)=>{
|
|
125
|
+
const [field, ...rest] = msg.split(' ');
|
|
126
|
+
return {
|
|
127
|
+
field: field || 'unknown',
|
|
128
|
+
message: rest.join(' ') || msg
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
statusCode,
|
|
133
|
+
message: 'Validation failed',
|
|
134
|
+
messageKey: ERROR_MESSAGES.VALIDATION,
|
|
135
|
+
errors
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
statusCode,
|
|
140
|
+
message: String(obj.message || 'Unknown error'),
|
|
141
|
+
messageKey: String(obj.messageKey || ERROR_MESSAGES.HTTP),
|
|
142
|
+
messageVariables: obj.messageVariables,
|
|
143
|
+
errors: obj.errors
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
statusCode,
|
|
148
|
+
message: 'Unknown error',
|
|
149
|
+
messageKey: ERROR_MESSAGES.UNKNOWN
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
resolveQueryFailedError(exception) {
|
|
153
|
+
const dbError = exception;
|
|
154
|
+
const code = dbError.driverError?.code ?? dbError.code;
|
|
155
|
+
const number = dbError.driverError?.number ?? dbError.number;
|
|
156
|
+
const errno = dbError.driverError?.errno ?? dbError.errno;
|
|
157
|
+
const sqlMessage = dbError.driverError?.sqlMessage ?? '';
|
|
158
|
+
const detail = dbError.driverError?.detail ?? dbError.detail ?? '';
|
|
159
|
+
const table = dbError.driverError?.table ?? dbError.table ?? '';
|
|
160
|
+
const column = dbError.driverError?.column ?? dbError.column;
|
|
161
|
+
const pgField = detail.match(/Key \(([^)]+)\)/)?.[1] ?? column;
|
|
162
|
+
// DUPLICATE / UNIQUE VIOLATION
|
|
163
|
+
// PG: 23505 | MSSQL: 2627 (constraint), 2601 (index) | MySQL: ER_DUP_ENTRY / errno 1062
|
|
164
|
+
if (code === '23505' || number === 2627 || number === 2601 || code === 'ER_DUP_ENTRY' || errno === 1062) {
|
|
165
|
+
const mysqlTable = sqlMessage.match(/for key '([^'.]+)\./)?.[1];
|
|
166
|
+
const mysqlIndex = sqlMessage.match(/for key '(?:[^.]+\.)?([^']+)'/)?.[1];
|
|
167
|
+
const resolvedTable = table || mysqlTable || 'record';
|
|
168
|
+
const resolvedField = pgField ? this.toHumanReadable(pgField) : mysqlIndex ? this.toHumanReadable(mysqlIndex) : undefined;
|
|
169
|
+
return new ConflictException(this.toHumanReadable(resolvedTable), resolvedField);
|
|
170
|
+
}
|
|
171
|
+
// FOREIGN KEY VIOLATION
|
|
172
|
+
// PG: 23503 | MSSQL: 547 | MySQL: ER_NO_REFERENCED_ROW_2/1452 (insert), ER_ROW_IS_REFERENCED_2/1451 (delete)
|
|
173
|
+
if (code === '23503' || number === 547 || code === 'ER_NO_REFERENCED_ROW_2' || code === 'ER_ROW_IS_REFERENCED_2' || errno === 1452 || errno === 1451) {
|
|
174
|
+
const mysqlColumn = sqlMessage.match(/FOREIGN KEY \(`([^`]+)`\)/)?.[1];
|
|
175
|
+
const raw = pgField ?? mysqlColumn;
|
|
176
|
+
const humanField = raw ? this.toHumanReadable(raw) : undefined;
|
|
177
|
+
return new BaseAppException({
|
|
178
|
+
message: humanField ? `Invalid reference for '${humanField}'` : 'Invalid reference',
|
|
179
|
+
messageKey: ERROR_MESSAGES.DB_FOREIGN_KEY,
|
|
180
|
+
messageVariables: humanField ? {
|
|
181
|
+
field: humanField
|
|
182
|
+
} : undefined,
|
|
183
|
+
status: HttpStatus.BAD_REQUEST
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// NOT NULL VIOLATION
|
|
187
|
+
// PG: 23502 | MSSQL: 515 | MySQL: ER_BAD_NULL_ERROR / errno 1048
|
|
188
|
+
if (code === '23502' || number === 515 || code === 'ER_BAD_NULL_ERROR' || errno === 1048) {
|
|
189
|
+
const mysqlColumn = sqlMessage.match(/Column '([^']+)' cannot be null/)?.[1];
|
|
190
|
+
const humanField = this.toHumanReadable(column ?? mysqlColumn ?? 'field');
|
|
191
|
+
return new BaseAppException({
|
|
192
|
+
message: `Required field '${humanField}' cannot be null`,
|
|
193
|
+
messageKey: ERROR_MESSAGES.DB_NOT_NULL,
|
|
194
|
+
messageVariables: {
|
|
195
|
+
field: humanField
|
|
196
|
+
},
|
|
197
|
+
status: HttpStatus.BAD_REQUEST
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
// INVALID INPUT SYNTAX — PostgreSQL only (MySQL silently coerces types)
|
|
201
|
+
if (code === '22P02') {
|
|
202
|
+
const typeMatch = exception.message.match(/invalid input syntax for(?:\s+type)?\s+(\w+)/i);
|
|
203
|
+
const type = typeMatch?.[1] ?? 'value';
|
|
204
|
+
const humanField = column ? this.toHumanReadable(column) : undefined;
|
|
205
|
+
return new BaseAppException({
|
|
206
|
+
message: humanField ? `Invalid ${type} value for '${humanField}'` : `Invalid ${type} value`,
|
|
207
|
+
messageKey: ERROR_MESSAGES.DB_INVALID_INPUT,
|
|
208
|
+
messageVariables: {
|
|
209
|
+
type,
|
|
210
|
+
...humanField !== undefined && {
|
|
211
|
+
field: humanField
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
status: HttpStatus.BAD_REQUEST
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
// VALUE TOO LONG
|
|
218
|
+
// PG: 22001 | MySQL: ER_DATA_TOO_LONG / errno 1406
|
|
219
|
+
if (code === '22001' || code === 'ER_DATA_TOO_LONG' || errno === 1406) {
|
|
220
|
+
const mysqlColumn = sqlMessage.match(/Data too long for column '([^']+)'/)?.[1];
|
|
221
|
+
const resolvedColumn = column ?? mysqlColumn;
|
|
222
|
+
const humanField = resolvedColumn ? this.toHumanReadable(resolvedColumn) : undefined;
|
|
223
|
+
return new BaseAppException({
|
|
224
|
+
message: humanField ? `Value too long for '${humanField}'` : 'Value exceeds maximum length',
|
|
225
|
+
messageKey: ERROR_MESSAGES.DB_VALUE_TOO_LONG,
|
|
226
|
+
messageVariables: humanField !== undefined ? {
|
|
227
|
+
field: humanField
|
|
228
|
+
} : undefined,
|
|
229
|
+
status: HttpStatus.BAD_REQUEST
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
// CHECK CONSTRAINT VIOLATION
|
|
233
|
+
// PG: 23514 | MySQL 8.0+: ER_CHECK_CONSTRAINT_VIOLATED / errno 3819
|
|
234
|
+
if (code === '23514' || code === 'ER_CHECK_CONSTRAINT_VIOLATED' || errno === 3819) {
|
|
235
|
+
const constraint = dbError.driverError?.constraint ?? dbError.constraint;
|
|
236
|
+
const humanTable = this.toHumanReadable(table || 'record');
|
|
237
|
+
return new BaseAppException({
|
|
238
|
+
message: `${humanTable} violates a validation rule`,
|
|
239
|
+
messageKey: ERROR_MESSAGES.DB_CHECK_CONSTRAINT,
|
|
240
|
+
messageVariables: {
|
|
241
|
+
entity: humanTable,
|
|
242
|
+
...constraint !== undefined && {
|
|
243
|
+
constraint
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
status: HttpStatus.BAD_REQUEST
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
// DEADLOCK
|
|
250
|
+
// PG: 40P01 | MSSQL: 1205 | MySQL: ER_LOCK_DEADLOCK / errno 1213
|
|
251
|
+
if (code === '40P01' || number === 1205 || code === 'ER_LOCK_DEADLOCK' || errno === 1213) {
|
|
252
|
+
return new BaseAppException({
|
|
253
|
+
message: 'The request could not be completed due to a conflict, please try again',
|
|
254
|
+
messageKey: ERROR_MESSAGES.DB_DEADLOCK,
|
|
255
|
+
status: HttpStatus.SERVICE_UNAVAILABLE
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
toHumanReadable(dbName) {
|
|
261
|
+
return dbName.split(', ').map((part)=>part.split('_').map((word)=>word.toLowerCase() === 'id' ? 'ID' : word.charAt(0).toUpperCase() + word.slice(1)).join(' ')).join(', ');
|
|
262
|
+
}
|
|
263
|
+
constructor(){
|
|
264
|
+
_define_property(this, "logger", new Logger('ExceptionFilter'));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
GlobalExceptionFilter = _ts_decorate([
|
|
268
|
+
Catch()
|
|
269
|
+
], GlobalExceptionFilter);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './global-exception.filter';
|
|
@@ -25,7 +25,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import { IS_PUBLIC_KEY } from '../constants';
|
|
28
|
+
import { AUTH_MESSAGES, IS_PUBLIC_KEY } from '../constants';
|
|
29
29
|
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
|
|
30
30
|
import { Reflector } from '@nestjs/core';
|
|
31
31
|
import { AuthGuard } from '@nestjs/passport';
|
|
@@ -43,7 +43,10 @@ export class JwtAuthGuard extends AuthGuard('jwt') {
|
|
|
43
43
|
}
|
|
44
44
|
handleRequest(err, user) {
|
|
45
45
|
if (err || !user) {
|
|
46
|
-
throw err || new UnauthorizedException(
|
|
46
|
+
throw err || new UnauthorizedException({
|
|
47
|
+
message: 'Invalid or expired token',
|
|
48
|
+
messageKey: AUTH_MESSAGES.TOKEN_INVALID
|
|
49
|
+
});
|
|
47
50
|
}
|
|
48
51
|
return user;
|
|
49
52
|
}
|