dt-common-device 3.0.9 → 3.0.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 (144) hide show
  1. package/dist/config/config.d.ts +1 -2
  2. package/dist/config/config.js +4 -5
  3. package/dist/device/cloud/interface.d.ts +101 -0
  4. package/dist/device/cloud/interface.js +3 -0
  5. package/dist/device/cloud/interfaces/IDeviceConnectionService.d.ts +7 -0
  6. package/dist/device/cloud/interfaces/IDeviceConnectionService.js +3 -0
  7. package/dist/device/cloud/interfaces/IDevicesService.d.ts +9 -0
  8. package/dist/device/cloud/services/Device.service.d.ts +39 -0
  9. package/dist/device/cloud/services/Device.service.js +9 -0
  10. package/dist/device/cloud/services/DeviceCloudService.d.ts +42 -0
  11. package/dist/device/cloud/services/DeviceCloudService.js +59 -0
  12. package/dist/device/cloud/services/DeviceHub.service.d.ts +3 -0
  13. package/dist/device/cloud/services/DeviceHub.service.js +6 -0
  14. package/dist/device/cloud/services/Hub.service.d.ts +25 -0
  15. package/dist/device/cloud/services/Hub.service.js +9 -0
  16. package/dist/device/cloud/services/SmartThingsDeviceService.d.ts +38 -0
  17. package/dist/device/cloud/services/SmartThingsDeviceService.js +52 -0
  18. package/dist/device/index.d.ts +4 -0
  19. package/dist/device/index.js +20 -0
  20. package/dist/device/local/events/EventHandler.js +6 -6
  21. package/dist/device/local/events/Events.d.ts +12 -33
  22. package/dist/device/local/events/Events.js +12 -33
  23. package/dist/device/local/interface.d.ts +0 -0
  24. package/dist/device/local/interface.js +1 -0
  25. package/dist/device/local/interfaces/IDevice.d.ts +1 -0
  26. package/dist/device/local/repository/Schedule.repository.d.ts +0 -1
  27. package/dist/device/local/repository/Schedule.repository.js +6 -6
  28. package/dist/device/local/services/DeviceHub.service.d.ts +11 -0
  29. package/dist/device/local/services/DeviceHub.service.js +40 -0
  30. package/dist/queue/entities/HybridHttpQueue.d.ts +4 -14
  31. package/dist/queue/entities/HybridHttpQueue.js +31 -119
  32. package/dist/queue/interfaces/IHybridHttpQueue.d.ts +2 -12
  33. package/dist/queue/interfaces/IJobResult.d.ts +1 -8
  34. package/dist/queue/interfaces/index.d.ts +0 -1
  35. package/dist/queue/interfaces/index.js +0 -1
  36. package/dist/queue/services/QueueService.d.ts +2 -12
  37. package/dist/queue/types/queue.types.d.ts +10 -29
  38. package/dist/queue/utils/jobUtils.d.ts +0 -3
  39. package/dist/queue/utils/jobUtils.js +0 -48
  40. package/dist/queue/utils/queueUtils.d.ts +7 -0
  41. package/dist/queue/utils/queueUtils.js +113 -4
  42. package/package.json +6 -1
  43. package/.eslintrc.js +0 -44
  44. package/dist/audit/AuditProperties.d.ts +0 -16
  45. package/dist/audit/AuditUtils.d.ts +0 -2
  46. package/dist/audit/AuditUtils.js +0 -36
  47. package/src/alerts/Alert.model.ts +0 -289
  48. package/src/alerts/Alert.repository.ts +0 -487
  49. package/src/alerts/Alert.service.ts +0 -711
  50. package/src/alerts/AlertBuilder.example.ts +0 -126
  51. package/src/alerts/AlertBuilder.ts +0 -208
  52. package/src/alerts/AlertService.example.ts +0 -232
  53. package/src/alerts/alert.types.ts +0 -64
  54. package/src/alerts/index.ts +0 -3
  55. package/src/audit/AuditProperties.ts +0 -16
  56. package/src/audit/AuditUtils.ts +0 -38
  57. package/src/config/config.ts +0 -202
  58. package/src/config/config.types.ts +0 -21
  59. package/src/connection/Connection.repository.ts +0 -52
  60. package/src/connection/Connection.service.ts +0 -39
  61. package/src/connection/IConnection.ts +0 -27
  62. package/src/connection/index.ts +0 -3
  63. package/src/constants/ConnectionProviders.ts +0 -11
  64. package/src/constants/Event.ts +0 -89
  65. package/src/constants/Service.ts +0 -17
  66. package/src/constants/index.ts +0 -3
  67. package/src/db/db.ts +0 -24
  68. package/src/db/index.ts +0 -2
  69. package/src/db/redis.ts +0 -20
  70. package/src/device/cloud/entities/CloudDevice.ts +0 -40
  71. package/src/device/cloud/entities/CloudDeviceService.ts +0 -8
  72. package/src/device/cloud/entities/DeviceFactory.ts +0 -27
  73. package/src/device/cloud/entities/index.ts +0 -3
  74. package/src/device/cloud/interfaces/ICloudDevice.ts +0 -14
  75. package/src/device/cloud/interfaces/ICloudDeviceService.ts +0 -6
  76. package/src/device/cloud/interfaces/IDeviceFactory.ts +0 -5
  77. package/src/device/cloud/interfaces/IRawDataTransformer.ts +0 -5
  78. package/src/device/cloud/interfaces/IRawDevice.ts +0 -19
  79. package/src/device/cloud/interfaces/index.ts +0 -5
  80. package/src/device/local/interfaces/IDevice.ts +0 -61
  81. package/src/device/local/interfaces/IDtDevice.ts +0 -16
  82. package/src/device/local/interfaces/ISchedule.ts +0 -40
  83. package/src/device/local/interfaces/index.ts +0 -3
  84. package/src/device/local/repository/Device.repository.ts +0 -368
  85. package/src/device/local/repository/Hub.repository.ts +0 -107
  86. package/src/device/local/repository/Schedule.repository.ts +0 -72
  87. package/src/device/local/services/Device.service.ts +0 -436
  88. package/src/device/local/services/Hub.service.ts +0 -57
  89. package/src/device/local/services/Schedule.service.ts +0 -26
  90. package/src/device/local/services/index.ts +0 -3
  91. package/src/docs/Alert.model.md +0 -319
  92. package/src/docs/Alerts&IssuesModel.md +0 -312
  93. package/src/docs/Issue.model.md +0 -386
  94. package/src/docs/SECURITY.md +0 -67
  95. package/src/docs/TROUBLESHOOTING.md +0 -184
  96. package/src/events/BaseEventHandler.ts +0 -145
  97. package/src/events/BaseEventTransformer.ts +0 -97
  98. package/src/events/DeviceEventHandler.ts +0 -213
  99. package/src/events/DeviceEventTransformerFactory.ts +0 -77
  100. package/src/events/EventHandler.ts +0 -124
  101. package/src/events/EventHandlerOrchestrator.ts +0 -119
  102. package/src/events/EventProcessingService.ts +0 -248
  103. package/src/events/InternalEventSubscription.ts +0 -194
  104. package/src/events/index.ts +0 -9
  105. package/src/events/interfaces/DeviceEvent.ts +0 -56
  106. package/src/events/interfaces/IEventHandler.ts +0 -28
  107. package/src/events/interfaces/IEventTransformer.ts +0 -8
  108. package/src/events/interfaces/IInternalEvent.ts +0 -33
  109. package/src/events/interfaces/index.ts +0 -4
  110. package/src/index.ts +0 -43
  111. package/src/issues/Issue.model.ts +0 -350
  112. package/src/issues/Issue.repository.ts +0 -517
  113. package/src/issues/Issue.service.ts +0 -932
  114. package/src/issues/IssueBuilder.example.ts +0 -210
  115. package/src/issues/IssueBuilder.ts +0 -263
  116. package/src/issues/IssueService.example.ts +0 -310
  117. package/src/issues/index.ts +0 -2
  118. package/src/issues/issue.types.ts +0 -98
  119. package/src/property/IProperty.ts +0 -30
  120. package/src/property/Property.repository.ts +0 -53
  121. package/src/property/Property.service.ts +0 -38
  122. package/src/property/index.ts +0 -2
  123. package/src/queue/entities/HybridHttpQueue.ts +0 -274
  124. package/src/queue/entities/index.ts +0 -1
  125. package/src/queue/index.ts +0 -6
  126. package/src/queue/interfaces/IHttpRequestJob.ts +0 -10
  127. package/src/queue/interfaces/IHybridHttpQueue.ts +0 -25
  128. package/src/queue/interfaces/IJobResult.ts +0 -15
  129. package/src/queue/interfaces/IRateLimitConfig.ts +0 -5
  130. package/src/queue/interfaces/index.ts +0 -4
  131. package/src/queue/services/QueueService.ts +0 -40
  132. package/src/queue/services/index.ts +0 -1
  133. package/src/queue/types/http.types.ts +0 -23
  134. package/src/queue/types/index.ts +0 -2
  135. package/src/queue/types/queue.types.ts +0 -21
  136. package/src/queue/utils/index.ts +0 -3
  137. package/src/queue/utils/jobUtils.ts +0 -79
  138. package/src/queue/utils/queueUtils.ts +0 -84
  139. package/src/queue/utils/rateLimit.utils.ts +0 -131
  140. package/src/utils/http.utils.ts +0 -143
  141. package/src/utils/index.ts +0 -2
  142. package/src/utils/redis.utils.ts +0 -74
  143. package/tsconfig.json +0 -20
  144. /package/dist/{audit/AuditProperties.js → device/cloud/interfaces/IDevicesService.js} +0 -0
@@ -1,12 +1,21 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.QueueUtils = void 0;
7
+ const rateLimit_utils_1 = require("./rateLimit.utils");
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const dt_audit_library_1 = require("dt-audit-library");
4
10
  const config_1 = require("../../config/config");
5
11
  const redis_1 = require("../../db/redis");
6
12
  class QueueUtils {
7
13
  static getQueueKey(microservice, connectionId, provider) {
8
14
  return `${microservice}_${provider}_${connectionId}`;
9
15
  }
16
+ static getRequestQueueKey(connectionId, provider) {
17
+ return `${connectionId}_${provider}`;
18
+ }
10
19
  static getOrCreateQueue(queueKey, queues) {
11
20
  return (queues.get(queueKey) ??
12
21
  queues
@@ -27,10 +36,9 @@ class QueueUtils {
27
36
  lockDuration: 300000,
28
37
  stalledInterval: 60000,
29
38
  });
30
- // Simplified event handlers
39
+ // Event handlers for job tracking
31
40
  worker.on("completed", (job) => {
32
41
  (0, config_1.getConfig)().LOGGER.info(`HTTP request completed: ${job.id} [${queueKey}]`);
33
- // job.returnvalue is the actual result, not a Promise
34
42
  const result = job.returnvalue;
35
43
  jobResults.set(job.id, {
36
44
  result,
@@ -47,10 +55,111 @@ class QueueUtils {
47
55
  });
48
56
  });
49
57
  worker.on("error", (err) => (0, config_1.getConfig)().LOGGER.error(`Worker error for ${queueKey}: ${err.message}`));
50
- worker.on("stalled", (jobId) => (0, config_1.getConfig)().LOGGER.warn(`Job ${jobId} stalled in worker ${queueKey}`));
51
- worker.on("active", (job) => (0, config_1.getConfig)().LOGGER.info(`HTTP request started: ${job.id} [${queueKey}]`));
52
58
  workers.set(queueKey, worker);
53
59
  (0, config_1.getConfig)().LOGGER.info(`Worker initialized for queue: ${queueKey}`);
54
60
  }
61
+ static async waitForRateLimitExpiry(connectionId, provider, rateLimitConfigs) {
62
+ const key = `rate_limit:${provider}:${connectionId}`;
63
+ const config = rateLimitConfigs.get(provider);
64
+ if (!config)
65
+ return;
66
+ while (true) {
67
+ const timestamps = await rateLimit_utils_1.RateLimitUtils.getRawRequestTimestamps(key);
68
+ const now = Date.now();
69
+ const windowStart = now - config.windowMs;
70
+ const recentRequests = timestamps.filter((t) => t > windowStart);
71
+ if (recentRequests.length < config.maxRequests) {
72
+ // Rate limit not exceeded, we can proceed
73
+ break;
74
+ }
75
+ // Calculate when the earliest request will expire
76
+ const earliestRequest = recentRequests[0];
77
+ const nextAvailableTime = earliestRequest + config.windowMs;
78
+ const delay = Math.max(nextAvailableTime - now, 1000); // At least 1 second
79
+ (0, config_1.getConfig)().LOGGER.info(`Rate limit exceeded for ${provider} [${connectionId}]. Waiting ${delay}ms until next allowed request. Current requests in window: ${recentRequests.length}/${config.maxRequests}`);
80
+ // Wait for the calculated delay
81
+ await new Promise((resolve) => setTimeout(resolve, delay));
82
+ }
83
+ }
84
+ static async executeHttpRequest(url, method, options, connectionId, provider) {
85
+ (0, config_1.getConfig)().LOGGER.info(`Executing: ${method} ${url} -> ${provider} [${connectionId}]`);
86
+ try {
87
+ // Record the request first
88
+ await rateLimit_utils_1.RateLimitUtils.recordRequest(connectionId, provider);
89
+ // Execute the HTTP request
90
+ const response = await (0, axios_1.default)({
91
+ method: method.toLowerCase(),
92
+ url: url,
93
+ headers: options.headers || {},
94
+ timeout: 30000,
95
+ ...(options.body && { data: options.body }),
96
+ ...(options.params && { params: options.params }),
97
+ });
98
+ (0, config_1.getConfig)().LOGGER.info(`HTTP request successful: ${method} ${url} for ${provider} [${connectionId}]`);
99
+ // Return only the response data
100
+ return response.data;
101
+ }
102
+ catch (error) {
103
+ (0, config_1.getConfig)().LOGGER.error(`HTTP request failed: ${error.message}`);
104
+ await (0, dt_audit_library_1.publishAudit)({
105
+ eventType: "http.request.error",
106
+ properties: {
107
+ connectionId,
108
+ provider,
109
+ endpoint: url,
110
+ method,
111
+ timestamp: Date.now(),
112
+ reason: "execution_error",
113
+ errorMessage: error.message,
114
+ },
115
+ });
116
+ // Throw the error instead of returning it
117
+ throw new Error(`HTTP request failed: ${error.message}`);
118
+ }
119
+ }
120
+ static async addJobToQueue(queueKey, jobData, delay, queues) {
121
+ const queue = this.getOrCreateQueue(queueKey, queues);
122
+ const job = await queue.add("http-request", jobData, {
123
+ delay,
124
+ attempts: 1,
125
+ removeOnComplete: { age: 300, count: 1 },
126
+ removeOnFail: { age: 300, count: 1 },
127
+ });
128
+ return job.id;
129
+ }
130
+ static async waitForJobCompletion(jobId, queueKey, jobResults) {
131
+ return new Promise((resolve, reject) => {
132
+ let timeoutId;
133
+ let checkCount = 0;
134
+ const maxChecks = 600;
135
+ const checkJob = async () => {
136
+ if (++checkCount >= maxChecks) {
137
+ clearTimeout(timeoutId);
138
+ return reject(new Error("Job timeout: Request took too long"));
139
+ }
140
+ try {
141
+ const memoryResult = jobResults.get(jobId);
142
+ if (memoryResult?.resolved) {
143
+ clearTimeout(timeoutId);
144
+ return memoryResult.result !== undefined
145
+ ? resolve(memoryResult.result)
146
+ : reject(new Error(memoryResult.error || "Unknown error"));
147
+ }
148
+ // Continue checking
149
+ timeoutId = setTimeout(checkJob, checkCount < 10 ? 50 : 100);
150
+ }
151
+ catch (error) {
152
+ clearTimeout(timeoutId);
153
+ reject(new Error(`Error checking job: ${error.message}`));
154
+ }
155
+ };
156
+ checkJob();
157
+ // Backup timeout
158
+ setTimeout(() => {
159
+ clearTimeout(timeoutId);
160
+ reject(new Error("Request timeout: Maximum wait time exceeded"));
161
+ }, 60000);
162
+ });
163
+ }
55
164
  }
56
165
  exports.QueueUtils = QueueUtils;
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "dt-common-device",
3
- "version": "3.0.9",
3
+ "version": "3.0.11",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
+ "files": [
7
+ "dist",
8
+ "README.md",
9
+ "LICENSE"
10
+ ],
6
11
  "scripts": {
7
12
  "build": "tsc",
8
13
  "patch": "npm version patch && npm run build && npm publish",
package/.eslintrc.js DELETED
@@ -1,44 +0,0 @@
1
- module.exports = {
2
- parser: "@typescript-eslint/parser",
3
- extends: ["eslint:recommended", "@typescript-eslint/recommended"],
4
- plugins: ["@typescript-eslint"],
5
- env: {
6
- node: true,
7
- es2020: true,
8
- },
9
- parserOptions: {
10
- ecmaVersion: 2020,
11
- sourceType: "module",
12
- },
13
- rules: {
14
- // Security rules
15
- "no-eval": "error",
16
- "no-implied-eval": "error",
17
- "no-new-func": "error",
18
- "no-script-url": "error",
19
- "no-unsafe-finally": "error",
20
-
21
- // TypeScript security
22
- "@typescript-eslint/no-explicit-any": "warn",
23
- "@typescript-eslint/no-unsafe-assignment": "warn",
24
- "@typescript-eslint/no-unsafe-call": "warn",
25
- "@typescript-eslint/no-unsafe-member-access": "warn",
26
- "@typescript-eslint/no-unsafe-return": "warn",
27
-
28
- // Code quality
29
- "@typescript-eslint/explicit-function-return-type": "warn",
30
- "@typescript-eslint/no-unused-vars": "error",
31
- "@typescript-eslint/prefer-const": "error",
32
- "@typescript-eslint/no-var-requires": "error",
33
-
34
- // Error handling
35
- "no-console": "warn",
36
- "no-throw-literal": "error",
37
-
38
- // Best practices
39
- "prefer-const": "error",
40
- "no-var": "error",
41
- "object-shorthand": "error",
42
- },
43
- ignorePatterns: ["dist/", "node_modules/", "*.js"],
44
- };
@@ -1,16 +0,0 @@
1
- export interface AuditProperties {
2
- resource: string;
3
- propertyId: string;
4
- propertyName?: string;
5
- userId?: string;
6
- userName?: string;
7
- deviceId: string;
8
- deviceName: string;
9
- zoneId?: string;
10
- zoneName?: string;
11
- accessGroupId?: string;
12
- accessGroupName?: string;
13
- scheduleId?: string;
14
- scheduleName?: string;
15
- [key: string]: any;
16
- }
@@ -1,2 +0,0 @@
1
- import { AuditProperties } from "./AuditProperties";
2
- export declare function buildAuditProperties(input: AuditProperties): Record<string, any>;
@@ -1,36 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildAuditProperties = buildAuditProperties;
4
- const AUDIT_FIELDS = [
5
- "resource",
6
- "propertyId",
7
- "propertyName",
8
- "userId",
9
- "userName",
10
- "deviceId",
11
- "deviceName",
12
- "zoneId",
13
- "zoneName",
14
- "accessGroupId",
15
- "accessGroupName",
16
- "scheduleId",
17
- "scheduleName",
18
- ];
19
- function buildAuditProperties(input) {
20
- // Normalize keys to camelCase for matching
21
- const normalized = { ...input };
22
- // Build the audit object with all standard fields
23
- const audit = {};
24
- for (const field of AUDIT_FIELDS) {
25
- // Try to find a matching key in input (case-insensitive)
26
- const foundKey = Object.keys(normalized).find((k) => k.toLowerCase() === field.toLowerCase());
27
- audit[field] = foundKey ? normalized[foundKey] : undefined;
28
- }
29
- // Merge in all other event-specific data (but don't overwrite audit fields)
30
- for (const key of Object.keys(normalized)) {
31
- if (!audit.hasOwnProperty(key)) {
32
- audit[key] = normalized[key];
33
- }
34
- }
35
- return audit;
36
- }
@@ -1,289 +0,0 @@
1
- import mongoose, { Schema, Model } from "mongoose";
2
- import {
3
- AlertCategory,
4
- AlertSeverity,
5
- EntityType,
6
- AlertDocument as IAlertDocument,
7
- CreateAlertData,
8
- UpdateAlertData,
9
- } from "./alert.types";
10
-
11
- // Interface for instance methods
12
- interface IAlertMethods {
13
- markAsRead(updatedBy: string): void;
14
- markAsUnread(updatedBy: string): void;
15
- activate(updatedBy: string): void;
16
- deactivate(updatedBy: string): void;
17
- snooze(until: Date, updatedBy: string): void;
18
- unsnooze(updatedBy: string): void;
19
- }
20
-
21
- // Interface for static methods
22
- interface IAlertModel extends Model<IAlertDocument, {}, IAlertMethods> {
23
- findByProperty(
24
- propertyId: string,
25
- includeDeleted?: boolean
26
- ): Promise<IAlertDocument[]>;
27
- findByEntity(
28
- entityId: string,
29
- entityType: EntityType,
30
- includeDeleted?: boolean
31
- ): Promise<IAlertDocument[]>;
32
- findByCategory(
33
- category: AlertCategory,
34
- includeDeleted?: boolean
35
- ): Promise<IAlertDocument[]>;
36
- findBySeverity(
37
- severity: AlertSeverity,
38
- includeDeleted?: boolean
39
- ): Promise<IAlertDocument[]>;
40
- findActive(includeDeleted?: boolean): Promise<IAlertDocument[]>;
41
- findUnread(includeDeleted?: boolean): Promise<IAlertDocument[]>;
42
- findSnoozed(includeDeleted?: boolean): Promise<IAlertDocument[]>;
43
- findExpiredSnooze(includeDeleted?: boolean): Promise<IAlertDocument[]>;
44
- }
45
-
46
- // Main Alert schema
47
- const AlertSchema = new Schema<IAlertDocument, IAlertModel, IAlertMethods>(
48
- {
49
- category: {
50
- type: [String],
51
- enum: Object.values(AlertCategory),
52
- required: true,
53
- validate: {
54
- validator: function (categories: string[]) {
55
- return categories && categories.length > 0;
56
- },
57
- message: "At least one category is required",
58
- },
59
- },
60
- propertyId: {
61
- type: String,
62
- required: true,
63
- index: true,
64
- },
65
- title: {
66
- type: String,
67
- required: true,
68
- trim: true,
69
- },
70
- description: {
71
- type: String,
72
- required: true,
73
- trim: true,
74
- },
75
- entityId: {
76
- type: String,
77
- index: true,
78
- },
79
- entityType: {
80
- type: String,
81
- enum: Object.values(EntityType),
82
- required: true,
83
- index: true,
84
- },
85
- severity: {
86
- type: String,
87
- enum: Object.values(AlertSeverity),
88
- default: AlertSeverity.MEDIUM,
89
- },
90
- isRead: {
91
- type: Boolean,
92
- default: false,
93
- index: true,
94
- },
95
- isActive: {
96
- type: Boolean,
97
- default: true,
98
- index: true,
99
- },
100
- isDeleted: {
101
- type: Boolean,
102
- default: false,
103
- },
104
- snoozeUntil: {
105
- type: Date,
106
- },
107
- createdBy: {
108
- type: String,
109
- },
110
- updatedBy: {
111
- type: String,
112
- },
113
- createdAt: {
114
- type: Date,
115
- default: Date.now,
116
- },
117
- updatedAt: {
118
- type: Date,
119
- default: Date.now,
120
- },
121
- },
122
- {
123
- timestamps: true,
124
- collection: "dt_alerts",
125
- }
126
- );
127
-
128
- // Compound indexes to match Prisma schema
129
- AlertSchema.index({ propertyId: 1, isActive: 1, isRead: 1 });
130
- AlertSchema.index({ entityId: 1, entityType: 1 });
131
- AlertSchema.index({ createdAt: 1 });
132
-
133
- // Pre-save middleware to update the updatedAt field
134
- AlertSchema.pre("save", function (next) {
135
- this.updatedAt = new Date();
136
- next();
137
- });
138
-
139
- // Pre-update middleware to update the updatedAt field
140
- AlertSchema.pre(
141
- ["updateOne", "findOneAndUpdate", "updateMany"],
142
- function (next) {
143
- this.set({ updatedAt: new Date() });
144
- next();
145
- }
146
- );
147
-
148
- // Instance methods
149
- AlertSchema.methods.markAsRead = function (updatedBy: string): void {
150
- this.isRead = true;
151
- this.updatedBy = updatedBy;
152
- };
153
-
154
- AlertSchema.methods.markAsUnread = function (updatedBy: string): void {
155
- this.isRead = false;
156
- this.updatedBy = updatedBy;
157
- };
158
-
159
- AlertSchema.methods.activate = function (updatedBy: string): void {
160
- this.isActive = true;
161
- this.updatedBy = updatedBy;
162
- };
163
-
164
- AlertSchema.methods.deactivate = function (updatedBy: string): void {
165
- this.isActive = false;
166
- this.updatedBy = updatedBy;
167
- };
168
-
169
- AlertSchema.methods.snooze = function (until: Date, updatedBy: string): void {
170
- this.snoozeUntil = until;
171
- this.updatedBy = updatedBy;
172
- };
173
-
174
- AlertSchema.methods.unsnooze = function (updatedBy: string): void {
175
- this.snoozeUntil = undefined;
176
- this.updatedBy = updatedBy;
177
- };
178
-
179
- // Static methods
180
- AlertSchema.statics.findByProperty = function (
181
- propertyId: string,
182
- includeDeleted = false
183
- ) {
184
- const query: any = { propertyId };
185
- if (!includeDeleted) {
186
- query.isDeleted = false;
187
- }
188
- return this.find(query).sort({ createdAt: -1 });
189
- };
190
-
191
- AlertSchema.statics.findByEntity = function (
192
- entityId: string,
193
- entityType: EntityType,
194
- includeDeleted = false
195
- ) {
196
- const query: any = { entityId, entityType };
197
- if (!includeDeleted) {
198
- query.isDeleted = false;
199
- }
200
- return this.find(query).sort({ createdAt: -1 });
201
- };
202
-
203
- AlertSchema.statics.findByCategory = function (
204
- category: AlertCategory,
205
- includeDeleted = false
206
- ) {
207
- const query: any = { category: { $in: [category] } };
208
- if (!includeDeleted) {
209
- query.isDeleted = false;
210
- }
211
- return this.find(query).sort({ createdAt: -1 });
212
- };
213
-
214
- AlertSchema.statics.findBySeverity = function (
215
- severity: AlertSeverity,
216
- includeDeleted = false
217
- ) {
218
- const query: any = { severity };
219
- if (!includeDeleted) {
220
- query.isDeleted = false;
221
- }
222
- return this.find(query).sort({ severity: -1, createdAt: -1 });
223
- };
224
-
225
- AlertSchema.statics.findActive = function (includeDeleted = false) {
226
- const query: any = { isActive: true };
227
- if (!includeDeleted) {
228
- query.isDeleted = false;
229
- }
230
- return this.find(query).sort({ severity: -1, createdAt: -1 });
231
- };
232
-
233
- AlertSchema.statics.findUnread = function (includeDeleted = false) {
234
- const query: any = { isRead: false };
235
- if (!includeDeleted) {
236
- query.isDeleted = false;
237
- }
238
- return this.find(query).sort({ severity: -1, createdAt: -1 });
239
- };
240
-
241
- AlertSchema.statics.findSnoozed = function (includeDeleted = false) {
242
- const query: any = { snoozeUntil: { $exists: true, $ne: null } };
243
- if (!includeDeleted) {
244
- query.isDeleted = false;
245
- }
246
- return this.find(query).sort({ snoozeUntil: 1 });
247
- };
248
-
249
- AlertSchema.statics.findExpiredSnooze = function (includeDeleted = false) {
250
- const query: any = {
251
- snoozeUntil: { $lt: new Date() },
252
- isActive: true,
253
- };
254
- if (!includeDeleted) {
255
- query.isDeleted = false;
256
- }
257
- return this.find(query).sort({ snoozeUntil: 1 });
258
- };
259
-
260
- // Virtual for snooze status
261
- AlertSchema.virtual("isSnoozed").get(function () {
262
- return this.snoozeUntil && this.snoozeUntil > new Date();
263
- });
264
-
265
- AlertSchema.virtual("isSnoozeExpired").get(function () {
266
- return this.snoozeUntil && this.snoozeUntil <= new Date();
267
- });
268
-
269
- // Ensure virtuals are serialized
270
- AlertSchema.set("toJSON", { virtuals: true });
271
- AlertSchema.set("toObject", { virtuals: true });
272
-
273
- // Create and export the model
274
- export const AlertModel = mongoose.model<IAlertDocument, IAlertModel>(
275
- "Alert",
276
- AlertSchema
277
- );
278
-
279
- // Export the schema for potential reuse
280
- export { AlertSchema };
281
-
282
- // Export types for external use
283
- export type {
284
- IAlertDocument,
285
- CreateAlertData,
286
- UpdateAlertData,
287
- IAlertMethods,
288
- IAlertModel,
289
- };