dt-common-device 14.0.1 → 14.0.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 (59) hide show
  1. package/dist/Integrations/twilio/twilio.service.d.ts +1 -2
  2. package/dist/Integrations/twilio/twilio.service.js +16 -20
  3. package/dist/alerts/alert.types.d.ts +5 -1
  4. package/dist/alerts/alert.types.js +4 -0
  5. package/dist/audit/AuditUtils.js +13 -0
  6. package/dist/audit/IAuditProperties.d.ts +7 -1
  7. package/dist/audit/IAuditProperties.js +1 -0
  8. package/dist/audit/PushAudit.d.ts +17 -1
  9. package/dist/audit/PushAudit.js +51 -9
  10. package/dist/config/config.d.ts +2 -1
  11. package/dist/config/config.js +14 -38
  12. package/dist/config/config.types.d.ts +3 -3
  13. package/dist/config/constants.js +1 -0
  14. package/dist/constants/ConnectionProviders.d.ts +4 -0
  15. package/dist/constants/ConnectionProviders.js +4 -0
  16. package/dist/constants/Event.d.ts +22 -0
  17. package/dist/constants/Event.js +22 -0
  18. package/dist/copilotQueue/utils/queueManager.js +2 -35
  19. package/dist/cronicle/Cronicle.service.d.ts +3 -3
  20. package/dist/cronicle/Cronicle.service.js +4 -3
  21. package/dist/cronicle/ICronicle.interface.d.ts +67 -0
  22. package/dist/db/db.js +32 -28
  23. package/dist/emails/emailService.js +23 -41
  24. package/dist/entities/admin/Admin.repository.d.ts +11 -2
  25. package/dist/entities/admin/Admin.repository.js +73 -2
  26. package/dist/entities/admin/Admin.service.d.ts +2 -1
  27. package/dist/entities/admin/Admin.service.js +12 -1
  28. package/dist/entities/admin/IAdmin.d.ts +40 -0
  29. package/dist/entities/connection/Connection.repository.js +12 -4
  30. package/dist/entities/connection/Connection.service.d.ts +2 -1
  31. package/dist/entities/connection/IConnection.d.ts +4 -1
  32. package/dist/entities/connection/IConnection.js +3 -0
  33. package/dist/entities/device/cloud/interfaces/IRawDevice.d.ts +3 -1
  34. package/dist/entities/device/cloud/interfaces/IRawDevice.js +2 -0
  35. package/dist/entities/device/local/interfaces/IDevice.d.ts +6 -0
  36. package/dist/entities/device/local/repository/Device.repository.d.ts +7 -0
  37. package/dist/entities/device/local/repository/Device.repository.js +11 -0
  38. package/dist/entities/device/local/services/Device.service.d.ts +8 -0
  39. package/dist/entities/device/local/services/Device.service.js +21 -0
  40. package/dist/entities/pms/IPms.d.ts +2 -1
  41. package/dist/entities/pms/IPms.js +1 -0
  42. package/dist/entities/pms/pms.service.js +22 -11
  43. package/dist/entities/property/Property.repository.d.ts +1 -0
  44. package/dist/entities/property/Property.repository.js +5 -0
  45. package/dist/entities/property/Property.service.d.ts +1 -0
  46. package/dist/entities/property/Property.service.js +6 -0
  47. package/dist/index.d.ts +1 -0
  48. package/dist/index.js +2 -0
  49. package/dist/issues/issue.types.d.ts +9 -2
  50. package/dist/issues/issue.types.js +8 -0
  51. package/dist/queue/entities/HybridHttpQueue.d.ts +0 -1
  52. package/dist/queue/entities/HybridHttpQueue.js +3 -5
  53. package/dist/queue/utils/queueUtils.d.ts +2 -2
  54. package/dist/queue/utils/queueUtils.js +16 -25
  55. package/dist/queue/utils/rateLimit.utils.js +19 -1
  56. package/dist/utils/http.utils.d.ts +3 -1
  57. package/dist/utils/http.utils.js +8 -0
  58. package/dist/webhookQueue/services/WebhookQueueService.js +3 -36
  59. package/package.json +3 -2
@@ -5,7 +5,8 @@ export declare enum PmsProvider {
5
5
  Yanolja = "Yanolja",
6
6
  SMOOBU = "Smoobu",
7
7
  WEBREZPRO = "Webrezpro",
8
- MEWS = "Mews"
8
+ MEWS = "Mews",
9
+ Infor = "Infor"
9
10
  }
10
11
  export interface IPmsSchedule {
11
12
  id: string;
@@ -10,4 +10,5 @@ var PmsProvider;
10
10
  PmsProvider["SMOOBU"] = "Smoobu";
11
11
  PmsProvider["WEBREZPRO"] = "Webrezpro";
12
12
  PmsProvider["MEWS"] = "Mews";
13
+ PmsProvider["Infor"] = "Infor";
13
14
  })(PmsProvider || (exports.PmsProvider = PmsProvider = {}));
@@ -148,19 +148,30 @@ let PmsService = (() => {
148
148
  if (!scheduleAccessGroups || scheduleAccessGroups.length === 0) {
149
149
  return false;
150
150
  }
151
- const orderedAccessGroups = [...scheduleAccessGroups].sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());
152
- // First entry new code should be assigned
153
- if (orderedAccessGroups.length < 2) {
151
+ // Split reservations often have multiple rows per schedule with the *same* startTime
152
+ // across different access groups. Sorting *all* rows globally made the last two entries
153
+ // always different access groups. Here we only consider rows for this accessGroupId,
154
+ // ordered by startTime, and require the latest segment to follow the previous one in time.
155
+ const rowsForThisAccessGroup = scheduleAccessGroups
156
+ .filter((row) => row.accessGroupId === accessGroupId)
157
+ .sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());
158
+ if (rowsForThisAccessGroup.length < 2) {
154
159
  return false;
155
160
  }
156
- const last = orderedAccessGroups?.at(-1);
157
- const secondLast = orderedAccessGroups?.at(-2);
158
- // True only if last and second last entries are same and equal to accessGroupId
159
- return (
160
- // This 1st condition doesnt seems to be required so verify with use case
161
- last.accessGroupId === accessGroupId &&
162
- secondLast.accessGroupId === accessGroupId //A(secondLast) A(last)
163
- );
161
+ const previousSegment = rowsForThisAccessGroup.at(-2);
162
+ const currentSegment = rowsForThisAccessGroup.at(-1);
163
+ if (!previousSegment.endTime || !currentSegment.startTime) {
164
+ return false;
165
+ }
166
+ const previousStart = new Date(previousSegment.startTime).getTime();
167
+ const previousEnd = new Date(previousSegment.endTime).getTime();
168
+ const currentStart = new Date(currentSegment.startTime).getTime();
169
+ // Same startTime as previous row (duplicate import, extra map row, etc.) — still reuse one code.
170
+ if (currentStart === previousStart) {
171
+ return true;
172
+ }
173
+ // Consecutive nights: current segment starts at or after previous segment ends.
174
+ return currentStart >= previousEnd;
164
175
  }
165
176
  };
166
177
  __setFunctionName(_classThis, "PmsService");
@@ -4,5 +4,6 @@ export declare class PropertyRepository {
4
4
  constructor();
5
5
  getPropertyPreferences(propertyId: string, keys?: string[]): Promise<IPropertySettings | null>;
6
6
  getProperty(propertyId: string): Promise<IProperty | null>;
7
+ getOrganizationByPropertyId(propertyId: string): Promise<any | null>;
7
8
  getAllProperties(): Promise<any[]>;
8
9
  }
@@ -41,6 +41,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
41
41
  exports.PropertyRepository = void 0;
42
42
  const db_1 = require("../../db");
43
43
  const typedi_1 = require("typedi");
44
+ const utils_1 = require("../../utils");
44
45
  let PropertyRepository = (() => {
45
46
  let _classDecorators = [(0, typedi_1.Service)()];
46
47
  let _classDescriptor;
@@ -84,6 +85,10 @@ let PropertyRepository = (() => {
84
85
  }
85
86
  return null;
86
87
  }
88
+ async getOrganizationByPropertyId(propertyId) {
89
+ const organization = await (0, utils_1.getAdminServiceAxiosInstance)().get(`/properties/${propertyId}/organizations`);
90
+ return organization.data.data;
91
+ }
87
92
  async getAllProperties() {
88
93
  try {
89
94
  //Retrieve all the properties ids from the database where isDeleted is false
@@ -5,4 +5,5 @@ export declare class LocalPropertyService {
5
5
  getProperty(propertyId: string): Promise<import("./IProperty").IProperty | null>;
6
6
  getPropertyTimeZone(propertyId: string): Promise<string>;
7
7
  getAllProperties(): Promise<any[]>;
8
+ getOrganizationByPropertyId(propertyId: string): Promise<any>;
8
9
  }
@@ -110,6 +110,12 @@ let LocalPropertyService = (() => {
110
110
  const properties = await this.propertyRepository.getAllProperties();
111
111
  return properties;
112
112
  }
113
+ async getOrganizationByPropertyId(propertyId) {
114
+ if (!propertyId) {
115
+ throw new Error("Property ID is required");
116
+ }
117
+ return await this.propertyRepository.getOrganizationByPropertyId(propertyId);
118
+ }
113
119
  };
114
120
  __setFunctionName(_classThis, "LocalPropertyService");
115
121
  (() => {
package/dist/index.d.ts CHANGED
@@ -25,3 +25,4 @@ export * from "./webhooks";
25
25
  export * from "./Integrations";
26
26
  export * from "./Integrations/twilio";
27
27
  export * from "./emails/emailService";
28
+ export * from "./entities/notification";
package/dist/index.js CHANGED
@@ -75,3 +75,5 @@ __exportStar(require("./Integrations"), exports);
75
75
  __exportStar(require("./Integrations/twilio"), exports);
76
76
  // Export Email Service
77
77
  __exportStar(require("./emails/emailService"), exports);
78
+ // Export Notification Service
79
+ __exportStar(require("./entities/notification"), exports);
@@ -31,6 +31,7 @@ export declare enum EntitySubType {
31
31
  ON_PREM_SERVER = "ON_PREM_SERVER",
32
32
  CLOUDBEDS = "CLOUDBEDS",
33
33
  STAYNTOUCH = "STAYNTOUCH",
34
+ INFOR = "INFOR",
34
35
  HOTELKEY = "HOTELKEY",
35
36
  YANOLJA = "YANOLJA",
36
37
  SKYTOUCH = "SKYTOUCH",
@@ -49,7 +50,9 @@ export declare enum EntitySubType {
49
50
  SALTOSPACE = "SALTOSPACE",
50
51
  SCHLAGE = "SCHLAGE",
51
52
  LOCKLY = "LOCKLY",
52
- SIFELY = "SIFELY"
53
+ SIFELY = "SIFELY",
54
+ GEOCODING = "GEOCODING",
55
+ ULTRALOCK = "ULTRALOCK"
53
56
  }
54
57
  export declare enum IssueStatus {
55
58
  PENDING = "PENDING",
@@ -85,7 +88,9 @@ export declare enum IssueType {
85
88
  LOW_GUEST_CODES = "LOW_GUEST_CODES",
86
89
  PMS_CODE_NOT_DELIVERED = "PMS_CODE_NOT_DELIVERED",
87
90
  SCHEDULE_CODE_NOT_ASSIGNED = "SCHEDULE_CODE_NOT_ASSIGNED",
88
- MISSING_ACCESS_GROUP = "MISSING_ACCESS_GROUP"
91
+ MISSING_ACCESS_GROUP = "MISSING_ACCESS_GROUP",
92
+ GEOCODING_FAILED = "GEOCODING_FAILED",
93
+ CLOUD_PROVIDER_EVENTS_ISSUE = "CLOUD_PROVIDER_EVENTS_ISSUE"
89
94
  }
90
95
  export declare const IssueDescriptions: {
91
96
  BATTERY_LOW: string;
@@ -101,6 +106,8 @@ export declare const IssueDescriptions: {
101
106
  PMS_CODE_NOT_DELIVERED: string;
102
107
  SCHEDULE_CODE_NOT_ASSIGNED: string;
103
108
  MISSING_ACCESS_GROUP: string;
109
+ GEOCODING_FAILED: string;
110
+ CLOUD_PROVIDER_EVENTS_ISSUE: string;
104
111
  };
105
112
  export interface IssueDocument {
106
113
  id: string;
@@ -40,6 +40,7 @@ var EntitySubType;
40
40
  // PMS PROVIDERS
41
41
  EntitySubType["CLOUDBEDS"] = "CLOUDBEDS";
42
42
  EntitySubType["STAYNTOUCH"] = "STAYNTOUCH";
43
+ EntitySubType["INFOR"] = "INFOR";
43
44
  EntitySubType["HOTELKEY"] = "HOTELKEY";
44
45
  EntitySubType["YANOLJA"] = "YANOLJA";
45
46
  EntitySubType["SKYTOUCH"] = "SKYTOUCH";
@@ -60,6 +61,9 @@ var EntitySubType;
60
61
  EntitySubType["SCHLAGE"] = "SCHLAGE";
61
62
  EntitySubType["LOCKLY"] = "LOCKLY";
62
63
  EntitySubType["SIFELY"] = "SIFELY";
64
+ // OTHER
65
+ EntitySubType["GEOCODING"] = "GEOCODING";
66
+ EntitySubType["ULTRALOCK"] = "ULTRALOCK";
63
67
  })(EntitySubType || (exports.EntitySubType = EntitySubType = {}));
64
68
  var IssueStatus;
65
69
  (function (IssueStatus) {
@@ -92,6 +96,8 @@ var IssueType;
92
96
  IssueType["PMS_CODE_NOT_DELIVERED"] = "PMS_CODE_NOT_DELIVERED";
93
97
  IssueType["SCHEDULE_CODE_NOT_ASSIGNED"] = "SCHEDULE_CODE_NOT_ASSIGNED";
94
98
  IssueType["MISSING_ACCESS_GROUP"] = "MISSING_ACCESS_GROUP";
99
+ IssueType["GEOCODING_FAILED"] = "GEOCODING_FAILED";
100
+ IssueType["CLOUD_PROVIDER_EVENTS_ISSUE"] = "CLOUD_PROVIDER_EVENTS_ISSUE";
95
101
  })(IssueType || (exports.IssueType = IssueType = {}));
96
102
  exports.IssueDescriptions = {
97
103
  [IssueType.BATTERY_LOW]: "The issue is raised when the battery level is lower than the threshold.",
@@ -107,4 +113,6 @@ exports.IssueDescriptions = {
107
113
  [IssueType.PMS_CODE_NOT_DELIVERED]: "The issue is raised when the code is not delivered to the PMS system.",
108
114
  [IssueType.SCHEDULE_CODE_NOT_ASSIGNED]: "The issue is raised when the code is not assigned to a schedule.",
109
115
  [IssueType.MISSING_ACCESS_GROUP]: "The issue is raised when the access group is missing from devicethread but present in PMS system.",
116
+ [IssueType.GEOCODING_FAILED]: "The issue is raised when the system fails to retrieve latitude and longitude from the provided property address.",
117
+ [IssueType.CLOUD_PROVIDER_EVENTS_ISSUE]: "The issue is raised when the system fails to automatically resolve the event subscription for the cloud provider (Lock or PMS)",
110
118
  };
@@ -5,7 +5,6 @@ export declare class HybridHttpQueue {
5
5
  private readonly rateLimitConfigs;
6
6
  private readonly queues;
7
7
  private readonly workers;
8
- private readonly jobResults;
9
8
  constructor();
10
9
  private addToQueue;
11
10
  private processHttpRequest;
@@ -54,7 +54,6 @@ let HybridHttpQueue = (() => {
54
54
  // BullMQ queues and workers for persistent storage
55
55
  this.queues = new Map();
56
56
  this.workers = new Map();
57
- this.jobResults = new Map();
58
57
  this.rateLimitConfigs = rateLimit_utils_1.RateLimitUtils.initializeRateLimitConfigs();
59
58
  }
60
59
  async addToQueue(connectionId, provider, url, method, options) {
@@ -89,7 +88,7 @@ let HybridHttpQueue = (() => {
89
88
  console.log(`[${new Date().toISOString()}] [HybridHttpQueue] Job added to queue - JobId: ${jobId}, QueueKey: ${queueKey}`);
90
89
  // Initialize worker if not exists
91
90
  console.log(`[${new Date().toISOString()}] [HybridHttpQueue] Initializing worker for queue: ${queueKey}`);
92
- queueUtils_1.QueueUtils.getOrCreateWorker(queueKey, this.workers, this.processHttpRequest.bind(this), this.jobResults);
91
+ queueUtils_1.QueueUtils.getOrCreateWorker(queueKey, this.workers, this.processHttpRequest.bind(this));
93
92
  // Verify worker was created
94
93
  if (this.workers.has(queueKey)) {
95
94
  console.log(`[${new Date().toISOString()}] [HybridHttpQueue] Worker confirmed for queue: ${queueKey}`);
@@ -100,7 +99,7 @@ let HybridHttpQueue = (() => {
100
99
  (0, config_1.getConfig)().LOGGER.info(`Request queued: ${method} ${url} -> ${provider} [${connectionId}]. Job ID: ${jobId}, Delay: ${delay}ms`);
101
100
  // Wait for job completion and return result
102
101
  // Simple: delay + windowMs + HTTP buffer timeout
103
- return queueUtils_1.QueueUtils.waitForJobCompletion(jobId, queueKey, delay, windowMs);
102
+ return queueUtils_1.QueueUtils.waitForJobCompletion(jobId, queueKey, delay, windowMs, this.queues);
104
103
  }
105
104
  async processHttpRequest(job) {
106
105
  // Log immediately when worker picks up the job
@@ -148,7 +147,7 @@ let HybridHttpQueue = (() => {
148
147
  return this.handleRequest(url, method, httpCallOption);
149
148
  }
150
149
  async handleRequest(url, method, options) {
151
- const { connectionId, provider, microservice } = jobUtils_1.JobUtils.extractConnectionDetails(options);
150
+ const { connectionId, provider } = jobUtils_1.JobUtils.extractConnectionDetails(options);
152
151
  // Check rate limit first
153
152
  const allowed = await rateLimit_utils_1.RateLimitUtils.isRateLimitAllowed(connectionId, provider, this.rateLimitConfigs);
154
153
  if (allowed) {
@@ -170,7 +169,6 @@ let HybridHttpQueue = (() => {
170
169
  ]);
171
170
  this.workers.clear();
172
171
  this.queues.clear();
173
- this.jobResults.clear();
174
172
  (0, config_1.getConfig)().LOGGER.info("HTTP queues shutdown complete");
175
173
  }
176
174
  };
@@ -4,9 +4,9 @@ export declare class QueueUtils {
4
4
  static getQueueKey(microservice: string, connectionId: string, provider: string): string;
5
5
  static getRequestQueueKey(connectionId: string, provider: string): string;
6
6
  static getOrCreateQueue(queueKey: string, queues: Map<string, any>): any;
7
- static getOrCreateWorker(queueKey: string, workers: Map<string, any>, processFunction: (job: any) => Promise<any>, jobResults: Map<string, any>): void;
7
+ static getOrCreateWorker(queueKey: string, workers: Map<string, any>, processFunction: (job: any) => Promise<any>): void;
8
8
  static waitForRateLimitExpiry(connectionId: string, provider: string, rateLimitConfigs: Map<string, IRateLimitConfig>): Promise<void>;
9
9
  static executeHttpRequest(url: string, method: string, options: HttpCallOption, connectionId: string, provider: string): Promise<any>;
10
10
  static addJobToQueue(queueKey: string, jobData: any, delay: number, queues: Map<string, any>): Promise<string>;
11
- static waitForJobCompletion(jobId: string, queueKey: string, jobDelay?: number, windowMs?: number): Promise<any>;
11
+ static waitForJobCompletion(jobId: string, queueKey: string, jobDelay: number | undefined, windowMs: number | undefined, queues: Map<string, any>): Promise<any>;
12
12
  }
@@ -24,7 +24,7 @@ class QueueUtils {
24
24
  }))
25
25
  .get(queueKey));
26
26
  }
27
- static getOrCreateWorker(queueKey, workers, processFunction, jobResults) {
27
+ static getOrCreateWorker(queueKey, workers, processFunction) {
28
28
  if (workers.has(queueKey)) {
29
29
  const existingWorker = workers.get(queueKey);
30
30
  console.log(`[${new Date().toISOString()}] [getOrCreateWorker] Worker already exists for queue: ${queueKey}, checking if it's running...`);
@@ -35,6 +35,8 @@ class QueueUtils {
35
35
  console.log(`[${new Date().toISOString()}] [getOrCreateWorker] Existing worker running status: ${isRunning}`);
36
36
  if (!isRunning) {
37
37
  console.log(`[${new Date().toISOString()}] [getOrCreateWorker] Worker not running, closing and recreating...`);
38
+ // Stale worker detected — surface at WARN level with count context
39
+ (0, config_1.getConfig)().LOGGER.warn(`Stale worker for queue ${queueKey} (not running), recreating. Active workers before recreate: ${workers.size}`);
38
40
  try {
39
41
  existingWorker.close();
40
42
  }
@@ -95,24 +97,11 @@ class QueueUtils {
95
97
  console.log(`[${new Date().toISOString()}] [Worker Event] Job completed - JobId: ${job.id}, QueueKey: ${queueKey}, ReturnValueType: ${typeof job.returnvalue}, HasReturnValue: ${job.returnvalue !== undefined}`);
96
98
  (0, config_1.getConfig)().LOGGER.info(`HTTP request completed: ${job.id} [${queueKey}]`);
97
99
  const result = job.returnvalue;
98
- const jobResult = {
99
- result,
100
- resolved: true,
101
- timestamp: Date.now(),
102
- };
103
100
  console.log(`[${new Date().toISOString()}] [Worker Event] Storing job result - JobId: ${job.id}, ResultType: ${typeof result}, Resolved: true`);
104
- jobResults.set(job.id, jobResult);
105
- console.log(`[${new Date().toISOString()}] [Worker Event] Job result stored in jobResults - JobId: ${job.id}, StoredResult: ${JSON.stringify(jobResult)}`);
106
101
  });
107
102
  worker.on("failed", (job, err) => {
108
103
  console.log(`[${new Date().toISOString()}] [Worker Event] Job failed - JobId: ${job?.id}, QueueKey: ${queueKey}, Error: ${err.message}`);
109
104
  (0, config_1.getConfig)().LOGGER.error(`HTTP request failed: ${job?.id} [${queueKey}], Error: ${err.message}`);
110
- jobResults.set(job.id, {
111
- error: err.message,
112
- resolved: true,
113
- timestamp: Date.now(),
114
- });
115
- console.log(`[${new Date().toISOString()}] [Worker Event] Job error stored in jobResults - JobId: ${job?.id}`);
116
105
  });
117
106
  worker.on("error", (err) => {
118
107
  console.log(`[${new Date().toISOString()}] [Worker Event] Worker error - QueueKey: ${queueKey}, Error: ${err.message}, Stack: ${err.stack}`);
@@ -129,7 +118,8 @@ class QueueUtils {
129
118
  });
130
119
  workers.set(queueKey, worker);
131
120
  console.log(`[${new Date().toISOString()}] [getOrCreateWorker] Worker initialized and stored for queue: ${queueKey}`);
132
- (0, config_1.getConfig)().LOGGER.info(`Worker initialized for queue: ${queueKey}`);
121
+ // Log total active workers — a growing count here signals a connection-proliferation leak
122
+ (0, config_1.getConfig)().LOGGER.info(`Worker initialized for queue: ${queueKey} (total active workers: ${workers.size})`);
133
123
  }
134
124
  static async waitForRateLimitExpiry(connectionId, provider, rateLimitConfigs) {
135
125
  const key = `rate_limit:${provider}:${connectionId}`;
@@ -215,22 +205,25 @@ class QueueUtils {
215
205
  queue.getActiveCount(),
216
206
  ]);
217
207
  console.log(`[${new Date().toISOString()}] [addJobToQueue] Queue state - QueueKey: ${queueKey}, Waiting: ${waiting}, Delayed: ${delayed}, Active: ${active}`);
208
+ // Warn via structured logger when backlog is building — correlates with heap stats
209
+ if (waiting + delayed > 1) {
210
+ (0, config_1.getConfig)().LOGGER.warn(`Queue backlog [${queueKey}]: waiting=${waiting} delayed=${delayed} active=${active}`);
211
+ }
218
212
  }
219
213
  catch (e) {
220
214
  // Ignore errors getting queue state
221
215
  }
222
216
  return job.id;
223
217
  }
224
- static async waitForJobCompletion(jobId, queueKey, jobDelay = 0, windowMs = 60000) {
218
+ static async waitForJobCompletion(jobId, queueKey, jobDelay = 0, windowMs = 60000, queues) {
225
219
  // Simple: delay (wait) + windowMs (execute) + HTTP timeout buffer
226
220
  const httpTimeout = 60000;
227
221
  const totalTimeout = jobDelay + windowMs + httpTimeout;
228
222
  console.log(`[${new Date().toISOString()}] [waitForJobCompletion] Waiting for job ${jobId} - delay: ${jobDelay}ms, timeout: ${totalTimeout}ms`);
229
223
  const startTime = Date.now();
230
- const { Queue } = require("bullmq");
231
- const queue = new Queue(queueKey, {
232
- connection: (0, redis_1.getRedisClient)(),
233
- });
224
+ // Reuse the existing queue instance — never create a new Queue per call
225
+ // (each new Queue duplicates the Redis connection, causing a connection leak)
226
+ const queue = this.getOrCreateQueue(queueKey, queues);
234
227
  return new Promise(async (resolve, reject) => {
235
228
  const checkInterval = setInterval(async () => {
236
229
  try {
@@ -239,7 +232,6 @@ class QueueUtils {
239
232
  const elapsed = Date.now() - startTime;
240
233
  if (elapsed > totalTimeout) {
241
234
  clearInterval(checkInterval);
242
- await queue.close();
243
235
  return reject(new Error(`Job ${jobId} not found and timeout exceeded`));
244
236
  }
245
237
  return; // Job not found yet, keep checking
@@ -249,26 +241,25 @@ class QueueUtils {
249
241
  console.log(`[${new Date().toISOString()}] [waitForJobCompletion] Job ${jobId} completed`);
250
242
  clearInterval(checkInterval);
251
243
  const result = job.returnvalue;
252
- await queue.close();
253
244
  return resolve(result);
254
245
  }
255
246
  if (state === "failed") {
256
247
  console.log(`[${new Date().toISOString()}] [waitForJobCompletion] Job ${jobId} failed: ${job.failedReason}`);
257
248
  clearInterval(checkInterval);
258
- await queue.close();
259
249
  return reject(new Error(job.failedReason || "Job failed"));
260
250
  }
261
251
  }
262
252
  catch (error) {
263
253
  clearInterval(checkInterval);
264
- await queue.close();
265
254
  reject(error);
266
255
  }
267
256
  }, 500); // Check every 500ms
268
257
  // Timeout
269
258
  setTimeout(() => {
270
259
  clearInterval(checkInterval);
271
- queue.close();
260
+ // Log via structured logger before rejecting so the timeout is visible in
261
+ // CloudWatch alongside the periodic heap stats — correlates queue stalls with memory pressure.
262
+ (0, config_1.getConfig)().LOGGER.error(`Job ${jobId} timed out after ${totalTimeout}ms [${queueKey}] (jobDelay=${jobDelay}ms)`);
272
263
  reject(new Error(`Request timeout: Maximum wait time exceeded (${totalTimeout}ms). Job delay was ${jobDelay}ms.`));
273
264
  }, totalTimeout);
274
265
  });
@@ -51,6 +51,12 @@ class RateLimitUtils {
51
51
  provider: constants_1.CONNECTION_PROVIDERS.STAYNTOUCH,
52
52
  maxTimeoutWindowMs: 120000,
53
53
  });
54
+ configs.set(constants_1.CONNECTION_PROVIDERS.INFOR, {
55
+ maxRequests: 10,
56
+ windowMs: 10000, // 10 seconds
57
+ provider: constants_1.CONNECTION_PROVIDERS.INFOR,
58
+ maxTimeoutWindowMs: 120000,
59
+ });
54
60
  configs.set(constants_1.CONNECTION_PROVIDERS.HOTELKEY, {
55
61
  maxRequests: 10,
56
62
  windowMs: 10000, // 10 seconds
@@ -76,7 +82,7 @@ class RateLimitUtils {
76
82
  maxTimeoutWindowMs: 120000,
77
83
  });
78
84
  configs.set(constants_1.CONNECTION_PROVIDERS.SMARTTHINGS, {
79
- maxRequests: 30,
85
+ maxRequests: 25,
80
86
  windowMs: 60000, // 1 minute
81
87
  provider: constants_1.CONNECTION_PROVIDERS.SMARTTHINGS,
82
88
  maxTimeoutWindowMs: 120000,
@@ -141,6 +147,18 @@ class RateLimitUtils {
141
147
  provider: constants_1.CONNECTION_PROVIDERS.SIFELY,
142
148
  maxTimeoutWindowMs: 120000,
143
149
  });
150
+ configs.set(constants_1.CONNECTION_PROVIDERS.DUSAW, {
151
+ maxRequests: 10,
152
+ windowMs: 60000, // 1 minute
153
+ provider: constants_1.CONNECTION_PROVIDERS.DUSAW,
154
+ maxTimeoutWindowMs: 120000,
155
+ });
156
+ configs.set(constants_1.CONNECTION_PROVIDERS.ULTRALOCK, {
157
+ maxRequests: 60,
158
+ windowMs: 60000, // 1 minute — adjust once UltraLock API docs confirm their limit
159
+ provider: constants_1.CONNECTION_PROVIDERS.ULTRALOCK,
160
+ maxTimeoutWindowMs: 120000,
161
+ });
144
162
  return configs;
145
163
  }
146
164
  static async isRateLimitAllowed(connectionId, provider, rateLimitConfigs) {
@@ -1,3 +1,4 @@
1
+ import axios from "axios";
1
2
  /**
2
3
  * Validates if a URL is properly formatted and accessible
3
4
  */
@@ -5,10 +6,11 @@ export declare function validateServiceUrl(url: string): boolean;
5
6
  /**
6
7
  * Creates a properly configured axios instance with error handling
7
8
  */
8
- export declare function createAxiosInstance(baseURL?: string): import("axios").AxiosInstance;
9
+ export declare function createAxiosInstance(baseURL?: string): axios.AxiosInstance;
9
10
  export declare function getDeviceServiceAxiosInstance(): any;
10
11
  export declare function getAdminServiceAxiosInstance(): any;
11
12
  export declare function getMonitoringServiceAxiosInstance(): any;
13
+ export declare function getNotificationServiceAxiosInstance(): any;
12
14
  /**
13
15
  * Retry function for failed HTTP requests
14
16
  */
@@ -8,6 +8,7 @@ exports.createAxiosInstance = createAxiosInstance;
8
8
  exports.getDeviceServiceAxiosInstance = getDeviceServiceAxiosInstance;
9
9
  exports.getAdminServiceAxiosInstance = getAdminServiceAxiosInstance;
10
10
  exports.getMonitoringServiceAxiosInstance = getMonitoringServiceAxiosInstance;
11
+ exports.getNotificationServiceAxiosInstance = getNotificationServiceAxiosInstance;
11
12
  exports.retryRequest = retryRequest;
12
13
  const config_1 = require("../config/config");
13
14
  const axios_1 = __importDefault(require("axios"));
@@ -105,6 +106,13 @@ function getMonitoringServiceAxiosInstance() {
105
106
  }
106
107
  return deviceMonitoringServiceAxiosInstance;
107
108
  }
109
+ let notificationServiceAxiosInstance = null;
110
+ function getNotificationServiceAxiosInstance() {
111
+ if (!notificationServiceAxiosInstance) {
112
+ notificationServiceAxiosInstance = createAxiosInstance((0, config_1.getNotificationServiceUrl)());
113
+ }
114
+ return notificationServiceAxiosInstance;
115
+ }
108
116
  /**
109
117
  * Retry function for failed HTTP requests
110
118
  */
@@ -1,20 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
2
  var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
19
3
  function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
20
4
  var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
@@ -49,23 +33,6 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
49
33
  }
50
34
  return useValue ? value : void 0;
51
35
  };
52
- var __importStar = (this && this.__importStar) || (function () {
53
- var ownKeys = function(o) {
54
- ownKeys = Object.getOwnPropertyNames || function (o) {
55
- var ar = [];
56
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
57
- return ar;
58
- };
59
- return ownKeys(o);
60
- };
61
- return function (mod) {
62
- if (mod && mod.__esModule) return mod;
63
- var result = {};
64
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
65
- __setModuleDefault(result, mod);
66
- return result;
67
- };
68
- })();
69
36
  var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
70
37
  if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
71
38
  return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
@@ -144,7 +111,7 @@ let WebhookQueueService = (() => {
144
111
  try {
145
112
  // Create queue instance if not exists locally
146
113
  if (!this.webhookQueues.has(queueName)) {
147
- const { Queue } = await Promise.resolve().then(() => __importStar(require("bullmq")));
114
+ const { Queue } = await import("bullmq");
148
115
  const queue = new Queue(queueName, {
149
116
  connection: (0, redis_1.getRedisClient)(),
150
117
  });
@@ -219,7 +186,7 @@ let WebhookQueueService = (() => {
219
186
  try {
220
187
  // Create queue instance if not exists locally
221
188
  if (!this.webhookQueues.has(queueName)) {
222
- const { Queue } = await Promise.resolve().then(() => __importStar(require("bullmq")));
189
+ const { Queue } = await import("bullmq");
223
190
  const queue = new Queue(queueName, {
224
191
  connection: (0, redis_1.getRedisClient)(),
225
192
  });
@@ -245,7 +212,7 @@ let WebhookQueueService = (() => {
245
212
  if (queues.has(queueKey)) {
246
213
  return queues.get(queueKey);
247
214
  }
248
- const { Queue } = await Promise.resolve().then(() => __importStar(require("bullmq")));
215
+ const { Queue } = await import("bullmq");
249
216
  const queue = new Queue(queueKey, {
250
217
  connection: (0, redis_1.getRedisClient)(),
251
218
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dt-common-device",
3
- "version": "14.0.1",
3
+ "version": "14.0.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -43,10 +43,11 @@
43
43
  "typescript": "^5.8.3"
44
44
  },
45
45
  "dependencies": {
46
- "@aws-sdk/client-s3": "^3.835.0",
46
+ "@aws-sdk/client-s3": "3.1015.0",
47
47
  "@aws-sdk/client-ses": "3.1003.0",
48
48
  "axios": "1.10.0",
49
49
  "bullmq": "5.56.4",
50
+ "class-validator": "0.15.1",
50
51
  "dt-audit-library": "1.7.1",
51
52
  "dt-pub-sub": "^1.0.0",
52
53
  "ioredis": "5.6.1",