@prosopo/database 3.15.19 → 3.15.21
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 +24 -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 +220 -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.d.ts.map +1 -1
- package/dist/databases/captcha.js +217 -253
- package/dist/databases/captcha.js.map +1 -1
- 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/dist/tests/integration/saveCaptchasSessionUpsert.integration.test.d.ts +2 -0
- package/dist/tests/integration/saveCaptchasSessionUpsert.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/saveCaptchasSessionUpsert.integration.test.js +64 -0
- package/dist/tests/integration/saveCaptchasSessionUpsert.integration.test.js.map +1 -0
- package/package.json +14 -14
|
@@ -1,258 +1,222 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
30
|
-
{
|
|
31
|
-
collectionName: "commitment",
|
|
32
|
-
modelName: "UserCommitment",
|
|
33
|
-
schema: typesDatabase.StoredUserCommitmentRecordSchema
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
collectionName: "puzzlecaptcha",
|
|
37
|
-
modelName: "PuzzleCaptcha",
|
|
38
|
-
schema: typesDatabase.StoredPuzzleCaptchaRecordSchema
|
|
39
|
-
}
|
|
1
|
+
const require_mongo = require("../base/mongo.cjs");
|
|
2
|
+
let _prosopo_common = require("@prosopo/common");
|
|
3
|
+
let _prosopo_logger = require("@prosopo/logger");
|
|
4
|
+
let _prosopo_types_database = require("@prosopo/types-database");
|
|
5
|
+
let bson = require("bson");
|
|
6
|
+
//#region src/databases/captcha.ts
|
|
7
|
+
var isBsonLong = (value) => typeof value === "object" && value !== null && "_bsontype" in value && value._bsontype === "Long";
|
|
8
|
+
var logger = (0, _prosopo_logger.getLogger)("info", "database:captcha");
|
|
9
|
+
var CAPTCHA_TABLES = [
|
|
10
|
+
{
|
|
11
|
+
collectionName: "session",
|
|
12
|
+
modelName: "Session",
|
|
13
|
+
schema: _prosopo_types_database.StoredSessionRecordSchema
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
collectionName: "powcaptcha",
|
|
17
|
+
modelName: "PowCaptcha",
|
|
18
|
+
schema: _prosopo_types_database.StoredPoWCaptchaRecordSchema
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
collectionName: "commitment",
|
|
22
|
+
modelName: "UserCommitment",
|
|
23
|
+
schema: _prosopo_types_database.StoredUserCommitmentRecordSchema
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
collectionName: "puzzlecaptcha",
|
|
27
|
+
modelName: "PuzzleCaptcha",
|
|
28
|
+
schema: _prosopo_types_database.StoredPuzzleCaptchaRecordSchema
|
|
29
|
+
}
|
|
40
30
|
];
|
|
41
|
-
class CaptchaDatabase extends
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
async getCaptchas(filter = {}, limit = 100) {
|
|
233
|
-
await this.connect();
|
|
234
|
-
try {
|
|
235
|
-
const commitmentResults = await this.tables.commitment.find(filter).limit(limit).lean();
|
|
236
|
-
const powCaptchaResults = await this.tables.powcaptcha.find(filter).limit(limit).lean();
|
|
237
|
-
const puzzleCaptchaResults = await this.tables.puzzlecaptcha.find(filter).limit(limit).lean();
|
|
238
|
-
return {
|
|
239
|
-
userCommitmentRecords: commitmentResults,
|
|
240
|
-
powCaptchaRecords: powCaptchaResults,
|
|
241
|
-
puzzleCaptchaRecords: puzzleCaptchaResults
|
|
242
|
-
};
|
|
243
|
-
} catch (error) {
|
|
244
|
-
throw new common.ProsopoDBError("DATABASE.QUERY_ERROR", {
|
|
245
|
-
context: {
|
|
246
|
-
error,
|
|
247
|
-
filter,
|
|
248
|
-
limit,
|
|
249
|
-
failedFuncName: this.getCaptchas.name
|
|
250
|
-
},
|
|
251
|
-
logger: this.logger
|
|
252
|
-
});
|
|
253
|
-
} finally {
|
|
254
|
-
await this.close();
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
31
|
+
var CaptchaDatabase = class CaptchaDatabase extends require_mongo.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 _prosopo_common.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 bson.Decimal128.fromString(bson.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
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
120
|
+
return { updateOne: {
|
|
121
|
+
filter: { sessionId: normalised.sessionId },
|
|
122
|
+
update: { $set: normalised },
|
|
123
|
+
upsert: true
|
|
124
|
+
} };
|
|
125
|
+
}));
|
|
126
|
+
logger.info(() => ({
|
|
127
|
+
data: {
|
|
128
|
+
upsertedCount: result.upsertedCount,
|
|
129
|
+
matchedCount: result.matchedCount,
|
|
130
|
+
modifiedCount: result.modifiedCount,
|
|
131
|
+
totalProcessed: sessionEvents.length
|
|
132
|
+
},
|
|
133
|
+
msg: "Mongo Saved Session Events"
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
if (imageCaptchaEvents.length) {
|
|
137
|
+
const result = await this.tables.commitment.bulkWrite(imageCaptchaEvents.map((doc) => {
|
|
138
|
+
const { _id, ...safeDoc } = doc;
|
|
139
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
140
|
+
return { updateOne: {
|
|
141
|
+
filter: { id: normalised.id },
|
|
142
|
+
update: { $set: normalised },
|
|
143
|
+
upsert: true
|
|
144
|
+
} };
|
|
145
|
+
}));
|
|
146
|
+
logger.info(() => ({
|
|
147
|
+
data: {
|
|
148
|
+
upsertedCount: result.upsertedCount,
|
|
149
|
+
matchedCount: result.matchedCount,
|
|
150
|
+
modifiedCount: result.modifiedCount,
|
|
151
|
+
totalProcessed: imageCaptchaEvents.length
|
|
152
|
+
},
|
|
153
|
+
msg: "Mongo Saved Image Events"
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
if (powCaptchaEvents.length) {
|
|
157
|
+
const result = await this.tables.powcaptcha.bulkWrite(powCaptchaEvents.map((doc) => {
|
|
158
|
+
const { _id, ...safeDoc } = doc;
|
|
159
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
160
|
+
return { updateOne: {
|
|
161
|
+
filter: { challenge: normalised.challenge },
|
|
162
|
+
update: { $set: normalised },
|
|
163
|
+
upsert: true
|
|
164
|
+
} };
|
|
165
|
+
}));
|
|
166
|
+
logger.info(() => ({
|
|
167
|
+
data: {
|
|
168
|
+
upsertedCount: result.upsertedCount,
|
|
169
|
+
matchedCount: result.matchedCount,
|
|
170
|
+
modifiedCount: result.modifiedCount,
|
|
171
|
+
totalProcessed: powCaptchaEvents.length
|
|
172
|
+
},
|
|
173
|
+
msg: "Mongo Saved PoW Events"
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
if (puzzleCaptchaEvents.length) {
|
|
177
|
+
const result = await this.tables.puzzlecaptcha.bulkWrite(puzzleCaptchaEvents.map((doc) => {
|
|
178
|
+
const { _id, ...safeDoc } = doc;
|
|
179
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
180
|
+
return { updateOne: {
|
|
181
|
+
filter: { challenge: normalised.challenge },
|
|
182
|
+
update: { $set: normalised },
|
|
183
|
+
upsert: true
|
|
184
|
+
} };
|
|
185
|
+
}));
|
|
186
|
+
logger.info(() => ({
|
|
187
|
+
data: {
|
|
188
|
+
upsertedCount: result.upsertedCount,
|
|
189
|
+
matchedCount: result.matchedCount,
|
|
190
|
+
modifiedCount: result.modifiedCount,
|
|
191
|
+
totalProcessed: puzzleCaptchaEvents.length
|
|
192
|
+
},
|
|
193
|
+
msg: "Mongo Saved Puzzle Events"
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
await this.close();
|
|
197
|
+
}
|
|
198
|
+
async getCaptchas(filter = {}, limit = 100) {
|
|
199
|
+
await this.connect();
|
|
200
|
+
try {
|
|
201
|
+
return {
|
|
202
|
+
userCommitmentRecords: await this.tables.commitment.find(filter).limit(limit).lean(),
|
|
203
|
+
powCaptchaRecords: await this.tables.powcaptcha.find(filter).limit(limit).lean(),
|
|
204
|
+
puzzleCaptchaRecords: await this.tables.puzzlecaptcha.find(filter).limit(limit).lean()
|
|
205
|
+
};
|
|
206
|
+
} catch (error) {
|
|
207
|
+
throw new _prosopo_common.ProsopoDBError("DATABASE.QUERY_ERROR", {
|
|
208
|
+
context: {
|
|
209
|
+
error,
|
|
210
|
+
filter,
|
|
211
|
+
limit,
|
|
212
|
+
failedFuncName: this.getCaptchas.name
|
|
213
|
+
},
|
|
214
|
+
logger: this.logger
|
|
215
|
+
});
|
|
216
|
+
} finally {
|
|
217
|
+
await this.close();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
//#endregion
|
|
258
222
|
exports.CaptchaDatabase = CaptchaDatabase;
|