@flusys/nestjs-iam 0.1.0-beta.1 → 0.1.0-beta.2

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 (118) hide show
  1. package/README.md +665 -0
  2. package/cjs/config/iam.constants.js +11 -0
  3. package/cjs/config/index.js +18 -0
  4. package/cjs/controllers/action.controller.js +117 -0
  5. package/cjs/controllers/company-action-permission.controller.js +110 -0
  6. package/cjs/controllers/index.js +23 -0
  7. package/cjs/controllers/my-permission.controller.js +90 -0
  8. package/cjs/controllers/role-permission.controller.js +160 -0
  9. package/cjs/controllers/role.controller.js +58 -0
  10. package/cjs/controllers/user-action-permission.controller.js +110 -0
  11. package/cjs/docs/iam-swagger.config.js +202 -0
  12. package/cjs/docs/index.js +18 -0
  13. package/cjs/dtos/action.dto.js +347 -0
  14. package/cjs/dtos/index.js +21 -0
  15. package/cjs/dtos/permission.dto.js +554 -0
  16. package/cjs/dtos/role.dto.js +238 -0
  17. package/cjs/entities/action-base.entity.js +135 -0
  18. package/cjs/entities/action.entity.js +28 -0
  19. package/cjs/entities/index.js +81 -0
  20. package/cjs/entities/permission-base.entity.js +156 -0
  21. package/cjs/entities/permission-with-company.entity.js +99 -0
  22. package/cjs/entities/role-base.entity.js +86 -0
  23. package/cjs/entities/role-with-company.entity.js +55 -0
  24. package/cjs/entities/role.entity.js +25 -0
  25. package/cjs/entities/user-iam-permission.entity.js +57 -0
  26. package/cjs/enums/action-type.enum.js +22 -0
  27. package/cjs/enums/index.js +19 -0
  28. package/cjs/enums/permission-type.enum.js +16 -0
  29. package/cjs/helpers/index.js +19 -0
  30. package/cjs/helpers/permission-evaluator.helper.js +175 -0
  31. package/cjs/helpers/permission-mode.helper.js +49 -0
  32. package/cjs/index.js +28 -79
  33. package/cjs/interfaces/action.interface.js +4 -0
  34. package/cjs/interfaces/iam-module-async-options.interface.js +4 -0
  35. package/cjs/interfaces/iam-module-options.interface.js +18 -0
  36. package/cjs/interfaces/index.js +21 -0
  37. package/cjs/interfaces/role.interface.js +7 -0
  38. package/cjs/modules/iam.module.js +237 -0
  39. package/cjs/modules/index.js +18 -0
  40. package/cjs/services/action.service.js +253 -0
  41. package/cjs/services/iam-config.service.js +107 -0
  42. package/cjs/services/iam-datasource.provider.js +205 -0
  43. package/cjs/services/index.js +23 -0
  44. package/cjs/services/permission-cache.service.js +308 -0
  45. package/cjs/services/permission.service.js +1020 -0
  46. package/cjs/services/role.service.js +181 -0
  47. package/cjs/types/index.js +18 -0
  48. package/cjs/types/logic-node.type.js +54 -0
  49. package/fesm/config/iam.constants.js +1 -0
  50. package/fesm/config/index.js +1 -0
  51. package/fesm/controllers/action.controller.js +107 -0
  52. package/fesm/controllers/company-action-permission.controller.js +100 -0
  53. package/fesm/controllers/index.js +7 -0
  54. package/fesm/controllers/my-permission.controller.js +80 -0
  55. package/fesm/controllers/role-permission.controller.js +150 -0
  56. package/fesm/controllers/role.controller.js +48 -0
  57. package/fesm/controllers/user-action-permission.controller.js +100 -0
  58. package/fesm/docs/iam-swagger.config.js +192 -0
  59. package/fesm/docs/index.js +1 -0
  60. package/fesm/dtos/action.dto.js +317 -0
  61. package/fesm/dtos/index.js +4 -0
  62. package/fesm/dtos/permission.dto.js +490 -0
  63. package/fesm/dtos/role.dto.js +214 -0
  64. package/fesm/entities/action-base.entity.js +128 -0
  65. package/fesm/entities/action.entity.js +18 -0
  66. package/fesm/entities/index.js +56 -0
  67. package/fesm/entities/permission-base.entity.js +138 -0
  68. package/fesm/entities/permission-with-company.entity.js +89 -0
  69. package/fesm/entities/role-base.entity.js +79 -0
  70. package/fesm/entities/role-with-company.entity.js +45 -0
  71. package/fesm/entities/role.entity.js +15 -0
  72. package/fesm/entities/user-iam-permission.entity.js +38 -0
  73. package/fesm/enums/action-type.enum.js +12 -0
  74. package/fesm/enums/index.js +2 -0
  75. package/fesm/enums/permission-type.enum.js +6 -0
  76. package/fesm/helpers/index.js +2 -0
  77. package/fesm/helpers/permission-evaluator.helper.js +165 -0
  78. package/fesm/helpers/permission-mode.helper.js +49 -0
  79. package/fesm/index.js +11 -79
  80. package/fesm/interfaces/action.interface.js +3 -0
  81. package/fesm/interfaces/iam-module-async-options.interface.js +3 -0
  82. package/fesm/interfaces/iam-module-options.interface.js +1 -0
  83. package/fesm/interfaces/index.js +4 -0
  84. package/fesm/interfaces/role.interface.js +4 -0
  85. package/fesm/modules/iam.module.js +227 -0
  86. package/fesm/modules/index.js +1 -0
  87. package/fesm/services/action.service.js +243 -0
  88. package/fesm/services/iam-config.service.js +97 -0
  89. package/fesm/services/iam-datasource.provider.js +154 -0
  90. package/fesm/services/index.js +6 -0
  91. package/fesm/services/permission-cache.service.js +298 -0
  92. package/fesm/services/permission.service.js +1010 -0
  93. package/fesm/services/role.service.js +171 -0
  94. package/fesm/types/index.js +1 -0
  95. package/fesm/types/logic-node.type.js +36 -0
  96. package/package.json +25 -25
  97. package/cjs/config-index.js +0 -1
  98. package/cjs/controllers-index.js +0 -1
  99. package/cjs/docs-index.js +0 -79
  100. package/cjs/dtos-index.js +0 -1
  101. package/cjs/entities-index.js +0 -1
  102. package/cjs/enums-index.js +0 -1
  103. package/cjs/helpers-index.js +0 -1
  104. package/cjs/interfaces-index.js +0 -1
  105. package/cjs/modules-index.js +0 -1
  106. package/cjs/services-index.js +0 -1
  107. package/cjs/types-index.js +0 -1
  108. package/fesm/config-index.js +0 -1
  109. package/fesm/controllers-index.js +0 -1
  110. package/fesm/docs-index.js +0 -79
  111. package/fesm/dtos-index.js +0 -1
  112. package/fesm/entities-index.js +0 -1
  113. package/fesm/enums-index.js +0 -1
  114. package/fesm/helpers-index.js +0 -1
  115. package/fesm/interfaces-index.js +0 -0
  116. package/fesm/modules-index.js +0 -1
  117. package/fesm/services-index.js +0 -1
  118. package/fesm/types-index.js +0 -1
@@ -0,0 +1,154 @@
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
+ function _ts_param(paramIndex, decorator) {
24
+ return function(target, key) {
25
+ decorator(target, key, paramIndex);
26
+ };
27
+ }
28
+ import { MultiTenantDataSourceService } from '@flusys/nestjs-shared/modules';
29
+ import { Inject, Injectable, Logger, Optional, Scope } from '@nestjs/common';
30
+ import { REQUEST } from '@nestjs/core';
31
+ import { Request } from 'express';
32
+ import { IAM_MODULE_OPTIONS } from '../config/iam.constants';
33
+ import { IAMModuleOptions } from '../interfaces/iam-module-options.interface';
34
+ export class IAMDataSourceProvider extends MultiTenantDataSourceService {
35
+ // ==================== Factory Methods ====================
36
+ /**
37
+ * Build parent options from IAMModuleOptions
38
+ */ static buildParentOptions(options) {
39
+ return {
40
+ bootstrapAppConfig: options.bootstrapAppConfig,
41
+ defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
42
+ tenantDefaultDatabaseConfig: options.config?.tenantDefaultDatabaseConfig,
43
+ tenants: options.config?.tenants
44
+ };
45
+ }
46
+ // ==================== Feature Flags ====================
47
+ /**
48
+ * Get global enable company feature flag
49
+ */ getEnableCompanyFeature() {
50
+ return this.iamOptions.bootstrapAppConfig?.enableCompanyFeature ?? false;
51
+ }
52
+ /**
53
+ * Get enable company feature for specific tenant
54
+ * Falls back to global setting if not specified per-tenant
55
+ */ getEnableCompanyFeatureForTenant(tenant) {
56
+ return tenant?.enableCompanyFeature ?? this.getEnableCompanyFeature();
57
+ }
58
+ /**
59
+ * Get enable company feature for current request context
60
+ */ getEnableCompanyFeatureForCurrentTenant() {
61
+ return this.getEnableCompanyFeatureForTenant(this.getCurrentTenant() ?? undefined);
62
+ }
63
+ // ==================== Entity Management ====================
64
+ /**
65
+ * Get IAM entities dynamically based on configuration
66
+ * Returns appropriate entities based on company feature and permission mode
67
+ */ async getIAMEntities() {
68
+ const { Action, Role, RoleWithCompany, UserIamPermission, UserIamPermissionWithCompany, getIAMEntitiesByConfig } = await import('../entities');
69
+ const enableCompanyFeature = this.getEnableCompanyFeatureForCurrentTenant();
70
+ const permissionMode = this.iamOptions.bootstrapAppConfig?.permissionMode || 'FULL';
71
+ return getIAMEntitiesByConfig(enableCompanyFeature, permissionMode);
72
+ }
73
+ // ==================== Overrides ====================
74
+ /**
75
+ * Override to dynamically set entities based on tenant config
76
+ */ async createDataSourceFromConfig(config) {
77
+ const entities = await this.getIAMEntities();
78
+ return super.createDataSourceFromConfig(config, entities);
79
+ }
80
+ /**
81
+ * Override to use IAM-specific static cache
82
+ */ async getSingleDataSource() {
83
+ if (!IAMDataSourceProvider.singleDataSource) {
84
+ if (IAMDataSourceProvider.singleConnectionLock) {
85
+ return IAMDataSourceProvider.singleConnectionLock;
86
+ }
87
+ const lockPromise = (async ()=>{
88
+ const config = this.getDefaultDatabaseConfig();
89
+ if (!config) {
90
+ throw new Error('Default database config is not available');
91
+ }
92
+ const ds = await this.createDataSourceFromConfig(config);
93
+ IAMDataSourceProvider.singleDataSource = ds;
94
+ IAMDataSourceProvider.initialized = true;
95
+ return ds;
96
+ })();
97
+ IAMDataSourceProvider.singleConnectionLock = lockPromise;
98
+ try {
99
+ return await lockPromise;
100
+ } finally{
101
+ IAMDataSourceProvider.singleConnectionLock = null;
102
+ }
103
+ }
104
+ return IAMDataSourceProvider.singleDataSource;
105
+ }
106
+ /**
107
+ * Override to use IAM-specific static cache for tenant connections
108
+ */ async getOrCreateTenantConnection(tenant) {
109
+ // Return existing initialized connection from IAM-specific cache
110
+ const existing = IAMDataSourceProvider.tenantConnections.get(tenant.id);
111
+ if (existing?.isInitialized) {
112
+ return existing;
113
+ }
114
+ // If another request is creating this tenant's connection, wait for it
115
+ const pendingConnection = IAMDataSourceProvider.connectionLocks.get(tenant.id);
116
+ if (pendingConnection) {
117
+ return pendingConnection;
118
+ }
119
+ // Create connection with lock to prevent race conditions
120
+ const config = this.buildTenantDatabaseConfig(tenant);
121
+ const connectionPromise = this.createDataSourceFromConfig(config);
122
+ IAMDataSourceProvider.connectionLocks.set(tenant.id, connectionPromise);
123
+ try {
124
+ const dataSource = await connectionPromise;
125
+ IAMDataSourceProvider.tenantConnections.set(tenant.id, dataSource);
126
+ return dataSource;
127
+ } finally{
128
+ IAMDataSourceProvider.connectionLocks.delete(tenant.id);
129
+ }
130
+ }
131
+ constructor(iamOptions, request){
132
+ super(IAMDataSourceProvider.buildParentOptions(iamOptions), request), _define_property(this, "iamOptions", void 0), _define_property(this, "logger", void 0), this.iamOptions = iamOptions, this.logger = new Logger(IAMDataSourceProvider.name);
133
+ }
134
+ }
135
+ // Override parent's static properties to have IAM-specific cache
136
+ _define_property(IAMDataSourceProvider, "tenantConnections", new Map());
137
+ _define_property(IAMDataSourceProvider, "singleDataSource", null);
138
+ _define_property(IAMDataSourceProvider, "tenantsRegistry", new Map());
139
+ _define_property(IAMDataSourceProvider, "initialized", false);
140
+ _define_property(IAMDataSourceProvider, "connectionLocks", new Map());
141
+ _define_property(IAMDataSourceProvider, "singleConnectionLock", null);
142
+ IAMDataSourceProvider = _ts_decorate([
143
+ Injectable({
144
+ scope: Scope.REQUEST
145
+ }),
146
+ _ts_param(0, Inject(IAM_MODULE_OPTIONS)),
147
+ _ts_param(1, Optional()),
148
+ _ts_param(1, Inject(REQUEST)),
149
+ _ts_metadata("design:type", Function),
150
+ _ts_metadata("design:paramtypes", [
151
+ typeof IAMModuleOptions === "undefined" ? Object : IAMModuleOptions,
152
+ typeof Request === "undefined" ? Object : Request
153
+ ])
154
+ ], IAMDataSourceProvider);
@@ -0,0 +1,6 @@
1
+ export * from './action.service';
2
+ export * from './iam-config.service';
3
+ export * from './iam-datasource.provider';
4
+ export * from './permission-cache.service';
5
+ export * from './permission.service';
6
+ export * from './role.service';
@@ -0,0 +1,298 @@
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
+ function _ts_param(paramIndex, decorator) {
24
+ return function(target, key) {
25
+ decorator(target, key, paramIndex);
26
+ };
27
+ }
28
+ import { HybridCache } from '@flusys/nestjs-shared';
29
+ import { Inject, Injectable, Logger } from '@nestjs/common';
30
+ export class PermissionCacheService {
31
+ // ==================== Cache Key Generation ====================
32
+ /**
33
+ * Generate cache key for user permissions (backend codes for PermissionGuard)
34
+ * Format matches PermissionGuard in nestjs-shared
35
+ */ generateCacheKey(options) {
36
+ const { userId, companyId, branchId, enableCompanyFeature } = options;
37
+ if (enableCompanyFeature && companyId) {
38
+ return `${this.CACHE_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`;
39
+ }
40
+ return `${this.CACHE_PREFIX}:user:${userId}`;
41
+ }
42
+ /**
43
+ * Generate cache key for full my-permissions response
44
+ */ generateMyPermissionsCacheKey(options) {
45
+ const { userId, companyId, branchId, enableCompanyFeature } = options;
46
+ if (enableCompanyFeature && companyId) {
47
+ return `${this.MY_PERMISSIONS_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`;
48
+ }
49
+ return `${this.MY_PERMISSIONS_PREFIX}:user:${userId}`;
50
+ }
51
+ // ==================== Cache Operations ====================
52
+ /**
53
+ * Set user permissions in cache
54
+ *
55
+ * @param options - Cache key options
56
+ * @param permissions - Array of permission codes (action codes)
57
+ */ async setPermissions(options, permissions) {
58
+ try {
59
+ const key = this.generateCacheKey(options);
60
+ await this.cacheManager.set(key, permissions, this.TTL);
61
+ this.logger.debug(`Cached ${permissions.length} permissions for key: ${key}`);
62
+ } catch (error) {
63
+ this.logger.error(`Failed to cache permissions: ${error}`);
64
+ // Don't throw - cache failure shouldn't break the operation
65
+ }
66
+ }
67
+ /**
68
+ * Get user permissions from cache
69
+ *
70
+ * @param options - Cache key options
71
+ * @returns Array of permission codes or null if not found
72
+ */ async getPermissions(options) {
73
+ try {
74
+ const key = this.generateCacheKey(options);
75
+ const result = await this.cacheManager.get(key);
76
+ return result || null;
77
+ } catch (error) {
78
+ this.logger.error(`Failed to get permissions from cache: ${error}`);
79
+ return null;
80
+ }
81
+ }
82
+ // ==================== My-Permissions Cache Operations ====================
83
+ /**
84
+ * Set full my-permissions response in cache
85
+ * Caches frontend actions and backend codes for quick retrieval
86
+ *
87
+ * @param options - Cache key options
88
+ * @param data - Full permissions data to cache
89
+ */ async setMyPermissions(options, data) {
90
+ try {
91
+ const key = this.generateMyPermissionsCacheKey(options);
92
+ await this.cacheManager.set(key, data, this.TTL);
93
+ this.logger.debug(`Cached my-permissions for key: ${key} (${data.frontendActions.length} frontend, ${data.backendCodes.length} backend)`);
94
+ } catch (error) {
95
+ this.logger.error(`Failed to cache my-permissions: ${error}`);
96
+ }
97
+ }
98
+ /**
99
+ * Get full my-permissions response from cache
100
+ *
101
+ * @param options - Cache key options
102
+ * @returns Cached permissions data or null if not found
103
+ */ async getMyPermissions(options) {
104
+ try {
105
+ const key = this.generateMyPermissionsCacheKey(options);
106
+ const result = await this.cacheManager.get(key);
107
+ if (result) {
108
+ this.logger.debug(`Cache hit for my-permissions: ${key}`);
109
+ }
110
+ return result || null;
111
+ } catch (error) {
112
+ this.logger.error(`Failed to get my-permissions from cache: ${error}`);
113
+ return null;
114
+ }
115
+ }
116
+ // ==================== Action Code Cache Operations ====================
117
+ /**
118
+ * Cache action codes to IDs mapping
119
+ * Used for parentCodes filter to avoid DB query on cache hit
120
+ *
121
+ * @param codeToIdMap - Map of action code to action ID
122
+ */ async setActionCodeMap(codeToIdMap) {
123
+ try {
124
+ const key = `${this.ACTION_CODE_PREFIX}:map`;
125
+ await this.cacheManager.set(key, codeToIdMap, this.ACTION_CODE_TTL);
126
+ this.logger.debug(`Cached ${Object.keys(codeToIdMap).length} action code mappings`);
127
+ } catch (error) {
128
+ this.logger.error(`Failed to cache action code map: ${error}`);
129
+ }
130
+ }
131
+ /**
132
+ * Get action IDs for given codes from cache
133
+ *
134
+ * @param codes - Array of action codes
135
+ * @returns Map of code to ID, or null if not cached
136
+ */ async getActionIdsByCodes(codes) {
137
+ try {
138
+ const key = `${this.ACTION_CODE_PREFIX}:map`;
139
+ const fullMap = await this.cacheManager.get(key);
140
+ if (!fullMap) {
141
+ return null;
142
+ }
143
+ // Return only requested codes
144
+ const result = {};
145
+ for (const code of codes){
146
+ if (fullMap[code]) {
147
+ result[code] = fullMap[code];
148
+ }
149
+ }
150
+ return Object.keys(result).length > 0 ? result : null;
151
+ } catch (error) {
152
+ this.logger.error(`Failed to get action IDs from cache: ${error}`);
153
+ return null;
154
+ }
155
+ }
156
+ /**
157
+ * Invalidate action code cache
158
+ * Call when actions are created, updated, or deleted
159
+ */ async invalidateActionCodeCache() {
160
+ try {
161
+ const key = `${this.ACTION_CODE_PREFIX}:map`;
162
+ await this.cacheManager.del(key);
163
+ this.logger.debug('Invalidated action code cache');
164
+ } catch (error) {
165
+ this.logger.warn(`Failed to invalidate action code cache: ${error}`);
166
+ }
167
+ }
168
+ // ==================== Cache Invalidation ====================
169
+ /**
170
+ * Invalidate cache for specific user
171
+ * Clears both permission codes and my-permissions cache for ALL branches
172
+ *
173
+ * @param userId - User ID
174
+ * @param companyId - Optional company ID
175
+ * @param branchIds - Optional array of branch IDs to invalidate (if not provided, only null branch is cleared)
176
+ */ async invalidateUser(userId, companyId, branchIds) {
177
+ try {
178
+ const keysToDelete = [
179
+ // Permission codes cache (for PermissionGuard) - user-based key
180
+ `${this.CACHE_PREFIX}:user:${userId}`,
181
+ // My-permissions cache (full response) - user-based key
182
+ `${this.MY_PERMISSIONS_PREFIX}:user:${userId}`
183
+ ];
184
+ // Add company-based keys if companyId provided
185
+ if (companyId) {
186
+ // If branchIds provided, invalidate all specified branches
187
+ // Otherwise, invalidate only null branch (company-wide)
188
+ const branches = branchIds?.length ? branchIds : [
189
+ null
190
+ ];
191
+ for (const branchId of branches){
192
+ keysToDelete.push(`${this.CACHE_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`, `${this.MY_PERMISSIONS_PREFIX}:company:${companyId}:branch:${branchId || 'null'}:user:${userId}`);
193
+ }
194
+ }
195
+ // Parallel deletion for better performance
196
+ await Promise.all(keysToDelete.map((key)=>this.cacheManager.del(key)));
197
+ this.logger.debug(`Invalidated ${keysToDelete.length} cache keys for user ${userId}`);
198
+ } catch (error) {
199
+ this.logger.warn(`Failed to invalidate user cache for ${userId}: ${error}`);
200
+ }
201
+ }
202
+ /**
203
+ * Invalidate cache for multiple users
204
+ * Useful when role or company permissions change
205
+ *
206
+ * @param userIds - Array of user IDs
207
+ * @param companyId - Optional company ID
208
+ * @param branchIds - Optional array of branch IDs to invalidate for each user
209
+ * @returns Number of users whose cache was invalidated
210
+ */ async invalidateUsers(userIds, companyId, branchIds) {
211
+ if (userIds.length === 0) {
212
+ return 0;
213
+ }
214
+ // Parallel invalidation for better performance
215
+ const results = await Promise.allSettled(userIds.map((userId)=>this.invalidateUser(userId, companyId, branchIds)));
216
+ const successCount = results.filter((r)=>r.status === 'fulfilled').length;
217
+ const failedCount = results.filter((r)=>r.status === 'rejected').length;
218
+ if (failedCount > 0) {
219
+ this.logger.warn(`Failed to invalidate cache for ${failedCount} users`);
220
+ }
221
+ if (successCount > 0) {
222
+ this.logger.log(`Invalidated cache for ${successCount} users`);
223
+ }
224
+ return successCount;
225
+ }
226
+ /**
227
+ * Invalidate cache for all users in company
228
+ * WARNING: Without pattern matching support in HybridCache, this method
229
+ * invalidates individual user caches passed in the userIds array.
230
+ * For true company-wide invalidation, consider upgrading HybridCache with keys() support.
231
+ *
232
+ * @param companyId - Company ID
233
+ * @returns Number of cache entries deleted (always 0 without keys() support)
234
+ */ async invalidateCompany(companyId) {
235
+ // Note: HybridCache doesn't support pattern matching (keys() method)
236
+ // Company-wide invalidation requires passing individual user IDs
237
+ // This is a placeholder that logs a warning
238
+ this.logger.warn(`invalidateCompany called for ${companyId}, but pattern matching is not supported. ` + `Use invalidateUsers() with specific user IDs instead.`);
239
+ return 0;
240
+ }
241
+ /**
242
+ * Invalidate cache for all users with specific role
243
+ *
244
+ * @param roleId - Role ID (for logging)
245
+ * @param userIds - Array of user IDs who have this role
246
+ * @param companyId - Optional company ID
247
+ * @param branchIds - Optional array of branch IDs to invalidate
248
+ * @returns Number of users whose cache was invalidated
249
+ */ async invalidateRole(roleId, userIds, companyId, branchIds) {
250
+ if (userIds.length === 0) {
251
+ this.logger.debug(`No users found for role ${roleId}`);
252
+ return 0;
253
+ }
254
+ const count = await this.invalidateUsers(userIds, companyId, branchIds);
255
+ if (count > 0) {
256
+ this.logger.log(`Invalidated cache for ${count} users with role ${roleId}`);
257
+ }
258
+ return count;
259
+ }
260
+ // ==================== Administrative Operations ====================
261
+ /**
262
+ * Clear all permission caches
263
+ * Uses HybridCache reset methods (memory and redis)
264
+ * WARNING: Use with caution - this affects all caches, not just permissions
265
+ */ async clearAll() {
266
+ try {
267
+ await this.cacheManager.reset(); // Clear memory cache
268
+ await this.cacheManager.resetL2(); // Clear redis cache
269
+ this.logger.warn('Cleared all cache entries (memory and redis)');
270
+ } catch (error) {
271
+ this.logger.error(`Failed to clear all caches: ${error}`);
272
+ }
273
+ }
274
+ constructor(cacheManager){
275
+ _define_property(this, "cacheManager", void 0);
276
+ _define_property(this, "logger", void 0);
277
+ _define_property(this, "TTL", void 0); // 1 hour
278
+ _define_property(this, "ACTION_CODE_TTL", void 0); // 2 hours for action codes (less frequent changes)
279
+ _define_property(this, "CACHE_PREFIX", void 0);
280
+ _define_property(this, "MY_PERMISSIONS_PREFIX", void 0);
281
+ _define_property(this, "ACTION_CODE_PREFIX", void 0);
282
+ this.cacheManager = cacheManager;
283
+ this.logger = new Logger(PermissionCacheService.name);
284
+ this.TTL = 3600000;
285
+ this.ACTION_CODE_TTL = 7200000;
286
+ this.CACHE_PREFIX = 'permissions';
287
+ this.MY_PERMISSIONS_PREFIX = 'my-permissions';
288
+ this.ACTION_CODE_PREFIX = 'action-codes';
289
+ }
290
+ }
291
+ PermissionCacheService = _ts_decorate([
292
+ Injectable(),
293
+ _ts_param(0, Inject('CACHE_INSTANCE')),
294
+ _ts_metadata("design:type", Function),
295
+ _ts_metadata("design:paramtypes", [
296
+ typeof HybridCache === "undefined" ? Object : HybridCache
297
+ ])
298
+ ], PermissionCacheService);