@flusys/nestjs-shared 0.1.0-beta.3 → 1.0.0-beta
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 +51 -29
- package/cjs/classes/api-controller.class.js +0 -21
- package/cjs/classes/api-service.class.js +4 -41
- package/cjs/classes/request-scoped-api.service.js +4 -53
- package/cjs/classes/winston.logger.class.js +5 -15
- package/cjs/constants/index.js +2 -11
- package/cjs/dtos/response-payload.dto.js +40 -85
- package/cjs/interfaces/logged-user-info.interface.js +1 -2
- package/cjs/interfaces/permission.interface.js +1 -10
- package/cjs/middlewares/logger.middleware.js +2 -6
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +9 -9
- package/fesm/classes/api-controller.class.js +1 -69
- package/fesm/classes/api-service.class.js +5 -46
- package/fesm/classes/request-scoped-api.service.js +4 -53
- package/fesm/classes/winston.logger.class.js +6 -18
- package/fesm/constants/index.js +14 -29
- package/fesm/dtos/response-payload.dto.js +44 -109
- package/fesm/interfaces/logged-user-info.interface.js +1 -2
- package/fesm/interfaces/permission.interface.js +0 -12
- package/fesm/middlewares/logger.middleware.js +2 -6
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +9 -9
- package/package.json +2 -2
|
@@ -14,14 +14,7 @@ function _define_property(obj, key, value) {
|
|
|
14
14
|
import { ErrorHandler } from '../utils/error-handler.util';
|
|
15
15
|
import { Logger, NotFoundException } from '@nestjs/common';
|
|
16
16
|
import { In } from 'typeorm';
|
|
17
|
-
/**
|
|
18
|
-
* Generic API service to handle common CRUD operations with caching support.
|
|
19
|
-
* This class is fully abstract and should be extended by concrete services
|
|
20
|
-
* for specific entities.
|
|
21
|
-
*/ export class ApiService {
|
|
22
|
-
// ---------------------------------------------------------------------
|
|
23
|
-
// INSERT SINGLE ENTITY
|
|
24
|
-
// ---------------------------------------------------------------------
|
|
17
|
+
/** Generic API service with CRUD operations and caching support */ export class ApiService {
|
|
25
18
|
async insert(dto, user) {
|
|
26
19
|
await this.ensureRepositoryInitialized();
|
|
27
20
|
const qr = this.repository.manager.connection.createQueryRunner();
|
|
@@ -48,9 +41,6 @@ import { In } from 'typeorm';
|
|
|
48
41
|
await qr.release();
|
|
49
42
|
}
|
|
50
43
|
}
|
|
51
|
-
// ---------------------------------------------------------------------
|
|
52
|
-
// INSERT MULTIPLE ENTITIES
|
|
53
|
-
// ---------------------------------------------------------------------
|
|
54
44
|
async insertMany(dtos, user) {
|
|
55
45
|
await this.ensureRepositoryInitialized();
|
|
56
46
|
const qr = this.repository.manager.connection.createQueryRunner();
|
|
@@ -81,9 +71,6 @@ import { In } from 'typeorm';
|
|
|
81
71
|
await qr.release();
|
|
82
72
|
}
|
|
83
73
|
}
|
|
84
|
-
// ---------------------------------------------------------------------
|
|
85
|
-
// UPDATE SINGLE ENTITY
|
|
86
|
-
// ---------------------------------------------------------------------
|
|
87
74
|
async update(dto, user) {
|
|
88
75
|
await this.ensureRepositoryInitialized();
|
|
89
76
|
const qr = this.repository.manager.connection.createQueryRunner();
|
|
@@ -110,9 +97,6 @@ import { In } from 'typeorm';
|
|
|
110
97
|
await qr.release();
|
|
111
98
|
}
|
|
112
99
|
}
|
|
113
|
-
// ---------------------------------------------------------------------
|
|
114
|
-
// UPDATE MULTIPLE ENTITIES
|
|
115
|
-
// ---------------------------------------------------------------------
|
|
116
100
|
async updateMany(dtos, user) {
|
|
117
101
|
await this.ensureRepositoryInitialized();
|
|
118
102
|
const qr = this.repository.manager.connection.createQueryRunner();
|
|
@@ -143,9 +127,6 @@ import { In } from 'typeorm';
|
|
|
143
127
|
await qr.release();
|
|
144
128
|
}
|
|
145
129
|
}
|
|
146
|
-
// ---------------------------------------------------------------------
|
|
147
|
-
// FIND BY IDS
|
|
148
|
-
// ---------------------------------------------------------------------
|
|
149
130
|
async findByIds(ids, user) {
|
|
150
131
|
await this.ensureRepositoryInitialized();
|
|
151
132
|
try {
|
|
@@ -170,9 +151,6 @@ import { In } from 'typeorm';
|
|
|
170
151
|
this.handleError(error, 'findByIds');
|
|
171
152
|
}
|
|
172
153
|
}
|
|
173
|
-
// ---------------------------------------------------------------------
|
|
174
|
-
// FIND BY ID
|
|
175
|
-
// ---------------------------------------------------------------------
|
|
176
154
|
async findById(id, user, select) {
|
|
177
155
|
await this.ensureRepositoryInitialized();
|
|
178
156
|
try {
|
|
@@ -211,9 +189,6 @@ import { In } from 'typeorm';
|
|
|
211
189
|
this.handleError(error, 'findById');
|
|
212
190
|
}
|
|
213
191
|
}
|
|
214
|
-
// ---------------------------------------------------------------------
|
|
215
|
-
// GET ALL (WITH FILTER, SORT, SEARCH, PAGINATION & CACHING)
|
|
216
|
-
// ---------------------------------------------------------------------
|
|
217
192
|
async getAll(search, filterAndPaginationDto, user) {
|
|
218
193
|
await this.ensureRepositoryInitialized();
|
|
219
194
|
try {
|
|
@@ -312,9 +287,6 @@ import { In } from 'typeorm';
|
|
|
312
287
|
this.handleError(error, 'getAll');
|
|
313
288
|
}
|
|
314
289
|
}
|
|
315
|
-
// ---------------------------------------------------------------------
|
|
316
|
-
// DELETE / RESTORE / HARD DELETE
|
|
317
|
-
// ---------------------------------------------------------------------
|
|
318
290
|
async delete(option, user) {
|
|
319
291
|
await this.ensureRepositoryInitialized();
|
|
320
292
|
const queryRunner = this.repository.manager.connection.createQueryRunner();
|
|
@@ -357,9 +329,7 @@ import { In } from 'typeorm';
|
|
|
357
329
|
await queryRunner.release();
|
|
358
330
|
}
|
|
359
331
|
}
|
|
360
|
-
//
|
|
361
|
-
// CACHING HELPERS
|
|
362
|
-
// ---------------------------------------------------------------------
|
|
332
|
+
// Caching
|
|
363
333
|
async clearCacheForAll() {
|
|
364
334
|
await this.utilsService.clearCache(this.entityName, this.cacheManager);
|
|
365
335
|
}
|
|
@@ -374,17 +344,8 @@ import { In } from 'typeorm';
|
|
|
374
344
|
});
|
|
375
345
|
ErrorHandler.rethrowError(error);
|
|
376
346
|
}
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
// ---------------------------------------------------------------------
|
|
380
|
-
/**
|
|
381
|
-
* Hook called before ANY repository access
|
|
382
|
-
* CRITICAL: Override this in REQUEST-scoped services that use lazy repository initialization
|
|
383
|
-
* Example use case: DataSource Provider pattern where repository is set dynamically
|
|
384
|
-
*/ async ensureRepositoryInitialized() {
|
|
385
|
-
// Default: no-op - repository is already initialized in constructor
|
|
386
|
-
// Override in child classes that need lazy initialization
|
|
387
|
-
}
|
|
347
|
+
// Hooks (override in child classes)
|
|
348
|
+
async ensureRepositoryInitialized() {}
|
|
388
349
|
async beforeInsertOperation(_dto, _user, _queryRunner) {}
|
|
389
350
|
async afterInsertOperation(_entity, _user, _queryRunner) {}
|
|
390
351
|
async beforeUpdateOperation(_dto, _user, _queryRunner) {}
|
|
@@ -433,9 +394,7 @@ import { In } from 'typeorm';
|
|
|
433
394
|
isRaw: false
|
|
434
395
|
};
|
|
435
396
|
}
|
|
436
|
-
//
|
|
437
|
-
// DTO <-> ENTITY CONVERSION
|
|
438
|
-
// ---------------------------------------------------------------------
|
|
397
|
+
// DTO conversion
|
|
439
398
|
async convertRequestDtoToEntity(dto, user) {
|
|
440
399
|
return Array.isArray(dto) ? await this.convertArrayDtoToEntities(dto, user) : [
|
|
441
400
|
await this.convertSingleDtoToEntity(dto, user)
|
|
@@ -20,18 +20,7 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
20
20
|
import { Injectable, Scope } from '@nestjs/common';
|
|
21
21
|
import { ApiService } from './api-service.class';
|
|
22
22
|
export class RequestScopedApiService extends ApiService {
|
|
23
|
-
|
|
24
|
-
* Ensures repository is initialized before any database access
|
|
25
|
-
* CRITICAL: Automatically called by ApiService before all CRUD operations
|
|
26
|
-
*
|
|
27
|
-
* This method:
|
|
28
|
-
* 1. Calls resolveEntity() to get the entity class
|
|
29
|
-
* 2. Calls getDataSourceProvider() to get the provider
|
|
30
|
-
* 3. Loads the repository from the provider
|
|
31
|
-
* 4. Marks initialization as complete
|
|
32
|
-
*
|
|
33
|
-
* @internal - Called automatically, do not call manually
|
|
34
|
-
*/ async ensureRepositoryInitialized() {
|
|
23
|
+
async ensureRepositoryInitialized() {
|
|
35
24
|
if (!this.repositoryInitialized) {
|
|
36
25
|
const entity = this.resolveEntity();
|
|
37
26
|
const provider = this.getDataSourceProvider();
|
|
@@ -39,26 +28,7 @@ export class RequestScopedApiService extends ApiService {
|
|
|
39
28
|
this.repositoryInitialized = true;
|
|
40
29
|
}
|
|
41
30
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Helper method to initialize additional repositories
|
|
44
|
-
* Useful when service needs multiple repositories beyond the primary entity repository
|
|
45
|
-
*
|
|
46
|
-
* @param entities - Array of entity classes to load repositories for
|
|
47
|
-
* @returns Array of initialized repositories in the same order as entities
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* ```typescript
|
|
51
|
-
* protected override async ensureRepositoryInitialized(): Promise<void> {
|
|
52
|
-
* await super.ensureRepositoryInitialized();
|
|
53
|
-
*
|
|
54
|
-
* if (this.configService.isCompanyFeatureEnabled() && !this.permissionRepository) {
|
|
55
|
-
* [this.permissionRepository] = await this.initializeAdditionalRepositories([
|
|
56
|
-
* UserCompanyPermission
|
|
57
|
-
* ]);
|
|
58
|
-
* }
|
|
59
|
-
* }
|
|
60
|
-
* ```
|
|
61
|
-
*/ async initializeAdditionalRepositories(entities) {
|
|
31
|
+
/** Initialize additional repositories beyond the primary entity */ async initializeAdditionalRepositories(entities) {
|
|
62
32
|
const provider = this.getDataSourceProvider();
|
|
63
33
|
const repositories = [];
|
|
64
34
|
for (const entity of entities){
|
|
@@ -66,31 +36,12 @@ export class RequestScopedApiService extends ApiService {
|
|
|
66
36
|
}
|
|
67
37
|
return repositories;
|
|
68
38
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Helper method to get the DataSource
|
|
71
|
-
* Useful when service needs direct DataSource access for transactions or custom queries
|
|
72
|
-
*
|
|
73
|
-
* @returns DataSource instance
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```typescript
|
|
77
|
-
* protected override async ensureRepositoryInitialized(): Promise<void> {
|
|
78
|
-
* await super.ensureRepositoryInitialized();
|
|
79
|
-
*
|
|
80
|
-
* if (!this.dataSource) {
|
|
81
|
-
* this.dataSource = await this.getDataSourceForService();
|
|
82
|
-
* }
|
|
83
|
-
* }
|
|
84
|
-
* ```
|
|
85
|
-
*/ async getDataSourceForService() {
|
|
39
|
+
/** Get DataSource for direct access (transactions, custom queries) */ async getDataSourceForService() {
|
|
86
40
|
const provider = this.getDataSourceProvider();
|
|
87
41
|
return await provider.getDataSource();
|
|
88
42
|
}
|
|
89
43
|
constructor(...args){
|
|
90
|
-
super(...args),
|
|
91
|
-
* Tracks whether repository has been initialized
|
|
92
|
-
* Automatically managed by ensureRepositoryInitialized()
|
|
93
|
-
*/ _define_property(this, "repositoryInitialized", false);
|
|
44
|
+
super(...args), _define_property(this, "repositoryInitialized", false);
|
|
94
45
|
}
|
|
95
46
|
}
|
|
96
47
|
RequestScopedApiService = _ts_decorate([
|
|
@@ -20,9 +20,7 @@ import * as TransportModule from 'winston-transport';
|
|
|
20
20
|
const Transport = TransportModule.default || TransportModule;
|
|
21
21
|
import * as DailyRotateFileModule from 'winston-daily-rotate-file';
|
|
22
22
|
const DailyRotateFile = DailyRotateFileModule.default || DailyRotateFileModule;
|
|
23
|
-
//
|
|
24
|
-
// CONFIGURATION (from envConfig)
|
|
25
|
-
// =============================================================================
|
|
23
|
+
// Configuration
|
|
26
24
|
const logConfig = envConfig.getLogConfig();
|
|
27
25
|
const LOG_DIR = logConfig.dir;
|
|
28
26
|
const LOG_LEVEL = logConfig.level;
|
|
@@ -35,9 +33,7 @@ if (!existsSync(LOG_DIR)) {
|
|
|
35
33
|
recursive: true
|
|
36
34
|
});
|
|
37
35
|
}
|
|
38
|
-
//
|
|
39
|
-
// CUSTOM FORMATS
|
|
40
|
-
// =============================================================================
|
|
36
|
+
// Custom Formats
|
|
41
37
|
/**
|
|
42
38
|
* Custom format for structured logging
|
|
43
39
|
* Includes: timestamp, level, context, requestId, userId, message, metadata
|
|
@@ -67,9 +63,7 @@ if (!existsSync(LOG_DIR)) {
|
|
|
67
63
|
const stackTrace = stack ? `\n${stack}` : '';
|
|
68
64
|
return `${timestamp} [${level.toUpperCase().padEnd(7)}] [${ctx}]${endpoint}${status}${time}${reqId}${user} ${message}${stackTrace}`;
|
|
69
65
|
});
|
|
70
|
-
//
|
|
71
|
-
// TRANSPORTS
|
|
72
|
-
// =============================================================================
|
|
66
|
+
// Transports
|
|
73
67
|
/**
|
|
74
68
|
* Daily rotating file transport for all logs
|
|
75
69
|
*/ const combinedRotateTransport = new DailyRotateFile({
|
|
@@ -90,9 +84,7 @@ if (!existsSync(LOG_DIR)) {
|
|
|
90
84
|
maxFiles: LOG_MAX_FILES,
|
|
91
85
|
level: 'error'
|
|
92
86
|
});
|
|
93
|
-
//
|
|
94
|
-
// TENANT-AWARE TRANSPORT
|
|
95
|
-
// =============================================================================
|
|
87
|
+
// Tenant-Aware Transport
|
|
96
88
|
/**
|
|
97
89
|
* Cache for tenant-specific transports
|
|
98
90
|
* Avoids creating new transport instances for each log entry
|
|
@@ -158,9 +150,7 @@ if (!existsSync(LOG_DIR)) {
|
|
|
158
150
|
stack: true
|
|
159
151
|
}), devConsoleFormat)
|
|
160
152
|
});
|
|
161
|
-
//
|
|
162
|
-
// LOGGER INSTANCES
|
|
163
|
-
// =============================================================================
|
|
153
|
+
// Logger Instances
|
|
164
154
|
/**
|
|
165
155
|
* Development logger configuration
|
|
166
156
|
* - Console output with colors
|
|
@@ -198,9 +188,7 @@ if (!existsSync(LOG_DIR)) {
|
|
|
198
188
|
],
|
|
199
189
|
exitOnError: false
|
|
200
190
|
});
|
|
201
|
-
//
|
|
202
|
-
// EXPORTS
|
|
203
|
-
// =============================================================================
|
|
191
|
+
// Exports
|
|
204
192
|
/**
|
|
205
193
|
* Winston logger instance
|
|
206
194
|
* - DEV: Console output with colors
|
package/fesm/constants/index.js
CHANGED
|
@@ -1,29 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// =============================================================================
|
|
16
|
-
/** Injection token for cache instance */ export const CACHE_INSTANCE = 'CACHE_INSTANCE';
|
|
17
|
-
/** Injection token for permission guard configuration */ export const PERMISSION_GUARD_CONFIG = 'PERMISSION_GUARD_CONFIG';
|
|
18
|
-
/** Injection token for logger instance */ export const LOGGER_INSTANCE = 'LOGGER_INSTANCE';
|
|
19
|
-
// =============================================================================
|
|
20
|
-
// HEADER NAMES
|
|
21
|
-
// =============================================================================
|
|
22
|
-
/** Header for idempotency key */ export const IDEMPOTENCY_KEY_HEADER = 'x-idempotency-key';
|
|
23
|
-
/** Header for request ID (correlation) */ export const REQUEST_ID_HEADER = 'x-request-id';
|
|
24
|
-
/** Header for client type (browser, mobile, api) */ export const CLIENT_TYPE_HEADER = 'x-client-type';
|
|
25
|
-
// =============================================================================
|
|
26
|
-
// CACHE KEY PREFIXES (Generic - usable by any package)
|
|
27
|
-
// =============================================================================
|
|
28
|
-
/** Cache key prefix for user permissions (generic, not auth-specific) */ export const PERMISSIONS_CACHE_PREFIX = 'permissions';
|
|
29
|
-
/** Cache key prefix for idempotency */ export const IDEMPOTENCY_CACHE_PREFIX = 'idempotency';
|
|
1
|
+
// Metadata keys
|
|
2
|
+
export const IS_PUBLIC_KEY = 'isPublic';
|
|
3
|
+
export const PERMISSIONS_KEY = 'permissions';
|
|
4
|
+
// Injection tokens
|
|
5
|
+
export const CACHE_INSTANCE = 'CACHE_INSTANCE';
|
|
6
|
+
export const PERMISSION_GUARD_CONFIG = 'PERMISSION_GUARD_CONFIG';
|
|
7
|
+
export const LOGGER_INSTANCE = 'LOGGER_INSTANCE';
|
|
8
|
+
// Header names
|
|
9
|
+
export const IDEMPOTENCY_KEY_HEADER = 'x-idempotency-key';
|
|
10
|
+
export const REQUEST_ID_HEADER = 'x-request-id';
|
|
11
|
+
export const CLIENT_TYPE_HEADER = 'x-client-type';
|
|
12
|
+
// Cache key prefixes
|
|
13
|
+
export const PERMISSIONS_CACHE_PREFIX = 'permissions';
|
|
14
|
+
export const IDEMPOTENCY_CACHE_PREFIX = 'idempotency';
|