@flusys/nestjs-shared 0.1.0-beta.3 → 1.0.0-rc
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 +106 -138
- package/cjs/classes/api-controller.class.js +9 -45
- package/cjs/classes/api-service.class.js +4 -41
- package/cjs/classes/index.js +1 -0
- package/cjs/classes/request-scoped-api.service.js +4 -53
- package/cjs/classes/winston.logger.class.js +5 -15
- package/cjs/constants/index.js +16 -11
- package/cjs/constants/permissions.js +174 -0
- package/cjs/decorators/api-response.decorator.js +1 -1
- package/cjs/decorators/index.js +1 -0
- package/cjs/decorators/sanitize-html.decorator.js +36 -0
- package/cjs/dtos/filter-and-pagination.dto.js +24 -34
- package/cjs/dtos/pagination.dto.js +4 -8
- package/cjs/dtos/response-payload.dto.js +35 -121
- package/cjs/entities/identity.js +4 -4
- package/cjs/entities/user-root.js +13 -14
- package/cjs/guards/permission.guard.js +39 -94
- package/cjs/interceptors/index.js +1 -0
- package/cjs/interceptors/set-create-by-on-body.interceptor.js +2 -30
- package/cjs/interceptors/set-delete-by-on-body.interceptor.js +2 -30
- package/cjs/interceptors/set-update-by-on-body.interceptor.js +2 -30
- package/cjs/interceptors/set-user-field-on-body.interceptor.js +43 -0
- package/cjs/interceptors/slug.interceptor.js +30 -9
- package/cjs/interfaces/datasource.interface.js +4 -0
- package/cjs/interfaces/index.js +2 -1
- package/cjs/interfaces/logged-user-info.interface.js +1 -2
- package/cjs/interfaces/module-config.interface.js +4 -0
- package/cjs/interfaces/permission.interface.js +1 -10
- package/cjs/middlewares/logger.middleware.js +2 -6
- package/cjs/modules/cache/cache.module.js +3 -3
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +31 -111
- package/cjs/modules/utils/utils.service.js +63 -145
- package/cjs/utils/error-handler.util.js +91 -13
- package/cjs/utils/html-sanitizer.util.js +74 -0
- package/cjs/utils/index.js +2 -0
- package/cjs/utils/query-helpers.util.js +53 -0
- package/classes/api-controller.class.d.ts +5 -5
- package/classes/api-service.class.d.ts +5 -5
- package/classes/index.d.ts +1 -0
- package/classes/request-scoped-api.service.d.ts +3 -2
- package/constants/index.d.ts +1 -0
- package/constants/permissions.d.ts +167 -0
- package/decorators/index.d.ts +1 -0
- package/decorators/sanitize-html.decorator.d.ts +2 -0
- package/dtos/filter-and-pagination.dto.d.ts +0 -2
- package/dtos/response-payload.dto.d.ts +0 -7
- package/fesm/classes/api-controller.class.js +10 -93
- package/fesm/classes/api-service.class.js +5 -46
- package/fesm/classes/index.js +2 -0
- package/fesm/classes/request-scoped-api.service.js +4 -53
- package/fesm/classes/winston.logger.class.js +6 -18
- package/fesm/constants/index.js +16 -29
- package/fesm/constants/permissions.js +121 -0
- package/fesm/decorators/api-response.decorator.js +1 -1
- package/fesm/decorators/index.js +1 -0
- package/fesm/decorators/sanitize-html.decorator.js +45 -0
- package/fesm/dtos/filter-and-pagination.dto.js +26 -47
- package/fesm/dtos/pagination.dto.js +4 -8
- package/fesm/dtos/response-payload.dto.js +39 -142
- package/fesm/entities/identity.js +4 -4
- package/fesm/entities/user-root.js +13 -14
- package/fesm/guards/permission.guard.js +39 -94
- package/fesm/interceptors/index.js +1 -0
- package/fesm/interceptors/set-create-by-on-body.interceptor.js +4 -30
- package/fesm/interceptors/set-delete-by-on-body.interceptor.js +4 -30
- package/fesm/interceptors/set-update-by-on-body.interceptor.js +4 -30
- package/fesm/interceptors/set-user-field-on-body.interceptor.js +36 -0
- package/fesm/interceptors/slug.interceptor.js +31 -10
- package/fesm/interfaces/datasource.interface.js +20 -0
- package/fesm/interfaces/index.js +2 -1
- package/fesm/interfaces/logged-user-info.interface.js +1 -2
- package/fesm/interfaces/module-config.interface.js +5 -0
- package/fesm/interfaces/permission.interface.js +0 -12
- package/fesm/middlewares/logger.middleware.js +2 -6
- package/fesm/modules/cache/cache.module.js +2 -2
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +31 -111
- package/fesm/modules/utils/utils.service.js +50 -143
- package/fesm/utils/error-handler.util.js +93 -14
- package/fesm/utils/html-sanitizer.util.js +82 -0
- package/fesm/utils/index.js +2 -0
- package/fesm/utils/query-helpers.util.js +78 -0
- package/interceptors/index.d.ts +1 -0
- package/interceptors/set-create-by-on-body.interceptor.d.ts +1 -5
- package/interceptors/set-delete-by-on-body.interceptor.d.ts +1 -5
- package/interceptors/set-update-by-on-body.interceptor.d.ts +1 -5
- package/interceptors/set-user-field-on-body.interceptor.d.ts +2 -0
- package/interceptors/slug.interceptor.d.ts +2 -1
- package/interfaces/api.interface.d.ts +2 -2
- package/interfaces/datasource.interface.d.ts +5 -0
- package/interfaces/identity.interface.d.ts +4 -4
- package/interfaces/index.d.ts +2 -1
- package/interfaces/module-config.interface.d.ts +6 -0
- package/interfaces/permission.interface.d.ts +0 -1
- package/modules/utils/utils.service.d.ts +10 -4
- package/package.json +4 -4
- package/utils/error-handler.util.d.ts +23 -13
- package/utils/html-sanitizer.util.d.ts +3 -0
- package/utils/index.d.ts +2 -0
- package/utils/query-helpers.util.d.ts +16 -0
- package/cjs/interfaces/base-query.interface.js +0 -6
- package/fesm/interfaces/base-query.interface.js +0 -3
- package/interfaces/base-query.interface.d.ts +0 -7
|
@@ -1,27 +1,104 @@
|
|
|
1
|
+
import { HttpStatus } from '@nestjs/common';
|
|
2
|
+
/** Check if running in production environment */ const IS_PRODUCTION = process.env.NODE_ENV === 'production';
|
|
3
|
+
/** Sensitive keys that should be redacted from logs */ const SENSITIVE_KEYS = [
|
|
4
|
+
'password',
|
|
5
|
+
'secret',
|
|
6
|
+
'token',
|
|
7
|
+
'apiKey',
|
|
8
|
+
'credential',
|
|
9
|
+
'authorization'
|
|
10
|
+
];
|
|
11
|
+
/** Patterns that indicate sensitive data in error messages */ const SENSITIVE_PATTERNS = [
|
|
12
|
+
/password/i,
|
|
13
|
+
/secret/i,
|
|
14
|
+
/token/i,
|
|
15
|
+
/key/i,
|
|
16
|
+
/credential/i,
|
|
17
|
+
/authorization/i,
|
|
18
|
+
/bearer/i
|
|
19
|
+
];
|
|
1
20
|
/**
|
|
2
|
-
* Error handling utility for consistent error logging and handling
|
|
21
|
+
* Error handling utility for consistent error logging and handling.
|
|
22
|
+
* Provides production-aware error sanitization to prevent sensitive data leakage.
|
|
3
23
|
*/ export class ErrorHandler {
|
|
4
24
|
/**
|
|
5
|
-
* Safely extract error message from unknown error
|
|
6
|
-
|
|
25
|
+
* Safely extract error message from unknown error.
|
|
26
|
+
* @param error - The error to extract message from
|
|
27
|
+
* @param sanitizeForClient - If true, redacts potentially sensitive info in production
|
|
28
|
+
*/ static getErrorMessage(error, sanitizeForClient = false) {
|
|
29
|
+
let message = 'Unknown error occurred';
|
|
7
30
|
if (error instanceof Error) {
|
|
8
|
-
|
|
31
|
+
message = error.message;
|
|
32
|
+
} else if (typeof error === 'string') {
|
|
33
|
+
message = error;
|
|
9
34
|
}
|
|
10
|
-
|
|
11
|
-
|
|
35
|
+
// Sanitize for client responses in production
|
|
36
|
+
if (sanitizeForClient && IS_PRODUCTION) {
|
|
37
|
+
if (this.containsSensitiveData(message)) {
|
|
38
|
+
return 'An unexpected error occurred. Please try again later.';
|
|
39
|
+
}
|
|
12
40
|
}
|
|
13
|
-
return
|
|
41
|
+
return message;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if a string contains potentially sensitive data.
|
|
45
|
+
*/ static containsSensitiveData(text) {
|
|
46
|
+
return SENSITIVE_PATTERNS.some((pattern)=>pattern.test(text));
|
|
14
47
|
}
|
|
15
48
|
/**
|
|
16
|
-
* Safely extract error stack from unknown error
|
|
17
|
-
|
|
49
|
+
* Safely extract error stack from unknown error.
|
|
50
|
+
* Returns undefined in production for client responses.
|
|
51
|
+
* @param error - The error to extract stack from
|
|
52
|
+
* @param forClient - If true, never returns stack in production
|
|
53
|
+
*/ static getErrorStack(error, forClient = false) {
|
|
54
|
+
// Never expose stack traces to clients in production
|
|
55
|
+
if (forClient && IS_PRODUCTION) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
18
58
|
if (error instanceof Error) {
|
|
19
59
|
return error.stack;
|
|
20
60
|
}
|
|
21
61
|
return undefined;
|
|
22
62
|
}
|
|
23
63
|
/**
|
|
24
|
-
* Create error
|
|
64
|
+
* Create a sanitized error response for clients.
|
|
65
|
+
* In production, sensitive data is redacted and stack traces removed.
|
|
66
|
+
*/ static createClientError(error, statusCode = HttpStatus.INTERNAL_SERVER_ERROR, code) {
|
|
67
|
+
const sanitizedError = {
|
|
68
|
+
message: this.getErrorMessage(error, true),
|
|
69
|
+
statusCode
|
|
70
|
+
};
|
|
71
|
+
if (code) {
|
|
72
|
+
sanitizedError.code = code;
|
|
73
|
+
}
|
|
74
|
+
// Include stack only in development
|
|
75
|
+
if (!IS_PRODUCTION) {
|
|
76
|
+
sanitizedError.stack = this.getErrorStack(error);
|
|
77
|
+
}
|
|
78
|
+
return sanitizedError;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Sanitize context data to redact sensitive fields from logs.
|
|
82
|
+
*/ static sanitizeContextForLogging(context) {
|
|
83
|
+
const sanitized = {};
|
|
84
|
+
for (const [key, value] of Object.entries(context)){
|
|
85
|
+
// Check if key contains sensitive words
|
|
86
|
+
const isSensitive = SENSITIVE_KEYS.some((sk)=>key.toLowerCase().includes(sk.toLowerCase()));
|
|
87
|
+
if (isSensitive) {
|
|
88
|
+
sanitized[key] = '[REDACTED]';
|
|
89
|
+
} else if (Array.isArray(value)) {
|
|
90
|
+
sanitized[key] = value.map((item)=>typeof item === 'object' && item !== null ? this.sanitizeContextForLogging(item) : item);
|
|
91
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
92
|
+
sanitized[key] = this.sanitizeContextForLogging(value);
|
|
93
|
+
} else {
|
|
94
|
+
sanitized[key] = value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return sanitized;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Create error context object for internal logging.
|
|
101
|
+
* Context data is sanitized to redact sensitive fields.
|
|
25
102
|
*/ static createErrorContext(error, context) {
|
|
26
103
|
const errorContext = {
|
|
27
104
|
error: {
|
|
@@ -33,20 +110,22 @@
|
|
|
33
110
|
errorContext.error.name = error.name;
|
|
34
111
|
}
|
|
35
112
|
if (context && Object.keys(context).length > 0) {
|
|
36
|
-
|
|
113
|
+
// Sanitize context to redact sensitive fields
|
|
114
|
+
errorContext.context = this.sanitizeContextForLogging(context);
|
|
37
115
|
}
|
|
38
116
|
return errorContext;
|
|
39
117
|
}
|
|
40
118
|
/**
|
|
41
|
-
* Log error with consistent format
|
|
119
|
+
* Log error with consistent format.
|
|
120
|
+
* Sensitive data in context is automatically redacted.
|
|
42
121
|
*/ static logError(logger, error, operation, context) {
|
|
43
122
|
const errorContext = this.createErrorContext(error, {
|
|
44
123
|
operation,
|
|
45
124
|
...context
|
|
46
125
|
});
|
|
47
126
|
const errorMessage = `Failed to ${operation}: ${errorContext.error.message}`;
|
|
48
|
-
|
|
49
|
-
logger.error(errorMessage, errorContext.error.stack,
|
|
127
|
+
// Log full details internally (stack traces are fine for internal logs)
|
|
128
|
+
logger.error(errorMessage, errorContext.error.stack, errorContext);
|
|
50
129
|
}
|
|
51
130
|
/**
|
|
52
131
|
* Re-throw error with proper type checking
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML Sanitizer Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for escaping HTML content to prevent XSS attacks.
|
|
5
|
+
* Use these utilities when interpolating user-provided variables into HTML content.
|
|
6
|
+
*/ /**
|
|
7
|
+
* HTML entity mapping for escaping special characters
|
|
8
|
+
*/ const HTML_ESCAPE_MAP = {
|
|
9
|
+
'&': '&',
|
|
10
|
+
'<': '<',
|
|
11
|
+
'>': '>',
|
|
12
|
+
'"': '"',
|
|
13
|
+
"'": ''',
|
|
14
|
+
'/': '/',
|
|
15
|
+
'`': '`',
|
|
16
|
+
'=': '='
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Regex pattern matching characters that need HTML escaping
|
|
20
|
+
*/ const HTML_ESCAPE_REGEX = /[&<>"'`=/]/g;
|
|
21
|
+
/**
|
|
22
|
+
* Escapes HTML special characters to prevent XSS attacks.
|
|
23
|
+
*
|
|
24
|
+
* @param str - The string to escape
|
|
25
|
+
* @returns The escaped string safe for HTML insertion
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* escapeHtml('<script>alert("xss")</script>')
|
|
30
|
+
* // Returns: '<script>alert("xss")</script>'
|
|
31
|
+
* ```
|
|
32
|
+
*/ export function escapeHtml(str) {
|
|
33
|
+
if (!str || typeof str !== 'string') {
|
|
34
|
+
return str ?? '';
|
|
35
|
+
}
|
|
36
|
+
return str.replace(HTML_ESCAPE_REGEX, (char)=>HTML_ESCAPE_MAP[char] || char);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Escapes all string values in a variables object for safe HTML interpolation.
|
|
40
|
+
* Non-string values are converted to strings and escaped.
|
|
41
|
+
*
|
|
42
|
+
* @param variables - Object containing template variables
|
|
43
|
+
* @returns New object with all string values HTML-escaped
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* escapeHtmlVariables({ name: '<script>xss</script>', count: 5 })
|
|
48
|
+
* // Returns: { name: '<script>xss</script>', count: '5' }
|
|
49
|
+
* ```
|
|
50
|
+
*/ export function escapeHtmlVariables(variables) {
|
|
51
|
+
if (!variables || typeof variables !== 'object') {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
const escaped = {};
|
|
55
|
+
for (const [key, value] of Object.entries(variables)){
|
|
56
|
+
if (value === null || value === undefined) {
|
|
57
|
+
escaped[key] = '';
|
|
58
|
+
} else if (typeof value === 'string') {
|
|
59
|
+
escaped[key] = escapeHtml(value);
|
|
60
|
+
} else if (typeof value === 'object') {
|
|
61
|
+
// For objects/arrays, stringify and escape
|
|
62
|
+
escaped[key] = escapeHtml(JSON.stringify(value));
|
|
63
|
+
} else {
|
|
64
|
+
// For numbers, booleans, etc., convert to string (no escaping needed)
|
|
65
|
+
escaped[key] = String(value);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return escaped;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Checks if a string contains potential HTML/script injection.
|
|
72
|
+
* Useful for logging or validation purposes.
|
|
73
|
+
*
|
|
74
|
+
* @param str - The string to check
|
|
75
|
+
* @returns True if the string contains HTML-like content
|
|
76
|
+
*/ export function containsHtmlContent(str) {
|
|
77
|
+
if (!str || typeof str !== 'string') {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
// Check for common HTML patterns
|
|
81
|
+
return /<[a-z][\s\S]*>/i.test(str) || /javascript:/i.test(str) || /on\w+=/i.test(str);
|
|
82
|
+
}
|
package/fesm/utils/index.js
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { BadRequestException } from '@nestjs/common';
|
|
2
|
+
/**
|
|
3
|
+
* Apply company filter to a query builder if company feature is enabled.
|
|
4
|
+
* Centralizes the common pattern of filtering by user's company.
|
|
5
|
+
*
|
|
6
|
+
* @param query - TypeORM SelectQueryBuilder
|
|
7
|
+
* @param config - Company filter configuration
|
|
8
|
+
* @param user - Current logged user (may be null)
|
|
9
|
+
* @returns The modified query builder
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* applyCompanyFilter(query, {
|
|
14
|
+
* isCompanyFeatureEnabled: this.config.isCompanyFeatureEnabled(),
|
|
15
|
+
* entityAlias: 'file_manager',
|
|
16
|
+
* }, user);
|
|
17
|
+
* ```
|
|
18
|
+
*/ export function applyCompanyFilter(query, config, user) {
|
|
19
|
+
const columnName = config.columnName ?? 'companyId';
|
|
20
|
+
if (config.isCompanyFeatureEnabled && user?.companyId) {
|
|
21
|
+
query.andWhere(`${config.entityAlias}.${columnName} = :companyId`, {
|
|
22
|
+
companyId: user.companyId
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return query;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build a where condition object with optional company filter.
|
|
29
|
+
*
|
|
30
|
+
* @param baseWhere - Base where condition object
|
|
31
|
+
* @param isCompanyFeatureEnabled - Whether company feature is enabled
|
|
32
|
+
* @param user - Current logged user
|
|
33
|
+
* @returns Where condition with companyId added if applicable
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const where = buildCompanyWhereCondition(
|
|
38
|
+
* { id: dto.id },
|
|
39
|
+
* this.config.isCompanyFeatureEnabled(),
|
|
40
|
+
* user
|
|
41
|
+
* );
|
|
42
|
+
* // Returns { id: dto.id, companyId: user.companyId } if company feature enabled
|
|
43
|
+
* ```
|
|
44
|
+
*/ export function buildCompanyWhereCondition(baseWhere, isCompanyFeatureEnabled, user) {
|
|
45
|
+
if (isCompanyFeatureEnabled && user?.companyId) {
|
|
46
|
+
return {
|
|
47
|
+
...baseWhere,
|
|
48
|
+
companyId: user.companyId
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return baseWhere;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Type guard to check if entity has companyId property
|
|
55
|
+
*/ export function hasCompanyId(entity) {
|
|
56
|
+
return entity !== null && typeof entity === 'object' && 'companyId' in entity;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Validates that an entity with companyId belongs to the user's company.
|
|
60
|
+
* Throws BadRequestException if the entity belongs to another company.
|
|
61
|
+
*
|
|
62
|
+
* @param entity - Entity to validate
|
|
63
|
+
* @param user - Current logged user
|
|
64
|
+
* @param isCompanyFeatureEnabled - Whether company feature is enabled
|
|
65
|
+
* @param entityName - Name of entity for error message (e.g., 'Email configuration')
|
|
66
|
+
* @throws BadRequestException if entity belongs to another company
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* validateCompanyOwnership(config, user, this.config.isCompanyFeatureEnabled(), 'Email configuration');
|
|
71
|
+
* ```
|
|
72
|
+
*/ export function validateCompanyOwnership(entity, user, isCompanyFeatureEnabled, entityName) {
|
|
73
|
+
if (isCompanyFeatureEnabled && user?.companyId && hasCompanyId(entity)) {
|
|
74
|
+
if (entity.companyId && entity.companyId !== user.companyId) {
|
|
75
|
+
throw new BadRequestException(`${entityName} belongs to another company`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
package/interceptors/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './delete-empty-id-from-body.interceptor';
|
|
|
2
2
|
export * from './idempotency.interceptor';
|
|
3
3
|
export * from './query-performance.interceptor';
|
|
4
4
|
export * from './response-meta.interceptor';
|
|
5
|
+
export * from './set-user-field-on-body.interceptor';
|
|
5
6
|
export * from './set-create-by-on-body.interceptor';
|
|
6
7
|
export * from './set-delete-by-on-body.interceptor';
|
|
7
8
|
export * from './set-update-by-on-body.interceptor';
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { Observable } from 'rxjs';
|
|
3
|
-
export declare class SetCreatedByOnBody implements NestInterceptor {
|
|
4
|
-
intercept<T = any>(context: ExecutionContext, next: CallHandler<T>): Observable<T>;
|
|
5
|
-
}
|
|
1
|
+
export declare const SetCreatedByOnBody: import("@nestjs/common").Type<import("@nestjs/common").NestInterceptor<any, any>>;
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { Observable } from 'rxjs';
|
|
3
|
-
export declare class SetDeletedByOnBody implements NestInterceptor {
|
|
4
|
-
intercept<T = any>(context: ExecutionContext, next: CallHandler<T>): Observable<T>;
|
|
5
|
-
}
|
|
1
|
+
export declare const SetDeletedByOnBody: import("@nestjs/common").Type<import("@nestjs/common").NestInterceptor<any, any>>;
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { Observable } from 'rxjs';
|
|
3
|
-
export declare class SetUpdateByOnBody implements NestInterceptor {
|
|
4
|
-
intercept<T = any>(context: ExecutionContext, next: CallHandler<T>): Observable<T>;
|
|
5
|
-
}
|
|
1
|
+
export declare const SetUpdateByOnBody: import("@nestjs/common").Type<import("@nestjs/common").NestInterceptor<any, any>>;
|
|
@@ -2,6 +2,7 @@ import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
|
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import { UtilsService } from '../modules/utils/utils.service';
|
|
4
4
|
export declare class Slug implements NestInterceptor {
|
|
5
|
-
utilsService
|
|
5
|
+
private readonly utilsService;
|
|
6
|
+
constructor(utilsService: UtilsService);
|
|
6
7
|
intercept<T = any>(context: ExecutionContext, next: CallHandler<T>): Observable<T>;
|
|
7
8
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DeleteDto, FilterAndPaginationDto } from '
|
|
2
|
-
import { ILoggedUserInfo } from '
|
|
1
|
+
import { DeleteDto, FilterAndPaginationDto } from '../dtos';
|
|
2
|
+
import { ILoggedUserInfo } from './logged-user-info.interface';
|
|
3
3
|
export interface IService<CreateDtoT, UpdateDtoT, InterfaceT> {
|
|
4
4
|
insert(addDto: CreateDtoT, user: ILoggedUserInfo | null): Promise<InterfaceT>;
|
|
5
5
|
insertMany(addDto: Array<CreateDtoT>, user: ILoggedUserInfo | null): Promise<Array<InterfaceT>>;
|
|
@@ -2,8 +2,8 @@ export interface IIdentity {
|
|
|
2
2
|
id: string;
|
|
3
3
|
createdAt: Date;
|
|
4
4
|
updatedAt: Date;
|
|
5
|
-
deletedAt: Date;
|
|
6
|
-
createdById: string;
|
|
7
|
-
updatedById: string;
|
|
8
|
-
deletedById: string;
|
|
5
|
+
deletedAt: Date | null;
|
|
6
|
+
createdById: string | null;
|
|
7
|
+
updatedById: string | null;
|
|
8
|
+
deletedById: string | null;
|
|
9
9
|
}
|
package/interfaces/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './api.interface';
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './datasource.interface';
|
|
3
3
|
export * from './identity.interface';
|
|
4
4
|
export * from './logged-user-info.interface';
|
|
5
5
|
export * from './logger.interface';
|
|
6
|
+
export * from './module-config.interface';
|
|
6
7
|
export * from './permission.interface';
|
|
@@ -11,7 +11,6 @@ export interface LegacyPermissionConfig {
|
|
|
11
11
|
export type PermissionConfig = PermissionCondition | LegacyPermissionConfig | string[];
|
|
12
12
|
export interface PermissionGuardConfig {
|
|
13
13
|
enableCompanyFeature?: boolean;
|
|
14
|
-
cacheKeyPrefix?: string;
|
|
15
14
|
userPermissionKeyFormat?: string;
|
|
16
15
|
companyPermissionKeyFormat?: string;
|
|
17
16
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import { HybridCache } from '
|
|
1
|
+
import { HybridCache } from '../../classes/hybrid-cache.class';
|
|
2
|
+
export declare const DEFAULT_PHONE_REGEX: RegExp;
|
|
3
|
+
export declare const DEFAULT_PHONE_COUNTRY_CODE = "+88";
|
|
4
|
+
export interface PhoneValidationConfig {
|
|
5
|
+
regex: RegExp;
|
|
6
|
+
countryCode: string;
|
|
7
|
+
}
|
|
2
8
|
export declare class UtilsService {
|
|
3
|
-
|
|
9
|
+
private readonly logger;
|
|
10
|
+
private phoneConfig;
|
|
11
|
+
setPhoneValidationConfig(config: PhoneValidationConfig): void;
|
|
4
12
|
getCacheKey(entityName: string, params: any, entityId?: string, tenantId?: string): string;
|
|
5
13
|
trackCacheKey(cacheKey: string, entityName: string, cacheManager: HybridCache, entityId?: string, tenantId?: string): Promise<void>;
|
|
6
14
|
clearCache(entityName: string, cacheManager: HybridCache, entityId?: string, tenantId?: string): Promise<void>;
|
|
@@ -16,6 +24,4 @@ export declare class UtilsService {
|
|
|
16
24
|
columnName: string;
|
|
17
25
|
value: string;
|
|
18
26
|
};
|
|
19
|
-
getOtpEmailFormat(otp: number, userName?: string | null | undefined): string;
|
|
20
|
-
getResetPasswordEmailFormat(resetLink: string, userName?: string | null | undefined): string;
|
|
21
27
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/nestjs-shared",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-rc",
|
|
4
4
|
"description": "Common shared utilities for Flusys NestJS applications",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "fesm/index.js",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
}
|
|
89
89
|
},
|
|
90
90
|
"peerDependencies": {
|
|
91
|
-
"@keyv/redis": "^
|
|
91
|
+
"@keyv/redis": "^5.0.0",
|
|
92
92
|
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
93
93
|
"@nestjs/config": "^3.0.0 || ^4.0.0",
|
|
94
94
|
"@nestjs/core": "^10.0.0 || ^11.0.0",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"@nestjs/passport": "^10.0.0 || ^11.0.0",
|
|
97
97
|
"@nestjs/swagger": "^7.0.0 || ^11.0.0",
|
|
98
98
|
"@nestjs/typeorm": "^10.0.0 || ^11.0.0",
|
|
99
|
-
"cacheable": "^
|
|
99
|
+
"cacheable": "^2.0.0",
|
|
100
100
|
"class-transformer": "^0.5.0",
|
|
101
101
|
"class-validator": "^0.14.0",
|
|
102
102
|
"keyv": "^5.0.0",
|
|
@@ -105,6 +105,6 @@
|
|
|
105
105
|
"winston-daily-rotate-file": "^5.0.0"
|
|
106
106
|
},
|
|
107
107
|
"dependencies": {
|
|
108
|
-
"@flusys/nestjs-core": "
|
|
108
|
+
"@flusys/nestjs-core": "1.0.0-rc"
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -1,13 +1,27 @@
|
|
|
1
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 interface ISanitizedError {
|
|
13
|
+
message: string;
|
|
14
|
+
code?: string;
|
|
15
|
+
statusCode?: number;
|
|
16
|
+
stack?: string;
|
|
17
|
+
}
|
|
2
18
|
export declare class ErrorHandler {
|
|
3
|
-
static getErrorMessage(error: unknown): string;
|
|
4
|
-
static
|
|
5
|
-
static
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
[key: string]: unknown;
|
|
10
|
-
}): {
|
|
19
|
+
static getErrorMessage(error: unknown, sanitizeForClient?: boolean): string;
|
|
20
|
+
private static containsSensitiveData;
|
|
21
|
+
static getErrorStack(error: unknown, forClient?: boolean): string | undefined;
|
|
22
|
+
static createClientError(error: unknown, statusCode?: number, code?: string): ISanitizedError;
|
|
23
|
+
private static sanitizeContextForLogging;
|
|
24
|
+
static createErrorContext(error: unknown, context?: IErrorContext): {
|
|
11
25
|
error: {
|
|
12
26
|
message: string;
|
|
13
27
|
stack?: string;
|
|
@@ -15,10 +29,6 @@ export declare class ErrorHandler {
|
|
|
15
29
|
};
|
|
16
30
|
context?: Record<string, unknown>;
|
|
17
31
|
};
|
|
18
|
-
static logError(logger: Logger, error: unknown, operation: string, context?:
|
|
19
|
-
entity?: string;
|
|
20
|
-
userId?: string;
|
|
21
|
-
[key: string]: unknown;
|
|
22
|
-
}): void;
|
|
32
|
+
static logError(logger: Logger, error: unknown, operation: string, context?: Omit<IErrorContext, 'operation'>): void;
|
|
23
33
|
static rethrowError(error: unknown): never;
|
|
24
34
|
}
|
package/utils/index.d.ts
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ObjectLiteral, SelectQueryBuilder } from 'typeorm';
|
|
2
|
+
import { ILoggedUserInfo } from '../interfaces';
|
|
3
|
+
export interface ICompanyFilterConfig {
|
|
4
|
+
isCompanyFeatureEnabled: boolean;
|
|
5
|
+
entityAlias: string;
|
|
6
|
+
columnName?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function applyCompanyFilter<T extends ObjectLiteral>(query: SelectQueryBuilder<T>, config: ICompanyFilterConfig, user: ILoggedUserInfo | null | undefined): SelectQueryBuilder<T>;
|
|
9
|
+
export declare function buildCompanyWhereCondition<T extends Record<string, unknown>>(baseWhere: T, isCompanyFeatureEnabled: boolean, user: ILoggedUserInfo | null | undefined): T & {
|
|
10
|
+
companyId?: string;
|
|
11
|
+
};
|
|
12
|
+
export interface ICompanyEnabled {
|
|
13
|
+
companyId?: string | null;
|
|
14
|
+
}
|
|
15
|
+
export declare function hasCompanyId<T>(entity: T): entity is T & ICompanyEnabled;
|
|
16
|
+
export declare function validateCompanyOwnership<T>(entity: T, user: ILoggedUserInfo | null | undefined, isCompanyFeatureEnabled: boolean, entityName: string): void;
|