@flusys/nestjs-shared 1.1.0-beta → 2.0.0
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 +501 -720
- package/cjs/classes/api-controller.class.js +9 -24
- package/cjs/classes/api-service.class.js +59 -92
- package/cjs/classes/index.js +1 -0
- package/cjs/classes/winston-logger-adapter.class.js +23 -40
- package/cjs/constants/index.js +14 -0
- package/cjs/constants/permissions.js +184 -0
- package/cjs/decorators/api-response.decorator.js +1 -1
- package/cjs/decorators/index.js +1 -0
- package/cjs/decorators/sanitize-html.decorator.js +36 -0
- package/cjs/dtos/delete.dto.js +10 -0
- package/cjs/dtos/filter-and-pagination.dto.js +24 -34
- package/cjs/dtos/pagination.dto.js +4 -8
- package/cjs/dtos/response-payload.dto.js +0 -116
- package/cjs/entities/identity.js +4 -4
- package/cjs/entities/user-root.js +13 -14
- package/cjs/guards/permission.guard.js +51 -105
- package/cjs/interceptors/index.js +1 -3
- 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/index.js +2 -1
- package/cjs/interfaces/module-config.interface.js +4 -0
- package/cjs/middlewares/logger.middleware.js +50 -89
- package/cjs/modules/cache/cache.module.js +3 -3
- package/cjs/modules/datasource/datasource.module.js +11 -14
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +29 -113
- package/cjs/modules/utils/utils.service.js +40 -203
- package/cjs/utils/error-handler.util.js +35 -12
- package/cjs/utils/html-sanitizer.util.js +64 -0
- package/cjs/utils/index.js +4 -0
- package/cjs/utils/query-helpers.util.js +53 -0
- package/cjs/utils/request.util.js +70 -0
- package/cjs/utils/string.util.js +63 -0
- package/classes/api-controller.class.d.ts +5 -5
- package/classes/api-service.class.d.ts +7 -5
- package/classes/index.d.ts +1 -0
- package/classes/request-scoped-api.service.d.ts +3 -2
- package/classes/winston-logger-adapter.class.d.ts +2 -0
- package/constants/index.d.ts +1 -0
- package/constants/permissions.d.ts +179 -0
- package/decorators/index.d.ts +1 -0
- package/decorators/sanitize-html.decorator.d.ts +2 -0
- package/dtos/delete.dto.d.ts +1 -0
- package/dtos/filter-and-pagination.dto.d.ts +0 -2
- package/dtos/response-payload.dto.d.ts +0 -20
- package/fesm/classes/api-controller.class.js +9 -24
- package/fesm/classes/api-service.class.js +59 -92
- package/fesm/classes/index.js +2 -0
- package/fesm/classes/winston-logger-adapter.class.js +23 -40
- package/fesm/constants/index.js +2 -0
- package/fesm/constants/permissions.js +128 -0
- package/fesm/decorators/api-response.decorator.js +1 -1
- package/fesm/decorators/index.js +1 -0
- package/fesm/decorators/sanitize-html.decorator.js +45 -0
- package/fesm/dtos/delete.dto.js +12 -2
- package/fesm/dtos/filter-and-pagination.dto.js +26 -47
- package/fesm/dtos/pagination.dto.js +4 -8
- package/fesm/dtos/response-payload.dto.js +0 -107
- package/fesm/entities/identity.js +4 -4
- package/fesm/entities/user-root.js +13 -14
- package/fesm/guards/permission.guard.js +51 -105
- package/fesm/interceptors/index.js +1 -3
- 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 +20 -0
- package/fesm/interfaces/index.js +2 -1
- package/fesm/interfaces/module-config.interface.js +5 -0
- package/fesm/middlewares/logger.middleware.js +50 -83
- package/fesm/modules/cache/cache.module.js +2 -2
- package/fesm/modules/datasource/datasource.module.js +11 -14
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +29 -113
- package/fesm/modules/utils/utils.service.js +41 -204
- package/fesm/utils/error-handler.util.js +36 -13
- package/fesm/utils/html-sanitizer.util.js +69 -0
- package/fesm/utils/index.js +4 -0
- package/fesm/utils/query-helpers.util.js +78 -0
- package/fesm/utils/request.util.js +58 -0
- package/fesm/utils/string.util.js +71 -0
- package/guards/permission.guard.d.ts +2 -0
- package/interceptors/index.d.ts +1 -3
- 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 +2 -2
- package/interfaces/datasource.interface.d.ts +5 -0
- package/interfaces/identity.interface.d.ts +4 -4
- package/interfaces/index.d.ts +2 -1
- package/interfaces/logged-user-info.interface.d.ts +0 -2
- package/interfaces/module-config.interface.d.ts +6 -0
- package/interfaces/permission.interface.d.ts +0 -1
- package/middlewares/logger.middleware.d.ts +2 -2
- package/modules/datasource/datasource.module.d.ts +1 -0
- package/modules/datasource/multi-tenant-datasource.service.d.ts +0 -1
- package/modules/utils/utils.service.d.ts +4 -14
- package/package.json +4 -4
- package/utils/error-handler.util.d.ts +14 -19
- package/utils/html-sanitizer.util.d.ts +2 -0
- package/utils/index.d.ts +4 -0
- package/utils/query-helpers.util.d.ts +16 -0
- package/utils/request.util.d.ts +4 -0
- package/utils/string.util.d.ts +2 -0
- 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/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/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
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { DeleteDto, FilterAndPaginationDto } from '
|
|
2
|
-
import { Identity } from '
|
|
3
|
-
import { ILoggedUserInfo, IService } from '
|
|
4
|
-
import { UtilsService } from '
|
|
1
|
+
import { DeleteDto, FilterAndPaginationDto } from '../dtos';
|
|
2
|
+
import { Identity } from '../entities';
|
|
3
|
+
import { ILoggedUserInfo, IService } from '../interfaces';
|
|
4
|
+
import { UtilsService } from '../modules/utils/utils.service';
|
|
5
5
|
import { Logger } from '@nestjs/common';
|
|
6
6
|
import { QueryRunner, Repository, SelectQueryBuilder } from 'typeorm';
|
|
7
7
|
import { HybridCache } from './hybrid-cache.class';
|
|
8
|
-
export declare abstract class ApiService<CreateDtoT extends
|
|
8
|
+
export declare abstract class ApiService<CreateDtoT extends object, UpdateDtoT extends {
|
|
9
9
|
id: string;
|
|
10
10
|
}, InterfaceT extends Identity, EntityT extends Identity, RepositoryT extends Repository<EntityT>> implements IService<CreateDtoT, UpdateDtoT, InterfaceT> {
|
|
11
11
|
protected entityName: string;
|
|
@@ -30,6 +30,8 @@ export declare abstract class ApiService<CreateDtoT extends Record<string, unkno
|
|
|
30
30
|
clearCacheForAll(): Promise<void>;
|
|
31
31
|
clearCacheForId(entities: EntityT[]): Promise<void>;
|
|
32
32
|
private handleError;
|
|
33
|
+
private ensureArray;
|
|
34
|
+
private executeInTransaction;
|
|
33
35
|
protected ensureRepositoryInitialized(): Promise<void>;
|
|
34
36
|
protected beforeInsertOperation(_dto: CreateDtoT | Array<CreateDtoT>, _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
|
|
35
37
|
protected afterInsertOperation(_entity: EntityT[], _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
|
package/classes/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { DataSource, EntityTarget, Repository } from 'typeorm';
|
|
2
2
|
import { Identity } from '../entities';
|
|
3
|
+
import { IDataSourceProvider } from '../interfaces';
|
|
3
4
|
import { ApiService } from './api-service.class';
|
|
4
|
-
export declare abstract class RequestScopedApiService<CreateDtoT extends
|
|
5
|
+
export declare abstract class RequestScopedApiService<CreateDtoT extends object, UpdateDtoT extends {
|
|
5
6
|
id: string;
|
|
6
7
|
}, InterfaceT extends Identity, EntityT extends Identity, RepositoryT extends Repository<EntityT>> extends ApiService<CreateDtoT, UpdateDtoT, InterfaceT, EntityT, RepositoryT> {
|
|
7
8
|
private repositoryInitialized;
|
|
8
9
|
protected abstract resolveEntity(): EntityTarget<EntityT>;
|
|
9
|
-
protected abstract getDataSourceProvider():
|
|
10
|
+
protected abstract getDataSourceProvider(): IDataSourceProvider;
|
|
10
11
|
protected ensureRepositoryInitialized(): Promise<void>;
|
|
11
12
|
protected initializeAdditionalRepositories(entities: EntityTarget<any>[]): Promise<Repository<any>[]>;
|
|
12
13
|
protected getDataSourceForService(): Promise<DataSource>;
|
|
@@ -3,6 +3,7 @@ import { ILogger } from '../interfaces/logger.interface';
|
|
|
3
3
|
export declare class WinstonLoggerAdapter implements ILogger {
|
|
4
4
|
private readonly context?;
|
|
5
5
|
constructor(context?: string);
|
|
6
|
+
private buildLogMeta;
|
|
6
7
|
log(message: string, context?: string, ...args: any[]): void;
|
|
7
8
|
error(message: string, trace?: string, context?: string, ...args: any[]): void;
|
|
8
9
|
warn(message: string, context?: string, ...args: any[]): void;
|
|
@@ -12,6 +13,7 @@ export declare class WinstonLoggerAdapter implements ILogger {
|
|
|
12
13
|
export declare class NestLoggerAdapter implements ILogger {
|
|
13
14
|
private readonly logger;
|
|
14
15
|
constructor(logger: Logger);
|
|
16
|
+
private formatMessage;
|
|
15
17
|
log(message: string, context?: string, ...args: any[]): void;
|
|
16
18
|
error(message: string, trace?: string, context?: string, ...args: any[]): void;
|
|
17
19
|
warn(message: string, context?: string, ...args: any[]): void;
|
package/constants/index.d.ts
CHANGED
|
@@ -8,3 +8,4 @@ export declare const REQUEST_ID_HEADER = "x-request-id";
|
|
|
8
8
|
export declare const CLIENT_TYPE_HEADER = "x-client-type";
|
|
9
9
|
export declare const PERMISSIONS_CACHE_PREFIX = "permissions";
|
|
10
10
|
export declare const IDEMPOTENCY_CACHE_PREFIX = "idempotency";
|
|
11
|
+
export * from './permissions';
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
export declare const USER_PERMISSIONS: {
|
|
2
|
+
readonly CREATE: "user.create";
|
|
3
|
+
readonly READ: "user.read";
|
|
4
|
+
readonly UPDATE: "user.update";
|
|
5
|
+
readonly DELETE: "user.delete";
|
|
6
|
+
};
|
|
7
|
+
export declare const COMPANY_PERMISSIONS: {
|
|
8
|
+
readonly CREATE: "company.create";
|
|
9
|
+
readonly READ: "company.read";
|
|
10
|
+
readonly UPDATE: "company.update";
|
|
11
|
+
readonly DELETE: "company.delete";
|
|
12
|
+
};
|
|
13
|
+
export declare const BRANCH_PERMISSIONS: {
|
|
14
|
+
readonly CREATE: "branch.create";
|
|
15
|
+
readonly READ: "branch.read";
|
|
16
|
+
readonly UPDATE: "branch.update";
|
|
17
|
+
readonly DELETE: "branch.delete";
|
|
18
|
+
};
|
|
19
|
+
export declare const ACTION_PERMISSIONS: {
|
|
20
|
+
readonly CREATE: "action.create";
|
|
21
|
+
readonly READ: "action.read";
|
|
22
|
+
readonly UPDATE: "action.update";
|
|
23
|
+
readonly DELETE: "action.delete";
|
|
24
|
+
};
|
|
25
|
+
export declare const ROLE_PERMISSIONS: {
|
|
26
|
+
readonly CREATE: "role.create";
|
|
27
|
+
readonly READ: "role.read";
|
|
28
|
+
readonly UPDATE: "role.update";
|
|
29
|
+
readonly DELETE: "role.delete";
|
|
30
|
+
};
|
|
31
|
+
export declare const ROLE_ACTION_PERMISSIONS: {
|
|
32
|
+
readonly READ: "role-action.read";
|
|
33
|
+
readonly ASSIGN: "role-action.assign";
|
|
34
|
+
};
|
|
35
|
+
export declare const USER_ROLE_PERMISSIONS: {
|
|
36
|
+
readonly READ: "user-role.read";
|
|
37
|
+
readonly ASSIGN: "user-role.assign";
|
|
38
|
+
};
|
|
39
|
+
export declare const USER_ACTION_PERMISSIONS: {
|
|
40
|
+
readonly READ: "user-action.read";
|
|
41
|
+
readonly ASSIGN: "user-action.assign";
|
|
42
|
+
};
|
|
43
|
+
export declare const COMPANY_ACTION_PERMISSIONS: {
|
|
44
|
+
readonly READ: "company-action.read";
|
|
45
|
+
readonly ASSIGN: "company-action.assign";
|
|
46
|
+
};
|
|
47
|
+
export declare const FILE_PERMISSIONS: {
|
|
48
|
+
readonly CREATE: "file.create";
|
|
49
|
+
readonly READ: "file.read";
|
|
50
|
+
readonly UPDATE: "file.update";
|
|
51
|
+
readonly DELETE: "file.delete";
|
|
52
|
+
};
|
|
53
|
+
export declare const FOLDER_PERMISSIONS: {
|
|
54
|
+
readonly CREATE: "folder.create";
|
|
55
|
+
readonly READ: "folder.read";
|
|
56
|
+
readonly UPDATE: "folder.update";
|
|
57
|
+
readonly DELETE: "folder.delete";
|
|
58
|
+
};
|
|
59
|
+
export declare const STORAGE_CONFIG_PERMISSIONS: {
|
|
60
|
+
readonly CREATE: "storage-config.create";
|
|
61
|
+
readonly READ: "storage-config.read";
|
|
62
|
+
readonly UPDATE: "storage-config.update";
|
|
63
|
+
readonly DELETE: "storage-config.delete";
|
|
64
|
+
};
|
|
65
|
+
export declare const EMAIL_CONFIG_PERMISSIONS: {
|
|
66
|
+
readonly CREATE: "email-config.create";
|
|
67
|
+
readonly READ: "email-config.read";
|
|
68
|
+
readonly UPDATE: "email-config.update";
|
|
69
|
+
readonly DELETE: "email-config.delete";
|
|
70
|
+
};
|
|
71
|
+
export declare const EMAIL_TEMPLATE_PERMISSIONS: {
|
|
72
|
+
readonly CREATE: "email-template.create";
|
|
73
|
+
readonly READ: "email-template.read";
|
|
74
|
+
readonly UPDATE: "email-template.update";
|
|
75
|
+
readonly DELETE: "email-template.delete";
|
|
76
|
+
};
|
|
77
|
+
export declare const FORM_PERMISSIONS: {
|
|
78
|
+
readonly CREATE: "form.create";
|
|
79
|
+
readonly READ: "form.read";
|
|
80
|
+
readonly UPDATE: "form.update";
|
|
81
|
+
readonly DELETE: "form.delete";
|
|
82
|
+
};
|
|
83
|
+
export declare const FORM_RESULT_PERMISSIONS: {
|
|
84
|
+
readonly CREATE: "form-result.create";
|
|
85
|
+
readonly READ: "form-result.read";
|
|
86
|
+
readonly UPDATE: "form-result.update";
|
|
87
|
+
readonly DELETE: "form-result.delete";
|
|
88
|
+
};
|
|
89
|
+
export declare const PERMISSIONS: {
|
|
90
|
+
readonly USER: {
|
|
91
|
+
readonly CREATE: "user.create";
|
|
92
|
+
readonly READ: "user.read";
|
|
93
|
+
readonly UPDATE: "user.update";
|
|
94
|
+
readonly DELETE: "user.delete";
|
|
95
|
+
};
|
|
96
|
+
readonly COMPANY: {
|
|
97
|
+
readonly CREATE: "company.create";
|
|
98
|
+
readonly READ: "company.read";
|
|
99
|
+
readonly UPDATE: "company.update";
|
|
100
|
+
readonly DELETE: "company.delete";
|
|
101
|
+
};
|
|
102
|
+
readonly BRANCH: {
|
|
103
|
+
readonly CREATE: "branch.create";
|
|
104
|
+
readonly READ: "branch.read";
|
|
105
|
+
readonly UPDATE: "branch.update";
|
|
106
|
+
readonly DELETE: "branch.delete";
|
|
107
|
+
};
|
|
108
|
+
readonly ACTION: {
|
|
109
|
+
readonly CREATE: "action.create";
|
|
110
|
+
readonly READ: "action.read";
|
|
111
|
+
readonly UPDATE: "action.update";
|
|
112
|
+
readonly DELETE: "action.delete";
|
|
113
|
+
};
|
|
114
|
+
readonly ROLE: {
|
|
115
|
+
readonly CREATE: "role.create";
|
|
116
|
+
readonly READ: "role.read";
|
|
117
|
+
readonly UPDATE: "role.update";
|
|
118
|
+
readonly DELETE: "role.delete";
|
|
119
|
+
};
|
|
120
|
+
readonly ROLE_ACTION: {
|
|
121
|
+
readonly READ: "role-action.read";
|
|
122
|
+
readonly ASSIGN: "role-action.assign";
|
|
123
|
+
};
|
|
124
|
+
readonly USER_ROLE: {
|
|
125
|
+
readonly READ: "user-role.read";
|
|
126
|
+
readonly ASSIGN: "user-role.assign";
|
|
127
|
+
};
|
|
128
|
+
readonly USER_ACTION: {
|
|
129
|
+
readonly READ: "user-action.read";
|
|
130
|
+
readonly ASSIGN: "user-action.assign";
|
|
131
|
+
};
|
|
132
|
+
readonly COMPANY_ACTION: {
|
|
133
|
+
readonly READ: "company-action.read";
|
|
134
|
+
readonly ASSIGN: "company-action.assign";
|
|
135
|
+
};
|
|
136
|
+
readonly FILE: {
|
|
137
|
+
readonly CREATE: "file.create";
|
|
138
|
+
readonly READ: "file.read";
|
|
139
|
+
readonly UPDATE: "file.update";
|
|
140
|
+
readonly DELETE: "file.delete";
|
|
141
|
+
};
|
|
142
|
+
readonly FOLDER: {
|
|
143
|
+
readonly CREATE: "folder.create";
|
|
144
|
+
readonly READ: "folder.read";
|
|
145
|
+
readonly UPDATE: "folder.update";
|
|
146
|
+
readonly DELETE: "folder.delete";
|
|
147
|
+
};
|
|
148
|
+
readonly STORAGE_CONFIG: {
|
|
149
|
+
readonly CREATE: "storage-config.create";
|
|
150
|
+
readonly READ: "storage-config.read";
|
|
151
|
+
readonly UPDATE: "storage-config.update";
|
|
152
|
+
readonly DELETE: "storage-config.delete";
|
|
153
|
+
};
|
|
154
|
+
readonly EMAIL_CONFIG: {
|
|
155
|
+
readonly CREATE: "email-config.create";
|
|
156
|
+
readonly READ: "email-config.read";
|
|
157
|
+
readonly UPDATE: "email-config.update";
|
|
158
|
+
readonly DELETE: "email-config.delete";
|
|
159
|
+
};
|
|
160
|
+
readonly EMAIL_TEMPLATE: {
|
|
161
|
+
readonly CREATE: "email-template.create";
|
|
162
|
+
readonly READ: "email-template.read";
|
|
163
|
+
readonly UPDATE: "email-template.update";
|
|
164
|
+
readonly DELETE: "email-template.delete";
|
|
165
|
+
};
|
|
166
|
+
readonly FORM: {
|
|
167
|
+
readonly CREATE: "form.create";
|
|
168
|
+
readonly READ: "form.read";
|
|
169
|
+
readonly UPDATE: "form.update";
|
|
170
|
+
readonly DELETE: "form.delete";
|
|
171
|
+
};
|
|
172
|
+
readonly FORM_RESULT: {
|
|
173
|
+
readonly CREATE: "form-result.create";
|
|
174
|
+
readonly READ: "form-result.read";
|
|
175
|
+
readonly UPDATE: "form-result.update";
|
|
176
|
+
readonly DELETE: "form-result.delete";
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
export type PermissionCode = (typeof PERMISSIONS)[keyof typeof PERMISSIONS][keyof (typeof PERMISSIONS)[keyof typeof PERMISSIONS]];
|
package/decorators/index.d.ts
CHANGED
package/dtos/delete.dto.d.ts
CHANGED
|
@@ -41,23 +41,3 @@ export declare class MessageResponseDto {
|
|
|
41
41
|
message: string;
|
|
42
42
|
_meta?: RequestMetaDto;
|
|
43
43
|
}
|
|
44
|
-
export declare class ValidationErrorDto {
|
|
45
|
-
field: string;
|
|
46
|
-
message: string;
|
|
47
|
-
constraint?: string;
|
|
48
|
-
}
|
|
49
|
-
export declare class ErrorResponseDto {
|
|
50
|
-
success: false;
|
|
51
|
-
message: string;
|
|
52
|
-
code?: string;
|
|
53
|
-
errors?: ValidationErrorDto[];
|
|
54
|
-
_meta?: RequestMetaDto;
|
|
55
|
-
}
|
|
56
|
-
export declare class ResponsePayloadDto<T> {
|
|
57
|
-
success: boolean;
|
|
58
|
-
message: string;
|
|
59
|
-
data?: T;
|
|
60
|
-
meta?: PaginationMetaDto | BulkMetaDto;
|
|
61
|
-
_meta?: RequestMetaDto;
|
|
62
|
-
}
|
|
63
|
-
export type ApiResponse<T> = SingleResponseDto<T> | ListResponseDto<T> | BulkResponseDto<T> | MessageResponseDto | ErrorResponseDto;
|
|
@@ -25,10 +25,10 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import { CurrentUser, Public, RequirePermission, RequireAnyPermission, RequirePermissionCondition } from '
|
|
29
|
-
import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto } from '
|
|
30
|
-
import { JwtAuthGuard, PermissionGuard } from '
|
|
31
|
-
import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '
|
|
28
|
+
import { CurrentUser, Public, RequirePermission, RequireAnyPermission, RequirePermissionCondition } from '../decorators';
|
|
29
|
+
import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto } from '../dtos';
|
|
30
|
+
import { JwtAuthGuard, PermissionGuard } from '../guards';
|
|
31
|
+
import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '../interceptors';
|
|
32
32
|
import { applyDecorators, Body, HttpCode, HttpStatus, Param, Post, Query, UseGuards, UseInterceptors, Version, VERSION_NEUTRAL } from '@nestjs/common';
|
|
33
33
|
import { ApiBearerAuth, ApiBody, ApiHeader, ApiOperation, ApiParam, ApiQuery, ApiResponse } from '@nestjs/swagger';
|
|
34
34
|
import { plainToInstance } from 'class-transformer';
|
|
@@ -94,8 +94,10 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
94
94
|
// 2. It's an object with 'level' property but no endpoint keys
|
|
95
95
|
const isGlobalSecurity = typeof securityConfig === 'string' || securityConfig && typeof securityConfig === 'object' && 'level' in securityConfig && !endpointKeys.some((key)=>key in securityConfig);
|
|
96
96
|
// Normalize security config for each endpoint
|
|
97
|
+
// IMPORTANT: When per-endpoint security is specified, default to 'jwt' for unconfigured endpoints
|
|
98
|
+
// to prevent accidentally exposing endpoints without authentication
|
|
97
99
|
const defaultSecurity = isGlobalSecurity ? normalizeSecurity(securityConfig) : {
|
|
98
|
-
level: '
|
|
100
|
+
level: 'jwt'
|
|
99
101
|
};
|
|
100
102
|
const security = {
|
|
101
103
|
insert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insert),
|
|
@@ -337,16 +339,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
|
|
|
337
339
|
HttpCode(HttpStatus.OK),
|
|
338
340
|
ApiOperation({
|
|
339
341
|
summary: 'Get all items with filters and pagination',
|
|
340
|
-
description:
|
|
341
|
-
Retrieves items with support for:
|
|
342
|
-
- **filter**: Apply field-based filters (e.g., \`{ "isActive": true }\`)
|
|
343
|
-
- **pagination**: Control page and page size
|
|
344
|
-
- **sort**: Order by any field (e.g., \`{ "createdAt": "DESC" }\`)
|
|
345
|
-
- **select**: Choose specific fields to return
|
|
346
|
-
- **withDeleted**: Include soft-deleted items
|
|
347
|
-
- **extraKey**: Include additional relations
|
|
348
|
-
- **q** (query param): Global text search
|
|
349
|
-
`
|
|
342
|
+
description: 'Supports filter, pagination, sort, select, withDeleted, and q (search) params'
|
|
350
343
|
}),
|
|
351
344
|
ApiQuery({
|
|
352
345
|
name: 'q',
|
|
@@ -375,15 +368,7 @@ Retrieves items with support for:
|
|
|
375
368
|
HttpCode(HttpStatus.OK),
|
|
376
369
|
ApiOperation({
|
|
377
370
|
summary: 'Delete, restore, or permanently remove items',
|
|
378
|
-
description:
|
|
379
|
-
Performs one of three actions:
|
|
380
|
-
|
|
381
|
-
- **"delete"** (soft delete): Marks items as deleted but keeps in database
|
|
382
|
-
- **"restore"**: Reverts soft-deleted items to active
|
|
383
|
-
- **"permanent"**: Completely removes items from database
|
|
384
|
-
|
|
385
|
-
Supports single ID or array of IDs for batch operations.
|
|
386
|
-
`
|
|
371
|
+
description: 'Types: delete (soft), restore, permanent. Supports batch IDs.'
|
|
387
372
|
}),
|
|
388
373
|
ApiResponse({
|
|
389
374
|
status: 200,
|
|
@@ -16,116 +16,52 @@ import { Logger, NotFoundException } from '@nestjs/common';
|
|
|
16
16
|
import { In } from 'typeorm';
|
|
17
17
|
/** Generic API service with CRUD operations and caching support */ export class ApiService {
|
|
18
18
|
async insert(dto, user) {
|
|
19
|
-
|
|
20
|
-
const qr = this.repository.manager.connection.createQueryRunner();
|
|
21
|
-
await qr.connect();
|
|
22
|
-
await qr.startTransaction();
|
|
23
|
-
try {
|
|
19
|
+
return this.executeInTransaction('insert', async (qr)=>{
|
|
24
20
|
await this.beforeInsertOperation(dto, user, qr);
|
|
25
21
|
const entities = await this.convertRequestDtoToEntity(dto, user);
|
|
26
22
|
const saved = await qr.manager.save(this.repository.target, entities);
|
|
27
|
-
await this.afterInsertOperation(saved, user, qr);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
])
|
|
34
|
-
]);
|
|
35
|
-
const first = Array.isArray(saved) ? saved[0] : saved;
|
|
36
|
-
return this.convertEntityToResponseDto(first, false);
|
|
37
|
-
} catch (error) {
|
|
38
|
-
await qr.rollbackTransaction();
|
|
39
|
-
this.handleError(error, 'insert');
|
|
40
|
-
} finally{
|
|
41
|
-
await qr.release();
|
|
42
|
-
}
|
|
23
|
+
await this.afterInsertOperation(this.ensureArray(saved), user, qr);
|
|
24
|
+
return {
|
|
25
|
+
saved,
|
|
26
|
+
returnFirst: true
|
|
27
|
+
};
|
|
28
|
+
});
|
|
43
29
|
}
|
|
44
30
|
async insertMany(dtos, user) {
|
|
45
|
-
|
|
46
|
-
const qr = this.repository.manager.connection.createQueryRunner();
|
|
47
|
-
await qr.connect();
|
|
48
|
-
await qr.startTransaction();
|
|
49
|
-
try {
|
|
31
|
+
return this.executeInTransaction('insertMany', async (qr)=>{
|
|
50
32
|
await this.beforeInsertOperation(dtos, user, qr);
|
|
51
33
|
const entities = await this.convertRequestDtoToEntity(dtos, user);
|
|
52
34
|
const saved = await qr.manager.save(this.repository.target, entities);
|
|
53
|
-
await this.afterInsertOperation(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.clearCacheForId(Array.isArray(saved) ? saved : [
|
|
60
|
-
saved
|
|
61
|
-
])
|
|
62
|
-
]);
|
|
63
|
-
const list = (Array.isArray(saved) ? saved : [
|
|
64
|
-
saved
|
|
65
|
-
]).map((e)=>this.convertEntityToResponseDto(e, false));
|
|
66
|
-
return list;
|
|
67
|
-
} catch (error) {
|
|
68
|
-
await qr.rollbackTransaction();
|
|
69
|
-
this.handleError(error, 'insertMany');
|
|
70
|
-
} finally{
|
|
71
|
-
await qr.release();
|
|
72
|
-
}
|
|
35
|
+
await this.afterInsertOperation(this.ensureArray(saved), user, qr);
|
|
36
|
+
return {
|
|
37
|
+
saved,
|
|
38
|
+
returnFirst: false
|
|
39
|
+
};
|
|
40
|
+
});
|
|
73
41
|
}
|
|
74
42
|
async update(dto, user) {
|
|
75
|
-
|
|
76
|
-
const qr = this.repository.manager.connection.createQueryRunner();
|
|
77
|
-
await qr.connect();
|
|
78
|
-
await qr.startTransaction();
|
|
79
|
-
try {
|
|
43
|
+
return this.executeInTransaction('update', async (qr)=>{
|
|
80
44
|
await this.beforeUpdateOperation(dto, user, qr);
|
|
81
45
|
const entities = await this.convertRequestDtoToEntity(dto, user);
|
|
82
46
|
const saved = await qr.manager.save(this.repository.target, entities);
|
|
83
|
-
await this.afterUpdateOperation(saved, user, qr);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
])
|
|
90
|
-
]);
|
|
91
|
-
const first = Array.isArray(saved) ? saved[0] : saved;
|
|
92
|
-
return this.convertEntityToResponseDto(first, false);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
await qr.rollbackTransaction();
|
|
95
|
-
this.handleError(error, 'update');
|
|
96
|
-
} finally{
|
|
97
|
-
await qr.release();
|
|
98
|
-
}
|
|
47
|
+
await this.afterUpdateOperation(this.ensureArray(saved), user, qr);
|
|
48
|
+
return {
|
|
49
|
+
saved,
|
|
50
|
+
returnFirst: true
|
|
51
|
+
};
|
|
52
|
+
});
|
|
99
53
|
}
|
|
100
54
|
async updateMany(dtos, user) {
|
|
101
|
-
|
|
102
|
-
const qr = this.repository.manager.connection.createQueryRunner();
|
|
103
|
-
await qr.connect();
|
|
104
|
-
await qr.startTransaction();
|
|
105
|
-
try {
|
|
55
|
+
return this.executeInTransaction('updateMany', async (qr)=>{
|
|
106
56
|
await this.beforeUpdateOperation(dtos, user, qr);
|
|
107
57
|
const entities = await this.convertRequestDtoToEntity(dtos, user);
|
|
108
58
|
const saved = await qr.manager.save(this.repository.target, entities);
|
|
109
|
-
await this.afterUpdateOperation(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.clearCacheForId(Array.isArray(saved) ? saved : [
|
|
116
|
-
saved
|
|
117
|
-
])
|
|
118
|
-
]);
|
|
119
|
-
const list = (Array.isArray(saved) ? saved : [
|
|
120
|
-
saved
|
|
121
|
-
]).map((e)=>this.convertEntityToResponseDto(e, false));
|
|
122
|
-
return list;
|
|
123
|
-
} catch (error) {
|
|
124
|
-
await qr.rollbackTransaction();
|
|
125
|
-
this.handleError(error, 'updateMany');
|
|
126
|
-
} finally{
|
|
127
|
-
await qr.release();
|
|
128
|
-
}
|
|
59
|
+
await this.afterUpdateOperation(this.ensureArray(saved), user, qr);
|
|
60
|
+
return {
|
|
61
|
+
saved,
|
|
62
|
+
returnFirst: false
|
|
63
|
+
};
|
|
64
|
+
});
|
|
129
65
|
}
|
|
130
66
|
async findByIds(ids, user) {
|
|
131
67
|
await this.ensureRepositoryInitialized();
|
|
@@ -344,6 +280,37 @@ import { In } from 'typeorm';
|
|
|
344
280
|
});
|
|
345
281
|
ErrorHandler.rethrowError(error);
|
|
346
282
|
}
|
|
283
|
+
/** Ensures value is always an array */ ensureArray(value) {
|
|
284
|
+
return Array.isArray(value) ? value : [
|
|
285
|
+
value
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
/** Executes operation in transaction with automatic cache clearing */ async executeInTransaction(operation, fn) {
|
|
289
|
+
await this.ensureRepositoryInitialized();
|
|
290
|
+
const qr = this.repository.manager.connection.createQueryRunner();
|
|
291
|
+
await qr.connect();
|
|
292
|
+
await qr.startTransaction();
|
|
293
|
+
try {
|
|
294
|
+
const { saved, returnFirst } = await fn(qr);
|
|
295
|
+
await qr.commitTransaction();
|
|
296
|
+
const savedArray = this.ensureArray(saved);
|
|
297
|
+
if (this.isCacheable) {
|
|
298
|
+
await Promise.all([
|
|
299
|
+
this.clearCacheForAll(),
|
|
300
|
+
this.clearCacheForId(savedArray)
|
|
301
|
+
]);
|
|
302
|
+
}
|
|
303
|
+
if (returnFirst) {
|
|
304
|
+
return this.convertEntityToResponseDto(savedArray[0], false);
|
|
305
|
+
}
|
|
306
|
+
return savedArray.map((e)=>this.convertEntityToResponseDto(e, false));
|
|
307
|
+
} catch (error) {
|
|
308
|
+
await qr.rollbackTransaction();
|
|
309
|
+
this.handleError(error, operation);
|
|
310
|
+
} finally{
|
|
311
|
+
await qr.release();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
347
314
|
// Hooks (override in child classes)
|
|
348
315
|
async ensureRepositoryInitialized() {}
|
|
349
316
|
async beforeInsertOperation(_dto, _user, _queryRunner) {}
|
package/fesm/classes/index.js
CHANGED
|
@@ -44,46 +44,31 @@ import { getRequestId, getUserId, getTenantId, getCompanyId } from '../middlewar
|
|
|
44
44
|
* // Output: { requestId: 'uuid', userId: 'user-id', context: 'MyService', message: 'Operation completed', orderId: '123' }
|
|
45
45
|
* ```
|
|
46
46
|
*/ export class WinstonLoggerAdapter {
|
|
47
|
-
|
|
48
|
-
const meta = args
|
|
49
|
-
|
|
47
|
+
buildLogMeta(context, args, extra) {
|
|
48
|
+
const meta = args?.length && typeof args[0] === 'object' ? args[0] : {};
|
|
49
|
+
return {
|
|
50
50
|
context: context || this.context,
|
|
51
51
|
...getCorrelationMeta(),
|
|
52
|
-
...meta
|
|
53
|
-
|
|
52
|
+
...meta,
|
|
53
|
+
...extra
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
log(message, context, ...args) {
|
|
57
|
+
winstonLogger.info(message, this.buildLogMeta(context, args));
|
|
54
58
|
}
|
|
55
59
|
error(message, trace, context, ...args) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
stack: trace,
|
|
60
|
-
...getCorrelationMeta(),
|
|
61
|
-
...meta
|
|
62
|
-
});
|
|
60
|
+
winstonLogger.error(message, this.buildLogMeta(context, args, {
|
|
61
|
+
stack: trace
|
|
62
|
+
}));
|
|
63
63
|
}
|
|
64
64
|
warn(message, context, ...args) {
|
|
65
|
-
|
|
66
|
-
winstonLogger.warn(message, {
|
|
67
|
-
context: context || this.context,
|
|
68
|
-
...getCorrelationMeta(),
|
|
69
|
-
...meta
|
|
70
|
-
});
|
|
65
|
+
winstonLogger.warn(message, this.buildLogMeta(context, args));
|
|
71
66
|
}
|
|
72
67
|
debug(message, context, ...args) {
|
|
73
|
-
|
|
74
|
-
winstonLogger.debug(message, {
|
|
75
|
-
context: context || this.context,
|
|
76
|
-
...getCorrelationMeta(),
|
|
77
|
-
...meta
|
|
78
|
-
});
|
|
68
|
+
winstonLogger.debug(message, this.buildLogMeta(context, args));
|
|
79
69
|
}
|
|
80
70
|
verbose(message, context, ...args) {
|
|
81
|
-
|
|
82
|
-
winstonLogger.verbose(message, {
|
|
83
|
-
context: context || this.context,
|
|
84
|
-
...getCorrelationMeta(),
|
|
85
|
-
...meta
|
|
86
|
-
});
|
|
71
|
+
winstonLogger.verbose(message, this.buildLogMeta(context, args));
|
|
87
72
|
}
|
|
88
73
|
constructor(context){
|
|
89
74
|
_define_property(this, "context", void 0);
|
|
@@ -97,25 +82,23 @@ import { getRequestId, getUserId, getTenantId, getCompanyId } from '../middlewar
|
|
|
97
82
|
* Use this when you need to use NestJS's built-in logger
|
|
98
83
|
* instead of Winston (e.g., for testing)
|
|
99
84
|
*/ export class NestLoggerAdapter {
|
|
85
|
+
formatMessage(message, args) {
|
|
86
|
+
return args?.length ? `${message} ${JSON.stringify(args)}` : message;
|
|
87
|
+
}
|
|
100
88
|
log(message, context, ...args) {
|
|
101
|
-
|
|
102
|
-
this.logger.log(logMessage, context);
|
|
89
|
+
this.logger.log(this.formatMessage(message, args), context);
|
|
103
90
|
}
|
|
104
91
|
error(message, trace, context, ...args) {
|
|
105
|
-
|
|
106
|
-
this.logger.error(logMessage, trace, context);
|
|
92
|
+
this.logger.error(this.formatMessage(message, args), trace, context);
|
|
107
93
|
}
|
|
108
94
|
warn(message, context, ...args) {
|
|
109
|
-
|
|
110
|
-
this.logger.warn(logMessage, context);
|
|
95
|
+
this.logger.warn(this.formatMessage(message, args), context);
|
|
111
96
|
}
|
|
112
97
|
debug(message, context, ...args) {
|
|
113
|
-
|
|
114
|
-
this.logger.debug(logMessage, context);
|
|
98
|
+
this.logger.debug(this.formatMessage(message, args), context);
|
|
115
99
|
}
|
|
116
100
|
verbose(message, context, ...args) {
|
|
117
|
-
|
|
118
|
-
this.logger.verbose(logMessage, context);
|
|
101
|
+
this.logger.verbose(this.formatMessage(message, args), context);
|
|
119
102
|
}
|
|
120
103
|
constructor(logger){
|
|
121
104
|
_define_property(this, "logger", void 0);
|
package/fesm/constants/index.js
CHANGED