@prosopo/database 3.15.19 → 3.15.20
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/.turbo/turbo-build$colon$cjs.log +20 -17
- package/.turbo/turbo-build$colon$tsc.log +16 -16
- package/.turbo/turbo-build.log +21 -18
- package/CHANGELOG.md +17 -0
- package/dist/_virtual/_rolldown/runtime.js +3 -0
- package/dist/base/index.js +1 -4
- package/dist/base/mongo.js +133 -137
- package/dist/base/mongoMemory.js +21 -21
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cjs/base/index.cjs +4 -6
- package/dist/cjs/base/mongo.cjs +140 -143
- package/dist/cjs/base/mongoMemory.cjs +22 -22
- package/dist/cjs/databases/captcha.cjs +213 -256
- package/dist/cjs/databases/centralDbStreamer.cjs +148 -170
- package/dist/cjs/databases/client.cjs +46 -62
- package/dist/cjs/databases/index.cjs +19 -19
- package/dist/cjs/databases/provider.cjs +1827 -2251
- package/dist/cjs/index.cjs +25 -23
- package/dist/cjs/mongooseOptions.cjs +59 -58
- package/dist/cjs/redisCache.cjs +432 -474
- package/dist/databases/captcha.js +210 -253
- package/dist/databases/centralDbStreamer.js +147 -169
- package/dist/databases/client.js +44 -60
- package/dist/databases/index.js +10 -14
- package/dist/databases/provider.d.ts +4 -4
- package/dist/databases/provider.d.ts.map +1 -1
- package/dist/databases/provider.js +1823 -2247
- package/dist/databases/provider.js.map +1 -1
- package/dist/index.js +9 -18
- package/dist/mongooseOptions.js +59 -59
- package/dist/redisCache.js +432 -474
- package/package.json +14 -14
|
@@ -1,258 +1,215 @@
|
|
|
1
|
+
import { MongoDatabase } from "../base/mongo.js";
|
|
1
2
|
import { ProsopoDBError } from "@prosopo/common";
|
|
2
3
|
import { getLogger } from "@prosopo/logger";
|
|
3
|
-
import {
|
|
4
|
+
import { StoredPoWCaptchaRecordSchema, StoredPuzzleCaptchaRecordSchema, StoredSessionRecordSchema, StoredUserCommitmentRecordSchema } from "@prosopo/types-database";
|
|
4
5
|
import { Decimal128, Long } from "bson";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
collectionName: "commitment",
|
|
30
|
-
modelName: "UserCommitment",
|
|
31
|
-
schema: StoredUserCommitmentRecordSchema
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
collectionName: "puzzlecaptcha",
|
|
35
|
-
modelName: "PuzzleCaptcha",
|
|
36
|
-
schema: StoredPuzzleCaptchaRecordSchema
|
|
37
|
-
}
|
|
6
|
+
//#region src/databases/captcha.ts
|
|
7
|
+
var isBsonLong = (value) => typeof value === "object" && value !== null && "_bsontype" in value && value._bsontype === "Long";
|
|
8
|
+
var logger = getLogger("info", "database:captcha");
|
|
9
|
+
var CAPTCHA_TABLES = [
|
|
10
|
+
{
|
|
11
|
+
collectionName: "session",
|
|
12
|
+
modelName: "Session",
|
|
13
|
+
schema: StoredSessionRecordSchema
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
collectionName: "powcaptcha",
|
|
17
|
+
modelName: "PowCaptcha",
|
|
18
|
+
schema: StoredPoWCaptchaRecordSchema
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
collectionName: "commitment",
|
|
22
|
+
modelName: "UserCommitment",
|
|
23
|
+
schema: StoredUserCommitmentRecordSchema
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
collectionName: "puzzlecaptcha",
|
|
27
|
+
modelName: "PuzzleCaptcha",
|
|
28
|
+
schema: StoredPuzzleCaptchaRecordSchema
|
|
29
|
+
}
|
|
38
30
|
];
|
|
39
|
-
class CaptchaDatabase extends MongoDatabase {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
105
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
matchedCount: result.matchedCount,
|
|
222
|
-
modifiedCount: result.modifiedCount,
|
|
223
|
-
totalProcessed: puzzleCaptchaEvents.length
|
|
224
|
-
},
|
|
225
|
-
msg: "Mongo Saved Puzzle Events"
|
|
226
|
-
}));
|
|
227
|
-
}
|
|
228
|
-
await this.close();
|
|
229
|
-
}
|
|
230
|
-
async getCaptchas(filter = {}, limit = 100) {
|
|
231
|
-
await this.connect();
|
|
232
|
-
try {
|
|
233
|
-
const commitmentResults = await this.tables.commitment.find(filter).limit(limit).lean();
|
|
234
|
-
const powCaptchaResults = await this.tables.powcaptcha.find(filter).limit(limit).lean();
|
|
235
|
-
const puzzleCaptchaResults = await this.tables.puzzlecaptcha.find(filter).limit(limit).lean();
|
|
236
|
-
return {
|
|
237
|
-
userCommitmentRecords: commitmentResults,
|
|
238
|
-
powCaptchaRecords: powCaptchaResults,
|
|
239
|
-
puzzleCaptchaRecords: puzzleCaptchaResults
|
|
240
|
-
};
|
|
241
|
-
} catch (error) {
|
|
242
|
-
throw new ProsopoDBError("DATABASE.QUERY_ERROR", {
|
|
243
|
-
context: {
|
|
244
|
-
error,
|
|
245
|
-
filter,
|
|
246
|
-
limit,
|
|
247
|
-
failedFuncName: this.getCaptchas.name
|
|
248
|
-
},
|
|
249
|
-
logger: this.logger
|
|
250
|
-
});
|
|
251
|
-
} finally {
|
|
252
|
-
await this.close();
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
export {
|
|
257
|
-
CaptchaDatabase
|
|
31
|
+
var CaptchaDatabase = class CaptchaDatabase extends MongoDatabase {
|
|
32
|
+
constructor(url, dbname, authSource, logger) {
|
|
33
|
+
super(url, dbname, authSource, logger);
|
|
34
|
+
this.indexesEnsured = false;
|
|
35
|
+
this.tables = {};
|
|
36
|
+
}
|
|
37
|
+
async connect() {
|
|
38
|
+
await super.connect();
|
|
39
|
+
CAPTCHA_TABLES.map(({ collectionName, modelName, schema }) => {
|
|
40
|
+
if (this.connection) this.tables[collectionName] = this.connection.model(modelName, schema);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
getTables() {
|
|
44
|
+
if (!this.tables) throw new ProsopoDBError("DATABASE.TABLES_UNDEFINED", {
|
|
45
|
+
context: { failedFuncName: this.getTables.name },
|
|
46
|
+
logger: this.logger
|
|
47
|
+
});
|
|
48
|
+
return this.tables;
|
|
49
|
+
}
|
|
50
|
+
async ensureIndexes() {
|
|
51
|
+
const indexPromises = [];
|
|
52
|
+
if (!this.indexesEnsured) CAPTCHA_TABLES.map(({ collectionName }) => {
|
|
53
|
+
indexPromises.push(new Promise((resolve) => {
|
|
54
|
+
if (this.connected) this.tables[collectionName].collection.dropIndexes().then(() => {
|
|
55
|
+
this.tables[collectionName].ensureIndexes().then(() => {
|
|
56
|
+
resolve();
|
|
57
|
+
}).catch((err) => {
|
|
58
|
+
this.logger.warn(() => ({
|
|
59
|
+
err,
|
|
60
|
+
msg: `Error creating indexes for collection ${collectionName}`
|
|
61
|
+
}));
|
|
62
|
+
resolve();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
else {
|
|
66
|
+
this.logger.info(() => ({ msg: `Skipping index creation for collection ${collectionName} as not connected` }));
|
|
67
|
+
resolve();
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
});
|
|
71
|
+
await Promise.all(indexPromises);
|
|
72
|
+
this.indexesEnsured = true;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Convert a BSON `Long` (which Mongoose's Decimal128 caster rejects)
|
|
76
|
+
* into a Decimal128 with the *unsigned* numeric value — so a Long
|
|
77
|
+
* representing an IPv6 lower half with bit 63 set (signed Long shows
|
|
78
|
+
* as a negative integer) converts to a positive Decimal128 that
|
|
79
|
+
* matches what `bigint→string→Decimal128` would have produced if the
|
|
80
|
+
* schema setter had run on the original write.
|
|
81
|
+
*
|
|
82
|
+
* Production background: pipeline-form updates (`updateOne(filter,
|
|
83
|
+
* [{ $set: ... }])`) bypass Mongoose schema casting, so a `bigint`
|
|
84
|
+
* passed for an IP half is serialised by the driver as BSON Int64
|
|
85
|
+
* (Long) instead of going through the `Decimal128` setter. The
|
|
86
|
+
* central-streaming sweep below reads those docs via `.lean()` and
|
|
87
|
+
* tries to replay them with `bulkWrite`, but `bulkWrite` also skips
|
|
88
|
+
* setters — so the cast fires raw and throws. Normalising the lean
|
|
89
|
+
* doc here is the only place that's guaranteed to run before the
|
|
90
|
+
* bulkWrite sees it.
|
|
91
|
+
*/
|
|
92
|
+
static normaliseCompositeIp(ip) {
|
|
93
|
+
if (!ip) return ip;
|
|
94
|
+
const normaliseHalf = (v) => {
|
|
95
|
+
if (isBsonLong(v)) {
|
|
96
|
+
const lng = v;
|
|
97
|
+
return Decimal128.fromString(Long.fromBits(lng.low, lng.high, true).toString());
|
|
98
|
+
}
|
|
99
|
+
return v;
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
...ip,
|
|
103
|
+
lower: normaliseHalf(ip.lower),
|
|
104
|
+
upper: normaliseHalf(ip.upper)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
static normaliseDocCompositeIps(doc) {
|
|
108
|
+
return {
|
|
109
|
+
...doc,
|
|
110
|
+
ipAddress: CaptchaDatabase.normaliseCompositeIp(doc.ipAddress),
|
|
111
|
+
providedIp: CaptchaDatabase.normaliseCompositeIp(doc.providedIp)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async saveCaptchas(sessionEvents, imageCaptchaEvents, powCaptchaEvents, puzzleCaptchaEvents = []) {
|
|
115
|
+
await this.connect();
|
|
116
|
+
if (sessionEvents.length) {
|
|
117
|
+
const result = await this.tables.session.bulkWrite(sessionEvents.map((document) => {
|
|
118
|
+
const { _id, ...safeDoc } = document;
|
|
119
|
+
return { insertOne: { document: CaptchaDatabase.normaliseDocCompositeIps(safeDoc) } };
|
|
120
|
+
}));
|
|
121
|
+
logger.info(() => ({
|
|
122
|
+
data: {
|
|
123
|
+
insertedCount: result.insertedCount,
|
|
124
|
+
totalProcessed: sessionEvents.length
|
|
125
|
+
},
|
|
126
|
+
msg: "Mongo Saved Session Events"
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
if (imageCaptchaEvents.length) {
|
|
130
|
+
const result = await this.tables.commitment.bulkWrite(imageCaptchaEvents.map((doc) => {
|
|
131
|
+
const { _id, ...safeDoc } = doc;
|
|
132
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
133
|
+
return { updateOne: {
|
|
134
|
+
filter: { id: normalised.id },
|
|
135
|
+
update: { $set: normalised },
|
|
136
|
+
upsert: true
|
|
137
|
+
} };
|
|
138
|
+
}));
|
|
139
|
+
logger.info(() => ({
|
|
140
|
+
data: {
|
|
141
|
+
upsertedCount: result.upsertedCount,
|
|
142
|
+
matchedCount: result.matchedCount,
|
|
143
|
+
modifiedCount: result.modifiedCount,
|
|
144
|
+
totalProcessed: imageCaptchaEvents.length
|
|
145
|
+
},
|
|
146
|
+
msg: "Mongo Saved Image Events"
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
if (powCaptchaEvents.length) {
|
|
150
|
+
const result = await this.tables.powcaptcha.bulkWrite(powCaptchaEvents.map((doc) => {
|
|
151
|
+
const { _id, ...safeDoc } = doc;
|
|
152
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
153
|
+
return { updateOne: {
|
|
154
|
+
filter: { challenge: normalised.challenge },
|
|
155
|
+
update: { $set: normalised },
|
|
156
|
+
upsert: true
|
|
157
|
+
} };
|
|
158
|
+
}));
|
|
159
|
+
logger.info(() => ({
|
|
160
|
+
data: {
|
|
161
|
+
upsertedCount: result.upsertedCount,
|
|
162
|
+
matchedCount: result.matchedCount,
|
|
163
|
+
modifiedCount: result.modifiedCount,
|
|
164
|
+
totalProcessed: powCaptchaEvents.length
|
|
165
|
+
},
|
|
166
|
+
msg: "Mongo Saved PoW Events"
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
if (puzzleCaptchaEvents.length) {
|
|
170
|
+
const result = await this.tables.puzzlecaptcha.bulkWrite(puzzleCaptchaEvents.map((doc) => {
|
|
171
|
+
const { _id, ...safeDoc } = doc;
|
|
172
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
173
|
+
return { updateOne: {
|
|
174
|
+
filter: { challenge: normalised.challenge },
|
|
175
|
+
update: { $set: normalised },
|
|
176
|
+
upsert: true
|
|
177
|
+
} };
|
|
178
|
+
}));
|
|
179
|
+
logger.info(() => ({
|
|
180
|
+
data: {
|
|
181
|
+
upsertedCount: result.upsertedCount,
|
|
182
|
+
matchedCount: result.matchedCount,
|
|
183
|
+
modifiedCount: result.modifiedCount,
|
|
184
|
+
totalProcessed: puzzleCaptchaEvents.length
|
|
185
|
+
},
|
|
186
|
+
msg: "Mongo Saved Puzzle Events"
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
await this.close();
|
|
190
|
+
}
|
|
191
|
+
async getCaptchas(filter = {}, limit = 100) {
|
|
192
|
+
await this.connect();
|
|
193
|
+
try {
|
|
194
|
+
return {
|
|
195
|
+
userCommitmentRecords: await this.tables.commitment.find(filter).limit(limit).lean(),
|
|
196
|
+
powCaptchaRecords: await this.tables.powcaptcha.find(filter).limit(limit).lean(),
|
|
197
|
+
puzzleCaptchaRecords: await this.tables.puzzlecaptcha.find(filter).limit(limit).lean()
|
|
198
|
+
};
|
|
199
|
+
} catch (error) {
|
|
200
|
+
throw new ProsopoDBError("DATABASE.QUERY_ERROR", {
|
|
201
|
+
context: {
|
|
202
|
+
error,
|
|
203
|
+
filter,
|
|
204
|
+
limit,
|
|
205
|
+
failedFuncName: this.getCaptchas.name
|
|
206
|
+
},
|
|
207
|
+
logger: this.logger
|
|
208
|
+
});
|
|
209
|
+
} finally {
|
|
210
|
+
await this.close();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
258
213
|
};
|
|
214
|
+
//#endregion
|
|
215
|
+
export { CaptchaDatabase };
|