@justair/justair-library 4.7.21 → 4.7.22
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.
- package/README +146 -146
- package/package.json +28 -28
- package/src/config/db.js +116 -116
- package/src/config/logger.js +44 -44
- package/src/index.js +116 -116
- package/src/models/admin.js +30 -30
- package/src/models/alerts.js +221 -221
- package/src/models/announcements.js +31 -31
- package/src/models/apiKey.js +22 -22
- package/src/models/configurations.js +17 -17
- package/src/models/contexts.js +13 -13
- package/src/models/dataCompleteness.js +42 -42
- package/src/models/events.js +123 -123
- package/src/models/features.js +14 -14
- package/src/models/jobs.js +49 -49
- package/src/models/lightmonitors.js +30 -30
- package/src/models/measurements.js +269 -269
- package/src/models/monitorRequests.js +25 -25
- package/src/models/monitorSuppliers.js +21 -21
- package/src/models/monitors.js +400 -399
- package/src/models/networkMetrics.js +42 -42
- package/src/models/organizations.js +97 -97
- package/src/models/parameters.js +11 -11
- package/src/models/referenceMonitorInfo.js +18 -18
- package/src/models/tests/admin.test.js +42 -42
- package/src/models/tests/configurations.test.js +44 -44
- package/src/models/tests/measurements.test.js +46 -46
- package/src/models/tests/monitorRequests.test.js +46 -46
- package/src/models/tests/monitors.test.js +62 -62
- package/src/models/tests/organizations.test.js +51 -51
- package/src/models/tests/users.test.js +54 -54
- package/src/models/usageMetrics.js +28 -28
- package/src/models/users.js +55 -55
- package/tsconfig.json +10 -10
package/src/models/monitors.js
CHANGED
|
@@ -1,399 +1,400 @@
|
|
|
1
|
-
import mongoose from "mongoose";
|
|
2
|
-
const parametersEnum = [
|
|
3
|
-
"NO2",
|
|
4
|
-
"SO2",
|
|
5
|
-
"PM2.5",
|
|
6
|
-
"PM10",
|
|
7
|
-
"Temperature",
|
|
8
|
-
"Humidity",
|
|
9
|
-
"OZONE",
|
|
10
|
-
"VOC",
|
|
11
|
-
"CO",
|
|
12
|
-
"NO",
|
|
13
|
-
"PM1",
|
|
14
|
-
"WS And Direction",
|
|
15
|
-
"DP",
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (value
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
decoder
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
decoder
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
"
|
|
172
|
-
"
|
|
173
|
-
"
|
|
174
|
-
"
|
|
175
|
-
"
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"Unhealthy",
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
"
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
monitorsSchema.index({ "location.
|
|
266
|
-
monitorsSchema.index({ "location.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
monitorsSchema.index({
|
|
271
|
-
monitorsSchema.index({
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
monitorsSchema.index({
|
|
279
|
-
monitorsSchema.index({
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
docToDelete.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
docToDelete.
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
const parametersEnum = [
|
|
3
|
+
"NO2",
|
|
4
|
+
"SO2",
|
|
5
|
+
"PM2.5",
|
|
6
|
+
"PM10",
|
|
7
|
+
"Temperature",
|
|
8
|
+
"Humidity",
|
|
9
|
+
"OZONE",
|
|
10
|
+
"VOC",
|
|
11
|
+
"CO",
|
|
12
|
+
"NO",
|
|
13
|
+
"PM1",
|
|
14
|
+
"WS And Direction",
|
|
15
|
+
"DP",
|
|
16
|
+
"CH4",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const noteSchema = mongoose.Schema({
|
|
20
|
+
note: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
type: {
|
|
25
|
+
type: String,
|
|
26
|
+
required: true,
|
|
27
|
+
},
|
|
28
|
+
monitorState: {
|
|
29
|
+
type: String,
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
adminId: {
|
|
33
|
+
type: mongoose.Types.ObjectId,
|
|
34
|
+
ref: "Admin",
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
adminName: {
|
|
38
|
+
type: String,
|
|
39
|
+
},
|
|
40
|
+
date: {
|
|
41
|
+
type: Date,
|
|
42
|
+
default: Date.now,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const correctionSchema = mongoose.Schema(
|
|
47
|
+
{
|
|
48
|
+
equationType: {
|
|
49
|
+
type: String,
|
|
50
|
+
enum: ["linear", "custom"],
|
|
51
|
+
required: true,
|
|
52
|
+
},
|
|
53
|
+
equation: {
|
|
54
|
+
type: String,
|
|
55
|
+
required: function () {
|
|
56
|
+
return this.equationType === "custom";
|
|
57
|
+
},
|
|
58
|
+
validate: {
|
|
59
|
+
validator: function (value) {
|
|
60
|
+
if (!value) return true; // Allow empty for non-custom types
|
|
61
|
+
if (value.includes("\0")) return false;
|
|
62
|
+
try {
|
|
63
|
+
const encoder = new TextEncoder();
|
|
64
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
65
|
+
decoder.decode(encoder.encode(value));
|
|
66
|
+
return true;
|
|
67
|
+
} catch (e) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
message: "Equation contains invalid UTF-8 characters",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
context: {
|
|
75
|
+
type: String,
|
|
76
|
+
enum: ["field", "colocation"],
|
|
77
|
+
required: false,
|
|
78
|
+
},
|
|
79
|
+
variables: {
|
|
80
|
+
required: function () {
|
|
81
|
+
return this.equationType === "linear";
|
|
82
|
+
},
|
|
83
|
+
type: Object,
|
|
84
|
+
},
|
|
85
|
+
dateCreated: {
|
|
86
|
+
type: Date,
|
|
87
|
+
default: Date.now,
|
|
88
|
+
},
|
|
89
|
+
applyCorrection: { type: Boolean, default: true },
|
|
90
|
+
},
|
|
91
|
+
{ _id: false } // no separate _id for sub-docs
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const correctionHistorySchema = mongoose.Schema(
|
|
95
|
+
{
|
|
96
|
+
pollutant: {
|
|
97
|
+
type: String,
|
|
98
|
+
required: true,
|
|
99
|
+
validate: {
|
|
100
|
+
validator: function (value) {
|
|
101
|
+
// Check for null bytes and validate UTF-8
|
|
102
|
+
if (value.includes("\0")) return false;
|
|
103
|
+
try {
|
|
104
|
+
const encoder = new TextEncoder();
|
|
105
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
106
|
+
decoder.decode(encoder.encode(value));
|
|
107
|
+
return true;
|
|
108
|
+
} catch (e) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
message: "Pollutant contains invalid UTF-8 characters",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
oldValue: {
|
|
116
|
+
type: correctionSchema,
|
|
117
|
+
default: undefined,
|
|
118
|
+
},
|
|
119
|
+
newValue: {
|
|
120
|
+
type: correctionSchema,
|
|
121
|
+
default: undefined,
|
|
122
|
+
},
|
|
123
|
+
changedAt: {
|
|
124
|
+
type: Date,
|
|
125
|
+
default: Date.now,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
{ _id: false }
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Monitor Audit Schema
|
|
132
|
+
const monitorAuditSchema = mongoose.Schema(
|
|
133
|
+
{
|
|
134
|
+
monitorId: { type: mongoose.Types.ObjectId, ref: "Monitors" },
|
|
135
|
+
orgId: { type: mongoose.Types.ObjectId, ref: "Organizations" },
|
|
136
|
+
timeUpdated: Date,
|
|
137
|
+
deletedAt: { type: Date, default: Date.now }, // Only populated on delete
|
|
138
|
+
monitorProperties: Object, // Stores properties of the monitor
|
|
139
|
+
monitorState: String, // Tracks the state of the monitor (e.g., "Deployed", "Maintenance")
|
|
140
|
+
monitorLocation: {
|
|
141
|
+
// Monitor GPS location (lat, lon)
|
|
142
|
+
type: { type: String, enum: ["Point"], required: true },
|
|
143
|
+
coordinates: { type: [Number], required: true },
|
|
144
|
+
},
|
|
145
|
+
parameterThresholds: Object,
|
|
146
|
+
corrections: {
|
|
147
|
+
type: Map,
|
|
148
|
+
of: correctionSchema,
|
|
149
|
+
default: {},
|
|
150
|
+
},
|
|
151
|
+
correctionHistory: {
|
|
152
|
+
type: [correctionHistorySchema],
|
|
153
|
+
default: [],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
timestamps: true,
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// Create the MonitorAudit model
|
|
162
|
+
const MonitorAudit = mongoose.model("MonitorAudit", monitorAuditSchema);
|
|
163
|
+
|
|
164
|
+
// Monitors Schema
|
|
165
|
+
const monitorsSchema = mongoose.Schema(
|
|
166
|
+
{
|
|
167
|
+
monitorCode: String,
|
|
168
|
+
monitorSupplier: {
|
|
169
|
+
type: String,
|
|
170
|
+
enum: [
|
|
171
|
+
"clarity",
|
|
172
|
+
"aeroqual",
|
|
173
|
+
"purple air",
|
|
174
|
+
"reference monitor",
|
|
175
|
+
"earthview",
|
|
176
|
+
"sensit",
|
|
177
|
+
"blue sky",
|
|
178
|
+
"aq mesh",
|
|
179
|
+
"quant aq",
|
|
180
|
+
"airGradient",
|
|
181
|
+
"oizom",
|
|
182
|
+
"metOne",
|
|
183
|
+
"aqMesh",
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
monitorType: String,
|
|
187
|
+
monitorIdFromSupplier: String,
|
|
188
|
+
measurementUpdate: Date,
|
|
189
|
+
monitorProperties: Object,
|
|
190
|
+
isPrivate: { type: Boolean, default: false, required: true },
|
|
191
|
+
monitorState: {
|
|
192
|
+
type: String,
|
|
193
|
+
enum: ["Collocation", "Deployed", "Maintenance", "Pending Deployment"],
|
|
194
|
+
},
|
|
195
|
+
monitorStateHistory: [Object],
|
|
196
|
+
monitorAlertStatus: {
|
|
197
|
+
type: String,
|
|
198
|
+
enum: [
|
|
199
|
+
"Good",
|
|
200
|
+
"Moderate",
|
|
201
|
+
"Unhealthy for SG",
|
|
202
|
+
"Unhealthy",
|
|
203
|
+
"Very Unhealthy",
|
|
204
|
+
"Hazardous",
|
|
205
|
+
"Bad",
|
|
206
|
+
],
|
|
207
|
+
default: "Good",
|
|
208
|
+
},
|
|
209
|
+
sponsor: { type: mongoose.Types.ObjectId, ref: "Organizations" },
|
|
210
|
+
sponsorName: String,
|
|
211
|
+
monitorLatitude: Number,
|
|
212
|
+
monitorLongitude: Number,
|
|
213
|
+
gpsLocation: {
|
|
214
|
+
type: { type: String, enum: ["Point"], required: true },
|
|
215
|
+
coordinates: { type: [Number], required: true },
|
|
216
|
+
},
|
|
217
|
+
location: Object,
|
|
218
|
+
context: [String],
|
|
219
|
+
colocationDate: Date,
|
|
220
|
+
deploymentDate: Date,
|
|
221
|
+
subscriptionDate: Date,
|
|
222
|
+
parameters: [
|
|
223
|
+
{
|
|
224
|
+
type: String,
|
|
225
|
+
enum: parametersEnum,
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
latestPM2_5: Number,
|
|
229
|
+
latestAQI_PM2_5: Number,
|
|
230
|
+
notes: [noteSchema],
|
|
231
|
+
calculatedAverages: [Object],
|
|
232
|
+
images: [String],
|
|
233
|
+
isActive: { type: Boolean, default: true },
|
|
234
|
+
parameterThresholds: Object,
|
|
235
|
+
completionPercentageThresholds: Object,
|
|
236
|
+
anomalyPercentageThresholds: Object,
|
|
237
|
+
// A Map, keyed by pollutant (e.g. "PM2_5"), storing Correction sub-docs
|
|
238
|
+
corrections: {
|
|
239
|
+
type: Map,
|
|
240
|
+
of: correctionSchema,
|
|
241
|
+
default: {},
|
|
242
|
+
},
|
|
243
|
+
correctionHistory: {
|
|
244
|
+
type: [correctionHistorySchema],
|
|
245
|
+
default: [],
|
|
246
|
+
},
|
|
247
|
+
applyCorrections: { type: Boolean, default: false },
|
|
248
|
+
pausedParameters: {
|
|
249
|
+
type: [{ parameter: String, timestamp: Date, isPaused: Boolean }],
|
|
250
|
+
default: [],
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
timestamps: true,
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
// Geographic queries - already exists
|
|
259
|
+
monitorsSchema.index({ gpsLocation: "2dsphere" });
|
|
260
|
+
|
|
261
|
+
// Sponsor-based queries
|
|
262
|
+
monitorsSchema.index({ sponsor: 1, isPrivate: 1, isActive: 1 });
|
|
263
|
+
|
|
264
|
+
// Location-based filtering
|
|
265
|
+
monitorsSchema.index({ "location.city": 1, "location.state": 1 });
|
|
266
|
+
monitorsSchema.index({ "location.neighborhood": 1 });
|
|
267
|
+
monitorsSchema.index({ "location.county": 1 });
|
|
268
|
+
|
|
269
|
+
// Query parameter filtering
|
|
270
|
+
monitorsSchema.index({ monitorSupplier: 1, monitorState: 1 });
|
|
271
|
+
monitorsSchema.index({ context: 1 });
|
|
272
|
+
monitorsSchema.index({ parameters: 1 });
|
|
273
|
+
|
|
274
|
+
// Monitor lookups by supplier
|
|
275
|
+
monitorsSchema.index({ sponsor: 1, monitorSupplier: 1 });
|
|
276
|
+
|
|
277
|
+
// Keep existing single-field indexes for backward compatibility
|
|
278
|
+
monitorsSchema.index({ monitorSupplier: 1 });
|
|
279
|
+
monitorsSchema.index({ monitorIdFromSupplier: 1 });
|
|
280
|
+
monitorsSchema.index({ monitorState: 1 });
|
|
281
|
+
|
|
282
|
+
//network metrics for anomalies
|
|
283
|
+
monitorsSchema.index({ sponsor: 1, isActive: 1, monitorSupplier: 1 })
|
|
284
|
+
|
|
285
|
+
// Pre-hook to log single document deletions
|
|
286
|
+
monitorsSchema.pre("findOneAndDelete", async function () {
|
|
287
|
+
const docToDelete = await this.model.findOne(this.getFilter()).lean();
|
|
288
|
+
if (docToDelete) {
|
|
289
|
+
console.log("Logging findOneAndDelete to monitor audit", docToDelete);
|
|
290
|
+
const auditLog = new MonitorAudit({
|
|
291
|
+
monitorId: docToDelete._id,
|
|
292
|
+
orgId: docToDelete.sponsor,
|
|
293
|
+
timeUpdated: docToDelete.updatedAt,
|
|
294
|
+
monitorProperties: docToDelete.monitorProperties,
|
|
295
|
+
monitorState: docToDelete.monitorState,
|
|
296
|
+
monitorLocation: {
|
|
297
|
+
type: "Point",
|
|
298
|
+
coordinates: [
|
|
299
|
+
docToDelete.monitorLongitude,
|
|
300
|
+
docToDelete.monitorLatitude,
|
|
301
|
+
],
|
|
302
|
+
},
|
|
303
|
+
parameterThresholds: docToDelete.parameterThresholds,
|
|
304
|
+
corrections: docToDelete.corrections,
|
|
305
|
+
correctionHistory: docToDelete.correctionHistory,
|
|
306
|
+
applyCorrections: docToDelete.applyCorrections,
|
|
307
|
+
deletedAt: new Date(),
|
|
308
|
+
});
|
|
309
|
+
await auditLog.save();
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Pre-hook to log multiple document deletions
|
|
314
|
+
monitorsSchema.pre("deleteMany", async function () {
|
|
315
|
+
console.log("deleteMany pre-hook triggered for monitors");
|
|
316
|
+
const docsToDelete = await this.model.find(this.getFilter()).lean();
|
|
317
|
+
|
|
318
|
+
if (docsToDelete.length) {
|
|
319
|
+
console.log(`Logging ${docsToDelete.length} monitor documents to audit`);
|
|
320
|
+
const auditLogs = docsToDelete.map((doc) => ({
|
|
321
|
+
monitorId: doc._id,
|
|
322
|
+
orgId: doc.sponsor,
|
|
323
|
+
timeUpdated: doc.updatedAt,
|
|
324
|
+
monitorProperties: doc.monitorProperties,
|
|
325
|
+
monitorState: doc.monitorState,
|
|
326
|
+
monitorLocation: {
|
|
327
|
+
type: "Point",
|
|
328
|
+
coordinates: [doc.monitorLongitude, doc.monitorLatitude],
|
|
329
|
+
},
|
|
330
|
+
parameterThresholds: doc.parameterThresholds,
|
|
331
|
+
corrections: doc.corrections,
|
|
332
|
+
correctionHistory: doc.correctionHistory,
|
|
333
|
+
applyCorrections: doc.applyCorrections,
|
|
334
|
+
deletedAt: new Date(),
|
|
335
|
+
}));
|
|
336
|
+
|
|
337
|
+
await MonitorAudit.insertMany(auditLogs);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Pre-hook to log a single document deletion (for deleteOne)
|
|
342
|
+
monitorsSchema.pre("deleteOne", async function () {
|
|
343
|
+
console.log("deleteOne pre-hook triggered for monitors");
|
|
344
|
+
const docToDelete = await this.model.findOne(this.getFilter()).lean();
|
|
345
|
+
|
|
346
|
+
if (docToDelete) {
|
|
347
|
+
console.log("Logging deleteOne to monitor audit", docToDelete);
|
|
348
|
+
const auditLog = new MonitorAudit({
|
|
349
|
+
monitorId: docToDelete._id,
|
|
350
|
+
orgId: docToDelete.sponsor,
|
|
351
|
+
timeUpdated: docToDelete.updatedAt,
|
|
352
|
+
monitorProperties: docToDelete.monitorProperties,
|
|
353
|
+
monitorState: docToDelete.monitorState,
|
|
354
|
+
monitorLocation: {
|
|
355
|
+
type: "Point",
|
|
356
|
+
coordinates: [
|
|
357
|
+
docToDelete.monitorLongitude,
|
|
358
|
+
docToDelete.monitorLatitude,
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
parameterThresholds: docToDelete.parameterThresholds,
|
|
362
|
+
corrections: docToDelete.corrections,
|
|
363
|
+
correctionHistory: docToDelete.correctionHistory,
|
|
364
|
+
applyCorrections: docToDelete.applyCorrections,
|
|
365
|
+
deletedAt: new Date(),
|
|
366
|
+
});
|
|
367
|
+
await auditLog.save();
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Pre-hook to log multiple document updates
|
|
372
|
+
monitorsSchema.pre("updateMany", async function () {
|
|
373
|
+
const docsToUpdate = await this.model.find(this.getFilter()).lean();
|
|
374
|
+
if (docsToUpdate.length) {
|
|
375
|
+
console.log(`Logging ${docsToUpdate.length} monitor documents to audit`);
|
|
376
|
+
const auditLogs = docsToUpdate.map((doc) => ({
|
|
377
|
+
monitorId: doc._id,
|
|
378
|
+
orgId: doc.sponsor,
|
|
379
|
+
timeUpdated: doc.updatedAt,
|
|
380
|
+
monitorProperties: doc.monitorProperties,
|
|
381
|
+
monitorState: doc.monitorState,
|
|
382
|
+
monitorLocation: {
|
|
383
|
+
type: "Point",
|
|
384
|
+
coordinates: [doc.monitorLongitude, doc.monitorLatitude],
|
|
385
|
+
},
|
|
386
|
+
parameterThresholds: doc.parameterThresholds,
|
|
387
|
+
corrections: doc.corrections,
|
|
388
|
+
correctionHistory: doc.correctionHistory,
|
|
389
|
+
applyCorrections: doc.applyCorrections,
|
|
390
|
+
deletedAt: null, // Not a deletion, so this field is null
|
|
391
|
+
}));
|
|
392
|
+
|
|
393
|
+
await MonitorAudit.insertMany(auditLogs);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Create the Monitors model
|
|
398
|
+
const Monitors = mongoose.model("Monitors", monitorsSchema);
|
|
399
|
+
|
|
400
|
+
export { monitorsSchema, Monitors, monitorAuditSchema, MonitorAudit };
|