@flusys/nestjs-shared 1.1.0-beta → 2.0.0-rc.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.
Files changed (113) hide show
  1. package/README.md +501 -720
  2. package/cjs/classes/api-controller.class.js +9 -24
  3. package/cjs/classes/api-service.class.js +59 -92
  4. package/cjs/classes/index.js +1 -0
  5. package/cjs/classes/winston-logger-adapter.class.js +23 -40
  6. package/cjs/constants/index.js +14 -0
  7. package/cjs/constants/permissions.js +184 -0
  8. package/cjs/decorators/api-response.decorator.js +1 -1
  9. package/cjs/decorators/index.js +1 -0
  10. package/cjs/decorators/sanitize-html.decorator.js +36 -0
  11. package/cjs/dtos/delete.dto.js +10 -0
  12. package/cjs/dtos/filter-and-pagination.dto.js +24 -34
  13. package/cjs/dtos/pagination.dto.js +4 -8
  14. package/cjs/dtos/response-payload.dto.js +0 -116
  15. package/cjs/entities/identity.js +4 -4
  16. package/cjs/entities/user-root.js +13 -14
  17. package/cjs/guards/permission.guard.js +51 -105
  18. package/cjs/interceptors/index.js +1 -3
  19. package/cjs/interceptors/set-user-field-on-body.interceptor.js +60 -0
  20. package/cjs/interceptors/slug.interceptor.js +30 -9
  21. package/cjs/interfaces/datasource.interface.js +4 -0
  22. package/cjs/interfaces/index.js +2 -1
  23. package/cjs/interfaces/module-config.interface.js +4 -0
  24. package/cjs/middlewares/logger.middleware.js +50 -89
  25. package/cjs/modules/cache/cache.module.js +3 -3
  26. package/cjs/modules/datasource/datasource.module.js +11 -14
  27. package/cjs/modules/datasource/multi-tenant-datasource.service.js +29 -113
  28. package/cjs/modules/utils/utils.service.js +40 -203
  29. package/cjs/utils/error-handler.util.js +35 -12
  30. package/cjs/utils/html-sanitizer.util.js +64 -0
  31. package/cjs/utils/index.js +4 -0
  32. package/cjs/utils/query-helpers.util.js +53 -0
  33. package/cjs/utils/request.util.js +70 -0
  34. package/cjs/utils/string.util.js +63 -0
  35. package/classes/api-controller.class.d.ts +5 -5
  36. package/classes/api-service.class.d.ts +7 -5
  37. package/classes/index.d.ts +1 -0
  38. package/classes/request-scoped-api.service.d.ts +3 -2
  39. package/classes/winston-logger-adapter.class.d.ts +2 -0
  40. package/constants/index.d.ts +1 -0
  41. package/constants/permissions.d.ts +179 -0
  42. package/decorators/index.d.ts +1 -0
  43. package/decorators/sanitize-html.decorator.d.ts +2 -0
  44. package/dtos/delete.dto.d.ts +1 -0
  45. package/dtos/filter-and-pagination.dto.d.ts +0 -2
  46. package/dtos/response-payload.dto.d.ts +0 -20
  47. package/fesm/classes/api-controller.class.js +9 -24
  48. package/fesm/classes/api-service.class.js +59 -92
  49. package/fesm/classes/index.js +2 -0
  50. package/fesm/classes/winston-logger-adapter.class.js +23 -40
  51. package/fesm/constants/index.js +2 -0
  52. package/fesm/constants/permissions.js +128 -0
  53. package/fesm/decorators/api-response.decorator.js +1 -1
  54. package/fesm/decorators/index.js +1 -0
  55. package/fesm/decorators/sanitize-html.decorator.js +45 -0
  56. package/fesm/dtos/delete.dto.js +12 -2
  57. package/fesm/dtos/filter-and-pagination.dto.js +26 -47
  58. package/fesm/dtos/pagination.dto.js +4 -8
  59. package/fesm/dtos/response-payload.dto.js +0 -107
  60. package/fesm/entities/identity.js +4 -4
  61. package/fesm/entities/user-root.js +13 -14
  62. package/fesm/guards/permission.guard.js +51 -105
  63. package/fesm/interceptors/index.js +1 -3
  64. package/fesm/interceptors/set-user-field-on-body.interceptor.js +39 -0
  65. package/fesm/interceptors/slug.interceptor.js +31 -10
  66. package/fesm/interfaces/datasource.interface.js +20 -0
  67. package/fesm/interfaces/index.js +2 -1
  68. package/fesm/interfaces/module-config.interface.js +5 -0
  69. package/fesm/middlewares/logger.middleware.js +50 -83
  70. package/fesm/modules/cache/cache.module.js +2 -2
  71. package/fesm/modules/datasource/datasource.module.js +11 -14
  72. package/fesm/modules/datasource/multi-tenant-datasource.service.js +29 -113
  73. package/fesm/modules/utils/utils.service.js +41 -204
  74. package/fesm/utils/error-handler.util.js +36 -13
  75. package/fesm/utils/html-sanitizer.util.js +69 -0
  76. package/fesm/utils/index.js +4 -0
  77. package/fesm/utils/query-helpers.util.js +78 -0
  78. package/fesm/utils/request.util.js +58 -0
  79. package/fesm/utils/string.util.js +71 -0
  80. package/guards/permission.guard.d.ts +2 -0
  81. package/interceptors/index.d.ts +1 -3
  82. package/interceptors/set-user-field-on-body.interceptor.d.ts +5 -0
  83. package/interceptors/slug.interceptor.d.ts +2 -1
  84. package/interfaces/api.interface.d.ts +2 -2
  85. package/interfaces/datasource.interface.d.ts +5 -0
  86. package/interfaces/identity.interface.d.ts +4 -4
  87. package/interfaces/index.d.ts +2 -1
  88. package/interfaces/logged-user-info.interface.d.ts +0 -2
  89. package/interfaces/module-config.interface.d.ts +6 -0
  90. package/interfaces/permission.interface.d.ts +0 -1
  91. package/middlewares/logger.middleware.d.ts +2 -2
  92. package/modules/datasource/datasource.module.d.ts +1 -0
  93. package/modules/datasource/multi-tenant-datasource.service.d.ts +0 -1
  94. package/modules/utils/utils.service.d.ts +4 -14
  95. package/package.json +4 -4
  96. package/utils/error-handler.util.d.ts +14 -19
  97. package/utils/html-sanitizer.util.d.ts +2 -0
  98. package/utils/index.d.ts +4 -0
  99. package/utils/query-helpers.util.d.ts +16 -0
  100. package/utils/request.util.d.ts +4 -0
  101. package/utils/string.util.d.ts +2 -0
  102. package/cjs/interceptors/set-create-by-on-body.interceptor.js +0 -40
  103. package/cjs/interceptors/set-delete-by-on-body.interceptor.js +0 -40
  104. package/cjs/interceptors/set-update-by-on-body.interceptor.js +0 -40
  105. package/cjs/interfaces/base-query.interface.js +0 -6
  106. package/fesm/interceptors/set-create-by-on-body.interceptor.js +0 -30
  107. package/fesm/interceptors/set-delete-by-on-body.interceptor.js +0 -30
  108. package/fesm/interceptors/set-update-by-on-body.interceptor.js +0 -30
  109. package/fesm/interfaces/base-query.interface.js +0 -3
  110. package/interceptors/set-create-by-on-body.interceptor.d.ts +0 -5
  111. package/interceptors/set-delete-by-on-body.interceptor.d.ts +0 -5
  112. package/interceptors/set-update-by-on-body.interceptor.d.ts +0 -5
  113. package/interfaces/base-query.interface.d.ts +0 -7
package/README.md CHANGED
@@ -8,9 +8,9 @@ This comprehensive guide covers the shared package - the shared NestJS infrastru
8
8
  ## Table of Contents
9
9
 
10
10
  - [Overview](#overview)
11
- - [Installation](#installation)
12
11
  - [Package Architecture](#package-architecture)
13
12
  - [ApiService - Generic CRUD Service](#apiservice---generic-crud-service)
13
+ - [RequestScopedApiService](#requestscopedapiservice)
14
14
  - [ApiController - Generic CRUD Controller](#apicontroller---generic-crud-controller)
15
15
  - [Decorators](#decorators)
16
16
  - [Guards](#guards)
@@ -20,7 +20,9 @@ This comprehensive guide covers the shared package - the shared NestJS infrastru
20
20
  - [Multi-Tenant DataSource](#multi-tenant-datasource)
21
21
  - [DTOs](#dtos)
22
22
  - [Base Entities](#base-entities)
23
+ - [Utilities](#utilities)
23
24
  - [Error Handling](#error-handling)
25
+ - [Constants](#constants)
24
26
  - [API Reference](#api-reference)
25
27
 
26
28
  ---
@@ -30,34 +32,26 @@ This comprehensive guide covers the shared package - the shared NestJS infrastru
30
32
  `@flusys/nestjs-shared` provides shared utilities for building scalable NestJS applications:
31
33
 
32
34
  - **Generic CRUD** - Standardized API controller and service patterns
33
- - **Permission System** - Role and permission-based access control
34
- - **Caching** - In-memory + Redis hybrid caching
35
+ - **Permission System** - Role and permission-based access control with complex logic
36
+ - **Caching** - In-memory + Redis hybrid caching (HybridCache)
35
37
  - **Request Correlation** - AsyncLocalStorage-based request tracking
36
38
  - **Middleware** - Logging, correlation, and performance monitoring
37
39
  - **Interceptors** - Response metadata, idempotency, auto field setting
38
40
  - **Multi-Tenancy** - Dynamic database connection management
39
- - **Error Handling** - Centralized error handling utilities
41
+ - **Error Handling** - Centralized error handling with sensitive data redaction
40
42
 
41
43
  ### Package Hierarchy
42
44
 
43
45
  ```
44
- @flusys/nestjs-core Pure TypeScript (foundation)
45
-
46
- @flusys/nestjs-shared Shared NestJS utilities (THIS PACKAGE)
47
-
48
- @flusys/nestjs-auth Uses common classes
49
-
50
- @flusys/nestjs-iam Uses common patterns
51
-
52
- @flusys/nestjs-storage Uses common patterns
53
- ```
54
-
55
- ---
56
-
57
- ## Installation
58
-
59
- ```bash
60
- npm install @flusys/nestjs-shared @flusys/nestjs-core
46
+ @flusys/nestjs-core <- Pure TypeScript (foundation)
47
+ |
48
+ @flusys/nestjs-shared <- Shared NestJS utilities (THIS PACKAGE)
49
+ |
50
+ @flusys/nestjs-auth <- Uses common classes
51
+ |
52
+ @flusys/nestjs-iam <- Uses common patterns
53
+ |
54
+ @flusys/nestjs-storage <- Uses common patterns
61
55
  ```
62
56
 
63
57
  ---
@@ -73,17 +67,19 @@ nestjs-shared/
73
67
  │ │ ├── request-scoped-api.service.ts # REQUEST-scoped service base
74
68
  │ │ ├── hybrid-cache.class.ts # Two-tier caching
75
69
  │ │ ├── winston.logger.class.ts # Winston logger config
76
- │ │ ├── winston-logger-adapter.class.ts # Logger adapters
77
- │ │ └── index.ts
70
+ │ │ ├── winston-logger-adapter.class.ts
71
+ │ │ └── nest-logger-adapter.class.ts
78
72
  │ │
79
73
  │ ├── constants/ # Injection tokens & constants
74
+ │ │ ├── permissions.ts # Permission constants
80
75
  │ │ └── index.ts
81
76
  │ │
82
77
  │ ├── decorators/ # Custom decorators
83
- │ │ ├── api-response.decorator.ts # Swagger response decorator
84
- │ │ ├── current-user.decorator.ts
85
- │ │ ├── public.decorator.ts
86
- │ │ ├── require-permission.decorator.ts
78
+ │ │ ├── api-response.decorator.ts # @ApiResponseDto
79
+ │ │ ├── current-user.decorator.ts # @CurrentUser
80
+ │ │ ├── public.decorator.ts # @Public
81
+ │ │ ├── require-permission.decorator.ts # @RequirePermission
82
+ │ │ ├── sanitize.decorator.ts # @SanitizeHtml, @SanitizeAndTrim
87
83
  │ │ └── index.ts
88
84
  │ │
89
85
  │ ├── dtos/ # Shared DTOs
@@ -95,18 +91,16 @@ nestjs-shared/
95
91
  │ │ └── index.ts
96
92
  │ │
97
93
  │ ├── entities/ # Base entities
98
- │ │ ├── identity.ts
99
- │ │ ├── user-root.ts
94
+ │ │ ├── identity.ts # Base entity with UUID
95
+ │ │ ├── user-root.ts # Base user entity
100
96
  │ │ └── index.ts
101
97
  │ │
102
98
  │ ├── exceptions/ # Custom exceptions
103
- │ │ ├── permission.exception.ts
104
- │ │ └── index.ts
99
+ │ │ └── permission.exception.ts # Permission-related exceptions
105
100
  │ │
106
101
  │ ├── guards/ # Authentication & authorization
107
- │ │ ├── jwt-auth.guard.ts # JWT token validation
108
- │ │ ├── permission.guard.ts # Permission checks
109
- │ │ └── index.ts
102
+ │ │ ├── jwt-auth.guard.ts # JWT token validation
103
+ │ │ └── permission.guard.ts # Permission checks
110
104
  │ │
111
105
  │ ├── interceptors/ # Request/response interceptors
112
106
  │ │ ├── delete-empty-id-from-body.interceptor.ts
@@ -116,36 +110,34 @@ nestjs-shared/
116
110
  │ │ ├── set-create-by-on-body.interceptor.ts
117
111
  │ │ ├── set-delete-by-on-body.interceptor.ts
118
112
  │ │ ├── set-update-by-on-body.interceptor.ts
119
- │ │ ├── slug.interceptor.ts
120
- │ │ └── index.ts
113
+ │ │ └── slug.interceptor.ts
121
114
  │ │
122
115
  │ ├── interfaces/ # TypeScript interfaces
123
- │ │ ├── api.interface.ts
124
- │ │ ├── base-query.interface.ts
116
+ │ │ ├── api.interface.ts # IService interface
125
117
  │ │ ├── identity.interface.ts
126
- │ │ ├── logged-user-info.interface.ts
127
- │ │ ├── logger.interface.ts
128
- │ │ ├── permission.interface.ts
129
- │ │ └── index.ts
118
+ │ │ ├── logged-user-info.interface.ts # ILoggedUserInfo
119
+ │ │ ├── logger.interface.ts # ILogger
120
+ │ │ ├── permission.interface.ts # PermissionCondition
121
+ │ │ └── datasource-provider.interface.ts
130
122
  │ │
131
123
  │ ├── middlewares/ # Middleware
132
- │ │ ├── logger.middleware.ts # Request logging & correlation
133
- │ │ └── index.ts
124
+ │ │ └── logger.middleware.ts # Request logging & correlation
134
125
  │ │
135
126
  │ ├── modules/ # NestJS modules
136
127
  │ │ ├── cache/cache.module.ts
137
128
  │ │ ├── datasource/
138
129
  │ │ │ ├── datasource.module.ts
139
- │ │ │ ├── multi-tenant-datasource.service.ts
140
- │ │ └── index.ts
141
- │ │ ├── utils/
142
- │ │ │ ├── utils.module.ts
143
- │ │ │ └── utils.service.ts
144
- │ │ └── index.ts
130
+ │ │ │ └── multi-tenant-datasource.service.ts
131
+ │ │ └── utils/
132
+ │ │ ├── utils.module.ts
133
+ │ │ └── utils.service.ts
145
134
  │ │
146
135
  │ └── utils/ # Utility functions
147
136
  │ ├── error-handler.util.ts
148
- └── index.ts
137
+ ├── query-helpers.util.ts
138
+ │ ├── string.util.ts
139
+ │ ├── request.util.ts
140
+ │ └── html-sanitizer.util.ts
149
141
  ```
150
142
 
151
143
  ---
@@ -160,11 +152,7 @@ The `ApiService` base class provides standardized CRUD operations with caching,
160
152
  import { ApiService, HybridCache } from '@flusys/nestjs-shared/classes';
161
153
  import { UtilsService } from '@flusys/nestjs-shared/modules';
162
154
  import { Injectable, Inject } from '@nestjs/common';
163
- import { InjectRepository } from '@nestjs/typeorm';
164
155
  import { Repository } from 'typeorm';
165
- import { User } from './user.entity';
166
- import { CreateUserDto, UpdateUserDto } from './user.dto';
167
- import { IUser } from './user.interface';
168
156
 
169
157
  @Injectable()
170
158
  export class UserService extends ApiService<
@@ -179,6 +167,7 @@ export class UserService extends ApiService<
179
167
  protected override repository: Repository<User>,
180
168
  @Inject('CACHE_INSTANCE')
181
169
  protected override cacheManager: HybridCache,
170
+ @Inject(UtilsService)
182
171
  protected override utilsService: UtilsService,
183
172
  ) {
184
173
  super(
@@ -190,30 +179,6 @@ export class UserService extends ApiService<
190
179
  true, // Enable caching
191
180
  );
192
181
  }
193
-
194
- // Override to customize DTO to entity conversion
195
- override async convertSingleDtoToEntity(
196
- dto: CreateUserDto | UpdateUserDto,
197
- user: ILoggedUserInfo,
198
- ): Promise<User> {
199
- const entity = new User();
200
- Object.assign(entity, dto);
201
- return entity;
202
- }
203
-
204
- // Override to customize query selection
205
- override async getSelectQuery(
206
- query: SelectQueryBuilder<User>,
207
- user: ILoggedUserInfo,
208
- select?: string[],
209
- ) {
210
- if (!select?.length) {
211
- select = ['id', 'name', 'email', 'createdAt'];
212
- }
213
- const selectFields = select.map(f => `${this.entityName}.${f}`);
214
- query.select(selectFields);
215
- return { query, isRaw: false };
216
- }
217
182
  }
218
183
  ```
219
184
 
@@ -225,11 +190,11 @@ export class UserService extends ApiService<
225
190
  | `insertMany(dtos, user)` | Create multiple entities |
226
191
  | `getById(id, user, select?)` | Get entity by ID |
227
192
  | `findById(id, user, select?)` | Find entity (returns null if not found) |
193
+ | `findByIds(ids, user, select?)` | Find multiple by IDs |
228
194
  | `getAll(dto, user)` | Get paginated list |
229
195
  | `update(dto, user)` | Update single entity |
230
196
  | `updateMany(dtos, user)` | Update multiple entities |
231
- | `delete(dto, user)` | Soft/permanent delete |
232
- | `restore(dto, user)` | Restore soft-deleted |
197
+ | `delete(dto, user)` | Soft/permanent delete or restore |
233
198
 
234
199
  ### Customization Hooks
235
200
 
@@ -245,6 +210,12 @@ export class UserService extends ApiService<...> {
245
210
  // Add WHERE filters
246
211
  override async getFilterQuery(query, filter, user): Promise<{ query, isRaw }> { }
247
212
 
213
+ // Add global search
214
+ override async getGlobalSearchQuery(query, globalSearch, user): Promise<{ query, isRaw }> { }
215
+
216
+ // Add sort order
217
+ override async getSortQuery(query, sort, user): Promise<{ query, isRaw }> { }
218
+
248
219
  // Add extra query conditions (e.g., company filtering)
249
220
  override async getExtraManipulateQuery(query, dto, user): Promise<{ query, isRaw }> { }
250
221
 
@@ -268,47 +239,73 @@ export class UserService extends ApiService<...> {
268
239
  }
269
240
  ```
270
241
 
271
- ### Company/Branch Filtering Example
242
+ ---
243
+
244
+ ## RequestScopedApiService
245
+
246
+ For dynamic entity resolution based on runtime configuration (e.g., company feature).
272
247
 
273
248
  ```typescript
274
- override async getExtraManipulateQuery(
275
- query: SelectQueryBuilder<User>,
276
- dto: FilterAndPaginationDto,
277
- user: ILoggedUserInfo,
278
- ) {
279
- // Filter by user's company
280
- if (user.companyId) {
281
- query.andWhere(`${this.entityName}.companyId = :companyId`, {
282
- companyId: user.companyId,
283
- });
249
+ import { RequestScopedApiService } from '@flusys/nestjs-shared/classes';
250
+ import { Injectable, Scope, Inject } from '@nestjs/common';
251
+ import { EntityTarget, Repository } from 'typeorm';
252
+
253
+ @Injectable({ scope: Scope.REQUEST })
254
+ export class RoleService extends RequestScopedApiService<
255
+ CreateRoleDto,
256
+ UpdateRoleDto,
257
+ IRole,
258
+ RoleBase,
259
+ Repository<RoleBase>
260
+ > {
261
+ constructor(
262
+ @Inject('CACHE_INSTANCE') protected override cacheManager: HybridCache,
263
+ @Inject(UtilsService) protected override utilsService: UtilsService,
264
+ @Inject(ModuleConfigService) private readonly config: ModuleConfigService,
265
+ @Inject(DataSourceProvider) private readonly provider: DataSourceProvider,
266
+ ) {
267
+ // Pass null for repository - will be initialized dynamically
268
+ super('role', null as any, cacheManager, utilsService, 'RoleService', true);
284
269
  }
285
270
 
286
- // Filter by user's branch
287
- if (user.branchId) {
288
- query.andWhere(`${this.entityName}.branchId = :branchId`, {
289
- branchId: user.branchId,
290
- });
271
+ // Required: Resolve which entity to use
272
+ protected resolveEntity(): EntityTarget<RoleBase> {
273
+ return this.config.isCompanyFeatureEnabled() ? RoleWithCompany : Role;
291
274
  }
292
275
 
293
- return { query, isRaw: false };
276
+ // Required: Return the DataSource provider
277
+ protected getDataSourceProvider(): IDataSourceProvider {
278
+ return this.provider;
279
+ }
280
+
281
+ // Optional: Initialize additional repositories
282
+ protected async initializeAdditionalRepositories(): Promise<void> {
283
+ this.actionRepository = await this.dataSourceProvider.getRepository(Action);
284
+ }
294
285
  }
295
286
  ```
296
287
 
288
+ ### Key Methods
289
+
290
+ | Method | Description |
291
+ |--------|-------------|
292
+ | `ensureRepositoryInitialized()` | Must call before using repository |
293
+ | `resolveEntity()` | Abstract - return entity class based on config |
294
+ | `getDataSourceProvider()` | Abstract - return datasource provider |
295
+ | `getDataSourceForService()` | Get raw DataSource for transactions |
296
+
297
297
  ---
298
298
 
299
299
  ## ApiController - Generic CRUD Controller
300
300
 
301
- The `createApiController` factory creates standardized REST API controllers.
301
+ The `createApiController` factory creates standardized POST-only RPC controllers.
302
302
 
303
303
  ### Basic Usage
304
304
 
305
305
  ```typescript
306
306
  import { createApiController } from '@flusys/nestjs-shared/classes';
307
- import { Controller } from '@nestjs/common';
307
+ import { Controller, Inject } from '@nestjs/common';
308
308
  import { ApiTags } from '@nestjs/swagger';
309
- import { CreateUserDto, UpdateUserDto, UserResponseDto } from './user.dto';
310
- import { IUser } from './user.interface';
311
- import { UserService } from './user.service';
312
309
 
313
310
  @ApiTags('Users')
314
311
  @Controller('users')
@@ -319,7 +316,7 @@ export class UserController extends createApiController<
319
316
  IUser,
320
317
  UserService
321
318
  >(CreateUserDto, UpdateUserDto, UserResponseDto) {
322
- constructor(protected service: UserService) {
319
+ constructor(@Inject(UserService) protected service: UserService) {
323
320
  super(service);
324
321
  }
325
322
  }
@@ -346,9 +343,7 @@ export class UserController extends createApiController(
346
343
  CreateUserDto,
347
344
  UpdateUserDto,
348
345
  UserResponseDto,
349
- {
350
- security: 'jwt', // All endpoints require JWT
351
- },
346
+ { security: 'jwt' }, // All endpoints require JWT
352
347
  ) {}
353
348
 
354
349
  // Per-endpoint security
@@ -367,29 +362,8 @@ export class UserController extends createApiController(
367
362
  },
368
363
  },
369
364
  ) {}
370
-
371
- // Permission combinations
372
- {
373
- security: {
374
- // Require ANY of these permissions
375
- insert: {
376
- level: 'permission',
377
- permissions: ['users.create', 'users.admin'],
378
- require: 'any', // Default
379
- },
380
-
381
- // Require ALL permissions
382
- delete: {
383
- level: 'permission',
384
- permissions: ['users.delete', 'users.admin'],
385
- require: 'all',
386
- },
387
- },
388
- }
389
365
  ```
390
366
 
391
- See [API-CONTROLLER-SECURITY.md](./API-CONTROLLER-SECURITY.md) for detailed security configuration.
392
-
393
367
  ---
394
368
 
395
369
  ## Decorators
@@ -404,16 +378,22 @@ import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
404
378
 
405
379
  @Controller('profile')
406
380
  export class ProfileController {
407
- @Get()
381
+ @Post('me')
408
382
  getProfile(@CurrentUser() user: ILoggedUserInfo) {
409
383
  return { userId: user.id, companyId: user.companyId };
410
384
  }
385
+
386
+ // Extract specific property
387
+ @Post('id')
388
+ getUserId(@CurrentUser('id') userId: string) {
389
+ return { userId };
390
+ }
411
391
  }
412
392
  ```
413
393
 
414
394
  ### @Public
415
395
 
416
- Mark route as public (skip authentication):
396
+ Mark route as public (skip authentication). **Use sparingly - security risk**.
417
397
 
418
398
  ```typescript
419
399
  import { Public } from '@flusys/nestjs-shared/decorators';
@@ -422,43 +402,44 @@ import { Public } from '@flusys/nestjs-shared/decorators';
422
402
  export class AuthController {
423
403
  @Public()
424
404
  @Post('login')
425
- login() {
426
- // No JWT required
427
- }
405
+ login() { }
428
406
  }
429
407
  ```
430
408
 
431
409
  ### @RequirePermission
432
410
 
433
- Require specific permission:
411
+ Require specific permission(s) - **AND logic** by default:
434
412
 
435
413
  ```typescript
436
414
  import { RequirePermission } from '@flusys/nestjs-shared/decorators';
437
415
 
438
416
  @Controller('admin')
439
417
  export class AdminController {
418
+ // Requires 'admin.dashboard' permission
440
419
  @RequirePermission('admin.dashboard')
441
- @Get('dashboard')
442
- getDashboard() {
443
- // Requires 'admin.dashboard' permission
444
- }
420
+ @Post('dashboard')
421
+ getDashboard() { }
422
+
423
+ // Requires BOTH permissions
424
+ @RequirePermission('users.read', 'admin.access')
425
+ @Post('users')
426
+ getUsers() { }
445
427
  }
446
428
  ```
447
429
 
448
430
  ### @RequireAnyPermission
449
431
 
450
- Require any of the listed permissions:
432
+ Require any of the listed permissions - **OR logic**:
451
433
 
452
434
  ```typescript
453
435
  import { RequireAnyPermission } from '@flusys/nestjs-shared/decorators';
454
436
 
455
437
  @Controller('reports')
456
438
  export class ReportsController {
439
+ // Requires 'reports.view' OR 'reports.admin'
457
440
  @RequireAnyPermission('reports.view', 'reports.admin')
458
- @Get()
459
- getReports() {
460
- // Requires 'reports.view' OR 'reports.admin'
461
- }
441
+ @Post()
442
+ getReports() { }
462
443
  }
463
444
  ```
464
445
 
@@ -471,14 +452,6 @@ import { RequirePermissionCondition } from '@flusys/nestjs-shared/decorators';
471
452
 
472
453
  @Controller('sensitive')
473
454
  export class SensitiveController {
474
- // Simple: User needs 'admin' OR 'manager'
475
- @RequirePermissionCondition({
476
- operator: 'or',
477
- permissions: ['admin', 'manager'],
478
- })
479
- @Get('simple')
480
- getSimpleData() {}
481
-
482
455
  // Complex: User needs 'users.read' AND ('admin' OR 'manager')
483
456
  @RequirePermissionCondition({
484
457
  operator: 'and',
@@ -487,128 +460,109 @@ export class SensitiveController {
487
460
  { operator: 'or', permissions: ['admin', 'manager'] }
488
461
  ]
489
462
  })
490
- @Get('complex')
491
- getComplexData() {}
492
-
493
- // Very complex: (A AND B) OR (C AND D)
494
- @RequirePermissionCondition({
495
- operator: 'or',
496
- children: [
497
- { operator: 'and', permissions: ['finance.read', 'finance.write'] },
498
- { operator: 'and', permissions: ['admin.full', 'reports.access'] }
499
- ]
500
- })
501
- @Get('very-complex')
502
- getVeryComplexData() {}
463
+ @Post('complex')
464
+ getComplexData() { }
503
465
  }
504
466
  ```
505
467
 
506
- **Note:** For simple "require ALL permissions" use case, use `@RequirePermission` which defaults to AND logic:
507
- ```typescript
508
- @RequirePermission('admin.access', 'security.clearance') // User needs BOTH
509
- ```
510
-
511
- ---
512
-
513
- ## Guards
468
+ ### @SanitizeHtml / @SanitizeAndTrim
514
469
 
515
- The shared package provides two guards for authentication and authorization:
470
+ Escape HTML entities for XSS prevention:
516
471
 
517
- ### JwtAuthGuard
472
+ ```typescript
473
+ import { SanitizeHtml, SanitizeAndTrim } from '@flusys/nestjs-shared/decorators';
518
474
 
519
- **Location:** `@flusys/nestjs-shared/guards/jwt-auth.guard.ts`
475
+ export class CreateCommentDto {
476
+ @SanitizeHtml()
477
+ @IsString()
478
+ content: string;
520
479
 
521
- The `JwtAuthGuard` validates JWT tokens for protected routes. It extends Passport's `AuthGuard('jwt')` and respects the `@Public()` decorator.
480
+ @SanitizeAndTrim() // Escapes HTML AND trims whitespace
481
+ @IsString()
482
+ title: string;
483
+ }
484
+ ```
522
485
 
523
- **Why in shared module:**
524
- - All feature modules (auth, iam, storage) need JWT authentication
525
- - Keeps feature modules independent (no cross-dependencies)
526
- - JwtStrategy registration remains in auth module
527
- - This guard just validates tokens using the registered strategy
486
+ ### @ApiResponseDto
528
487
 
529
- **Usage:**
488
+ Generates Swagger schema for response:
530
489
 
531
490
  ```typescript
532
- import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
533
- import { UseGuards } from '@nestjs/common';
491
+ import { ApiResponseDto } from '@flusys/nestjs-shared/decorators';
534
492
 
535
- // Protect entire controller
536
493
  @Controller('users')
537
- @UseGuards(JwtAuthGuard)
538
494
  export class UserController {
539
- // All routes protected
495
+ @Post('get-all')
496
+ @ApiResponseDto(UserResponseDto, true, 'list') // Array with PaginationMetaDto
497
+ getAll() { }
540
498
 
541
- @Post('login')
542
- @Public() // Skip JWT check for this route
543
- async login() { }
499
+ @Post('insert')
500
+ @ApiResponseDto(UserResponseDto, false) // Single item
501
+ insert() { }
544
502
  }
503
+ ```
504
+
505
+ ---
506
+
507
+ ## Guards
508
+
509
+ ### JwtAuthGuard
545
510
 
546
- // Or apply globally
511
+ Validates JWT tokens for protected routes. Extends Passport's `AuthGuard('jwt')` and respects `@Public()` decorator.
512
+
513
+ ```typescript
514
+ import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
515
+
516
+ // Apply globally in main.ts
547
517
  @Module({
548
- providers: [
549
- {
550
- provide: APP_GUARD,
551
- useClass: JwtAuthGuard,
552
- },
553
- ],
518
+ providers: [{ provide: APP_GUARD, useClass: JwtAuthGuard }],
554
519
  })
555
520
  export class AppModule {}
556
521
  ```
557
522
 
558
- **Features:**
559
- - Validates JWT tokens from `Authorization: Bearer <token>` header
560
- - Respects `@Public()` decorator to skip authentication
561
- - Throws `UnauthorizedException` for invalid/expired tokens
562
- - Works with JwtStrategy registered in auth module
563
-
564
- **Note:** The JwtStrategy (which registers the JWT validation logic with Passport) is still in the auth module at `@flusys/nestjs-auth/strategies/jwt.strategy.ts`. This guard uses that registered strategy.
523
+ **Important:** Constructor needs `@Inject(Reflector)` for bundled code.
565
524
 
566
525
  ### PermissionGuard
567
526
 
568
- **Location:** `@flusys/nestjs-shared/guards/permission.guard.ts`
569
-
570
- The `PermissionGuard` handles permission-based access control.
527
+ Checks user permissions from cache with AND/OR/nested logic support.
571
528
 
572
529
  ```typescript
573
- import { Module } from '@nestjs/common';
574
- import { APP_GUARD } from '@nestjs/core';
575
530
  import { PermissionGuard } from '@flusys/nestjs-shared/guards';
576
531
 
577
532
  @Module({
578
533
  providers: [
534
+ { provide: APP_GUARD, useClass: PermissionGuard },
579
535
  {
580
- provide: APP_GUARD,
581
- useClass: PermissionGuard,
536
+ provide: 'PERMISSION_GUARD_CONFIG',
537
+ useValue: {
538
+ enableCompanyFeature: true,
539
+ userPermissionKeyFormat: 'permissions:user:{userId}',
540
+ companyPermissionKeyFormat: 'permissions:company:{companyId}:branch:{branchId}:user:{userId}',
541
+ },
582
542
  },
583
543
  ],
584
544
  })
585
545
  export class AppModule {}
586
546
  ```
587
547
 
588
- ### Permission Cache Key Format
548
+ **Cache Key Formats:**
589
549
 
590
550
  ```typescript
591
551
  // Without company feature
592
552
  `permissions:user:{userId}`
593
553
 
594
- // With company feature (includes branchId for DIRECT permissions)
554
+ // With company feature
595
555
  `permissions:company:{companyId}:branch:{branchId}:user:{userId}`
596
556
  ```
597
557
 
598
- ### Permission Guard Configuration
558
+ ### Permission Exceptions
599
559
 
600
560
  ```typescript
601
- // Configure via PermissionModule options
602
- PermissionModule.forRoot({
603
- // Cache TTL in seconds
604
- cacheTtl: 3600,
605
-
606
- // Permission check mode
607
- mode: 'RBAC' | 'DIRECT' | 'FULL',
608
-
609
- // Custom permission resolver
610
- resolver: CustomPermissionResolver,
611
- });
561
+ import {
562
+ InsufficientPermissionsException, // 403 - Missing permissions
563
+ NoPermissionsFoundException, // 403 - No permissions in cache
564
+ PermissionSystemUnavailableException, // 500 - Cache unavailable
565
+ } from '@flusys/nestjs-shared/exceptions';
612
566
  ```
613
567
 
614
568
  ---
@@ -617,39 +571,24 @@ PermissionModule.forRoot({
617
571
 
618
572
  ### LoggerMiddleware
619
573
 
620
- **Location:** `@flusys/nestjs-shared/middlewares/logger.middleware.ts`
621
-
622
- Combined middleware that handles:
623
- 1. **Request Correlation** - Tracks requests across services using AsyncLocalStorage
624
- 2. **HTTP Request/Response Logging** - Structured logging with Winston
625
- 3. **Detailed Request/Response Information** - Complete visibility into all requests
626
-
627
- **Key Features:**
628
- - **Request ID Generation** - Automatic UUID generation or uses `x-request-id` header
629
- - **Tenant ID Tracking** - Extracts `x-tenant-id` from headers for multi-tenant apps
630
- - **AsyncLocalStorage Context** - Thread-safe request context accessible anywhere
631
- - **Security** - Automatically redacts sensitive headers (authorization, cookie, x-api-key)
632
- - **Performance Monitoring** - Logs slow requests (>3s) with dedicated warning logs
633
- - **Body Truncation** - Limits log size to 1000 characters
634
- - **Debug Mode** - Conditionally logs headers and body based on LOG_LEVEL=debug
635
- - **Complete Request Details** - URL, path, query params, content type, user agent, client IP
636
- - **Complete Response Details** - Status code, message, content type, content length, user/company context
637
- - **Multiple Response Hooks** - Captures responses via `res.send()`, `res.json()`, and `res.end()`
638
- - **Error Handling** - Logs response errors with stack traces
639
- - **User Context** - Automatically includes userId and companyId in response logs if available
574
+ Combined middleware for request correlation and HTTP logging.
575
+
576
+ **Features:**
577
+ - Request ID generation/tracking (UUID or from `x-request-id` header)
578
+ - Tenant ID tracking (from `x-tenant-id` header)
579
+ - AsyncLocalStorage context for thread-safe access
580
+ - Automatic sensitive header redaction (authorization, cookie, x-api-key)
581
+ - Performance monitoring (warns on requests > 3s)
582
+ - Body truncation (max 1000 chars)
640
583
 
641
584
  **Usage:**
642
585
 
643
586
  ```typescript
644
587
  import { LoggerMiddleware } from '@flusys/nestjs-shared/middlewares';
645
- import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
646
588
 
647
- @Module({
648
- // ...
649
- })
589
+ @Module({})
650
590
  export class AppModule implements NestModule {
651
591
  configure(consumer: MiddlewareConsumer) {
652
- // Apply to all routes
653
592
  consumer.apply(LoggerMiddleware).forRoutes('*');
654
593
  }
655
594
  }
@@ -670,195 +609,28 @@ import {
670
609
  @Injectable()
671
610
  export class MyService {
672
611
  async doSomething() {
673
- const requestId = getRequestId(); // Get current request ID
674
- const tenantId = getTenantId(); // Get tenant ID from header
675
-
676
- // Set user context after authentication
612
+ const requestId = getRequestId();
613
+ const tenantId = getTenantId();
677
614
  setUserId('user-123');
678
- setCompanyId('company-456');
679
-
680
- // Use in logs
681
- this.logger.info('Processing request', {
682
- requestId,
683
- tenantId,
684
- userId: getUserId(),
685
- companyId: getCompanyId(),
686
- });
687
- }
688
- }
689
- ```
690
-
691
- **Request Context Interface:**
692
-
693
- ```typescript
694
- interface IRequestContext {
695
- requestId: string; // UUID or from x-request-id header
696
- tenantId?: string; // From x-tenant-id header
697
- userId?: string; // Set after authentication
698
- companyId?: string; // Set after authentication
699
- startTime: number; // Request start timestamp
700
- }
701
- ```
702
-
703
- **Configuration:**
704
-
705
- ```typescript
706
- // Environment-based configuration
707
- const IS_DEBUG = envConfig.getLogConfig().level === 'debug';
708
- const TENANT_ID_HEADER = 'x-tenant-id';
709
- const EXCLUDED_PATHS = ['/health', '/metrics', '/favicon.ico'];
710
- const EXCLUDED_HEADERS = ['authorization', 'cookie', 'x-api-key'];
711
- const MAX_BODY_LOG_SIZE = 1000;
712
- ```
713
-
714
- **Console Log Format (Development):**
715
-
716
- The development console logger displays HTTP requests in human-readable format:
717
-
718
- ```
719
- 2026-01-17 22:41:23 [INFO ] [HTTP] [POST /auth/login] [200] (45ms) [uuid] Incoming request
720
- 2026-01-17 22:41:23 [INFO ] [HTTP] [POST /auth/login] [200] (45ms) [uuid] (user:user-123) Response [200]
721
- 2026-01-17 22:41:25 [WARN ] [HTTP] [GET /api/users] [404] (12ms) [uuid] Response [404]
722
- 2026-01-17 22:41:30 [ERROR ] [HTTP] [POST /api/orders] [500] (234ms) [uuid] (user:user-123) Response [500]
723
- ```
724
-
725
- **Format Structure:**
726
- - `[timestamp]` - Request timestamp
727
- - `[level]` - Log level (INFO, WARN, ERROR)
728
- - `[context]` - Always "HTTP" for HTTP requests
729
- - `[METHOD /path]` - HTTP method and endpoint path
730
- - `[statusCode]` - HTTP response status (only in response logs)
731
- - `(duration)` - Request duration (only in response logs)
732
- - `[uuid]` - Request correlation ID
733
- - `(user:userId)` - User ID if authenticated (only in response logs)
734
-
735
- **File Log Format (Production):**
736
-
737
- ```json
738
- // Incoming request (Enhanced with full details)
739
- {
740
- "level": "info",
741
- "message": "Incoming request",
742
- "context": "HTTP",
743
- "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
744
- "tenantId": "tenant-123",
745
- "method": "POST",
746
- "url": "/api/users/insert?sort=name",
747
- "path": "/api/users/insert",
748
- "query": { "sort": "name" },
749
- "ip": "192.168.1.100",
750
- "userAgent": "Mozilla/5.0...",
751
- "contentType": "application/json",
752
- "contentLength": "342",
753
- "headers": { ... }, // Only in debug mode
754
- "body": { ... }, // Only in debug mode, truncated to 1000 chars
755
- "params": { ... } // Only in debug mode (route params)
756
- }
757
-
758
- // Response (Enhanced with full details)
759
- {
760
- "level": "info",
761
- "message": "Response [200]",
762
- "context": "HTTP",
763
- "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
764
- "tenantId": "tenant-123",
765
- "method": "POST",
766
- "url": "/api/users/insert?sort=name",
767
- "path": "/api/users/insert",
768
- "statusCode": 200,
769
- "statusMessage": "OK",
770
- "duration": "125ms",
771
- "durationMs": 125,
772
- "contentType": "application/json; charset=utf-8",
773
- "contentLength": "156",
774
- "userId": "user-456", // Automatically added if available
775
- "companyId": "company-789" // Automatically added if available
776
- }
777
-
778
- // Error response (includes response body automatically)
779
- {
780
- "level": "warn",
781
- "message": "Response [400]",
782
- "context": "HTTP",
783
- "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
784
- "tenantId": "tenant-123",
785
- "method": "POST",
786
- "url": "/api/users/insert",
787
- "path": "/api/users/insert",
788
- "statusCode": 400,
789
- "statusMessage": "Bad Request",
790
- "duration": "45ms",
791
- "durationMs": 45,
792
- "userId": "user-456",
793
- "companyId": "company-789",
794
- "responseBody": {
795
- "statusCode": 400,
796
- "message": "Validation failed",
797
- "errors": ["Email is required"]
798
615
  }
799
616
  }
800
-
801
- // Slow request warning (Enhanced with more context)
802
- {
803
- "level": "warn",
804
- "message": "Slow request detected",
805
- "context": "HTTP",
806
- "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
807
- "tenantId": "tenant-123",
808
- "userId": "user-456",
809
- "companyId": "company-789",
810
- "method": "POST",
811
- "url": "/api/reports/generate",
812
- "path": "/api/reports/generate",
813
- "duration": "3245ms",
814
- "durationMs": 3245,
815
- "threshold": "3000ms"
816
- }
817
-
818
- // Response error
819
- {
820
- "level": "error",
821
- "message": "Response error",
822
- "context": "HTTP",
823
- "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
824
- "tenantId": "tenant-123",
825
- "method": "POST",
826
- "url": "/api/users/insert",
827
- "path": "/api/users/insert",
828
- "error": "Socket hang up",
829
- "stack": "Error: Socket hang up\n at ..."
830
- }
831
617
  ```
832
618
 
833
- **Benefits:**
834
-
835
- - **Request Tracing** - Correlate logs across multiple services using requestId
836
- - **Multi-Tenant Support** - Automatic tenant isolation in logs
837
- - **Security** - Sensitive data automatically redacted
838
- - **Performance Monitoring** - Identify slow endpoints
839
- - **Debugging** - Conditional verbose logging
840
-
841
619
  ---
842
620
 
843
621
  ## Interceptors
844
622
 
845
623
  ### ResponseMetaInterceptor
846
624
 
847
- Adds request metadata to responses:
625
+ Adds `_meta` to all responses:
848
626
 
849
627
  ```typescript
850
- import { ResponseMetaInterceptor } from '@flusys/nestjs-shared/interceptors';
851
-
852
- @UseInterceptors(ResponseMetaInterceptor)
853
- @Controller('users')
854
- export class UserController {}
855
-
856
- // Response:
628
+ // Response includes:
857
629
  {
858
630
  "data": [...],
859
- "meta": {
860
- "timestamp": "2024-01-01T00:00:00.000Z",
631
+ "_meta": {
861
632
  "requestId": "abc-123",
633
+ "timestamp": "2024-01-01T00:00:00.000Z",
862
634
  "responseTime": 45
863
635
  }
864
636
  }
@@ -866,61 +638,23 @@ export class UserController {}
866
638
 
867
639
  ### IdempotencyInterceptor
868
640
 
869
- Prevent duplicate requests:
641
+ Prevents duplicate POST requests using `X-Idempotency-Key` header. Caches responses for 24 hours.
870
642
 
871
- ```typescript
872
- import { IdempotencyInterceptor } from '@flusys/nestjs-shared/interceptors';
643
+ ### SetCreatedByOnBody / SetUpdateByOnBody / SetDeletedByOnBody
873
644
 
874
- @UseInterceptors(IdempotencyInterceptor)
875
- @Controller('payments')
876
- export class PaymentController {
877
- @Post()
878
- // Clients send X-Idempotency-Key header
879
- // Duplicate requests return cached response
880
- processPayment() {}
881
- }
882
- ```
645
+ Auto-set audit user IDs on request body from authenticated user.
883
646
 
884
- ### SetCreatedByOnBody / SetUpdateByOnBody
647
+ ### DeleteEmptyIdFromBodyInterceptor
885
648
 
886
- Auto-set user IDs on request body:
649
+ Removes empty `id` fields from request body (single and array bodies).
887
650
 
888
- ```typescript
889
- import { SetCreatedByOnBody, SetUpdateByOnBody } from '@flusys/nestjs-shared/interceptors';
651
+ ### QueryPerformanceInterceptor
890
652
 
891
- @Controller('posts')
892
- export class PostController {
893
- @UseInterceptors(SetCreatedByOnBody)
894
- @Post()
895
- create(@Body() dto: CreatePostDto) {
896
- // dto.createdById is automatically set to current user ID
897
- }
898
-
899
- @UseInterceptors(SetUpdateByOnBody)
900
- @Put()
901
- update(@Body() dto: UpdatePostDto) {
902
- // dto.updatedById is automatically set to current user ID
903
- }
904
- }
905
- ```
653
+ Monitors execution time and warns if request > 1000ms (configurable).
906
654
 
907
655
  ### Slug Interceptor
908
656
 
909
- Auto-generate slugs from name field:
910
-
911
- ```typescript
912
- import { Slug } from '@flusys/nestjs-shared/interceptors';
913
-
914
- @Controller('products')
915
- export class ProductController {
916
- @UseInterceptors(Slug)
917
- @Post()
918
- create(@Body() dto: CreateProductDto) {
919
- // dto.slug is auto-generated from dto.name
920
- // "My Product" -> "my-product"
921
- }
922
- }
923
- ```
657
+ Auto-generates `slug` from `name` field using `UtilsService.transformToSlug()`.
924
658
 
925
659
  ---
926
660
 
@@ -928,29 +662,22 @@ export class ProductController {
928
662
 
929
663
  ### HybridCache
930
664
 
931
- Two-tier caching with in-memory (fast) and Redis (distributed):
665
+ Two-tier caching with in-memory (L1) and Redis (L2):
932
666
 
933
667
  ```typescript
934
668
  import { HybridCache } from '@flusys/nestjs-shared/classes';
935
669
 
936
670
  @Injectable()
937
671
  export class MyService {
938
- constructor(
939
- @Inject('CACHE_INSTANCE')
940
- private cache: HybridCache,
941
- ) {}
672
+ constructor(@Inject('CACHE_INSTANCE') private cache: HybridCache) {}
942
673
 
943
674
  async getData(key: string) {
944
675
  // Check cache first
945
676
  const cached = await this.cache.get(key);
946
677
  if (cached) return cached;
947
678
 
948
- // Fetch from database
949
679
  const data = await this.fetchFromDb();
950
-
951
- // Store in cache (TTL in seconds)
952
- await this.cache.set(key, data, 3600);
953
-
680
+ await this.cache.set(key, data, 3600); // TTL in seconds
954
681
  return data;
955
682
  }
956
683
 
@@ -958,9 +685,9 @@ export class MyService {
958
685
  await this.cache.del(key);
959
686
  }
960
687
 
961
- async invalidatePattern(pattern: string) {
962
- // Delete all keys matching pattern
963
- await this.cache.delByPattern('users:*');
688
+ async invalidateAll() {
689
+ await this.cache.reset(); // Clear L1
690
+ await this.cache.resetL2(); // Clear L2 (Redis)
964
691
  }
965
692
  }
966
693
  ```
@@ -972,40 +699,26 @@ import { CacheModule } from '@flusys/nestjs-shared/modules';
972
699
 
973
700
  @Module({
974
701
  imports: [
975
- CacheModule.forRoot({
976
- // In-memory cache config
977
- memory: {
978
- max: 500, // Max items
979
- ttl: 3600, // Default TTL (seconds)
980
- },
981
-
982
- // Redis config (optional)
983
- redis: {
984
- url: 'redis://localhost:6379',
985
- ttl: 3600,
986
- },
987
- }),
702
+ CacheModule.forRoot(
703
+ true, // isGlobal
704
+ 60_000, // memoryTtl (ms)
705
+ 5000 // memorySize (LRU max items)
706
+ ),
988
707
  ],
989
708
  })
990
709
  export class AppModule {}
991
710
  ```
992
711
 
993
- ### Cache Methods
994
-
995
- | Method | Description |
996
- |--------|-------------|
997
- | `get(key)` | Get cached value |
998
- | `set(key, value, ttl?)` | Set value with optional TTL |
999
- | `del(key)` | Delete single key |
1000
- | `delByPattern(pattern)` | Delete keys matching pattern |
1001
- | `reset()` | Clear all cache |
1002
- | `has(key)` | Check if key exists |
712
+ **Configuration via `USE_CACHE_LABEL` env:**
713
+ - `'memory'` - L1 only
714
+ - `'redis'` - L2 only
715
+ - `'hybrid'` - Both (default)
1003
716
 
1004
717
  ---
1005
718
 
1006
719
  ## Multi-Tenant DataSource
1007
720
 
1008
- Dynamic database connection management for multi-tenant applications.
721
+ Dynamic database connection management with connection pooling.
1009
722
 
1010
723
  ### Setup
1011
724
 
@@ -1015,7 +728,7 @@ import { DataSourceModule } from '@flusys/nestjs-shared/modules';
1015
728
  @Module({
1016
729
  imports: [
1017
730
  DataSourceModule.forRoot({
1018
- // Default database config (used as template)
731
+ bootstrapAppConfig: { databaseMode: 'multi-tenant' },
1019
732
  defaultDatabaseConfig: {
1020
733
  type: 'mysql',
1021
734
  host: 'localhost',
@@ -1023,11 +736,9 @@ import { DataSourceModule } from '@flusys/nestjs-shared/modules';
1023
736
  username: 'root',
1024
737
  password: 'password',
1025
738
  },
1026
-
1027
- // Tenant configurations
1028
739
  tenants: [
1029
- { id: 'tenant1', database: 'tenant1_db', isActive: true },
1030
- { id: 'tenant2', database: 'tenant2_db', isActive: true },
740
+ { id: 'tenant1', database: 'tenant1_db' },
741
+ { id: 'tenant2', database: 'tenant2_db', host: 'other-server.com' },
1031
742
  ],
1032
743
  }),
1033
744
  ],
@@ -1035,44 +746,18 @@ import { DataSourceModule } from '@flusys/nestjs-shared/modules';
1035
746
  export class AppModule {}
1036
747
  ```
1037
748
 
1038
- ### Usage with Tenant Header
1039
-
1040
- ```typescript
1041
- // Client sends X-Tenant-ID header
1042
- // DataSource automatically switches to tenant's database
1043
-
1044
- @Injectable()
1045
- export class TenantService {
1046
- constructor(
1047
- @Inject('DATASOURCE_PROVIDER')
1048
- private dataSourceProvider: MultiTenantDataSourceService,
1049
- ) {}
1050
-
1051
- async getConnection(tenantId: string) {
1052
- return this.dataSourceProvider.getConnection(tenantId);
1053
- }
1054
- }
1055
- ```
1056
-
1057
- ### Tenant Resolution
749
+ ### MultiTenantDataSourceService
1058
750
 
1059
- ```typescript
1060
- // Default: Resolved from X-Tenant-ID header
1061
- @Controller('users')
1062
- export class UserController {
1063
- // Automatically uses tenant-specific database
1064
- }
1065
-
1066
- // Manual tenant specification
1067
- @Injectable()
1068
- export class CrossTenantService {
1069
- async copyData(fromTenant: string, toTenant: string) {
1070
- const fromConn = await this.dataSourceProvider.getConnection(fromTenant);
1071
- const toConn = await this.dataSourceProvider.getConnection(toTenant);
1072
- // ...
1073
- }
1074
- }
1075
- ```
751
+ | Method | Description |
752
+ |--------|-------------|
753
+ | `getDataSource()` | Get DataSource for current tenant (from header) |
754
+ | `getDataSourceForTenant(id)` | Get DataSource for specific tenant |
755
+ | `getRepository(entity)` | Get repository for current tenant |
756
+ | `withTenant(id, callback)` | Execute callback with specific tenant |
757
+ | `forAllTenants(callback)` | Execute callback for all tenants |
758
+ | `registerTenant(config)` | Register new tenant at runtime |
759
+ | `removeTenant(id)` | Remove tenant and close connection |
760
+ | `getCurrentTenantId()` | Get tenant ID from request header |
1076
761
 
1077
762
  ---
1078
763
 
@@ -1080,92 +765,71 @@ export class CrossTenantService {
1080
765
 
1081
766
  ### FilterAndPaginationDto
1082
767
 
1083
- Standard filtering and pagination:
1084
-
1085
768
  ```typescript
1086
769
  import { FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
1087
770
 
1088
771
  // Request body
1089
772
  {
1090
- "filter": {
1091
- "status": "active",
1092
- "category": "electronics"
1093
- },
1094
- "pagination": {
1095
- "page": 1,
1096
- "limit": 10
1097
- },
1098
- "sort": {
1099
- "field": "createdAt",
1100
- "order": "DESC"
1101
- },
1102
- "search": {
1103
- "fields": ["name", "description"],
1104
- "value": "laptop"
1105
- },
1106
- "select": ["id", "name", "price"]
773
+ "filter": { "status": "active" },
774
+ "pagination": { "currentPage": 0, "pageSize": 10 },
775
+ "sort": { "createdAt": "DESC" },
776
+ "select": ["id", "name", "email"],
777
+ "withDeleted": false
1107
778
  }
1108
779
  ```
1109
780
 
1110
781
  ### DeleteDto
1111
782
 
1112
- Soft or permanent delete:
1113
-
1114
783
  ```typescript
1115
784
  import { DeleteDto } from '@flusys/nestjs-shared/dtos';
1116
785
 
1117
- // Soft delete (sets deletedAt)
1118
- {
1119
- "id": "user-123",
1120
- "type": "soft"
1121
- }
786
+ // Soft delete
787
+ { "id": "uuid", "type": "delete" }
788
+
789
+ // Restore
790
+ { "id": "uuid", "type": "restore" }
1122
791
 
1123
792
  // Permanent delete
1124
- {
1125
- "id": "user-123",
1126
- "type": "permanent"
1127
- }
793
+ { "id": "uuid", "type": "permanent" }
1128
794
 
1129
795
  // Multiple IDs
1130
- {
1131
- "id": ["user-123", "user-456"],
1132
- "type": "soft"
1133
- }
796
+ { "id": ["uuid1", "uuid2"], "type": "delete" }
1134
797
  ```
1135
798
 
1136
- ### ResponsePayloadDto
1137
-
1138
- Standardized response format:
799
+ ### Response DTOs
1139
800
 
1140
801
  ```typescript
1141
- import { ResponsePayloadDto } from '@flusys/nestjs-shared/dtos';
802
+ // Single item
803
+ class SingleResponseDto<T> {
804
+ success: boolean;
805
+ message: string;
806
+ data?: T;
807
+ _meta?: RequestMetaDto;
808
+ }
1142
809
 
1143
- // Success response
1144
- {
1145
- "success": true,
1146
- "data": { ... },
1147
- "message": "Operation successful"
810
+ // Paginated list
811
+ class ListResponseDto<T> {
812
+ success: boolean;
813
+ message: string;
814
+ data?: T[];
815
+ meta: PaginationMetaDto; // { total, page, pageSize, count, hasMore?, totalPages? }
816
+ _meta?: RequestMetaDto;
1148
817
  }
1149
818
 
1150
- // Paginated response
1151
- {
1152
- "success": true,
1153
- "data": [...],
1154
- "pagination": {
1155
- "total": 100,
1156
- "page": 1,
1157
- "limit": 10,
1158
- "totalPages": 10
1159
- }
819
+ // Bulk operations
820
+ class BulkResponseDto<T> {
821
+ success: boolean;
822
+ message: string;
823
+ data?: T[];
824
+ meta: BulkMetaDto; // { count, failed?, total? }
825
+ _meta?: RequestMetaDto;
1160
826
  }
1161
827
 
1162
- // Error response
1163
- {
1164
- "success": false,
1165
- "error": {
1166
- "code": "VALIDATION_ERROR",
1167
- "message": "Invalid input"
1168
- }
828
+ // Message only
829
+ class MessageResponseDto {
830
+ success: boolean;
831
+ message: string;
832
+ _meta?: RequestMetaDto;
1169
833
  }
1170
834
  ```
1171
835
 
@@ -1183,65 +847,174 @@ import { Identity } from '@flusys/nestjs-shared/entities';
1183
847
  @Entity()
1184
848
  export class Product extends Identity {
1185
849
  // Inherited: id, createdAt, updatedAt, deletedAt
850
+ // Inherited: createdById, updatedById, deletedById
1186
851
 
1187
852
  @Column()
1188
853
  name: string;
1189
-
1190
- @Column()
1191
- price: number;
1192
854
  }
1193
855
  ```
1194
856
 
1195
857
  ### UserRoot Entity
1196
858
 
1197
- Base user entity:
859
+ Base user entity with common fields:
1198
860
 
1199
861
  ```typescript
1200
862
  import { UserRoot } from '@flusys/nestjs-shared/entities';
1201
863
 
1202
864
  @Entity()
1203
865
  export class User extends UserRoot {
1204
- // Inherited: id, name, email, password, phone, isActive, createdAt, updatedAt, deletedAt
1205
-
1206
- @Column({ nullable: true })
1207
- customField: string;
866
+ // Inherited: id, name, email, phone, profilePictureId
867
+ // Inherited: isActive, emailVerified, phoneVerified
868
+ // Inherited: lastLoginAt, additionalFields
869
+ // Inherited: createdAt, updatedAt, deletedAt
1208
870
  }
1209
871
  ```
1210
872
 
1211
873
  ---
1212
874
 
1213
- ## Error Handling
875
+ ## Utilities
876
+
877
+ ### Query Helpers
1214
878
 
1215
- ### ErrorHandler
879
+ ```typescript
880
+ import {
881
+ applyCompanyFilter,
882
+ buildCompanyWhereCondition,
883
+ hasCompanyId,
884
+ validateCompanyOwnership,
885
+ } from '@flusys/nestjs-shared/utils';
1216
886
 
1217
- Centralized error handling utility:
887
+ // Add company filter to TypeORM query
888
+ applyCompanyFilter(query, {
889
+ isCompanyFeatureEnabled: true,
890
+ entityAlias: 'entity',
891
+ }, user);
892
+
893
+ // Build where condition for company
894
+ const where = buildCompanyWhereCondition(baseWhere, user, isCompanyFeatureEnabled);
895
+
896
+ // Validate entity belongs to user's company
897
+ validateCompanyOwnership(entity, user, 'Entity');
898
+ ```
899
+
900
+ ### String Utilities
1218
901
 
1219
902
  ```typescript
1220
- import { ErrorHandler } from '@flusys/nestjs-shared/utils';
903
+ import { generateSlug, generateUniqueSlug } from '@flusys/nestjs-shared/utils';
904
+
905
+ // Generate URL-friendly slug
906
+ const slug = generateSlug('My Product Name', 100);
907
+ // Returns: 'my-product-name'
908
+
909
+ // Generate unique slug with collision detection
910
+ const uniqueSlug = await generateUniqueSlug(
911
+ 'My Product',
912
+ async (pattern) => existingSlugs.filter(s => s.startsWith(pattern)),
913
+ 100
914
+ );
915
+ ```
1221
916
 
1222
- @Injectable()
1223
- export class UserService {
1224
- async createUser(dto: CreateUserDto) {
1225
- try {
1226
- return await this.repository.save(dto);
1227
- } catch (error) {
1228
- // Handles TypeORM errors, validation errors, etc.
1229
- throw ErrorHandler.handle(error, 'Failed to create user');
1230
- }
1231
- }
917
+ ### Request Utilities
918
+
919
+ ```typescript
920
+ import {
921
+ isBrowserRequest,
922
+ buildCookieOptions,
923
+ parseDurationToMs,
924
+ } from '@flusys/nestjs-shared/utils';
925
+
926
+ // Detect browser vs API client
927
+ const isBrowser = isBrowserRequest(req);
928
+
929
+ // Build secure cookie options
930
+ const cookieOpts = buildCookieOptions(req);
931
+
932
+ // Parse duration string
933
+ const ms = parseDurationToMs('7d'); // 604800000
934
+ ```
935
+
936
+ ### HTML Sanitizer
937
+
938
+ ```typescript
939
+ import { escapeHtml, escapeHtmlVariables } from '@flusys/nestjs-shared/utils';
940
+
941
+ // Escape single string
942
+ const safe = escapeHtml('<script>alert("xss")</script>');
943
+ // Returns: '&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;'
944
+
945
+ // Escape all values in an object
946
+ const safeVars = escapeHtmlVariables({
947
+ userName: '<script>evil</script>',
948
+ message: 'Hello, World!',
949
+ });
950
+ ```
951
+
952
+ ---
953
+
954
+ ## Error Handling
955
+
956
+ ### Error Handler Utilities
957
+
958
+ ```typescript
959
+ import {
960
+ getErrorMessage,
961
+ logError,
962
+ rethrowError,
963
+ logAndRethrow,
964
+ } from '@flusys/nestjs-shared/utils';
965
+
966
+ interface IErrorContext {
967
+ operation?: string;
968
+ entity?: string;
969
+ userId?: string;
970
+ id?: string;
971
+ companyId?: string;
972
+ data?: Record<string, unknown>;
1232
973
  }
974
+
975
+ // Safe error message extraction
976
+ const message = getErrorMessage(error);
977
+
978
+ // Log with sensitive key redaction (password, secret, token, apiKey)
979
+ logError(logger, error, 'createUser', { userId: user.id });
980
+
981
+ // Type-safe rethrow
982
+ rethrowError(error);
983
+
984
+ // Combined log + rethrow
985
+ logAndRethrow(logger, error, 'updateUser', context);
1233
986
  ```
1234
987
 
1235
- ### Error Types
988
+ ---
989
+
990
+ ## Constants
991
+
992
+ ### Permission Constants
1236
993
 
1237
994
  ```typescript
1238
- // Automatically converted to appropriate HTTP exception:
995
+ import { PERMISSIONS } from '@flusys/nestjs-shared/constants';
996
+
997
+ // Organized by module:
998
+ PERMISSIONS.USER.CREATE // 'user.create'
999
+ PERMISSIONS.USER.READ // 'user.read'
1000
+ PERMISSIONS.USER.UPDATE // 'user.update'
1001
+ PERMISSIONS.USER.DELETE // 'user.delete'
1239
1002
 
1240
- // TypeORM unique constraint → ConflictException (409)
1241
- // TypeORM foreign key → BadRequestException (400)
1242
- // Validation error → BadRequestException (400)
1243
- // Not found → NotFoundException (404)
1244
- // Unknown → InternalServerErrorException (500)
1003
+ PERMISSIONS.COMPANY.CREATE // 'company.create'
1004
+ PERMISSIONS.BRANCH.CREATE // 'branch.create'
1005
+
1006
+ PERMISSIONS.ROLE.CREATE // 'role.create'
1007
+ PERMISSIONS.FILE.CREATE // 'file.create'
1008
+ PERMISSIONS.FOLDER.CREATE // 'folder.create'
1009
+ // ...etc
1010
+ ```
1011
+
1012
+ ### Injection Tokens
1013
+
1014
+ ```typescript
1015
+ 'CACHE_INSTANCE' // HybridCache provider
1016
+ 'PERMISSION_GUARD_CONFIG' // PermissionGuard config
1017
+ 'LOGGER_INSTANCE' // Logger provider
1245
1018
  ```
1246
1019
 
1247
1020
  ---
@@ -1254,8 +1027,11 @@ export class UserService {
1254
1027
  // Classes
1255
1028
  import {
1256
1029
  ApiService,
1030
+ RequestScopedApiService,
1257
1031
  createApiController,
1258
1032
  HybridCache,
1033
+ WinstonLoggerAdapter,
1034
+ NestLoggerAdapter,
1259
1035
  } from '@flusys/nestjs-shared/classes';
1260
1036
 
1261
1037
  // Decorators
@@ -1265,13 +1041,13 @@ import {
1265
1041
  RequirePermission,
1266
1042
  RequireAnyPermission,
1267
1043
  RequirePermissionCondition,
1044
+ SanitizeHtml,
1045
+ SanitizeAndTrim,
1046
+ ApiResponseDto,
1268
1047
  } from '@flusys/nestjs-shared/decorators';
1269
1048
 
1270
1049
  // Guards
1271
- import {
1272
- JwtAuthGuard,
1273
- PermissionGuard,
1274
- } from '@flusys/nestjs-shared/guards';
1050
+ import { JwtAuthGuard, PermissionGuard } from '@flusys/nestjs-shared/guards';
1275
1051
 
1276
1052
  // Interceptors
1277
1053
  import {
@@ -1279,6 +1055,9 @@ import {
1279
1055
  IdempotencyInterceptor,
1280
1056
  SetCreatedByOnBody,
1281
1057
  SetUpdateByOnBody,
1058
+ SetDeletedByOnBody,
1059
+ DeleteEmptyIdFromBodyInterceptor,
1060
+ QueryPerformanceInterceptor,
1282
1061
  Slug,
1283
1062
  } from '@flusys/nestjs-shared/interceptors';
1284
1063
 
@@ -1287,31 +1066,76 @@ import {
1287
1066
  CacheModule,
1288
1067
  DataSourceModule,
1289
1068
  UtilsModule,
1069
+ UtilsService,
1070
+ MultiTenantDataSourceService,
1290
1071
  } from '@flusys/nestjs-shared/modules';
1291
1072
 
1292
1073
  // DTOs
1293
1074
  import {
1294
1075
  FilterAndPaginationDto,
1076
+ PaginationDto,
1295
1077
  DeleteDto,
1296
- ResponsePayloadDto,
1078
+ GetByIdBodyDto,
1079
+ SingleResponseDto,
1080
+ ListResponseDto,
1081
+ BulkResponseDto,
1082
+ MessageResponseDto,
1083
+ IdentityResponseDto,
1084
+ PaginationMetaDto,
1085
+ BulkMetaDto,
1086
+ RequestMetaDto,
1297
1087
  } from '@flusys/nestjs-shared/dtos';
1298
1088
 
1299
1089
  // Entities
1300
- import {
1301
- Identity,
1302
- UserRoot,
1303
- } from '@flusys/nestjs-shared/entities';
1090
+ import { Identity, UserRoot } from '@flusys/nestjs-shared/entities';
1304
1091
 
1305
1092
  // Interfaces
1306
1093
  import {
1307
1094
  ILoggedUserInfo,
1308
- IPermissionConfig,
1095
+ IService,
1096
+ IDataSourceProvider,
1097
+ IModuleConfigService,
1098
+ ILogger,
1099
+ PermissionCondition,
1100
+ PermissionOperator,
1309
1101
  } from '@flusys/nestjs-shared/interfaces';
1310
1102
 
1311
1103
  // Utilities
1312
1104
  import {
1313
- ErrorHandler,
1105
+ getErrorMessage,
1106
+ logError,
1107
+ applyCompanyFilter,
1108
+ buildCompanyWhereCondition,
1109
+ validateCompanyOwnership,
1110
+ generateSlug,
1111
+ generateUniqueSlug,
1112
+ isBrowserRequest,
1113
+ buildCookieOptions,
1114
+ parseDurationToMs,
1115
+ escapeHtml,
1116
+ escapeHtmlVariables,
1314
1117
  } from '@flusys/nestjs-shared/utils';
1118
+
1119
+ // Middleware
1120
+ import {
1121
+ LoggerMiddleware,
1122
+ getRequestId,
1123
+ getTenantId,
1124
+ getUserId,
1125
+ getCompanyId,
1126
+ setUserId,
1127
+ setCompanyId,
1128
+ } from '@flusys/nestjs-shared/middlewares';
1129
+
1130
+ // Exceptions
1131
+ import {
1132
+ InsufficientPermissionsException,
1133
+ NoPermissionsFoundException,
1134
+ PermissionSystemUnavailableException,
1135
+ } from '@flusys/nestjs-shared/exceptions';
1136
+
1137
+ // Constants
1138
+ import { PERMISSIONS } from '@flusys/nestjs-shared/constants';
1315
1139
  ```
1316
1140
 
1317
1141
  ---
@@ -1321,101 +1145,58 @@ import {
1321
1145
  ### 1. Use Generic Service Pattern
1322
1146
 
1323
1147
  ```typescript
1324
- // Extend ApiService for consistent CRUD
1148
+ // Extend ApiService for consistent CRUD
1325
1149
  @Injectable()
1326
1150
  export class ProductService extends ApiService<...> {
1327
1151
  // Override hooks for customization
1328
1152
  }
1329
1153
 
1330
- // Don't create custom CRUD from scratch
1331
- @Injectable()
1332
- export class ProductService {
1333
- async getAll() { /* custom implementation */ }
1334
- async getById() { /* custom implementation */ }
1335
- // Inconsistent, error-prone
1336
- }
1154
+ // For dynamic entities, use RequestScopedApiService
1155
+ @Injectable({ scope: Scope.REQUEST })
1156
+ export class RoleService extends RequestScopedApiService<...> { }
1157
+ ```
1158
+
1159
+ ### 2. Always Use @Inject() Decorators
1160
+
1161
+ Required for esbuild bundled code:
1162
+
1163
+ ```typescript
1164
+ // CORRECT
1165
+ constructor(
1166
+ @Inject(MyService) private readonly myService: MyService,
1167
+ @Inject('CACHE_INSTANCE') private readonly cache: HybridCache,
1168
+ ) {}
1169
+
1170
+ // WRONG - fails in bundled code
1171
+ constructor(private readonly myService: MyService) {}
1337
1172
  ```
1338
1173
 
1339
- ### 2. Use Decorators Consistently
1174
+ ### 3. Use Decorators Consistently
1340
1175
 
1341
1176
  ```typescript
1342
- // Use built-in decorators
1177
+ // Use built-in decorators
1343
1178
  @CurrentUser() user: ILoggedUserInfo
1344
- @Public()
1345
1179
  @RequirePermission('users.create')
1180
+ @Public() // Use sparingly!
1346
1181
 
1347
- // Don't access request directly
1348
- @Req() req: Request
1349
- const user = req.user; // Not type-safe
1182
+ // Don't access request directly
1183
+ @Req() req: Request // Not type-safe
1350
1184
  ```
1351
1185
 
1352
- ### 3. Configure Security at Controller Level
1186
+ ### 4. Configure Security at Controller Level
1353
1187
 
1354
1188
  ```typescript
1355
- // Configure security in createApiController
1189
+ // GOOD - configure in createApiController
1356
1190
  export class UserController extends createApiController(..., {
1357
- security: { ... },
1191
+ security: { insert: 'permission', ... },
1358
1192
  }) {}
1359
1193
 
1360
- // Don't add @UseGuards to each endpoint
1194
+ // AVOID - adding guards to each endpoint
1361
1195
  @UseGuards(JwtGuard)
1362
1196
  @Post('create')
1363
1197
  create() {}
1364
1198
  ```
1365
1199
 
1366
- ### 4. Use Cache for Repeated Queries
1367
-
1368
- ```typescript
1369
- // ✅ Cache expensive operations
1370
- async getPermissions(userId: string) {
1371
- const cacheKey = `permissions:${userId}`;
1372
- const cached = await this.cache.get(cacheKey);
1373
- if (cached) return cached;
1374
-
1375
- const permissions = await this.fetchPermissions(userId);
1376
- await this.cache.set(cacheKey, permissions, 3600);
1377
- return permissions;
1378
- }
1379
- ```
1380
-
1381
- ---
1382
-
1383
- ## Dependencies
1384
-
1385
- - `@nestjs/common`
1386
- - `@nestjs/core`
1387
- - `@nestjs/typeorm`
1388
- - `@nestjs/swagger`
1389
- - `typeorm`
1390
- - `class-validator`
1391
- - `class-transformer`
1392
- - `@flusys/nestjs-core`
1393
-
1394
- ---
1395
-
1396
- ## Related Documentation
1397
-
1398
- - [API Controller Security Guide](./API-CONTROLLER-SECURITY.md)
1399
- - [Core Package Guide](./CORE-GUIDE.md)
1400
- - [Auth Package Guide](./AUTH-GUIDE.md)
1401
-
1402
- ---
1403
-
1404
- ## Summary
1405
-
1406
- The `@flusys/nestjs-shared` package provides:
1407
-
1408
- - **Generic CRUD** - Consistent API patterns
1409
- - **Permission System** - Flexible access control
1410
- - **Caching** - High-performance data access
1411
- - **Multi-Tenancy** - Database isolation
1412
- - **Request Correlation** - AsyncLocalStorage-based tracking
1413
- - **Middleware** - Logging and performance monitoring
1414
- - **Interceptors** - Request/response processing
1415
- - **Error Handling** - Centralized error management
1416
-
1417
- This is the shared infrastructure layer used by all other Flusys packages.
1418
-
1419
1200
  ---
1420
1201
 
1421
- **Last Updated:** 2026-02-16
1202
+ **Last Updated:** 2026-02-21