@justair/justair-library 4.4.1 → 4.4.3
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/package.json +1 -1
- package/src/config/db.js +89 -13
- package/src/models/measurements.js +57 -1
package/package.json
CHANGED
package/src/config/db.js
CHANGED
|
@@ -1,39 +1,115 @@
|
|
|
1
1
|
import mongoose from "mongoose";
|
|
2
2
|
|
|
3
3
|
class Database {
|
|
4
|
-
static
|
|
4
|
+
static instances = new Map(); // Store multiple instances by connection key
|
|
5
|
+
|
|
5
6
|
constructor({ CONNECTION_URL, database }) {
|
|
6
7
|
this.CONNECTION_URL = CONNECTION_URL;
|
|
7
8
|
this.database = database;
|
|
9
|
+
this.connectionKey = `${CONNECTION_URL}:${database}`;
|
|
10
|
+
this.isConnected = false;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
async connect() {
|
|
11
|
-
if
|
|
12
|
-
|
|
14
|
+
// Check if we're already connected to this specific database
|
|
15
|
+
if (this.isConnected && mongoose.connection.readyState === 1) {
|
|
16
|
+
// Verify we're connected to the right database
|
|
17
|
+
if (mongoose.connection.db.databaseName === this.database) {
|
|
18
|
+
console.log(`Already connected to database: ${this.database}`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
13
21
|
}
|
|
22
|
+
|
|
14
23
|
try {
|
|
24
|
+
// Only disconnect if we're connected to a different database
|
|
25
|
+
if (mongoose.connection.readyState === 1) {
|
|
26
|
+
const currentDb = mongoose.connection.db.databaseName;
|
|
27
|
+
if (currentDb !== this.database) {
|
|
28
|
+
console.log(
|
|
29
|
+
`Switching from database: ${currentDb} to: ${this.database}`
|
|
30
|
+
);
|
|
31
|
+
await mongoose.disconnect();
|
|
32
|
+
} else {
|
|
33
|
+
// Already connected to the correct database
|
|
34
|
+
this.isConnected = true;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
15
39
|
await mongoose.connect(this.CONNECTION_URL, {
|
|
16
40
|
authSource: "admin",
|
|
17
41
|
ssl: true,
|
|
18
42
|
dbName: this.database,
|
|
19
43
|
});
|
|
20
|
-
|
|
44
|
+
|
|
45
|
+
this.isConnected = true;
|
|
46
|
+
console.log(`Database connection successful: ${this.database}`);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
this.isConnected = false;
|
|
49
|
+
console.error(`Database connection error for ${this.database}: ${err}`);
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async disconnect() {
|
|
55
|
+
try {
|
|
56
|
+
if (mongoose.connection.readyState === 1) {
|
|
57
|
+
await mongoose.disconnect();
|
|
58
|
+
this.isConnected = false;
|
|
59
|
+
console.log(`Disconnected from database: ${this.database}`);
|
|
60
|
+
}
|
|
21
61
|
} catch (err) {
|
|
22
|
-
console.error(
|
|
62
|
+
console.error(`Error disconnecting from database: ${err}`);
|
|
23
63
|
throw err;
|
|
24
64
|
}
|
|
25
65
|
}
|
|
26
66
|
|
|
27
67
|
static async getInstance(config) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
68
|
+
const connectionKey = `${config.CONNECTION_URL}:${config.database}`;
|
|
69
|
+
|
|
70
|
+
// Check if we have an instance for this connection
|
|
71
|
+
if (Database.instances.has(connectionKey)) {
|
|
72
|
+
const instance = Database.instances.get(connectionKey);
|
|
73
|
+
|
|
74
|
+
// Ensure the connection is still valid
|
|
75
|
+
if (!instance.isConnected || mongoose.connection.readyState !== 1) {
|
|
76
|
+
await instance.connect();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return instance;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Create new instance
|
|
83
|
+
const instance = new Database(config);
|
|
84
|
+
await instance.connect();
|
|
85
|
+
Database.instances.set(connectionKey, instance);
|
|
86
|
+
|
|
87
|
+
return instance;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static getCurrentDatabase() {
|
|
91
|
+
if (mongoose.connection.readyState === 1) {
|
|
92
|
+
return mongoose.connection.db.databaseName;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static async closeAllConnections() {
|
|
98
|
+
try {
|
|
99
|
+
if (mongoose.connection.readyState === 1) {
|
|
100
|
+
await mongoose.disconnect();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Mark all instances as disconnected
|
|
104
|
+
for (const instance of Database.instances.values()) {
|
|
105
|
+
instance.isConnected = false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log("All database connections closed");
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.error("Error closing database connections:", err);
|
|
111
|
+
throw err;
|
|
35
112
|
}
|
|
36
|
-
return Database.instance;
|
|
37
113
|
}
|
|
38
114
|
}
|
|
39
115
|
|
|
@@ -8,6 +8,40 @@ const correctionSnapshotSchema = new mongoose.Schema(
|
|
|
8
8
|
{ _id: false } // Not generating separate _id for each correction
|
|
9
9
|
);
|
|
10
10
|
|
|
11
|
+
// Annotation Schema
|
|
12
|
+
const annotationSchema = new mongoose.Schema(
|
|
13
|
+
{
|
|
14
|
+
measurementIdentifier: {
|
|
15
|
+
type: String,
|
|
16
|
+
required: true,
|
|
17
|
+
trim: true,
|
|
18
|
+
// Examples: "PM2_5", "PM10", "NO2", "O3", etc.
|
|
19
|
+
},
|
|
20
|
+
comment: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true,
|
|
23
|
+
trim: true,
|
|
24
|
+
},
|
|
25
|
+
adminId: {
|
|
26
|
+
type: mongoose.Types.ObjectId,
|
|
27
|
+
ref: "Admin",
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
timestamp: {
|
|
31
|
+
type: Date,
|
|
32
|
+
default: Date.now,
|
|
33
|
+
required: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
_id: true,
|
|
38
|
+
timestamps: false,
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Add index for efficient querying
|
|
43
|
+
annotationSchema.index({ measurementIdentifier: 1, timestamp: -1 });
|
|
44
|
+
|
|
11
45
|
// Audit Schema
|
|
12
46
|
const auditSchema = mongoose.Schema(
|
|
13
47
|
{
|
|
@@ -25,6 +59,7 @@ const auditSchema = mongoose.Schema(
|
|
|
25
59
|
default: {},
|
|
26
60
|
},
|
|
27
61
|
correctedNormalizedMeasurements: Object,
|
|
62
|
+
annotations: [annotationSchema], // Include annotations in audit trail
|
|
28
63
|
},
|
|
29
64
|
{
|
|
30
65
|
timestamps: true,
|
|
@@ -49,6 +84,10 @@ const measurementsSchema = mongoose.Schema(
|
|
|
49
84
|
default: {},
|
|
50
85
|
},
|
|
51
86
|
correctedNormalizedMeasurements: Object,
|
|
87
|
+
annotations: {
|
|
88
|
+
type: [annotationSchema],
|
|
89
|
+
default: [],
|
|
90
|
+
},
|
|
52
91
|
},
|
|
53
92
|
{
|
|
54
93
|
timestamps: true,
|
|
@@ -58,6 +97,12 @@ const measurementsSchema = mongoose.Schema(
|
|
|
58
97
|
measurementsSchema.index({ monitorId: 1 });
|
|
59
98
|
measurementsSchema.index({ timeUpdated: 1 });
|
|
60
99
|
|
|
100
|
+
measurementsSchema.index({
|
|
101
|
+
"annotations.measurementIdentifier": 1,
|
|
102
|
+
"annotations.timestamp": -1,
|
|
103
|
+
});
|
|
104
|
+
measurementsSchema.index({ "annotations.userId": 1 });
|
|
105
|
+
|
|
61
106
|
// Pre-hook to log single document deletions
|
|
62
107
|
measurementsSchema.pre("findOneAndDelete", async function () {
|
|
63
108
|
const docToDelete = await this.model.findOne(this.getFilter());
|
|
@@ -74,6 +119,7 @@ measurementsSchema.pre("findOneAndDelete", async function () {
|
|
|
74
119
|
appliedCorrections: docToDelete.appliedCorrections,
|
|
75
120
|
correctedNormalizedMeasurements:
|
|
76
121
|
docToDelete.correctedNormalizedMeasurements,
|
|
122
|
+
annotations: docToDelete.annotations, // Include annotations in audit
|
|
77
123
|
deletedAt: new Date(),
|
|
78
124
|
});
|
|
79
125
|
await auditLog.save();
|
|
@@ -97,6 +143,7 @@ measurementsSchema.pre("deleteMany", async function () {
|
|
|
97
143
|
flags: doc.flags,
|
|
98
144
|
appliedCorrections: doc.appliedCorrections,
|
|
99
145
|
correctedNormalizedMeasurements: doc.correctedNormalizedMeasurements,
|
|
146
|
+
annotations: doc.annotations, // Include annotations in audit
|
|
100
147
|
deletedAt: new Date(),
|
|
101
148
|
}));
|
|
102
149
|
|
|
@@ -122,6 +169,7 @@ measurementsSchema.pre("deleteOne", async function () {
|
|
|
122
169
|
appliedCorrections: docToDelete.appliedCorrections,
|
|
123
170
|
correctedNormalizedMeasurements:
|
|
124
171
|
docToDelete.correctedNormalizedMeasurements,
|
|
172
|
+
annotations: docToDelete.annotations, // Include annotations in audit
|
|
125
173
|
deletedAt: new Date(),
|
|
126
174
|
});
|
|
127
175
|
await auditLog.save();
|
|
@@ -144,6 +192,7 @@ measurementsSchema.pre("findOneAndUpdate", async function () {
|
|
|
144
192
|
appliedCorrections: docToUpdate.appliedCorrections,
|
|
145
193
|
correctedNormalizedMeasurements:
|
|
146
194
|
docToUpdate.correctedNormalizedMeasurements,
|
|
195
|
+
annotations: docToUpdate.annotations, // Include annotations in audit
|
|
147
196
|
deletedAt: null, // No deletion happening, so set it to null or undefined
|
|
148
197
|
});
|
|
149
198
|
await auditLog.save();
|
|
@@ -165,6 +214,7 @@ measurementsSchema.pre("updateMany", async function () {
|
|
|
165
214
|
flags: doc.flags,
|
|
166
215
|
appliedCorrections: doc.appliedCorrections,
|
|
167
216
|
correctedNormalizedMeasurements: doc.correctedNormalizedMeasurements,
|
|
217
|
+
annotations: doc.annotations, // Include annotations in audit
|
|
168
218
|
deletedAt: null, // No deletion happening
|
|
169
219
|
}));
|
|
170
220
|
|
|
@@ -174,4 +224,10 @@ measurementsSchema.pre("updateMany", async function () {
|
|
|
174
224
|
|
|
175
225
|
const Measurements = mongoose.model("Measurements", measurementsSchema);
|
|
176
226
|
|
|
177
|
-
export {
|
|
227
|
+
export {
|
|
228
|
+
measurementsSchema,
|
|
229
|
+
Measurements,
|
|
230
|
+
Audit,
|
|
231
|
+
auditSchema,
|
|
232
|
+
annotationSchema,
|
|
233
|
+
};
|