@nest-omni/core 4.1.3-1 → 4.1.3-11

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 (249) hide show
  1. package/audit/audit.module.d.ts +10 -0
  2. package/audit/audit.module.js +39 -1
  3. package/audit/controllers/audit.controller.d.ts +24 -0
  4. package/audit/controllers/audit.controller.js +24 -0
  5. package/audit/decorators/audit-controller.decorator.d.ts +9 -1
  6. package/audit/decorators/audit-controller.decorator.js +11 -2
  7. package/audit/decorators/audit-operation.decorator.d.ts +45 -0
  8. package/audit/decorators/audit-operation.decorator.js +49 -0
  9. package/audit/decorators/entity-audit.decorator.d.ts +76 -1
  10. package/audit/decorators/entity-audit.decorator.js +135 -3
  11. package/audit/decorators/index.d.ts +1 -0
  12. package/audit/decorators/index.js +1 -0
  13. package/audit/dto/audit-log-query.dto.d.ts +3 -0
  14. package/audit/dto/audit-log-query.dto.js +3 -0
  15. package/audit/dto/begin-transaction.dto.d.ts +3 -0
  16. package/audit/dto/begin-transaction.dto.js +3 -0
  17. package/audit/dto/compare-entities.dto.d.ts +3 -0
  18. package/audit/dto/compare-entities.dto.js +3 -0
  19. package/audit/dto/pre-check-restore.dto.d.ts +3 -0
  20. package/audit/dto/pre-check-restore.dto.js +3 -0
  21. package/audit/dto/restore-entity.dto.d.ts +3 -0
  22. package/audit/dto/restore-entity.dto.js +3 -0
  23. package/audit/entities/entity-audit-log.entity.d.ts +8 -0
  24. package/audit/entities/entity-audit-log.entity.js +33 -1
  25. package/audit/entities/entity-transaction.entity.d.ts +10 -0
  26. package/audit/entities/entity-transaction.entity.js +33 -1
  27. package/audit/entities/index.d.ts +2 -0
  28. package/audit/entities/index.js +2 -0
  29. package/audit/entities/manual-operation-log.entity.d.ts +4 -0
  30. package/audit/entities/manual-operation-log.entity.js +4 -0
  31. package/audit/entities/operation-template.entity.d.ts +4 -0
  32. package/audit/entities/operation-template.entity.js +4 -0
  33. package/audit/enums/audit.enums.d.ts +45 -5
  34. package/audit/enums/audit.enums.js +47 -4
  35. package/audit/index.d.ts +3 -1
  36. package/audit/index.js +30 -1
  37. package/audit/interceptors/audit.interceptor.d.ts +15 -0
  38. package/audit/interceptors/audit.interceptor.js +23 -1
  39. package/audit/interfaces/audit.interfaces.d.ts +182 -2
  40. package/audit/services/audit-context.service.d.ts +15 -0
  41. package/audit/services/audit-context.service.js +15 -0
  42. package/audit/services/audit-strategy.service.d.ts +6 -0
  43. package/audit/services/audit-strategy.service.js +13 -0
  44. package/audit/services/entity-audit.service.d.ts +129 -3
  45. package/audit/services/entity-audit.service.js +301 -6
  46. package/audit/services/index.d.ts +2 -0
  47. package/audit/services/index.js +2 -0
  48. package/audit/services/manual-audit-log.service.d.ts +124 -0
  49. package/audit/services/manual-audit-log.service.js +138 -0
  50. package/audit/services/multi-database.service.d.ts +12 -0
  51. package/audit/services/multi-database.service.js +12 -0
  52. package/audit/services/operation-description.service.d.ts +59 -0
  53. package/audit/services/operation-description.service.js +76 -2
  54. package/audit/services/transaction-audit.service.d.ts +30 -0
  55. package/audit/services/transaction-audit.service.js +47 -0
  56. package/audit/subscribers/entity-audit.subscriber.d.ts +15 -0
  57. package/audit/subscribers/entity-audit.subscriber.js +29 -1
  58. package/cache/cache-metrics.service.d.ts +67 -0
  59. package/cache/cache-metrics.service.js +68 -4
  60. package/cache/cache-serialization.service.d.ts +31 -0
  61. package/cache/cache-serialization.service.js +25 -0
  62. package/cache/cache.constants.d.ts +9 -0
  63. package/cache/cache.constants.js +9 -0
  64. package/cache/cache.health.d.ts +26 -0
  65. package/cache/cache.health.js +30 -0
  66. package/cache/cache.module.d.ts +86 -0
  67. package/cache/cache.module.js +71 -0
  68. package/cache/cache.service.d.ts +140 -0
  69. package/cache/cache.service.js +157 -0
  70. package/cache/cache.warmup.service.d.ts +39 -0
  71. package/cache/cache.warmup.service.js +32 -0
  72. package/cache/decorators/cache-evict.decorator.d.ts +47 -0
  73. package/cache/decorators/cache-evict.decorator.js +56 -0
  74. package/cache/decorators/cache-put.decorator.d.ts +34 -0
  75. package/cache/decorators/cache-put.decorator.js +39 -0
  76. package/cache/decorators/cacheable.decorator.d.ts +40 -0
  77. package/cache/decorators/cacheable.decorator.js +55 -0
  78. package/cache/dependencies/callback.dependency.d.ts +33 -0
  79. package/cache/dependencies/callback.dependency.js +39 -1
  80. package/cache/dependencies/chain.dependency.d.ts +28 -0
  81. package/cache/dependencies/chain.dependency.js +34 -0
  82. package/cache/dependencies/db.dependency.d.ts +45 -0
  83. package/cache/dependencies/db.dependency.js +48 -1
  84. package/cache/dependencies/file.dependency.d.ts +32 -0
  85. package/cache/dependencies/file.dependency.js +34 -0
  86. package/cache/dependencies/tag.dependency.d.ts +36 -0
  87. package/cache/dependencies/tag.dependency.js +36 -0
  88. package/cache/dependencies/time.dependency.d.ts +43 -0
  89. package/cache/dependencies/time.dependency.js +43 -0
  90. package/cache/examples/basic-usage.d.ts +15 -0
  91. package/cache/examples/basic-usage.js +62 -8
  92. package/cache/index.js +9 -0
  93. package/cache/interfaces/cache-dependency.interface.d.ts +53 -0
  94. package/cache/interfaces/cache-options.interface.d.ts +81 -0
  95. package/cache/interfaces/cache-options.interface.js +6 -0
  96. package/cache/interfaces/cache-provider.interface.d.ts +78 -0
  97. package/cache/providers/base-cache.provider.d.ts +14 -0
  98. package/cache/providers/base-cache.provider.js +16 -0
  99. package/cache/providers/cls-cache.provider.d.ts +20 -0
  100. package/cache/providers/cls-cache.provider.js +28 -0
  101. package/cache/providers/memory-cache.provider.d.ts +23 -0
  102. package/cache/providers/memory-cache.provider.js +26 -0
  103. package/cache/providers/redis-cache.provider.d.ts +26 -0
  104. package/cache/providers/redis-cache.provider.js +29 -0
  105. package/cache/utils/dependency-manager.util.d.ts +52 -0
  106. package/cache/utils/dependency-manager.util.js +59 -0
  107. package/cache/utils/key-generator.util.d.ts +42 -0
  108. package/cache/utils/key-generator.util.js +53 -1
  109. package/common/abstract.entity.d.ts +14 -0
  110. package/common/abstract.entity.js +14 -0
  111. package/common/boilerplate.polyfill.d.ts +142 -4
  112. package/common/boilerplate.polyfill.js +24 -100
  113. package/common/dto/dto-container.d.ts +16 -0
  114. package/common/dto/dto-container.js +20 -0
  115. package/common/dto/dto-decorators.d.ts +18 -0
  116. package/common/dto/dto-decorators.js +14 -0
  117. package/common/dto/dto-extensions.d.ts +11 -0
  118. package/common/dto/dto-extensions.js +9 -0
  119. package/common/dto/dto-service-accessor.d.ts +17 -0
  120. package/common/dto/dto-service-accessor.js +18 -0
  121. package/common/dto/dto-transformer.d.ts +12 -0
  122. package/common/dto/dto-transformer.js +9 -0
  123. package/common/dto/index.js +2 -0
  124. package/common/examples/paginate-and-map.example.d.ts +6 -0
  125. package/common/examples/paginate-and-map.example.js +26 -0
  126. package/common/utils.d.ts +15 -0
  127. package/common/utils.js +15 -0
  128. package/constants/language-code.js +1 -0
  129. package/decorators/field.decorators.js +8 -1
  130. package/decorators/property.decorators.js +1 -0
  131. package/decorators/public-route.decorator.js +1 -0
  132. package/decorators/transform.decorators.d.ts +27 -0
  133. package/decorators/transform.decorators.js +29 -0
  134. package/decorators/translate.decorator.js +1 -0
  135. package/decorators/user.decorator.js +1 -0
  136. package/decorators/validator.decorators.d.ts +8 -18
  137. package/decorators/validator.decorators.js +22 -190
  138. package/filters/constraint-errors.js +1 -0
  139. package/helpers/common.helper.d.ts +13 -0
  140. package/helpers/common.helper.js +13 -0
  141. package/http-client/config/http-client.config.d.ts +15 -0
  142. package/http-client/config/http-client.config.js +25 -9
  143. package/http-client/decorators/http-client.decorators.d.ts +63 -0
  144. package/http-client/decorators/http-client.decorators.js +71 -3
  145. package/http-client/entities/http-log.entity.d.ts +229 -0
  146. package/http-client/entities/http-log.entity.js +6 -1
  147. package/http-client/errors/http-client.errors.d.ts +57 -0
  148. package/http-client/errors/http-client.errors.js +58 -0
  149. package/http-client/examples/advanced-usage.example.d.ts +41 -0
  150. package/http-client/examples/advanced-usage.example.js +68 -24
  151. package/http-client/examples/auth-with-waiting-lock.example.d.ts +31 -0
  152. package/http-client/examples/auth-with-waiting-lock.example.js +52 -5
  153. package/http-client/examples/basic-usage.example.d.ts +60 -0
  154. package/http-client/examples/basic-usage.example.js +60 -0
  155. package/http-client/examples/multi-api-configuration.example.d.ts +60 -0
  156. package/http-client/examples/multi-api-configuration.example.js +76 -5
  157. package/http-client/http-client.module.d.ts +13 -0
  158. package/http-client/http-client.module.js +20 -5
  159. package/http-client/index.js +8 -0
  160. package/http-client/interfaces/api-client-config.interface.d.ts +125 -0
  161. package/http-client/interfaces/api-client-config.interface.js +3 -0
  162. package/http-client/interfaces/http-client-config.interface.d.ts +60 -0
  163. package/http-client/services/api-client-registry.service.d.ts +57 -0
  164. package/http-client/services/api-client-registry.service.js +84 -1
  165. package/http-client/services/cache.service.d.ts +52 -0
  166. package/http-client/services/cache.service.js +72 -3
  167. package/http-client/services/circuit-breaker.service.d.ts +46 -0
  168. package/http-client/services/circuit-breaker.service.js +52 -0
  169. package/http-client/services/http-client.service.d.ts +67 -0
  170. package/http-client/services/http-client.service.js +105 -4
  171. package/http-client/services/http-log-query.service.d.ts +83 -0
  172. package/http-client/services/http-log-query.service.js +122 -1
  173. package/http-client/services/http-replay.service.d.ts +101 -0
  174. package/http-client/services/http-replay.service.js +86 -0
  175. package/http-client/services/log-cleanup.service.d.ts +63 -0
  176. package/http-client/services/log-cleanup.service.js +54 -2
  177. package/http-client/services/logging.service.d.ts +40 -0
  178. package/http-client/services/logging.service.js +53 -0
  179. package/http-client/utils/call-stack-extractor.util.d.ts +37 -0
  180. package/http-client/utils/call-stack-extractor.util.js +48 -0
  181. package/http-client/utils/context-extractor.util.d.ts +49 -0
  182. package/http-client/utils/context-extractor.util.js +52 -0
  183. package/http-client/utils/curl-generator.util.d.ts +21 -0
  184. package/http-client/utils/curl-generator.util.js +44 -3
  185. package/http-client/utils/request-id.util.d.ts +18 -0
  186. package/http-client/utils/request-id.util.js +20 -0
  187. package/http-client/utils/retry-recorder.util.d.ts +42 -0
  188. package/http-client/utils/retry-recorder.util.js +44 -0
  189. package/i18n/en_US/validation.json +2 -1
  190. package/i18n/zh_CN/validation.json +2 -1
  191. package/index.js +8 -0
  192. package/interceptors/translation-interceptor.service.js +5 -0
  193. package/package.json +1 -1
  194. package/providers/context.provider.js +2 -0
  195. package/providers/generator.provider.d.ts +4 -0
  196. package/providers/generator.provider.js +4 -0
  197. package/redis-lock/comprehensive-lock-cleanup.service.d.ts +94 -0
  198. package/redis-lock/comprehensive-lock-cleanup.service.js +253 -0
  199. package/redis-lock/examples/lock-strategy.examples.d.ts +89 -0
  200. package/redis-lock/examples/lock-strategy.examples.js +130 -15
  201. package/redis-lock/index.d.ts +2 -0
  202. package/redis-lock/index.js +8 -1
  203. package/redis-lock/lock-heartbeat.service.d.ts +78 -0
  204. package/redis-lock/lock-heartbeat.service.js +222 -0
  205. package/redis-lock/redis-lock.decorator.d.ts +101 -0
  206. package/redis-lock/redis-lock.decorator.js +120 -0
  207. package/redis-lock/redis-lock.module.d.ts +66 -0
  208. package/redis-lock/redis-lock.module.js +175 -70
  209. package/redis-lock/redis-lock.service.d.ts +278 -0
  210. package/redis-lock/redis-lock.service.js +282 -12
  211. package/setup/bootstrap.setup.js +20 -0
  212. package/setup/mode.setup.d.ts +44 -0
  213. package/setup/mode.setup.js +44 -0
  214. package/setup/schedule.decorator.d.ts +227 -0
  215. package/setup/schedule.decorator.js +235 -12
  216. package/setup/worker.decorator.d.ts +86 -0
  217. package/setup/worker.decorator.js +88 -0
  218. package/shared/serviceRegistryModule.js +27 -14
  219. package/shared/services/api-config.service.d.ts +3 -0
  220. package/shared/services/api-config.service.js +20 -9
  221. package/validator-json/decorators.d.ts +17 -0
  222. package/validator-json/decorators.js +17 -2
  223. package/validator-json/default.d.ts +6 -0
  224. package/validator-json/default.js +30 -2
  225. package/validator-json/defaultConverters.js +1 -0
  226. package/validator-json/options.d.ts +23 -0
  227. package/validators/common-validators.d.ts +143 -0
  228. package/validators/common-validators.js +249 -0
  229. package/validators/custom-validate.examples.d.ts +96 -0
  230. package/validators/custom-validate.examples.js +400 -0
  231. package/validators/custom-validate.validator.d.ts +134 -0
  232. package/validators/custom-validate.validator.js +214 -0
  233. package/validators/index.d.ts +2 -0
  234. package/validators/index.js +2 -0
  235. package/validators/is-exists.validator.d.ts +18 -4
  236. package/validators/is-exists.validator.js +67 -6
  237. package/validators/is-unique.validator.d.ts +32 -5
  238. package/validators/is-unique.validator.js +99 -17
  239. package/validators/skip-empty.validator.d.ts +5 -0
  240. package/validators/skip-empty.validator.js +5 -0
  241. package/vault/interfaces/vault-options.interface.d.ts +9 -0
  242. package/vault/vault-config.loader.d.ts +30 -0
  243. package/vault/vault-config.loader.js +48 -1
  244. package/vault/vault-config.service.d.ts +53 -0
  245. package/vault/vault-config.service.js +57 -0
  246. package/vault/vault.module.d.ts +4 -0
  247. package/vault/vault.module.js +4 -0
  248. package/decorators/examples/validation-decorators.example.d.ts +0 -69
  249. package/decorators/examples/validation-decorators.example.js +0 -331
@@ -21,39 +21,96 @@ var RedisLockService_1;
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.RedisLockService = exports.LockStrategy = void 0;
23
23
  const common_1 = require("@nestjs/common");
24
+ /**
25
+ * Lock acquisition strategy
26
+ */
24
27
  var LockStrategy;
25
28
  (function (LockStrategy) {
29
+ /**
30
+ * Skip execution if lock cannot be acquired immediately
31
+ * This is the default behavior for backward compatibility
32
+ */
26
33
  LockStrategy["SKIP"] = "SKIP";
34
+ /**
35
+ * Throw an error if lock cannot be acquired
36
+ * Useful for critical operations that must have exclusive access
37
+ */
27
38
  LockStrategy["THROW"] = "THROW";
39
+ /**
40
+ * Wait/block until the lock can be acquired
41
+ * Will retry indefinitely with the specified retry delay
42
+ */
28
43
  LockStrategy["WAIT"] = "WAIT";
29
44
  })(LockStrategy || (exports.LockStrategy = LockStrategy = {}));
45
+ /**
46
+ * Redis-based distributed lock service
47
+ *
48
+ * Provides thread-safe, distributed locking mechanism using Redis.
49
+ * Supports automatic lock expiration, retries, proper lock release,
50
+ * and automatic lock extension.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * constructor(private lockService: RedisLockService) {}
55
+ *
56
+ * async processTask() {
57
+ * const result = await this.lockService.acquireLock('task:process', { ttl: 60000 });
58
+ *
59
+ * if (result.acquired) {
60
+ * try {
61
+ * // Do work
62
+ * } finally {
63
+ * await this.lockService.releaseLock('task:process', result.lockValue);
64
+ * }
65
+ * }
66
+ * }
67
+ * ```
68
+ */
30
69
  let RedisLockService = RedisLockService_1 = class RedisLockService {
31
70
  constructor() {
32
71
  this.logger = new common_1.Logger(RedisLockService_1.name);
33
72
  this.redis = null;
34
73
  this.defaultOptions = {
35
- ttl: 300000,
36
- retryCount: 0,
37
- retryDelay: 100,
74
+ ttl: 300000, // 5 minutes
75
+ retryCount: 3, // Increased from 0 to provide reasonable retry attempts
76
+ retryDelay: 500, // Increased from 100 to reduce retry frequency
38
77
  keyPrefix: 'lock',
39
78
  throwOnFailure: false,
40
79
  strategy: LockStrategy.SKIP,
41
80
  waitTimeout: undefined,
42
- autoExtend: 0,
81
+ autoExtend: 0, // No auto-extension by default
82
+ useExponentialBackoff: true, // Enable exponential backoff by default
83
+ maxRetryDelay: 30000, // Maximum retry delay of 30 seconds
84
+ retryJitter: 100, // Add random jitter to avoid synchronized retries
43
85
  };
86
+ // Set global instance when created
44
87
  if (!RedisLockService_1.globalInstance) {
45
88
  RedisLockService_1.globalInstance = this;
46
89
  }
90
+ // Initialize instance ID
91
+ this.instanceId = this.getInstanceId();
47
92
  }
93
+ /**
94
+ * Get the global singleton instance
95
+ * This allows decorators to access the service without dependency injection
96
+ */
48
97
  static getGlobalInstance() {
49
98
  return RedisLockService_1.globalInstance;
50
99
  }
100
+ /**
101
+ * Get or create the global singleton instance
102
+ * If no instance exists, it will create one
103
+ */
51
104
  static getOrCreateGlobalInstance() {
52
105
  if (!RedisLockService_1.globalInstance) {
53
106
  RedisLockService_1.globalInstance = new RedisLockService_1();
54
107
  }
55
108
  return RedisLockService_1.globalInstance;
56
109
  }
110
+ /**
111
+ * Set the global singleton instance
112
+ * Useful for testing or manual setup
113
+ */
57
114
  static setGlobalInstance(instance) {
58
115
  RedisLockService_1.globalInstance = instance;
59
116
  }
@@ -69,37 +126,65 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
69
126
  }
70
127
  onModuleDestroy() {
71
128
  return __awaiter(this, void 0, void 0, function* () {
129
+ // We don't manage the Redis connection lifecycle anymore
130
+ // The injected Redis client is managed by the Redis module
72
131
  this.logger.log('RedisLockService destroyed');
73
132
  });
74
133
  }
134
+ /**
135
+ * Set Redis client (required for dependency injection)
136
+ * This service requires an external Redis client to function
137
+ */
75
138
  setRedisClient(redis) {
76
139
  this.redis = redis;
77
140
  this.logger.log('Redis client set for RedisLockService');
78
141
  }
142
+ /**
143
+ * Set default TTL for locks
144
+ */
79
145
  setDefaultTtl(ttl) {
80
146
  this.defaultOptions.ttl = ttl;
81
147
  }
148
+ /**
149
+ * Set default key prefix for locks
150
+ */
82
151
  setDefaultKeyPrefix(prefix) {
83
152
  this.defaultOptions.keyPrefix = prefix;
84
153
  }
154
+ /**
155
+ * Set default retry count for lock acquisition
156
+ */
85
157
  setDefaultRetryCount(count) {
86
158
  this.defaultOptions.retryCount = count;
87
159
  }
160
+ /**
161
+ * Set default retry delay for lock acquisition
162
+ */
88
163
  setDefaultRetryDelay(delay) {
89
164
  this.defaultOptions.retryDelay = delay;
90
165
  }
166
+ /**
167
+ * Acquire a distributed lock
168
+ *
169
+ * @param key - Lock key (will be prefixed with 'lock:')
170
+ * @param options - Lock options
171
+ * @returns Lock acquisition result
172
+ */
91
173
  acquireLock(key_1) {
92
174
  return __awaiter(this, arguments, void 0, function* (key, options = {}) {
175
+ var _a, _b, _c, _d, _e;
93
176
  const redis = yield this.getRedis();
94
177
  const opts = Object.assign(Object.assign({}, this.defaultOptions), options);
178
+ // Handle backward compatibility: if throwOnFailure is true, use THROW strategy
95
179
  if (opts.throwOnFailure && !options.strategy) {
96
180
  opts.strategy = LockStrategy.THROW;
97
181
  }
98
182
  const lockKey = this.buildLockKey(key, opts.keyPrefix);
99
183
  const lockValue = this.generateLockValue();
184
+ // Determine max attempts based on strategy
100
185
  let maxAttempts;
101
186
  if (opts.strategy === LockStrategy.WAIT) {
102
- maxAttempts = Number.MAX_SAFE_INTEGER;
187
+ maxAttempts = Number.MAX_SAFE_INTEGER; // Infinite retries for WAIT strategy
103
188
  }
104
189
  else {
105
190
  maxAttempts = opts.retryCount + 1;
@@ -107,6 +192,7 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
107
192
  let attempts = 0;
108
193
  const startTime = Date.now();
109
194
  while (attempts < maxAttempts) {
195
+ // Check timeout for WAIT strategy
110
196
  if (opts.strategy === LockStrategy.WAIT && opts.waitTimeout) {
111
197
  const elapsed = Date.now() - startTime;
112
198
  if (elapsed >= opts.waitTimeout) {
@@ -116,9 +202,11 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
116
202
  }
117
203
  }
118
204
  try {
205
+ // Use SET with NX (Not eXists) and PX (expiration in milliseconds)
119
206
  const result = yield redis.set(lockKey, lockValue, 'PX', opts.ttl, 'NX');
120
207
  if (result === 'OK') {
121
208
  this.logger.debug(`Lock acquired: ${lockKey} (value: ${lockValue}, ttl: ${opts.ttl}ms, attempts: ${attempts + 1})`);
209
+ // Set up auto-extension if enabled
122
210
  let autoExtendTimer;
123
211
  if (opts.autoExtend && opts.autoExtend > 0) {
124
212
  autoExtendTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
@@ -132,7 +220,9 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
132
220
  }
133
221
  return { acquired: true, lockValue, autoExtendTimer };
134
222
  }
223
+ // Lock acquisition failed
135
224
  attempts++;
225
+ // For WAIT strategy, log less frequently to avoid spam
136
226
  if (opts.strategy === LockStrategy.WAIT) {
137
227
  if (attempts === 1 || attempts % 10 === 0) {
138
228
  this.logger.debug(`Waiting for lock ${lockKey}... (attempt ${attempts})`);
@@ -141,19 +231,47 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
141
231
  else if (attempts < maxAttempts) {
142
232
  this.logger.debug(`Lock acquisition failed for ${lockKey}, retrying... (${attempts}/${opts.retryCount})`);
143
233
  }
234
+ // Wait before next attempt (except for last attempt)
144
235
  if (attempts < maxAttempts) {
145
- yield this.sleep(opts.retryDelay);
236
+ // Add random jitter to avoid synchronized retries
237
+ const jitter = opts.retryJitter ? Math.random() * opts.retryJitter : 0;
238
+ const totalDelay = opts.retryDelay + jitter;
239
+ yield this.sleep(totalDelay);
146
240
  }
147
241
  }
148
242
  catch (error) {
149
- const errorMessage = `Error acquiring lock for ${lockKey}: ${error.message}`;
150
- this.logger.error(errorMessage, error.stack);
151
- if (opts.strategy === LockStrategy.THROW) {
152
- throw new Error(errorMessage);
243
+ // Enhanced error classification and handling
244
+ if (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('ECONNREFUSED')) ||
245
+ ((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('ECONNRESET')) ||
246
+ error.code === 'ECONNREFUSED') {
247
+ // Connection error, will retry - log at debug level
248
+ this.logger.debug(`Redis connection error, will retry: ${error.message}`);
249
+ }
250
+ else if (((_c = error.message) === null || _c === void 0 ? void 0 : _c.includes('READONLY')) ||
251
+ ((_d = error.message) === null || _d === void 0 ? void 0 : _d.includes('LOADING')) ||
252
+ ((_e = error.message) === null || _e === void 0 ? void 0 : _e.includes('MASTERDOWN'))) {
253
+ // Redis server error, log at warn level
254
+ this.logger.warn(`Redis server error: ${error.message}`);
255
+ }
256
+ else {
257
+ // Other errors, log at error level
258
+ const errorMessage = `Error acquiring lock for ${lockKey}: ${error.message}`;
259
+ this.logger.error(errorMessage, error.stack);
260
+ if (opts.strategy === LockStrategy.THROW) {
261
+ throw new Error(errorMessage);
262
+ }
263
+ return { acquired: false, error: errorMessage };
264
+ }
265
+ // For retryable errors, continue with retry logic
266
+ attempts++;
267
+ if (attempts < maxAttempts) {
268
+ const jitter = opts.retryJitter ? Math.random() * opts.retryJitter : 0;
269
+ const totalDelay = opts.retryDelay + jitter;
270
+ yield this.sleep(totalDelay);
153
271
  }
154
- return { acquired: false, error: errorMessage };
155
272
  }
156
273
  }
274
+ // Max attempts reached
157
275
  const failureMessage = `Failed to acquire lock for ${lockKey} after ${attempts} attempts`;
158
276
  this.logger.warn(failureMessage);
159
277
  if (opts.strategy === LockStrategy.THROW) {
@@ -162,11 +280,23 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
162
280
  return { acquired: false, error: failureMessage };
163
281
  });
164
282
  }
283
+ /**
284
+ * Release a distributed lock
285
+ *
286
+ * Uses Lua script to ensure atomic check-and-delete operation
287
+ * Only releases the lock if the value matches (prevents releasing someone else's lock)
288
+ *
289
+ * @param key - Lock key
290
+ * @param lockValue - Lock value obtained during acquisition
291
+ * @param keyPrefix - Key prefix (must match acquisition)
292
+ * @returns True if lock was released, false otherwise
293
+ */
165
294
  releaseLock(key_1, lockValue_1) {
166
295
  return __awaiter(this, arguments, void 0, function* (key, lockValue, keyPrefix = 'lock') {
167
296
  const redis = yield this.getRedis();
168
297
  const lockKey = this.buildLockKey(key, keyPrefix);
169
298
  try {
299
+ // Lua script to atomically check and delete the lock
170
300
  const script = `
171
301
  if redis.call("get", KEYS[1]) == ARGV[1] then
172
302
  return redis.call("del", KEYS[1])
@@ -190,11 +320,21 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
190
320
  }
191
321
  });
192
322
  }
323
+ /**
324
+ * Extend the expiration time of an existing lock
325
+ *
326
+ * @param key - Lock key
327
+ * @param lockValue - Lock value obtained during acquisition
328
+ * @param ttl - New TTL in milliseconds
329
+ * @param keyPrefix - Key prefix (must match acquisition)
330
+ * @returns True if lock was extended, false otherwise
331
+ */
193
332
  extendLock(key_1, lockValue_1, ttl_1) {
194
333
  return __awaiter(this, arguments, void 0, function* (key, lockValue, ttl, keyPrefix = 'lock') {
195
334
  const redis = yield this.getRedis();
196
335
  const lockKey = this.buildLockKey(key, keyPrefix);
197
336
  try {
337
+ // Lua script to atomically check value and extend TTL
198
338
  const script = `
199
339
  if redis.call("get", KEYS[1]) == ARGV[1] then
200
340
  return redis.call("pexpire", KEYS[1], ARGV[2])
@@ -218,6 +358,13 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
218
358
  }
219
359
  });
220
360
  }
361
+ /**
362
+ * Check if a lock exists
363
+ *
364
+ * @param key - Lock key
365
+ * @param keyPrefix - Key prefix
366
+ * @returns True if lock exists, false otherwise
367
+ */
221
368
  isLocked(key_1) {
222
369
  return __awaiter(this, arguments, void 0, function* (key, keyPrefix = 'lock') {
223
370
  const redis = yield this.getRedis();
@@ -232,6 +379,14 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
232
379
  }
233
380
  });
234
381
  }
382
+ /**
383
+ * Execute a function with automatic lock acquisition and release
384
+ *
385
+ * @param key - Lock key
386
+ * @param fn - Function to execute while holding the lock
387
+ * @param options - Lock options
388
+ * @returns Result of the function execution, or null if lock couldn't be acquired
389
+ */
235
390
  withLock(key_1, fn_1) {
236
391
  return __awaiter(this, arguments, void 0, function* (key, fn, options = {}) {
237
392
  const lockResult = yield this.acquireLock(key, options);
@@ -247,6 +402,7 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
247
402
  throw error;
248
403
  }
249
404
  finally {
405
+ // Clear auto-extension timer if it exists
250
406
  if (lockResult.autoExtendTimer) {
251
407
  clearInterval(lockResult.autoExtendTimer);
252
408
  }
@@ -254,6 +410,13 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
254
410
  }
255
411
  });
256
412
  }
413
+ /**
414
+ * Get lock information from Redis
415
+ *
416
+ * @param key - Lock key
417
+ * @param keyPrefix - Key prefix
418
+ * @returns Lock information including TTL and value
419
+ */
257
420
  getLockInfo(key_1) {
258
421
  return __awaiter(this, arguments, void 0, function* (key, keyPrefix = 'lock') {
259
422
  const redis = yield this.getRedis();
@@ -274,6 +437,13 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
274
437
  }
275
438
  });
276
439
  }
440
+ /**
441
+ * Force release a lock (use with caution)
442
+ *
443
+ * @param key - Lock key
444
+ * @param keyPrefix - Key prefix
445
+ * @returns True if lock was released, false otherwise
446
+ */
277
447
  forceRelease(key_1) {
278
448
  return __awaiter(this, arguments, void 0, function* (key, keyPrefix = 'lock') {
279
449
  const redis = yield this.getRedis();
@@ -295,6 +465,25 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
295
465
  }
296
466
  });
297
467
  }
468
+ /**
469
+ * Clean up locks by pattern
470
+ * Useful for cleaning up locks after process restart or for specific services
471
+ *
472
+ * WARNING: Use with caution in production! This will forcefully delete locks.
473
+ *
474
+ * @param pattern - Lock pattern (e.g., 'MyService:*' or '*:migration:*')
475
+ * @param keyPrefix - Key prefix (default: 'lock')
476
+ * @returns Number of locks deleted
477
+ *
478
+ * @example
479
+ * ```typescript
480
+ * // Clean up all locks for MyService
481
+ * await lockService.cleanupLocksByPattern('MyService:*');
482
+ *
483
+ * // Clean up all migration locks
484
+ * await lockService.cleanupLocksByPattern('*:migration:*');
485
+ * ```
486
+ */
298
487
  cleanupLocksByPattern(pattern_1) {
299
488
  return __awaiter(this, arguments, void 0, function* (pattern, keyPrefix = 'lock') {
300
489
  const redis = yield this.getRedis();
@@ -315,6 +504,28 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
315
504
  }
316
505
  });
317
506
  }
507
+ /**
508
+ * Clean up locks on startup
509
+ * This method is useful for cleaning up stale locks from previous process instances
510
+ *
511
+ * WARNING: Only use this if you're sure the locks are from previous process instances!
512
+ *
513
+ * @param patterns - Array of lock patterns to clean up
514
+ * @param keyPrefix - Key prefix (default: 'lock')
515
+ * @returns Total number of locks deleted
516
+ *
517
+ * @example
518
+ * ```typescript
519
+ * // In your module's onModuleInit
520
+ * async onModuleInit() {
521
+ * // Clean up locks from this service that might be left from previous restart
522
+ * await this.lockService.cleanupOnStartup([
523
+ * 'MyService:*',
524
+ * 'AnotherService:*'
525
+ * ]);
526
+ * }
527
+ * ```
528
+ */
318
529
  cleanupOnStartup(patterns_1) {
319
530
  return __awaiter(this, arguments, void 0, function* (patterns, keyPrefix = 'lock') {
320
531
  this.logger.log(`Starting lock cleanup for ${patterns.length} pattern(s) on startup...`);
@@ -327,10 +538,17 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
327
538
  return totalDeleted;
328
539
  });
329
540
  }
541
+ /**
542
+ * Health check for Redis connection
543
+ * Returns information about Redis connectivity and lock service status
544
+ *
545
+ * @returns Health check result
546
+ */
330
547
  healthCheck() {
331
548
  return __awaiter(this, void 0, void 0, function* () {
332
549
  try {
333
550
  const redis = yield this.getRedis();
551
+ // Try to ping Redis
334
552
  const pong = yield redis.ping();
335
553
  if (pong === 'PONG') {
336
554
  return {
@@ -356,6 +574,14 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
356
574
  }
357
575
  });
358
576
  }
577
+ /**
578
+ * Get all active locks with optional pattern filtering
579
+ * Useful for monitoring and debugging
580
+ *
581
+ * @param pattern - Optional pattern to filter locks (e.g., 'MyService:*')
582
+ * @param keyPrefix - Key prefix (default: 'lock')
583
+ * @returns Array of active lock information
584
+ */
359
585
  getActiveLocks() {
360
586
  return __awaiter(this, arguments, void 0, function* (pattern = '*', keyPrefix = 'lock') {
361
587
  const redis = yield this.getRedis();
@@ -370,6 +596,7 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
370
596
  redis.get(key),
371
597
  redis.pttl(key),
372
598
  ]);
599
+ // Remove the key prefix for cleaner output
373
600
  const cleanKey = key.startsWith(`${keyPrefix}:`)
374
601
  ? key.substring(`${keyPrefix}:`.length)
375
602
  : key;
@@ -387,6 +614,10 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
387
614
  }
388
615
  });
389
616
  }
617
+ /**
618
+ * Get Redis connection
619
+ * Requires external Redis client to be set
620
+ */
390
621
  getRedis() {
391
622
  return __awaiter(this, void 0, void 0, function* () {
392
623
  if (!this.redis) {
@@ -395,12 +626,51 @@ let RedisLockService = RedisLockService_1 = class RedisLockService {
395
626
  return this.redis;
396
627
  });
397
628
  }
629
+ /**
630
+ * Generate a unique lock value
631
+ * Format: instanceId:timestamp:random:pid
632
+ *
633
+ * @returns Unique lock identifier
634
+ */
398
635
  generateLockValue() {
399
- return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}-${process.pid}`;
636
+ const timestamp = Date.now();
637
+ const random = Math.random().toString(36).substring(2, 15);
638
+ const pid = process.pid;
639
+ return `${this.instanceId}:${timestamp}:${random}:${pid}`;
640
+ }
641
+ /**
642
+ * Get instance identifier
643
+ * Used to distinguish different process instances in distributed environments
644
+ *
645
+ * @returns Instance identifier
646
+ */
647
+ getInstanceId() {
648
+ // 1. Kubernetes Pod name
649
+ if (process.env.HOSTNAME) {
650
+ return process.env.HOSTNAME;
651
+ }
652
+ // 2. Custom instance ID
653
+ if (process.env.INSTANCE_ID) {
654
+ return process.env.INSTANCE_ID;
655
+ }
656
+ // 3. Generate unique ID
657
+ return `instance-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
400
658
  }
659
+ /**
660
+ * Build the full Redis key for a lock
661
+ *
662
+ * @param key - Lock key
663
+ * @param prefix - Key prefix
664
+ * @returns Full Redis key
665
+ */
401
666
  buildLockKey(key, prefix = 'lock') {
402
667
  return `${prefix}:${key}`;
403
668
  }
669
+ /**
670
+ * Sleep for a specified duration
671
+ *
672
+ * @param ms - Milliseconds to sleep
673
+ */
404
674
  sleep(ms) {
405
675
  return new Promise((resolve) => setTimeout(resolve, ms));
406
676
  }
@@ -19,12 +19,15 @@ const process = require("process");
19
19
  const mode_setup_1 = require("./mode.setup");
20
20
  function findValidRootPath() {
21
21
  const getAppRootPath = () => {
22
+ // 优先使用 require.main.filename(应用入口文件)
22
23
  if (require.main && require.main.filename) {
23
24
  return (0, path_1.dirname)(require.main.filename);
24
25
  }
26
+ // fallback 到 process.argv[1]
25
27
  if (process.argv[1]) {
26
28
  return (0, path_1.dirname)(process.argv[1]);
27
29
  }
30
+ // 最后 fallback 到当前工作目录
28
31
  return process.cwd();
29
32
  };
30
33
  const possibleRootPaths = [getAppRootPath(), process.cwd(), __dirname];
@@ -56,6 +59,8 @@ function findValidRootPath() {
56
59
  throw new Error(`No valid .env file: ${envFilePath}`);
57
60
  }
58
61
  const { envFilePath, rootPath, baseEnvFilePath } = findValidRootPath();
62
+ // 配置加载顺序: base.env < 环境.env < Vault
63
+ // 1. 先加载本地配置文件 (base.env 和 环境.env)
59
64
  dotenv.config({ path: [envFilePath, baseEnvFilePath] });
60
65
  process.env.ROOT_PATH = rootPath;
61
66
  process.env.ENV_FILE_PATH = envFilePath;
@@ -121,34 +126,47 @@ const vault_1 = require("../vault");
121
126
  const dto_1 = require("../common/dto");
122
127
  function bootstrapSetup(AppModule, SetupSwagger) {
123
128
  return __awaiter(this, void 0, void 0, function* () {
129
+ // 2. 加载 Vault 配置(会覆盖 .env 中的配置)
124
130
  yield vault_1.VaultConfigLoader.loadVaultConfig();
131
+ // 获取应用模式
125
132
  const shouldStartHttp = (0, mode_setup_1.shouldStartHttpServer)();
133
+ // 创建应用实例
126
134
  const app = yield core_1.NestFactory.create(AppModule, {
127
135
  bufferLogs: true,
128
136
  autoFlushLogs: true,
129
137
  });
138
+ // 设置容器 - 类似class-validator的useContainer
130
139
  const appModuleRef = app.select(AppModule);
131
140
  (0, class_validator_1.useContainer)(appModuleRef, { fallbackOnErrors: true });
141
+ // 初始化DTO容器
132
142
  dto_1.DtoContainer.useContainer(appModuleRef);
143
+ // 获取核心服务
133
144
  const configService = app.select(__1.ServiceRegistryModule).get(__1.ApiConfigService);
134
145
  const logger = app.get(nestjs_pino_1.Logger);
135
146
  app.useLogger(logger);
136
147
  app.flushLogs();
148
+ // 记录应用模式
137
149
  logger.log(`Application Mode: ${(0, mode_setup_1.getModeDescription)()}`);
138
150
  logger.log(`Environment: ${process.env.NODE_ENV || 'unknown'}`);
139
151
  app.enableShutdownHooks();
152
+ // 基础配置
140
153
  app.enableVersioning();
141
154
  app.enable('trust proxy');
142
155
  app.set('query parser', 'extended');
156
+ // 中间件配置
143
157
  app.use(bodyParse.urlencoded({
144
158
  extended: true,
145
159
  parameterLimit: 1000,
146
160
  }), bodyParse.json({ limit: '50mb' }), new nestjs_cls_1.ClsMiddleware({
147
161
  saveReq: true,
162
+ // 启用请求保存,解决CLS相关的上下文问题
148
163
  }).use, (0, __1.RequestIdMiddleware)(), (0, __1.PowerByMiddleware)(), (0, __1.OmniAuthMiddleware)(), compression());
164
+ // 全局过滤器
149
165
  const reflector = app.get(core_1.Reflector);
150
166
  app.useGlobalFilters(new setup_1.SentryGlobalFilter(), new __1.HttpExceptionFilter(), new __1.QueryFailedFilter(reflector));
167
+ // 全局拦截器
151
168
  app.useGlobalInterceptors(new __1.LanguageInterceptor(), new __1.TranslationInterceptor(), new nestjs_pino_1.LoggerErrorInterceptor());
169
+ // 全局管道
152
170
  app.useGlobalPipes(new nestjs_i18n_1.I18nValidationPipe({
153
171
  whitelist: true,
154
172
  errorHttpStatusCode: common_1.HttpStatus.UNPROCESSABLE_ENTITY,
@@ -156,6 +174,7 @@ function bootstrapSetup(AppModule, SetupSwagger) {
156
174
  stopAtFirstError: true,
157
175
  validationError: { target: false, value: false },
158
176
  }));
177
+ // 可选功能模块 - 仅在 HTTP 或 Hybrid 模式下启用
159
178
  if (shouldStartHttp) {
160
179
  if (configService.documentationEnabled && SetupSwagger) {
161
180
  SetupSwagger(app, configService.documentationPath);
@@ -181,6 +200,7 @@ function bootstrapSetup(AppModule, SetupSwagger) {
181
200
  else {
182
201
  logger.log('Running in Worker-only mode - HTTP server not started');
183
202
  logger.log('Application is ready to process background tasks');
203
+ // 在 Worker 模式下,应用会持续运行但不监听 HTTP 请求
184
204
  yield app.init();
185
205
  }
186
206
  return app;
@@ -1,12 +1,56 @@
1
+ /**
2
+ * Application Mode Detection and Management System
3
+ *
4
+ * This module provides utilities for detecting and managing different application modes:
5
+ * - HTTP: Only HTTP server (no workers/schedulers)
6
+ * - WORKER: Only background workers and schedulers (no HTTP server)
7
+ * - HYBRID: Both HTTP server and workers (default)
8
+ */
1
9
  export declare enum ApplicationMode {
2
10
  HTTP = "http",
3
11
  WORKER = "worker",
4
12
  HYBRID = "hybrid"
5
13
  }
14
+ /**
15
+ * Get the current application mode from environment variables
16
+ * Supports both APP_MODE and MODE environment variables
17
+ *
18
+ * @returns The current application mode
19
+ */
6
20
  export declare function getApplicationMode(): ApplicationMode;
21
+ /**
22
+ * Check if the current mode should process queues and scheduled tasks
23
+ *
24
+ * @returns True if workers should be registered
25
+ */
7
26
  export declare function shouldProcessQueues(): boolean;
27
+ /**
28
+ * Check if the current mode should start HTTP server
29
+ *
30
+ * @returns True if HTTP server should be started
31
+ */
8
32
  export declare function shouldStartHttpServer(): boolean;
33
+ /**
34
+ * Check if running in HTTP-only mode
35
+ *
36
+ * @returns True if running in HTTP-only mode
37
+ */
9
38
  export declare function isHttpMode(): boolean;
39
+ /**
40
+ * Check if running in Worker-only mode
41
+ *
42
+ * @returns True if running in Worker-only mode
43
+ */
10
44
  export declare function isWorkerMode(): boolean;
45
+ /**
46
+ * Check if running in Hybrid mode
47
+ *
48
+ * @returns True if running in Hybrid mode
49
+ */
11
50
  export declare function isHybridMode(): boolean;
51
+ /**
52
+ * Get a human-readable description of the current mode
53
+ *
54
+ * @returns Description of the current mode
55
+ */
12
56
  export declare function getModeDescription(): string;