@flusys/nestjs-shared 1.1.0 → 1.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -1103
- package/cjs/classes/api-controller.class.js +320 -29
- package/cjs/classes/api-service.class.js +219 -64
- package/cjs/classes/handle-custome-db.js +37 -0
- package/cjs/classes/index.js +1 -2
- package/cjs/classes/winston.logger.class.js +103 -70
- package/cjs/constants/index.js +1 -4
- package/cjs/constants/message-keys.js +68 -0
- package/cjs/constants/permissions.js +114 -11
- package/cjs/decorators/api-response.decorator.js +1 -2
- package/cjs/decorators/index.js +1 -1
- package/cjs/decorators/log-action.decorator.js +149 -0
- package/cjs/decorators/require-permission.decorator.js +5 -5
- package/cjs/dtos/get-by-ids.dto.js +65 -0
- package/cjs/dtos/identity-response.dto.js +4 -8
- package/cjs/dtos/index.js +1 -0
- package/cjs/dtos/response-payload.dto.js +72 -0
- package/cjs/entities/identity.js +8 -2
- package/cjs/entities/index.js +1 -0
- package/cjs/entities/raw-type.js +4 -0
- package/cjs/entities/user-root.js +2 -1
- package/cjs/enums/index.js +20 -0
- package/cjs/enums/notification-type.enum.js +17 -0
- package/cjs/enums/participant-status.enum.js +17 -0
- package/cjs/enums/recurrence-type.enum.js +18 -0
- package/cjs/exceptions/base-app.exception.js +151 -0
- package/cjs/exceptions/index.js +1 -0
- package/cjs/exceptions/permission.exception.js +13 -9
- package/cjs/filters/global-exception.filter.js +279 -0
- package/cjs/filters/index.js +18 -0
- package/cjs/guards/jwt-auth.guard.js +4 -1
- package/cjs/guards/permission.guard.js +71 -77
- package/cjs/index.js +2 -0
- package/cjs/interceptors/idempotency.interceptor.js +1 -1
- package/cjs/interceptors/index.js +0 -1
- package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
- package/cjs/interfaces/index.js +2 -1
- package/cjs/interfaces/notification-adapter.interface.js +11 -0
- package/cjs/interfaces/permission.interface.js +1 -1
- package/cjs/middlewares/logger.middleware.js +83 -26
- package/cjs/modules/datasource/index.js +0 -1
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +46 -85
- package/cjs/modules/utils/utils.service.js +4 -20
- package/cjs/utils/date-time.util.js +94 -0
- package/cjs/utils/index.js +1 -1
- package/cjs/utils/query-helpers.util.js +8 -1
- package/cjs/utils/request.util.js +25 -0
- package/classes/api-controller.class.d.ts +14 -6
- package/classes/api-service.class.d.ts +25 -22
- package/classes/handle-custome-db.d.ts +3 -0
- package/classes/index.d.ts +1 -2
- package/classes/winston.logger.class.d.ts +1 -0
- package/constants/index.d.ts +1 -1
- package/constants/message-keys.d.ts +45 -0
- package/constants/permissions.d.ts +127 -0
- package/decorators/api-response.decorator.d.ts +1 -1
- package/decorators/current-user.decorator.d.ts +1 -1
- package/decorators/index.d.ts +1 -1
- package/decorators/log-action.decorator.d.ts +8 -0
- package/decorators/require-permission.decorator.d.ts +2 -2
- package/dtos/get-by-ids.dto.d.ts +4 -0
- package/dtos/index.d.ts +1 -0
- package/dtos/response-payload.dto.d.ts +8 -0
- package/entities/index.d.ts +1 -0
- package/entities/raw-type.d.ts +1 -0
- package/enums/index.d.ts +3 -0
- package/enums/notification-type.enum.d.ts +6 -0
- package/enums/participant-status.enum.d.ts +6 -0
- package/enums/recurrence-type.enum.d.ts +7 -0
- package/exceptions/base-app.exception.d.ts +41 -0
- package/exceptions/index.d.ts +1 -0
- package/exceptions/permission.exception.d.ts +2 -2
- package/fesm/classes/api-controller.class.js +321 -30
- package/fesm/classes/api-service.class.js +222 -67
- package/fesm/classes/handle-custome-db.js +19 -0
- package/fesm/classes/index.js +1 -2
- package/fesm/classes/winston.logger.class.js +100 -68
- package/fesm/constants/index.js +2 -1
- package/fesm/constants/message-keys.js +49 -0
- package/fesm/constants/permissions.js +86 -14
- package/fesm/decorators/api-response.decorator.js +2 -20
- package/fesm/decorators/index.js +1 -1
- package/fesm/decorators/log-action.decorator.js +139 -0
- package/fesm/decorators/require-permission.decorator.js +17 -65
- package/fesm/dtos/get-by-ids.dto.js +55 -0
- package/fesm/dtos/identity-response.dto.js +4 -8
- package/fesm/dtos/index.js +1 -0
- package/fesm/dtos/response-payload.dto.js +72 -0
- package/fesm/entities/identity.js +8 -2
- package/fesm/entities/index.js +1 -0
- package/fesm/entities/raw-type.js +1 -0
- package/fesm/entities/user-root.js +2 -1
- package/fesm/enums/index.js +3 -0
- package/fesm/enums/notification-type.enum.js +7 -0
- package/fesm/enums/participant-status.enum.js +7 -0
- package/fesm/enums/recurrence-type.enum.js +8 -0
- package/fesm/exceptions/base-app.exception.js +115 -0
- package/fesm/exceptions/index.js +1 -0
- package/fesm/exceptions/permission.exception.js +16 -18
- package/fesm/filters/global-exception.filter.js +269 -0
- package/fesm/filters/index.js +1 -0
- package/fesm/guards/jwt-auth.guard.js +5 -2
- package/fesm/guards/permission.guard.js +73 -79
- package/fesm/index.js +2 -0
- package/fesm/interceptors/idempotency.interceptor.js +2 -2
- package/fesm/interceptors/index.js +0 -1
- package/fesm/interfaces/datasource.interface.js +1 -20
- package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
- package/fesm/interfaces/index.js +2 -1
- package/fesm/interfaces/notification-adapter.interface.js +1 -0
- package/fesm/interfaces/permission.interface.js +1 -3
- package/fesm/middlewares/logger.middleware.js +83 -26
- package/fesm/modules/datasource/index.js +0 -1
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +47 -86
- package/fesm/modules/utils/utils.service.js +5 -21
- package/fesm/utils/date-time.util.js +70 -0
- package/fesm/utils/index.js +1 -1
- package/fesm/utils/query-helpers.util.js +8 -1
- package/fesm/utils/request.util.js +22 -0
- package/filters/global-exception.filter.d.ts +9 -0
- package/filters/index.d.ts +1 -0
- package/guards/permission.guard.d.ts +3 -7
- package/index.d.ts +2 -0
- package/interceptors/idempotency.interceptor.d.ts +1 -1
- package/interceptors/index.d.ts +0 -1
- package/interfaces/api.interface.d.ts +2 -0
- package/interfaces/event-manager-adapter.interface.d.ts +43 -0
- package/interfaces/index.d.ts +2 -1
- package/interfaces/notification-adapter.interface.d.ts +21 -0
- package/interfaces/permission.interface.d.ts +13 -8
- package/modules/datasource/index.d.ts +0 -1
- package/modules/datasource/multi-tenant-datasource.service.d.ts +5 -15
- package/modules/utils/utils.service.d.ts +0 -1
- package/package.json +21 -14
- package/utils/date-time.util.d.ts +8 -0
- package/utils/index.d.ts +1 -1
- package/utils/request.util.d.ts +2 -0
- package/cjs/classes/request-scoped-api.service.js +0 -61
- package/cjs/classes/winston-logger-adapter.class.js +0 -104
- package/cjs/decorators/sanitize-html.decorator.js +0 -36
- package/cjs/interceptors/query-performance.interceptor.js +0 -66
- package/cjs/interfaces/logger.interface.js +0 -7
- package/cjs/modules/datasource/datasource.module.js +0 -117
- package/cjs/utils/error-handler.util.js +0 -90
- package/classes/request-scoped-api.service.d.ts +0 -14
- package/classes/winston-logger-adapter.class.d.ts +0 -22
- package/decorators/sanitize-html.decorator.d.ts +0 -2
- package/fesm/classes/request-scoped-api.service.js +0 -51
- package/fesm/classes/winston-logger-adapter.class.js +0 -107
- package/fesm/decorators/sanitize-html.decorator.js +0 -45
- package/fesm/interceptors/query-performance.interceptor.js +0 -56
- package/fesm/interfaces/logger.interface.js +0 -4
- package/fesm/modules/datasource/datasource.module.js +0 -107
- package/fesm/utils/error-handler.util.js +0 -82
- package/interceptors/query-performance.interceptor.d.ts +0 -8
- package/interfaces/logger.interface.d.ts +0 -7
- package/modules/datasource/datasource.module.d.ts +0 -19
- package/utils/error-handler.util.d.ts +0 -19
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "ErrorHandler", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return ErrorHandler;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
/** Sensitive keys that should be redacted from logs */ const SENSITIVE_KEYS = [
|
|
12
|
-
'password',
|
|
13
|
-
'secret',
|
|
14
|
-
'token',
|
|
15
|
-
'apiKey',
|
|
16
|
-
'credential',
|
|
17
|
-
'authorization'
|
|
18
|
-
];
|
|
19
|
-
let ErrorHandler = class ErrorHandler {
|
|
20
|
-
/**
|
|
21
|
-
* Safely extract error message from unknown error.
|
|
22
|
-
*/ static getErrorMessage(error) {
|
|
23
|
-
if (error instanceof Error) {
|
|
24
|
-
return error.message;
|
|
25
|
-
}
|
|
26
|
-
if (typeof error === 'string') {
|
|
27
|
-
return error;
|
|
28
|
-
}
|
|
29
|
-
return 'Unknown error occurred';
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Sanitize context data to redact sensitive fields from logs.
|
|
33
|
-
*/ static sanitizeContextForLogging(context) {
|
|
34
|
-
const sanitized = {};
|
|
35
|
-
for (const [key, value] of Object.entries(context)){
|
|
36
|
-
const isSensitive = SENSITIVE_KEYS.some((sk)=>key.toLowerCase().includes(sk.toLowerCase()));
|
|
37
|
-
if (isSensitive) {
|
|
38
|
-
sanitized[key] = '[REDACTED]';
|
|
39
|
-
} else if (Array.isArray(value)) {
|
|
40
|
-
sanitized[key] = value.map((item)=>typeof item === 'object' && item !== null ? this.sanitizeContextForLogging(item) : item);
|
|
41
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
42
|
-
sanitized[key] = this.sanitizeContextForLogging(value);
|
|
43
|
-
} else {
|
|
44
|
-
sanitized[key] = value;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return sanitized;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Create error context object for internal logging.
|
|
51
|
-
*/ static createErrorContext(error, context) {
|
|
52
|
-
const errorContext = {
|
|
53
|
-
error: {
|
|
54
|
-
message: this.getErrorMessage(error)
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
if (error instanceof Error) {
|
|
58
|
-
errorContext.error.stack = error.stack;
|
|
59
|
-
errorContext.error.name = error.name;
|
|
60
|
-
}
|
|
61
|
-
if (context && Object.keys(context).length > 0) {
|
|
62
|
-
errorContext.context = this.sanitizeContextForLogging(context);
|
|
63
|
-
}
|
|
64
|
-
return errorContext;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Log error with consistent format.
|
|
68
|
-
*/ static logError(logger, error, operation, context) {
|
|
69
|
-
const errorContext = this.createErrorContext(error, {
|
|
70
|
-
operation,
|
|
71
|
-
...context
|
|
72
|
-
});
|
|
73
|
-
const errorMessage = `Failed to ${operation}: ${errorContext.error.message}`;
|
|
74
|
-
logger.error(errorMessage, errorContext.error.stack, errorContext);
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Re-throw error with proper type checking.
|
|
78
|
-
*/ static rethrowError(error) {
|
|
79
|
-
if (error instanceof Error) {
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
throw new Error(`Unexpected error: ${String(error)}`);
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Log error and re-throw (common pattern).
|
|
86
|
-
*/ static logAndRethrow(logger, error, operation, context) {
|
|
87
|
-
this.logError(logger, error, operation, context);
|
|
88
|
-
this.rethrowError(error);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { DataSource, EntityTarget, Repository } from 'typeorm';
|
|
2
|
-
import { Identity } from '../entities';
|
|
3
|
-
import { IDataSourceProvider } from '../interfaces';
|
|
4
|
-
import { ApiService } from './api-service.class';
|
|
5
|
-
export declare abstract class RequestScopedApiService<CreateDtoT extends object, UpdateDtoT extends {
|
|
6
|
-
id: string;
|
|
7
|
-
}, InterfaceT extends Identity, EntityT extends Identity, RepositoryT extends Repository<EntityT>> extends ApiService<CreateDtoT, UpdateDtoT, InterfaceT, EntityT, RepositoryT> {
|
|
8
|
-
private repositoryInitialized;
|
|
9
|
-
protected abstract resolveEntity(): EntityTarget<EntityT>;
|
|
10
|
-
protected abstract getDataSourceProvider(): IDataSourceProvider;
|
|
11
|
-
protected ensureRepositoryInitialized(): Promise<void>;
|
|
12
|
-
protected initializeAdditionalRepositories(entities: EntityTarget<any>[]): Promise<Repository<any>[]>;
|
|
13
|
-
protected getDataSourceForService(): Promise<DataSource>;
|
|
14
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Logger } from '@nestjs/common';
|
|
2
|
-
import { ILogger } from '../interfaces/logger.interface';
|
|
3
|
-
export declare class WinstonLoggerAdapter implements ILogger {
|
|
4
|
-
private readonly context?;
|
|
5
|
-
constructor(context?: string);
|
|
6
|
-
private buildLogMeta;
|
|
7
|
-
log(message: string, context?: string, ...args: any[]): void;
|
|
8
|
-
error(message: string, trace?: string, context?: string, ...args: any[]): void;
|
|
9
|
-
warn(message: string, context?: string, ...args: any[]): void;
|
|
10
|
-
debug(message: string, context?: string, ...args: any[]): void;
|
|
11
|
-
verbose(message: string, context?: string, ...args: any[]): void;
|
|
12
|
-
}
|
|
13
|
-
export declare class NestLoggerAdapter implements ILogger {
|
|
14
|
-
private readonly logger;
|
|
15
|
-
constructor(logger: Logger);
|
|
16
|
-
private formatMessage;
|
|
17
|
-
log(message: string, context?: string, ...args: any[]): void;
|
|
18
|
-
error(message: string, trace?: string, context?: string, ...args: any[]): void;
|
|
19
|
-
warn(message: string, context?: string, ...args: any[]): void;
|
|
20
|
-
debug(message: string, context?: string, ...args: any[]): void;
|
|
21
|
-
verbose(message: string, context?: string, ...args: any[]): void;
|
|
22
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
function _define_property(obj, key, value) {
|
|
2
|
-
if (key in obj) {
|
|
3
|
-
Object.defineProperty(obj, key, {
|
|
4
|
-
value: value,
|
|
5
|
-
enumerable: true,
|
|
6
|
-
configurable: true,
|
|
7
|
-
writable: true
|
|
8
|
-
});
|
|
9
|
-
} else {
|
|
10
|
-
obj[key] = value;
|
|
11
|
-
}
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
-
}
|
|
20
|
-
import { Injectable, Scope } from '@nestjs/common';
|
|
21
|
-
import { ApiService } from './api-service.class';
|
|
22
|
-
export class RequestScopedApiService extends ApiService {
|
|
23
|
-
async ensureRepositoryInitialized() {
|
|
24
|
-
if (!this.repositoryInitialized) {
|
|
25
|
-
const entity = this.resolveEntity();
|
|
26
|
-
const provider = this.getDataSourceProvider();
|
|
27
|
-
this.repository = await provider.getRepository(entity);
|
|
28
|
-
this.repositoryInitialized = true;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/** Initialize additional repositories beyond the primary entity */ async initializeAdditionalRepositories(entities) {
|
|
32
|
-
const provider = this.getDataSourceProvider();
|
|
33
|
-
const repositories = [];
|
|
34
|
-
for (const entity of entities){
|
|
35
|
-
repositories.push(await provider.getRepository(entity));
|
|
36
|
-
}
|
|
37
|
-
return repositories;
|
|
38
|
-
}
|
|
39
|
-
/** Get DataSource for direct access (transactions, custom queries) */ async getDataSourceForService() {
|
|
40
|
-
const provider = this.getDataSourceProvider();
|
|
41
|
-
return await provider.getDataSource();
|
|
42
|
-
}
|
|
43
|
-
constructor(...args){
|
|
44
|
-
super(...args), _define_property(this, "repositoryInitialized", false);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
RequestScopedApiService = _ts_decorate([
|
|
48
|
-
Injectable({
|
|
49
|
-
scope: Scope.REQUEST
|
|
50
|
-
})
|
|
51
|
-
], RequestScopedApiService);
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
function _define_property(obj, key, value) {
|
|
2
|
-
if (key in obj) {
|
|
3
|
-
Object.defineProperty(obj, key, {
|
|
4
|
-
value: value,
|
|
5
|
-
enumerable: true,
|
|
6
|
-
configurable: true,
|
|
7
|
-
writable: true
|
|
8
|
-
});
|
|
9
|
-
} else {
|
|
10
|
-
obj[key] = value;
|
|
11
|
-
}
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
import { instance as winstonLogger } from './winston.logger.class';
|
|
15
|
-
import { getRequestId, getUserId, getTenantId, getCompanyId } from '../middlewares/logger.middleware';
|
|
16
|
-
/**
|
|
17
|
-
* Get correlation context for logging
|
|
18
|
-
* Automatically includes requestId, userId, tenantId, companyId if available
|
|
19
|
-
*/ function getCorrelationMeta() {
|
|
20
|
-
const meta = {};
|
|
21
|
-
const requestId = getRequestId();
|
|
22
|
-
const userId = getUserId();
|
|
23
|
-
const tenantId = getTenantId();
|
|
24
|
-
const companyId = getCompanyId();
|
|
25
|
-
if (requestId) meta.requestId = requestId;
|
|
26
|
-
if (userId) meta.userId = userId;
|
|
27
|
-
if (tenantId) meta.tenantId = tenantId;
|
|
28
|
-
if (companyId) meta.companyId = companyId;
|
|
29
|
-
return meta;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Winston Logger Adapter
|
|
33
|
-
* Adapts Winston logger to ILogger interface for dependency injection
|
|
34
|
-
*
|
|
35
|
-
* Features:
|
|
36
|
-
* - Automatic correlation ID injection
|
|
37
|
-
* - User ID tracking
|
|
38
|
-
* - Structured metadata logging
|
|
39
|
-
*
|
|
40
|
-
* Usage:
|
|
41
|
-
* ```typescript
|
|
42
|
-
* const logger = new WinstonLoggerAdapter('MyService');
|
|
43
|
-
* logger.log('Operation completed', undefined, { orderId: '123' });
|
|
44
|
-
* // Output: { requestId: 'uuid', userId: 'user-id', context: 'MyService', message: 'Operation completed', orderId: '123' }
|
|
45
|
-
* ```
|
|
46
|
-
*/ export class WinstonLoggerAdapter {
|
|
47
|
-
buildLogMeta(context, args, extra) {
|
|
48
|
-
const meta = args?.length && typeof args[0] === 'object' ? args[0] : {};
|
|
49
|
-
return {
|
|
50
|
-
context: context || this.context,
|
|
51
|
-
...getCorrelationMeta(),
|
|
52
|
-
...meta,
|
|
53
|
-
...extra
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
log(message, context, ...args) {
|
|
57
|
-
winstonLogger.info(message, this.buildLogMeta(context, args));
|
|
58
|
-
}
|
|
59
|
-
error(message, trace, context, ...args) {
|
|
60
|
-
winstonLogger.error(message, this.buildLogMeta(context, args, {
|
|
61
|
-
stack: trace
|
|
62
|
-
}));
|
|
63
|
-
}
|
|
64
|
-
warn(message, context, ...args) {
|
|
65
|
-
winstonLogger.warn(message, this.buildLogMeta(context, args));
|
|
66
|
-
}
|
|
67
|
-
debug(message, context, ...args) {
|
|
68
|
-
winstonLogger.debug(message, this.buildLogMeta(context, args));
|
|
69
|
-
}
|
|
70
|
-
verbose(message, context, ...args) {
|
|
71
|
-
winstonLogger.verbose(message, this.buildLogMeta(context, args));
|
|
72
|
-
}
|
|
73
|
-
constructor(context){
|
|
74
|
-
_define_property(this, "context", void 0);
|
|
75
|
-
this.context = context;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* NestJS Logger Adapter
|
|
80
|
-
* Adapts NestJS Logger to ILogger interface
|
|
81
|
-
*
|
|
82
|
-
* Use this when you need to use NestJS's built-in logger
|
|
83
|
-
* instead of Winston (e.g., for testing)
|
|
84
|
-
*/ export class NestLoggerAdapter {
|
|
85
|
-
formatMessage(message, args) {
|
|
86
|
-
return args?.length ? `${message} ${JSON.stringify(args)}` : message;
|
|
87
|
-
}
|
|
88
|
-
log(message, context, ...args) {
|
|
89
|
-
this.logger.log(this.formatMessage(message, args), context);
|
|
90
|
-
}
|
|
91
|
-
error(message, trace, context, ...args) {
|
|
92
|
-
this.logger.error(this.formatMessage(message, args), trace, context);
|
|
93
|
-
}
|
|
94
|
-
warn(message, context, ...args) {
|
|
95
|
-
this.logger.warn(this.formatMessage(message, args), context);
|
|
96
|
-
}
|
|
97
|
-
debug(message, context, ...args) {
|
|
98
|
-
this.logger.debug(this.formatMessage(message, args), context);
|
|
99
|
-
}
|
|
100
|
-
verbose(message, context, ...args) {
|
|
101
|
-
this.logger.verbose(this.formatMessage(message, args), context);
|
|
102
|
-
}
|
|
103
|
-
constructor(logger){
|
|
104
|
-
_define_property(this, "logger", void 0);
|
|
105
|
-
this.logger = logger;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { Transform } from 'class-transformer';
|
|
2
|
-
import { escapeHtml } from '../utils/html-sanitizer.util';
|
|
3
|
-
/**
|
|
4
|
-
* Decorator that sanitizes HTML content in string fields to prevent XSS attacks.
|
|
5
|
-
* Applies HTML escaping during transformation (when class-transformer processes the DTO).
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```typescript
|
|
9
|
-
* class CreateCommentDto {
|
|
10
|
-
* @SanitizeHtml()
|
|
11
|
-
* @IsString()
|
|
12
|
-
* content: string;
|
|
13
|
-
* }
|
|
14
|
-
* ```
|
|
15
|
-
*
|
|
16
|
-
* Input: '<script>alert("xss")</script>'
|
|
17
|
-
* Output: '<script>alert("xss")</script>'
|
|
18
|
-
*/ export function SanitizeHtml() {
|
|
19
|
-
return Transform(({ value })=>{
|
|
20
|
-
if (typeof value === 'string') {
|
|
21
|
-
return escapeHtml(value);
|
|
22
|
-
}
|
|
23
|
-
return value;
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Decorator that sanitizes HTML and trims whitespace from string fields.
|
|
28
|
-
* Combines sanitization with trimming for cleaner input handling.
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```typescript
|
|
32
|
-
* class CreatePostDto {
|
|
33
|
-
* @SanitizeAndTrim()
|
|
34
|
-
* @IsString()
|
|
35
|
-
* title: string;
|
|
36
|
-
* }
|
|
37
|
-
* ```
|
|
38
|
-
*/ export function SanitizeAndTrim() {
|
|
39
|
-
return Transform(({ value })=>{
|
|
40
|
-
if (typeof value === 'string') {
|
|
41
|
-
return escapeHtml(value.trim());
|
|
42
|
-
}
|
|
43
|
-
return value;
|
|
44
|
-
});
|
|
45
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
function _define_property(obj, key, value) {
|
|
2
|
-
if (key in obj) {
|
|
3
|
-
Object.defineProperty(obj, key, {
|
|
4
|
-
value: value,
|
|
5
|
-
enumerable: true,
|
|
6
|
-
configurable: true,
|
|
7
|
-
writable: true
|
|
8
|
-
});
|
|
9
|
-
} else {
|
|
10
|
-
obj[key] = value;
|
|
11
|
-
}
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
-
}
|
|
20
|
-
function _ts_metadata(k, v) {
|
|
21
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
-
}
|
|
23
|
-
import { Injectable, Logger } from '@nestjs/common';
|
|
24
|
-
import { tap } from 'rxjs/operators';
|
|
25
|
-
export class QueryPerformanceInterceptor {
|
|
26
|
-
intercept(context, next) {
|
|
27
|
-
const request = context.switchToHttp().getRequest();
|
|
28
|
-
const { method, url } = request;
|
|
29
|
-
const handler = context.getHandler().name;
|
|
30
|
-
const controller = context.getClass().name;
|
|
31
|
-
const startTime = Date.now();
|
|
32
|
-
return next.handle().pipe(tap(()=>{
|
|
33
|
-
const executionTime = Date.now() - startTime;
|
|
34
|
-
if (executionTime > this.threshold) {
|
|
35
|
-
this.logger.warn(`Slow request detected: ${method} ${url} | ` + `Controller: ${controller}.${handler} | ` + `Execution time: ${executionTime}ms`);
|
|
36
|
-
} else {
|
|
37
|
-
// Optional: Log all requests in debug mode
|
|
38
|
-
this.logger.debug(`${method} ${url} | ${controller}.${handler} | ${executionTime}ms`);
|
|
39
|
-
}
|
|
40
|
-
}));
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* @param threshold - Time threshold in milliseconds (default: 1000ms)
|
|
44
|
-
*/ constructor(threshold = 1000){
|
|
45
|
-
_define_property(this, "logger", new Logger(QueryPerformanceInterceptor.name));
|
|
46
|
-
_define_property(this, "threshold", void 0);
|
|
47
|
-
this.threshold = threshold;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
QueryPerformanceInterceptor = _ts_decorate([
|
|
51
|
-
Injectable(),
|
|
52
|
-
_ts_metadata("design:type", Function),
|
|
53
|
-
_ts_metadata("design:paramtypes", [
|
|
54
|
-
Number
|
|
55
|
-
])
|
|
56
|
-
], QueryPerformanceInterceptor);
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
}
|
|
7
|
-
import { MODULE_OPTIONS } from '@flusys/nestjs-core';
|
|
8
|
-
import { Module } from '@nestjs/common';
|
|
9
|
-
import { MultiTenantDataSourceService } from './multi-tenant-datasource.service';
|
|
10
|
-
export class DataSourceModule {
|
|
11
|
-
/**
|
|
12
|
-
* Register with static options
|
|
13
|
-
*/ static forRoot(options) {
|
|
14
|
-
const { global = false, ...moduleOptions } = options;
|
|
15
|
-
return {
|
|
16
|
-
module: DataSourceModule,
|
|
17
|
-
global,
|
|
18
|
-
providers: [
|
|
19
|
-
{
|
|
20
|
-
provide: MODULE_OPTIONS,
|
|
21
|
-
useValue: moduleOptions
|
|
22
|
-
},
|
|
23
|
-
MultiTenantDataSourceService
|
|
24
|
-
],
|
|
25
|
-
exports: [
|
|
26
|
-
MODULE_OPTIONS,
|
|
27
|
-
MultiTenantDataSourceService
|
|
28
|
-
]
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Register with async options
|
|
33
|
-
*/ static forRootAsync(asyncOptions) {
|
|
34
|
-
const { global = false, imports = [] } = asyncOptions;
|
|
35
|
-
return {
|
|
36
|
-
module: DataSourceModule,
|
|
37
|
-
global,
|
|
38
|
-
imports,
|
|
39
|
-
providers: [
|
|
40
|
-
...this.createAsyncProviders(asyncOptions),
|
|
41
|
-
MultiTenantDataSourceService
|
|
42
|
-
],
|
|
43
|
-
exports: [
|
|
44
|
-
MODULE_OPTIONS,
|
|
45
|
-
MultiTenantDataSourceService
|
|
46
|
-
]
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Import for child modules (uses existing MODULE_OPTIONS)
|
|
51
|
-
*/ static forFeature() {
|
|
52
|
-
return {
|
|
53
|
-
module: DataSourceModule,
|
|
54
|
-
providers: [
|
|
55
|
-
MultiTenantDataSourceService
|
|
56
|
-
],
|
|
57
|
-
exports: [
|
|
58
|
-
MultiTenantDataSourceService
|
|
59
|
-
]
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Create async providers based on options
|
|
64
|
-
*/ static createAsyncProviders(options) {
|
|
65
|
-
if (options.useFactory) {
|
|
66
|
-
return [
|
|
67
|
-
{
|
|
68
|
-
provide: MODULE_OPTIONS,
|
|
69
|
-
useFactory: options.useFactory,
|
|
70
|
-
inject: options.inject || []
|
|
71
|
-
}
|
|
72
|
-
];
|
|
73
|
-
}
|
|
74
|
-
if (options.useClass) {
|
|
75
|
-
return [
|
|
76
|
-
{
|
|
77
|
-
provide: options.useClass,
|
|
78
|
-
useClass: options.useClass
|
|
79
|
-
},
|
|
80
|
-
this.createFactoryProvider(options.useClass)
|
|
81
|
-
];
|
|
82
|
-
}
|
|
83
|
-
if (options.useExisting) {
|
|
84
|
-
return [
|
|
85
|
-
this.createFactoryProvider(options.useExisting)
|
|
86
|
-
];
|
|
87
|
-
}
|
|
88
|
-
return [
|
|
89
|
-
{
|
|
90
|
-
provide: MODULE_OPTIONS,
|
|
91
|
-
useValue: {}
|
|
92
|
-
}
|
|
93
|
-
];
|
|
94
|
-
}
|
|
95
|
-
static createFactoryProvider(factoryClass) {
|
|
96
|
-
return {
|
|
97
|
-
provide: MODULE_OPTIONS,
|
|
98
|
-
useFactory: async (factory)=>factory.createOptions(),
|
|
99
|
-
inject: [
|
|
100
|
-
factoryClass
|
|
101
|
-
]
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
DataSourceModule = _ts_decorate([
|
|
106
|
-
Module({})
|
|
107
|
-
], DataSourceModule);
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/** Sensitive keys that should be redacted from logs */ const SENSITIVE_KEYS = [
|
|
2
|
-
'password',
|
|
3
|
-
'secret',
|
|
4
|
-
'token',
|
|
5
|
-
'apiKey',
|
|
6
|
-
'credential',
|
|
7
|
-
'authorization'
|
|
8
|
-
];
|
|
9
|
-
/**
|
|
10
|
-
* Error handling utility for consistent error logging and handling.
|
|
11
|
-
*/ export class ErrorHandler {
|
|
12
|
-
/**
|
|
13
|
-
* Safely extract error message from unknown error.
|
|
14
|
-
*/ static getErrorMessage(error) {
|
|
15
|
-
if (error instanceof Error) {
|
|
16
|
-
return error.message;
|
|
17
|
-
}
|
|
18
|
-
if (typeof error === 'string') {
|
|
19
|
-
return error;
|
|
20
|
-
}
|
|
21
|
-
return 'Unknown error occurred';
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Sanitize context data to redact sensitive fields from logs.
|
|
25
|
-
*/ static sanitizeContextForLogging(context) {
|
|
26
|
-
const sanitized = {};
|
|
27
|
-
for (const [key, value] of Object.entries(context)){
|
|
28
|
-
const isSensitive = SENSITIVE_KEYS.some((sk)=>key.toLowerCase().includes(sk.toLowerCase()));
|
|
29
|
-
if (isSensitive) {
|
|
30
|
-
sanitized[key] = '[REDACTED]';
|
|
31
|
-
} else if (Array.isArray(value)) {
|
|
32
|
-
sanitized[key] = value.map((item)=>typeof item === 'object' && item !== null ? this.sanitizeContextForLogging(item) : item);
|
|
33
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
34
|
-
sanitized[key] = this.sanitizeContextForLogging(value);
|
|
35
|
-
} else {
|
|
36
|
-
sanitized[key] = value;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return sanitized;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Create error context object for internal logging.
|
|
43
|
-
*/ static createErrorContext(error, context) {
|
|
44
|
-
const errorContext = {
|
|
45
|
-
error: {
|
|
46
|
-
message: this.getErrorMessage(error)
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
if (error instanceof Error) {
|
|
50
|
-
errorContext.error.stack = error.stack;
|
|
51
|
-
errorContext.error.name = error.name;
|
|
52
|
-
}
|
|
53
|
-
if (context && Object.keys(context).length > 0) {
|
|
54
|
-
errorContext.context = this.sanitizeContextForLogging(context);
|
|
55
|
-
}
|
|
56
|
-
return errorContext;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Log error with consistent format.
|
|
60
|
-
*/ static logError(logger, error, operation, context) {
|
|
61
|
-
const errorContext = this.createErrorContext(error, {
|
|
62
|
-
operation,
|
|
63
|
-
...context
|
|
64
|
-
});
|
|
65
|
-
const errorMessage = `Failed to ${operation}: ${errorContext.error.message}`;
|
|
66
|
-
logger.error(errorMessage, errorContext.error.stack, errorContext);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Re-throw error with proper type checking.
|
|
70
|
-
*/ static rethrowError(error) {
|
|
71
|
-
if (error instanceof Error) {
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
throw new Error(`Unexpected error: ${String(error)}`);
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Log error and re-throw (common pattern).
|
|
78
|
-
*/ static logAndRethrow(logger, error, operation, context) {
|
|
79
|
-
this.logError(logger, error, operation, context);
|
|
80
|
-
this.rethrowError(error);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
|
|
2
|
-
import { Observable } from 'rxjs';
|
|
3
|
-
export declare class QueryPerformanceInterceptor implements NestInterceptor {
|
|
4
|
-
private readonly logger;
|
|
5
|
-
private readonly threshold;
|
|
6
|
-
constructor(threshold?: number);
|
|
7
|
-
intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
|
|
8
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export interface ILogger {
|
|
2
|
-
log(message: string, context?: string, ...args: any[]): void;
|
|
3
|
-
error(message: string, trace?: string, context?: string, ...args: any[]): void;
|
|
4
|
-
warn(message: string, context?: string, ...args: any[]): void;
|
|
5
|
-
debug(message: string, context?: string, ...args: any[]): void;
|
|
6
|
-
verbose(message: string, context?: string, ...args: any[]): void;
|
|
7
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { IAsyncModuleOptions, IDataSourceServiceOptions, IModuleOptionsFactory } from '@flusys/nestjs-core';
|
|
2
|
-
import { DynamicModule, Type } from '@nestjs/common';
|
|
3
|
-
export interface DataSourceModuleOptions extends IDataSourceServiceOptions {
|
|
4
|
-
global?: boolean;
|
|
5
|
-
}
|
|
6
|
-
export interface DataSourceOptionsFactory extends IModuleOptionsFactory<IDataSourceServiceOptions> {
|
|
7
|
-
createOptions(): Promise<IDataSourceServiceOptions> | IDataSourceServiceOptions;
|
|
8
|
-
}
|
|
9
|
-
export interface DataSourceModuleAsyncOptions extends IAsyncModuleOptions<IDataSourceServiceOptions> {
|
|
10
|
-
useClass?: Type<DataSourceOptionsFactory>;
|
|
11
|
-
useExisting?: Type<DataSourceOptionsFactory>;
|
|
12
|
-
}
|
|
13
|
-
export declare class DataSourceModule {
|
|
14
|
-
static forRoot(options: DataSourceModuleOptions): DynamicModule;
|
|
15
|
-
static forRootAsync(asyncOptions: DataSourceModuleAsyncOptions): DynamicModule;
|
|
16
|
-
static forFeature(): DynamicModule;
|
|
17
|
-
private static createAsyncProviders;
|
|
18
|
-
private static createFactoryProvider;
|
|
19
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Logger } from '@nestjs/common';
|
|
2
|
-
export interface IErrorContext {
|
|
3
|
-
operation?: string;
|
|
4
|
-
entity?: string;
|
|
5
|
-
userId?: string;
|
|
6
|
-
id?: string;
|
|
7
|
-
companyId?: string;
|
|
8
|
-
branchId?: string;
|
|
9
|
-
sectionId?: string;
|
|
10
|
-
data?: Record<string, unknown>;
|
|
11
|
-
}
|
|
12
|
-
export declare class ErrorHandler {
|
|
13
|
-
static getErrorMessage(error: unknown): string;
|
|
14
|
-
private static sanitizeContextForLogging;
|
|
15
|
-
private static createErrorContext;
|
|
16
|
-
static logError(logger: Logger, error: unknown, operation: string, context?: Omit<IErrorContext, 'operation'>): void;
|
|
17
|
-
static rethrowError(error: unknown): never;
|
|
18
|
-
static logAndRethrow(logger: Logger, error: unknown, operation: string, context?: Omit<IErrorContext, 'operation'>): never;
|
|
19
|
-
}
|