@flusys/nestjs-shared 1.0.0-beta → 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.
Files changed (90) hide show
  1. package/README.md +56 -110
  2. package/cjs/classes/api-controller.class.js +9 -24
  3. package/cjs/classes/index.js +1 -0
  4. package/cjs/constants/index.js +14 -0
  5. package/cjs/constants/permissions.js +174 -0
  6. package/cjs/decorators/api-response.decorator.js +1 -1
  7. package/cjs/decorators/index.js +1 -0
  8. package/cjs/decorators/sanitize-html.decorator.js +36 -0
  9. package/cjs/dtos/filter-and-pagination.dto.js +24 -34
  10. package/cjs/dtos/pagination.dto.js +4 -8
  11. package/cjs/dtos/response-payload.dto.js +0 -41
  12. package/cjs/entities/identity.js +4 -4
  13. package/cjs/entities/user-root.js +13 -14
  14. package/cjs/guards/permission.guard.js +39 -94
  15. package/cjs/interceptors/index.js +1 -0
  16. package/cjs/interceptors/set-create-by-on-body.interceptor.js +2 -30
  17. package/cjs/interceptors/set-delete-by-on-body.interceptor.js +2 -30
  18. package/cjs/interceptors/set-update-by-on-body.interceptor.js +2 -30
  19. package/cjs/interceptors/set-user-field-on-body.interceptor.js +43 -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/modules/cache/cache.module.js +3 -3
  25. package/cjs/modules/datasource/multi-tenant-datasource.service.js +30 -110
  26. package/cjs/modules/utils/utils.service.js +63 -145
  27. package/cjs/utils/error-handler.util.js +91 -13
  28. package/cjs/utils/html-sanitizer.util.js +74 -0
  29. package/cjs/utils/index.js +2 -0
  30. package/cjs/utils/query-helpers.util.js +53 -0
  31. package/classes/api-controller.class.d.ts +5 -5
  32. package/classes/api-service.class.d.ts +5 -5
  33. package/classes/index.d.ts +1 -0
  34. package/classes/request-scoped-api.service.d.ts +3 -2
  35. package/constants/index.d.ts +1 -0
  36. package/constants/permissions.d.ts +167 -0
  37. package/decorators/index.d.ts +1 -0
  38. package/decorators/sanitize-html.decorator.d.ts +2 -0
  39. package/dtos/filter-and-pagination.dto.d.ts +0 -2
  40. package/dtos/response-payload.dto.d.ts +0 -7
  41. package/fesm/classes/api-controller.class.js +9 -24
  42. package/fesm/classes/index.js +2 -0
  43. package/fesm/constants/index.js +2 -0
  44. package/fesm/constants/permissions.js +121 -0
  45. package/fesm/decorators/api-response.decorator.js +1 -1
  46. package/fesm/decorators/index.js +1 -0
  47. package/fesm/decorators/sanitize-html.decorator.js +45 -0
  48. package/fesm/dtos/filter-and-pagination.dto.js +26 -47
  49. package/fesm/dtos/pagination.dto.js +4 -8
  50. package/fesm/dtos/response-payload.dto.js +0 -38
  51. package/fesm/entities/identity.js +4 -4
  52. package/fesm/entities/user-root.js +13 -14
  53. package/fesm/guards/permission.guard.js +39 -94
  54. package/fesm/interceptors/index.js +1 -0
  55. package/fesm/interceptors/set-create-by-on-body.interceptor.js +4 -30
  56. package/fesm/interceptors/set-delete-by-on-body.interceptor.js +4 -30
  57. package/fesm/interceptors/set-update-by-on-body.interceptor.js +4 -30
  58. package/fesm/interceptors/set-user-field-on-body.interceptor.js +36 -0
  59. package/fesm/interceptors/slug.interceptor.js +31 -10
  60. package/fesm/interfaces/datasource.interface.js +20 -0
  61. package/fesm/interfaces/index.js +2 -1
  62. package/fesm/interfaces/module-config.interface.js +5 -0
  63. package/fesm/modules/cache/cache.module.js +2 -2
  64. package/fesm/modules/datasource/multi-tenant-datasource.service.js +30 -110
  65. package/fesm/modules/utils/utils.service.js +50 -143
  66. package/fesm/utils/error-handler.util.js +93 -14
  67. package/fesm/utils/html-sanitizer.util.js +82 -0
  68. package/fesm/utils/index.js +2 -0
  69. package/fesm/utils/query-helpers.util.js +78 -0
  70. package/interceptors/index.d.ts +1 -0
  71. package/interceptors/set-create-by-on-body.interceptor.d.ts +1 -5
  72. package/interceptors/set-delete-by-on-body.interceptor.d.ts +1 -5
  73. package/interceptors/set-update-by-on-body.interceptor.d.ts +1 -5
  74. package/interceptors/set-user-field-on-body.interceptor.d.ts +2 -0
  75. package/interceptors/slug.interceptor.d.ts +2 -1
  76. package/interfaces/api.interface.d.ts +2 -2
  77. package/interfaces/datasource.interface.d.ts +5 -0
  78. package/interfaces/identity.interface.d.ts +4 -4
  79. package/interfaces/index.d.ts +2 -1
  80. package/interfaces/module-config.interface.d.ts +6 -0
  81. package/interfaces/permission.interface.d.ts +0 -1
  82. package/modules/utils/utils.service.d.ts +10 -4
  83. package/package.json +4 -4
  84. package/utils/error-handler.util.d.ts +23 -13
  85. package/utils/html-sanitizer.util.d.ts +3 -0
  86. package/utils/index.d.ts +2 -0
  87. package/utils/query-helpers.util.d.ts +16 -0
  88. package/cjs/interfaces/base-query.interface.js +0 -6
  89. package/fesm/interfaces/base-query.interface.js +0 -3
  90. package/interfaces/base-query.interface.d.ts +0 -7
@@ -8,28 +8,104 @@ Object.defineProperty(exports, "ErrorHandler", {
8
8
  return ErrorHandler;
9
9
  }
10
10
  });
11
+ const _common = require("@nestjs/common");
12
+ /** Check if running in production environment */ const IS_PRODUCTION = process.env.NODE_ENV === 'production';
13
+ /** Sensitive keys that should be redacted from logs */ const SENSITIVE_KEYS = [
14
+ 'password',
15
+ 'secret',
16
+ 'token',
17
+ 'apiKey',
18
+ 'credential',
19
+ 'authorization'
20
+ ];
21
+ /** Patterns that indicate sensitive data in error messages */ const SENSITIVE_PATTERNS = [
22
+ /password/i,
23
+ /secret/i,
24
+ /token/i,
25
+ /key/i,
26
+ /credential/i,
27
+ /authorization/i,
28
+ /bearer/i
29
+ ];
11
30
  let ErrorHandler = class ErrorHandler {
12
31
  /**
13
- * Safely extract error message from unknown error
14
- */ static getErrorMessage(error) {
32
+ * Safely extract error message from unknown error.
33
+ * @param error - The error to extract message from
34
+ * @param sanitizeForClient - If true, redacts potentially sensitive info in production
35
+ */ static getErrorMessage(error, sanitizeForClient = false) {
36
+ let message = 'Unknown error occurred';
15
37
  if (error instanceof Error) {
16
- return error.message;
38
+ message = error.message;
39
+ } else if (typeof error === 'string') {
40
+ message = error;
17
41
  }
18
- if (typeof error === 'string') {
19
- return error;
42
+ // Sanitize for client responses in production
43
+ if (sanitizeForClient && IS_PRODUCTION) {
44
+ if (this.containsSensitiveData(message)) {
45
+ return 'An unexpected error occurred. Please try again later.';
46
+ }
20
47
  }
21
- return 'Unknown error occurred';
48
+ return message;
49
+ }
50
+ /**
51
+ * Check if a string contains potentially sensitive data.
52
+ */ static containsSensitiveData(text) {
53
+ return SENSITIVE_PATTERNS.some((pattern)=>pattern.test(text));
22
54
  }
23
55
  /**
24
- * Safely extract error stack from unknown error
25
- */ static getErrorStack(error) {
56
+ * Safely extract error stack from unknown error.
57
+ * Returns undefined in production for client responses.
58
+ * @param error - The error to extract stack from
59
+ * @param forClient - If true, never returns stack in production
60
+ */ static getErrorStack(error, forClient = false) {
61
+ // Never expose stack traces to clients in production
62
+ if (forClient && IS_PRODUCTION) {
63
+ return undefined;
64
+ }
26
65
  if (error instanceof Error) {
27
66
  return error.stack;
28
67
  }
29
68
  return undefined;
30
69
  }
31
70
  /**
32
- * Create error context object for logging
71
+ * Create a sanitized error response for clients.
72
+ * In production, sensitive data is redacted and stack traces removed.
73
+ */ static createClientError(error, statusCode = _common.HttpStatus.INTERNAL_SERVER_ERROR, code) {
74
+ const sanitizedError = {
75
+ message: this.getErrorMessage(error, true),
76
+ statusCode
77
+ };
78
+ if (code) {
79
+ sanitizedError.code = code;
80
+ }
81
+ // Include stack only in development
82
+ if (!IS_PRODUCTION) {
83
+ sanitizedError.stack = this.getErrorStack(error);
84
+ }
85
+ return sanitizedError;
86
+ }
87
+ /**
88
+ * Sanitize context data to redact sensitive fields from logs.
89
+ */ static sanitizeContextForLogging(context) {
90
+ const sanitized = {};
91
+ for (const [key, value] of Object.entries(context)){
92
+ // Check if key contains sensitive words
93
+ const isSensitive = SENSITIVE_KEYS.some((sk)=>key.toLowerCase().includes(sk.toLowerCase()));
94
+ if (isSensitive) {
95
+ sanitized[key] = '[REDACTED]';
96
+ } else if (Array.isArray(value)) {
97
+ sanitized[key] = value.map((item)=>typeof item === 'object' && item !== null ? this.sanitizeContextForLogging(item) : item);
98
+ } else if (typeof value === 'object' && value !== null) {
99
+ sanitized[key] = this.sanitizeContextForLogging(value);
100
+ } else {
101
+ sanitized[key] = value;
102
+ }
103
+ }
104
+ return sanitized;
105
+ }
106
+ /**
107
+ * Create error context object for internal logging.
108
+ * Context data is sanitized to redact sensitive fields.
33
109
  */ static createErrorContext(error, context) {
34
110
  const errorContext = {
35
111
  error: {
@@ -41,20 +117,22 @@ let ErrorHandler = class ErrorHandler {
41
117
  errorContext.error.name = error.name;
42
118
  }
43
119
  if (context && Object.keys(context).length > 0) {
44
- errorContext.context = context;
120
+ // Sanitize context to redact sensitive fields
121
+ errorContext.context = this.sanitizeContextForLogging(context);
45
122
  }
46
123
  return errorContext;
47
124
  }
48
125
  /**
49
- * Log error with consistent format
126
+ * Log error with consistent format.
127
+ * Sensitive data in context is automatically redacted.
50
128
  */ static logError(logger, error, operation, context) {
51
129
  const errorContext = this.createErrorContext(error, {
52
130
  operation,
53
131
  ...context
54
132
  });
55
133
  const errorMessage = `Failed to ${operation}: ${errorContext.error.message}`;
56
- const loggerContext = logger.context || 'ErrorHandler';
57
- logger.error(errorMessage, errorContext.error.stack, loggerContext, errorContext);
134
+ // Log full details internally (stack traces are fine for internal logs)
135
+ logger.error(errorMessage, errorContext.error.stack, errorContext);
58
136
  }
59
137
  /**
60
138
  * Re-throw error with proper type checking
@@ -0,0 +1,74 @@
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
+ */ "use strict";
9
+ Object.defineProperty(exports, "__esModule", {
10
+ value: true
11
+ });
12
+ function _export(target, all) {
13
+ for(var name in all)Object.defineProperty(target, name, {
14
+ enumerable: true,
15
+ get: Object.getOwnPropertyDescriptor(all, name).get
16
+ });
17
+ }
18
+ _export(exports, {
19
+ get containsHtmlContent () {
20
+ return containsHtmlContent;
21
+ },
22
+ get escapeHtml () {
23
+ return escapeHtml;
24
+ },
25
+ get escapeHtmlVariables () {
26
+ return escapeHtmlVariables;
27
+ }
28
+ });
29
+ const HTML_ESCAPE_MAP = {
30
+ '&': '&',
31
+ '<': '&lt;',
32
+ '>': '&gt;',
33
+ '"': '&quot;',
34
+ "'": '&#x27;',
35
+ '/': '&#x2F;',
36
+ '`': '&#x60;',
37
+ '=': '&#x3D;'
38
+ };
39
+ /**
40
+ * Regex pattern matching characters that need HTML escaping
41
+ */ const HTML_ESCAPE_REGEX = /[&<>"'`=/]/g;
42
+ function escapeHtml(str) {
43
+ if (!str || typeof str !== 'string') {
44
+ return str ?? '';
45
+ }
46
+ return str.replace(HTML_ESCAPE_REGEX, (char)=>HTML_ESCAPE_MAP[char] || char);
47
+ }
48
+ function escapeHtmlVariables(variables) {
49
+ if (!variables || typeof variables !== 'object') {
50
+ return {};
51
+ }
52
+ const escaped = {};
53
+ for (const [key, value] of Object.entries(variables)){
54
+ if (value === null || value === undefined) {
55
+ escaped[key] = '';
56
+ } else if (typeof value === 'string') {
57
+ escaped[key] = escapeHtml(value);
58
+ } else if (typeof value === 'object') {
59
+ // For objects/arrays, stringify and escape
60
+ escaped[key] = escapeHtml(JSON.stringify(value));
61
+ } else {
62
+ // For numbers, booleans, etc., convert to string (no escaping needed)
63
+ escaped[key] = String(value);
64
+ }
65
+ }
66
+ return escaped;
67
+ }
68
+ function containsHtmlContent(str) {
69
+ if (!str || typeof str !== 'string') {
70
+ return false;
71
+ }
72
+ // Check for common HTML patterns
73
+ return /<[a-z][\s\S]*>/i.test(str) || /javascript:/i.test(str) || /on\w+=/i.test(str);
74
+ }
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
5
  _export_star(require("./error-handler.util"), exports);
6
+ _export_star(require("./html-sanitizer.util"), exports);
7
+ _export_star(require("./query-helpers.util"), exports);
6
8
  function _export_star(from, to) {
7
9
  Object.keys(from).forEach(function(k) {
8
10
  if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get applyCompanyFilter () {
13
+ return applyCompanyFilter;
14
+ },
15
+ get buildCompanyWhereCondition () {
16
+ return buildCompanyWhereCondition;
17
+ },
18
+ get hasCompanyId () {
19
+ return hasCompanyId;
20
+ },
21
+ get validateCompanyOwnership () {
22
+ return validateCompanyOwnership;
23
+ }
24
+ });
25
+ const _common = require("@nestjs/common");
26
+ function applyCompanyFilter(query, config, user) {
27
+ const columnName = config.columnName ?? 'companyId';
28
+ if (config.isCompanyFeatureEnabled && user?.companyId) {
29
+ query.andWhere(`${config.entityAlias}.${columnName} = :companyId`, {
30
+ companyId: user.companyId
31
+ });
32
+ }
33
+ return query;
34
+ }
35
+ function buildCompanyWhereCondition(baseWhere, isCompanyFeatureEnabled, user) {
36
+ if (isCompanyFeatureEnabled && user?.companyId) {
37
+ return {
38
+ ...baseWhere,
39
+ companyId: user.companyId
40
+ };
41
+ }
42
+ return baseWhere;
43
+ }
44
+ function hasCompanyId(entity) {
45
+ return entity !== null && typeof entity === 'object' && 'companyId' in entity;
46
+ }
47
+ function validateCompanyOwnership(entity, user, isCompanyFeatureEnabled, entityName) {
48
+ if (isCompanyFeatureEnabled && user?.companyId && hasCompanyId(entity)) {
49
+ if (entity.companyId && entity.companyId !== user.companyId) {
50
+ throw new _common.BadRequestException(`${entityName} belongs to another company`);
51
+ }
52
+ }
53
+ }
@@ -1,6 +1,6 @@
1
- import { BulkResponseDto, DeleteDto, FilterAndPaginationDto, GetByIdBodyDto, ListResponseDto, MessageResponseDto, SingleResponseDto } from '@flusys/nestjs-shared/dtos';
2
- import { Identity } from '@flusys/nestjs-shared/entities';
3
- import { ILoggedUserInfo, IService, PermissionCondition, PermissionOperator } from '@flusys/nestjs-shared/interfaces';
1
+ import { BulkResponseDto, DeleteDto, FilterAndPaginationDto, GetByIdBodyDto, ListResponseDto, MessageResponseDto, SingleResponseDto } from '../dtos';
2
+ import { Identity } from '../entities';
3
+ import { ILoggedUserInfo, IService, PermissionCondition, PermissionOperator } from '../interfaces';
4
4
  import { Type } from '@nestjs/common';
5
5
  export type ApiEndpoint = 'insert' | 'insertMany' | 'getById' | 'getAll' | 'update' | 'updateMany' | 'delete';
6
6
  export type SecurityLevel = 'public' | 'jwt' | 'permission';
@@ -16,9 +16,9 @@ export type ApiSecurityConfig = {
16
16
  export interface ApiControllerOptions {
17
17
  security?: ApiSecurityConfig | EndpointSecurity | SecurityLevel;
18
18
  }
19
- export declare function createApiController<CreateDtoT extends Record<string, unknown>, UpdateDtoT extends {
19
+ export declare function createApiController<CreateDtoT extends object, UpdateDtoT extends {
20
20
  id: string;
21
- }, ResponseDtoT extends Record<string, unknown>, InterfaceT extends Identity, ServiceT extends IService<CreateDtoT, UpdateDtoT, InterfaceT>>(createDtoClass: Type<CreateDtoT>, updateDtoClass: Type<UpdateDtoT>, responseDtoClass: Type<ResponseDtoT>, options?: ApiControllerOptions): abstract new (service: ServiceT) => {
21
+ }, ResponseDtoT extends object, InterfaceT extends Identity, ServiceT extends IService<CreateDtoT, UpdateDtoT, InterfaceT>>(createDtoClass: Type<CreateDtoT>, updateDtoClass: Type<UpdateDtoT>, responseDtoClass: Type<ResponseDtoT>, options?: ApiControllerOptions): abstract new (service: ServiceT) => {
22
22
  service: ServiceT;
23
23
  insert(addDto: CreateDtoT, user: ILoggedUserInfo | null): Promise<SingleResponseDto<ResponseDtoT>>;
24
24
  insertMany(addDto: CreateDtoT[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<ResponseDtoT>>;
@@ -1,11 +1,11 @@
1
- import { DeleteDto, FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
2
- import { Identity } from '@flusys/nestjs-shared/entities';
3
- import { ILoggedUserInfo, IService } from '@flusys/nestjs-shared/interfaces';
4
- import { UtilsService } from '@flusys/nestjs-shared/modules';
1
+ import { DeleteDto, FilterAndPaginationDto } from '../dtos';
2
+ import { Identity } from '../entities';
3
+ import { ILoggedUserInfo, IService } from '../interfaces';
4
+ import { UtilsService } from '../modules/utils/utils.service';
5
5
  import { Logger } from '@nestjs/common';
6
6
  import { QueryRunner, Repository, SelectQueryBuilder } from 'typeorm';
7
7
  import { HybridCache } from './hybrid-cache.class';
8
- export declare abstract class ApiService<CreateDtoT extends Record<string, unknown>, UpdateDtoT extends {
8
+ export declare abstract class ApiService<CreateDtoT extends object, UpdateDtoT extends {
9
9
  id: string;
10
10
  }, InterfaceT extends Identity, EntityT extends Identity, RepositoryT extends Repository<EntityT>> implements IService<CreateDtoT, UpdateDtoT, InterfaceT> {
11
11
  protected entityName: string;
@@ -4,3 +4,4 @@ export * from './request-scoped-api.service';
4
4
  export * from './hybrid-cache.class';
5
5
  export * from './winston-logger-adapter.class';
6
6
  export * from './winston.logger.class';
7
+ export * from '../constants/permissions';
@@ -1,12 +1,13 @@
1
1
  import { DataSource, EntityTarget, Repository } from 'typeorm';
2
2
  import { Identity } from '../entities';
3
+ import { IDataSourceProvider } from '../interfaces';
3
4
  import { ApiService } from './api-service.class';
4
- export declare abstract class RequestScopedApiService<CreateDtoT extends Record<string, unknown>, UpdateDtoT extends {
5
+ export declare abstract class RequestScopedApiService<CreateDtoT extends object, UpdateDtoT extends {
5
6
  id: string;
6
7
  }, InterfaceT extends Identity, EntityT extends Identity, RepositoryT extends Repository<EntityT>> extends ApiService<CreateDtoT, UpdateDtoT, InterfaceT, EntityT, RepositoryT> {
7
8
  private repositoryInitialized;
8
9
  protected abstract resolveEntity(): EntityTarget<EntityT>;
9
- protected abstract getDataSourceProvider(): any;
10
+ protected abstract getDataSourceProvider(): IDataSourceProvider;
10
11
  protected ensureRepositoryInitialized(): Promise<void>;
11
12
  protected initializeAdditionalRepositories(entities: EntityTarget<any>[]): Promise<Repository<any>[]>;
12
13
  protected getDataSourceForService(): Promise<DataSource>;
@@ -8,3 +8,4 @@ export declare const REQUEST_ID_HEADER = "x-request-id";
8
8
  export declare const CLIENT_TYPE_HEADER = "x-client-type";
9
9
  export declare const PERMISSIONS_CACHE_PREFIX = "permissions";
10
10
  export declare const IDEMPOTENCY_CACHE_PREFIX = "idempotency";
11
+ export * from './permissions';
@@ -0,0 +1,167 @@
1
+ export declare const USER_PERMISSIONS: {
2
+ readonly CREATE: "user.create";
3
+ readonly READ: "user.read";
4
+ readonly UPDATE: "user.update";
5
+ readonly DELETE: "user.delete";
6
+ };
7
+ export declare const COMPANY_PERMISSIONS: {
8
+ readonly CREATE: "company.create";
9
+ readonly READ: "company.read";
10
+ readonly UPDATE: "company.update";
11
+ readonly DELETE: "company.delete";
12
+ };
13
+ export declare const BRANCH_PERMISSIONS: {
14
+ readonly CREATE: "branch.create";
15
+ readonly READ: "branch.read";
16
+ readonly UPDATE: "branch.update";
17
+ readonly DELETE: "branch.delete";
18
+ };
19
+ export declare const ACTION_PERMISSIONS: {
20
+ readonly CREATE: "action.create";
21
+ readonly READ: "action.read";
22
+ readonly UPDATE: "action.update";
23
+ readonly DELETE: "action.delete";
24
+ };
25
+ export declare const ROLE_PERMISSIONS: {
26
+ readonly CREATE: "role.create";
27
+ readonly READ: "role.read";
28
+ readonly UPDATE: "role.update";
29
+ readonly DELETE: "role.delete";
30
+ };
31
+ export declare const ROLE_ACTION_PERMISSIONS: {
32
+ readonly READ: "role-action.read";
33
+ readonly ASSIGN: "role-action.assign";
34
+ };
35
+ export declare const USER_ROLE_PERMISSIONS: {
36
+ readonly READ: "user-role.read";
37
+ readonly ASSIGN: "user-role.assign";
38
+ };
39
+ export declare const USER_ACTION_PERMISSIONS: {
40
+ readonly READ: "user-action.read";
41
+ readonly ASSIGN: "user-action.assign";
42
+ };
43
+ export declare const COMPANY_ACTION_PERMISSIONS: {
44
+ readonly READ: "company-action.read";
45
+ readonly ASSIGN: "company-action.assign";
46
+ };
47
+ export declare const FILE_PERMISSIONS: {
48
+ readonly CREATE: "file.create";
49
+ readonly READ: "file.read";
50
+ readonly UPDATE: "file.update";
51
+ readonly DELETE: "file.delete";
52
+ };
53
+ export declare const FOLDER_PERMISSIONS: {
54
+ readonly CREATE: "folder.create";
55
+ readonly READ: "folder.read";
56
+ readonly UPDATE: "folder.update";
57
+ readonly DELETE: "folder.delete";
58
+ };
59
+ export declare const STORAGE_CONFIG_PERMISSIONS: {
60
+ readonly CREATE: "storage-config.create";
61
+ readonly READ: "storage-config.read";
62
+ readonly UPDATE: "storage-config.update";
63
+ readonly DELETE: "storage-config.delete";
64
+ };
65
+ export declare const EMAIL_CONFIG_PERMISSIONS: {
66
+ readonly CREATE: "email-config.create";
67
+ readonly READ: "email-config.read";
68
+ readonly UPDATE: "email-config.update";
69
+ readonly DELETE: "email-config.delete";
70
+ };
71
+ export declare const EMAIL_TEMPLATE_PERMISSIONS: {
72
+ readonly CREATE: "email-template.create";
73
+ readonly READ: "email-template.read";
74
+ readonly UPDATE: "email-template.update";
75
+ readonly DELETE: "email-template.delete";
76
+ };
77
+ export declare const FORM_PERMISSIONS: {
78
+ readonly CREATE: "form.create";
79
+ readonly READ: "form.read";
80
+ readonly UPDATE: "form.update";
81
+ readonly DELETE: "form.delete";
82
+ };
83
+ export declare const PERMISSIONS: {
84
+ readonly USER: {
85
+ readonly CREATE: "user.create";
86
+ readonly READ: "user.read";
87
+ readonly UPDATE: "user.update";
88
+ readonly DELETE: "user.delete";
89
+ };
90
+ readonly COMPANY: {
91
+ readonly CREATE: "company.create";
92
+ readonly READ: "company.read";
93
+ readonly UPDATE: "company.update";
94
+ readonly DELETE: "company.delete";
95
+ };
96
+ readonly BRANCH: {
97
+ readonly CREATE: "branch.create";
98
+ readonly READ: "branch.read";
99
+ readonly UPDATE: "branch.update";
100
+ readonly DELETE: "branch.delete";
101
+ };
102
+ readonly ACTION: {
103
+ readonly CREATE: "action.create";
104
+ readonly READ: "action.read";
105
+ readonly UPDATE: "action.update";
106
+ readonly DELETE: "action.delete";
107
+ };
108
+ readonly ROLE: {
109
+ readonly CREATE: "role.create";
110
+ readonly READ: "role.read";
111
+ readonly UPDATE: "role.update";
112
+ readonly DELETE: "role.delete";
113
+ };
114
+ readonly ROLE_ACTION: {
115
+ readonly READ: "role-action.read";
116
+ readonly ASSIGN: "role-action.assign";
117
+ };
118
+ readonly USER_ROLE: {
119
+ readonly READ: "user-role.read";
120
+ readonly ASSIGN: "user-role.assign";
121
+ };
122
+ readonly USER_ACTION: {
123
+ readonly READ: "user-action.read";
124
+ readonly ASSIGN: "user-action.assign";
125
+ };
126
+ readonly COMPANY_ACTION: {
127
+ readonly READ: "company-action.read";
128
+ readonly ASSIGN: "company-action.assign";
129
+ };
130
+ readonly FILE: {
131
+ readonly CREATE: "file.create";
132
+ readonly READ: "file.read";
133
+ readonly UPDATE: "file.update";
134
+ readonly DELETE: "file.delete";
135
+ };
136
+ readonly FOLDER: {
137
+ readonly CREATE: "folder.create";
138
+ readonly READ: "folder.read";
139
+ readonly UPDATE: "folder.update";
140
+ readonly DELETE: "folder.delete";
141
+ };
142
+ readonly STORAGE_CONFIG: {
143
+ readonly CREATE: "storage-config.create";
144
+ readonly READ: "storage-config.read";
145
+ readonly UPDATE: "storage-config.update";
146
+ readonly DELETE: "storage-config.delete";
147
+ };
148
+ readonly EMAIL_CONFIG: {
149
+ readonly CREATE: "email-config.create";
150
+ readonly READ: "email-config.read";
151
+ readonly UPDATE: "email-config.update";
152
+ readonly DELETE: "email-config.delete";
153
+ };
154
+ readonly EMAIL_TEMPLATE: {
155
+ readonly CREATE: "email-template.create";
156
+ readonly READ: "email-template.read";
157
+ readonly UPDATE: "email-template.update";
158
+ readonly DELETE: "email-template.delete";
159
+ };
160
+ readonly FORM: {
161
+ readonly CREATE: "form.create";
162
+ readonly READ: "form.read";
163
+ readonly UPDATE: "form.update";
164
+ readonly DELETE: "form.delete";
165
+ };
166
+ };
167
+ export type PermissionCode = (typeof PERMISSIONS)[keyof typeof PERMISSIONS][keyof (typeof PERMISSIONS)[keyof typeof PERMISSIONS]];
@@ -2,3 +2,4 @@ export * from './api-response.decorator';
2
2
  export * from './current-user.decorator';
3
3
  export * from './public.decorator';
4
4
  export * from './require-permission.decorator';
5
+ export * from './sanitize-html.decorator';
@@ -0,0 +1,2 @@
1
+ export declare function SanitizeHtml(): PropertyDecorator;
2
+ export declare function SanitizeAndTrim(): PropertyDecorator;
@@ -5,9 +5,7 @@ export declare class FilterAndPaginationDto {
5
5
  sort?: Record<string, 'ASC' | 'DESC'>;
6
6
  select?: string[];
7
7
  withDeleted?: boolean;
8
- extraKey?: string[];
9
8
  }
10
9
  export declare class GetByIdBodyDto {
11
10
  select?: string[];
12
- extraKey?: string[];
13
11
  }
@@ -53,11 +53,4 @@ export declare class ErrorResponseDto {
53
53
  errors?: ValidationErrorDto[];
54
54
  _meta?: RequestMetaDto;
55
55
  }
56
- export declare class ResponsePayloadDto<T> {
57
- success: boolean;
58
- message: string;
59
- data?: T;
60
- meta?: PaginationMetaDto | BulkMetaDto;
61
- _meta?: RequestMetaDto;
62
- }
63
56
  export type ApiResponse<T> = SingleResponseDto<T> | ListResponseDto<T> | BulkResponseDto<T> | MessageResponseDto | ErrorResponseDto;
@@ -25,10 +25,10 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { CurrentUser, Public, RequirePermission, RequireAnyPermission, RequirePermissionCondition } from '@flusys/nestjs-shared/decorators';
29
- import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto } from '@flusys/nestjs-shared/dtos';
30
- import { JwtAuthGuard, PermissionGuard } from '@flusys/nestjs-shared/guards';
31
- import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '@flusys/nestjs-shared/interceptors';
28
+ import { CurrentUser, Public, RequirePermission, RequireAnyPermission, RequirePermissionCondition } from '../decorators';
29
+ import { DeleteDto, FilterAndPaginationDto, GetByIdBodyDto } from '../dtos';
30
+ import { JwtAuthGuard, PermissionGuard } from '../guards';
31
+ import { IdempotencyInterceptor, SetCreatedByOnBody, SetDeletedByOnBody, SetUpdateByOnBody, Slug } from '../interceptors';
32
32
  import { applyDecorators, Body, HttpCode, HttpStatus, Param, Post, Query, UseGuards, UseInterceptors, Version, VERSION_NEUTRAL } from '@nestjs/common';
33
33
  import { ApiBearerAuth, ApiBody, ApiHeader, ApiOperation, ApiParam, ApiQuery, ApiResponse } from '@nestjs/swagger';
34
34
  import { plainToInstance } from 'class-transformer';
@@ -94,8 +94,10 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
94
94
  // 2. It's an object with 'level' property but no endpoint keys
95
95
  const isGlobalSecurity = typeof securityConfig === 'string' || securityConfig && typeof securityConfig === 'object' && 'level' in securityConfig && !endpointKeys.some((key)=>key in securityConfig);
96
96
  // Normalize security config for each endpoint
97
+ // IMPORTANT: When per-endpoint security is specified, default to 'jwt' for unconfigured endpoints
98
+ // to prevent accidentally exposing endpoints without authentication
97
99
  const defaultSecurity = isGlobalSecurity ? normalizeSecurity(securityConfig) : {
98
- level: 'public'
100
+ level: 'jwt'
99
101
  };
100
102
  const security = {
101
103
  insert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insert),
@@ -337,16 +339,7 @@ import { ApiResponseDto } from '../decorators/api-response.decorator';
337
339
  HttpCode(HttpStatus.OK),
338
340
  ApiOperation({
339
341
  summary: 'Get all items with filters and pagination',
340
- description: `
341
- Retrieves items with support for:
342
- - **filter**: Apply field-based filters (e.g., \`{ "isActive": true }\`)
343
- - **pagination**: Control page and page size
344
- - **sort**: Order by any field (e.g., \`{ "createdAt": "DESC" }\`)
345
- - **select**: Choose specific fields to return
346
- - **withDeleted**: Include soft-deleted items
347
- - **extraKey**: Include additional relations
348
- - **q** (query param): Global text search
349
- `
342
+ description: 'Supports filter, pagination, sort, select, withDeleted, and q (search) params'
350
343
  }),
351
344
  ApiQuery({
352
345
  name: 'q',
@@ -375,15 +368,7 @@ Retrieves items with support for:
375
368
  HttpCode(HttpStatus.OK),
376
369
  ApiOperation({
377
370
  summary: 'Delete, restore, or permanently remove items',
378
- description: `
379
- Performs one of three actions:
380
-
381
- - **"delete"** (soft delete): Marks items as deleted but keeps in database
382
- - **"restore"**: Reverts soft-deleted items to active
383
- - **"permanent"**: Completely removes items from database
384
-
385
- Supports single ID or array of IDs for batch operations.
386
- `
371
+ description: 'Types: delete (soft), restore, permanent. Supports batch IDs.'
387
372
  }),
388
373
  ApiResponse({
389
374
  status: 200,
@@ -4,3 +4,5 @@ export * from './request-scoped-api.service';
4
4
  export * from './hybrid-cache.class';
5
5
  export * from './winston-logger-adapter.class';
6
6
  export * from './winston.logger.class';
7
+ // Re-export permission constants for convenience
8
+ export * from '../constants/permissions';
@@ -12,3 +12,5 @@ export const CLIENT_TYPE_HEADER = 'x-client-type';
12
12
  // Cache key prefixes
13
13
  export const PERMISSIONS_CACHE_PREFIX = 'permissions';
14
14
  export const IDEMPOTENCY_CACHE_PREFIX = 'idempotency';
15
+ // Permission codes
16
+ export * from './permissions';