@prosopo/database 3.14.0 → 3.14.2
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 +4 -4
- package/.turbo/turbo-build$colon$tsc.log +16 -16
- package/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +45 -0
- package/dist/cjs/databases/captcha.cjs +51 -5
- package/dist/cjs/databases/provider.cjs +96 -31
- package/dist/databases/captcha.d.ts +2 -0
- package/dist/databases/captcha.d.ts.map +1 -1
- package/dist/databases/captcha.js +51 -5
- package/dist/databases/captcha.js.map +1 -1
- package/dist/databases/provider.d.ts.map +1 -1
- package/dist/databases/provider.js +96 -31
- package/dist/databases/provider.js.map +1 -1
- package/dist/tests/integration/providedIpPipelineCast.integration.test.d.ts +2 -0
- package/dist/tests/integration/providedIpPipelineCast.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/providedIpPipelineCast.integration.test.js +139 -0
- package/dist/tests/integration/providedIpPipelineCast.integration.test.js.map +1 -0
- package/dist/tests/integration/randomSamplingPerformance.integration.test.d.ts +2 -0
- package/dist/tests/integration/randomSamplingPerformance.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/randomSamplingPerformance.integration.test.js +248 -0
- package/dist/tests/integration/randomSamplingPerformance.integration.test.js.map +1 -0
- package/dist/tests/integration/sessionRecordProjection.integration.test.d.ts +2 -0
- package/dist/tests/integration/sessionRecordProjection.integration.test.d.ts.map +1 -0
- package/dist/tests/integration/sessionRecordProjection.integration.test.js +156 -0
- package/dist/tests/integration/sessionRecordProjection.integration.test.js.map +1 -0
- package/package.json +10 -9
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/database@3.14.
|
|
2
|
+
> @prosopo/database@3.14.2 build:cjs
|
|
3
3
|
> NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
|
|
4
4
|
|
|
5
5
|
ViteCommonJSConfig: .
|
|
@@ -46,7 +46,7 @@ rendering chunks...
|
|
|
46
46
|
[2mdist/cjs/[22m[36mdatabases/client.cjs [39m[1m[2m 1.91 kB[22m[1m[22m
|
|
47
47
|
[2mdist/cjs/[22m[36mdatabases/centralDbStreamer.cjs [39m[1m[2m 4.20 kB[22m[1m[22m
|
|
48
48
|
[2mdist/cjs/[22m[36mbase/mongo.cjs [39m[1m[2m 4.58 kB[22m[1m[22m
|
|
49
|
-
[2mdist/cjs/[22m[36mdatabases/captcha.cjs [39m[1m[2m
|
|
49
|
+
[2mdist/cjs/[22m[36mdatabases/captcha.cjs [39m[1m[2m 7.87 kB[22m[1m[22m
|
|
50
50
|
[2mdist/cjs/[22m[36mredisCache.cjs [39m[1m[2m12.40 kB[22m[1m[22m
|
|
51
|
-
[2mdist/cjs/[22m[36mdatabases/provider.cjs [39m[1m[
|
|
52
|
-
[32m✓ built in
|
|
51
|
+
[2mdist/cjs/[22m[36mdatabases/provider.cjs [39m[1m[2m69.18 kB[22m[1m[22m
|
|
52
|
+
[32m✓ built in 468ms[39m
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/database@3.14.
|
|
2
|
+
> @prosopo/database@3.14.2 build:tsc
|
|
3
3
|
> tsc --build --verbose
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
1:48:40 PM - Projects in this build:
|
|
6
6
|
* ../../dev/config/tsconfig.json
|
|
7
7
|
* ../locale/tsconfig.json
|
|
8
8
|
* ../util/tsconfig.json
|
|
@@ -17,31 +17,31 @@
|
|
|
17
17
|
* ../types-database/tsconfig.json
|
|
18
18
|
* tsconfig.json
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
1:48:40 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
1:48:40 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
1:48:40 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
1:48:40 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
1:48:40 PM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
1:48:40 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
1:48:40 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
1:48:40 PM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
1:48:40 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/tests/testRedisConnection.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
1:48:40 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
1:48:40 PM - Project '../user-access-policy/tsconfig.json' is up to date because newest input '../user-access-policy/src/transformRule.ts' is older than output '../user-access-policy/tsconfig.tsbuildinfo'
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
1:48:40 PM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/index.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
1:48:40 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
1:48:40 PM - Building project '/home/runner/work/captcha/captcha/packages/database/tsconfig.json'...
|
|
47
47
|
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/database@3.14.
|
|
2
|
+
> @prosopo/database@3.14.2 build
|
|
3
3
|
> npm run build:cross-env -- --mode ${NODE_ENV:-development}
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @prosopo/database@3.14.
|
|
6
|
+
> @prosopo/database@3.14.2 build:cross-env
|
|
7
7
|
> vite build --config vite.esm.config.ts --mode production
|
|
8
8
|
|
|
9
9
|
ViteEsmConfig: .
|
|
@@ -50,7 +50,7 @@ rendering chunks...
|
|
|
50
50
|
[2mdist/[22m[36mdatabases/client.js [39m[1m[2m 1.79 kB[22m[1m[22m
|
|
51
51
|
[2mdist/[22m[36mdatabases/centralDbStreamer.js [39m[1m[2m 4.09 kB[22m[1m[22m
|
|
52
52
|
[2mdist/[22m[36mbase/mongo.js [39m[1m[2m 4.14 kB[22m[1m[22m
|
|
53
|
-
[2mdist/[22m[36mdatabases/captcha.js [39m[1m[2m
|
|
53
|
+
[2mdist/[22m[36mdatabases/captcha.js [39m[1m[2m 7.45 kB[22m[1m[22m
|
|
54
54
|
[2mdist/[22m[36mredisCache.js [39m[1m[2m12.30 kB[22m[1m[22m
|
|
55
|
-
[2mdist/[22m[36mdatabases/provider.js [39m[1m[
|
|
56
|
-
[32m✓ built in
|
|
55
|
+
[2mdist/[22m[36mdatabases/provider.js [39m[1m[2m68.96 kB[22m[1m[22m
|
|
56
|
+
[32m✓ built in 472ms[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @prosopo/database
|
|
2
2
|
|
|
3
|
+
## 3.14.2
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
- 4626340: perf(provider): cut p95 on /captcha/frictionless and /captcha/image
|
|
7
|
+
|
|
8
|
+
Replaces the `$match → $sample` random-captcha lookup with an indexed
|
|
9
|
+
range scan over a new `{datasetId, solved, randomKey}` compound index;
|
|
10
|
+
reorders the `sampleContextEntropy` aggregation so `$sample` runs
|
|
11
|
+
before `$lookup`; batches three pairs of independent awaits in the
|
|
12
|
+
frictionless handler via `Promise.all`. Adds an integration test
|
|
13
|
+
asserting via `.explain()` and wall-clock timing that the new paths
|
|
14
|
+
are quantifiably faster. The legacy aggregation remains as a fallback
|
|
15
|
+
in `getRandomCaptcha` so deployment can precede the
|
|
16
|
+
providerBackfillCaptchaRandomKey rollout.
|
|
17
|
+
- 5f47c42: fix(database): widen `getSessionRecordBySessionId` projection so post-PoW routing escalations stop failing with `DATABASE.SESSION_STORE_FAILED`. The projection added in #2393 dropped `token`, `score`, `threshold`, `providerSelectEntropy`, `ipAddress`, etc., which `buildEscalation` forwards into a new session via `frictionlessManager.createSession`. With routing machines enabled for edge (2026-06-16) every escalation 500'd. Headers are now enumerated key-by-key so `headers.x-tls-clienthello` (multi-KB TLS ClientHello) stays out of the read.
|
|
18
|
+
- Updated dependencies [4626340]
|
|
19
|
+
- Updated dependencies [6962179]
|
|
20
|
+
- @prosopo/types@4.6.1
|
|
21
|
+
- @prosopo/types-database@4.10.2
|
|
22
|
+
- @prosopo/user-access-policy@3.10.2
|
|
23
|
+
|
|
24
|
+
## 3.14.1
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- 44eaebf: Fix `providedIp.lower` being persisted as BSON `Long` instead of `Decimal128` on `usercommitments`, which aborted the central-streaming sweep on a `CastError` and stopped *all* records from being streamed off the affected provider node.
|
|
28
|
+
|
|
29
|
+
Three changes, each addressing one half of the regression introduced by #2681 ("lifecycle timestamps + submit→verify recency window"):
|
|
30
|
+
|
|
31
|
+
- **provider.ts** — `updateDappUserCommitment` and `updatePuzzleCaptchaRecord` now branch between pipeline-form (`updateOne(filter, [{ $set: ... }])`) and ordinary `$set`. The pipeline form bypasses Mongoose schema casting, so a `bigint` IP half lands on disk as BSON Int64 (Long) rather than going through the `bigint → string → Decimal128` setter on `CompositeIpAddressRecordSchemaObj`. The pipeline form is only used when an `$ifNull` is genuinely required; the `imgCaptchaTasks` side-update call site only carries `providedIp`/`metadata`, so it now takes the ordinary path and the setter runs.
|
|
32
|
+
- **captcha.ts** — `CaptchaDatabase.saveCaptchas` normalises `ipAddress` / `providedIp` composite-IP halves on each lean doc before the `bulkWrite`. `Model.bulkWrite` skips setters, so a Long-typed `lower` (whose unsigned value exceeds `Number.MAX_SAFE_INTEGER` — every IPv6 lower with bit 63 set) hits the Decimal128 caster raw and the ordered bulkWrite aborts the entire batch. Normalisation converts the Long via `Long.fromBits(low, high, /*unsigned*/ true).toString() → Decimal128`, matching what the original schema setter would have produced.
|
|
33
|
+
- **types-database/provider.ts** — `CompositeIpAddressRecordSchemaObj.lower/upper` setters extracted into a shared `normaliseIpHalf` that also handles BSON `Long`. Defensive cover for the `updateOne`/`save`/`create` paths the streamer uses (those *do* run setters); does not run under `bulkWrite`, which is why the captcha.ts normalisation is also needed.
|
|
34
|
+
|
|
35
|
+
Long-class checks use duck-typed `_bsontype === "Long"` rather than `instanceof Long` to stay robust against hoisting differences between the top-level `bson` import and the copy the MongoDB driver uses for deserialisation.
|
|
36
|
+
|
|
37
|
+
Regression test under `packages/database/src/tests/integration/providedIpPipelineCast.integration.test.ts` pins both halves: ordinary `$set` writes Decimal128, `saveCaptchas` drains a Long-poisoned lean doc through to a clean Decimal128 at central, and a negative-control test keeps the underlying Mongoose pipeline-cast behaviour visible so a future regression on either side is unambiguous.
|
|
38
|
+
- Updated dependencies [55b1388]
|
|
39
|
+
- Updated dependencies [44eaebf]
|
|
40
|
+
- @prosopo/util@3.3.0
|
|
41
|
+
- @prosopo/types@4.6.0
|
|
42
|
+
- @prosopo/types-database@4.10.1
|
|
43
|
+
- @prosopo/logger@1.0.3
|
|
44
|
+
- @prosopo/user-access-policy@3.10.1
|
|
45
|
+
- @prosopo/common@3.1.39
|
|
46
|
+
- @prosopo/redis-client@1.0.24
|
|
47
|
+
|
|
3
48
|
## 3.14.0
|
|
4
49
|
### Minor Changes
|
|
5
50
|
|
|
@@ -3,9 +3,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const common = require("@prosopo/common");
|
|
4
4
|
const logger$1 = require("@prosopo/logger");
|
|
5
5
|
const typesDatabase = require("@prosopo/types-database");
|
|
6
|
+
const bson = require("bson");
|
|
6
7
|
require("../base/index.cjs");
|
|
7
8
|
const mongo = require("../base/mongo.cjs");
|
|
8
9
|
var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
|
|
10
|
+
const isBsonLong = (value) => typeof value === "object" && value !== null && "_bsontype" in value && value._bsontype === "Long";
|
|
9
11
|
const logger = logger$1.getLogger("info", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("databases/captcha.cjs", document.baseURI).href);
|
|
10
12
|
var TableNames = /* @__PURE__ */ ((TableNames2) => {
|
|
11
13
|
TableNames2["frictionlessToken"] = "frictionlessToken";
|
|
@@ -85,6 +87,48 @@ class CaptchaDatabase extends mongo.MongoDatabase {
|
|
|
85
87
|
await Promise.all(indexPromises);
|
|
86
88
|
this.indexesEnsured = true;
|
|
87
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Convert a BSON `Long` (which Mongoose's Decimal128 caster rejects)
|
|
92
|
+
* into a Decimal128 with the *unsigned* numeric value — so a Long
|
|
93
|
+
* representing an IPv6 lower half with bit 63 set (signed Long shows
|
|
94
|
+
* as a negative integer) converts to a positive Decimal128 that
|
|
95
|
+
* matches what `bigint→string→Decimal128` would have produced if the
|
|
96
|
+
* schema setter had run on the original write.
|
|
97
|
+
*
|
|
98
|
+
* Production background: pipeline-form updates (`updateOne(filter,
|
|
99
|
+
* [{ $set: ... }])`) bypass Mongoose schema casting, so a `bigint`
|
|
100
|
+
* passed for an IP half is serialised by the driver as BSON Int64
|
|
101
|
+
* (Long) instead of going through the `Decimal128` setter. The
|
|
102
|
+
* central-streaming sweep below reads those docs via `.lean()` and
|
|
103
|
+
* tries to replay them with `bulkWrite`, but `bulkWrite` also skips
|
|
104
|
+
* setters — so the cast fires raw and throws. Normalising the lean
|
|
105
|
+
* doc here is the only place that's guaranteed to run before the
|
|
106
|
+
* bulkWrite sees it.
|
|
107
|
+
*/
|
|
108
|
+
static normaliseCompositeIp(ip) {
|
|
109
|
+
if (!ip) return ip;
|
|
110
|
+
const normaliseHalf = (v) => {
|
|
111
|
+
if (isBsonLong(v)) {
|
|
112
|
+
const lng = v;
|
|
113
|
+
return bson.Decimal128.fromString(
|
|
114
|
+
bson.Long.fromBits(lng.low, lng.high, true).toString()
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return v;
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
...ip,
|
|
121
|
+
lower: normaliseHalf(ip.lower),
|
|
122
|
+
upper: normaliseHalf(ip.upper)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
static normaliseDocCompositeIps(doc) {
|
|
126
|
+
return {
|
|
127
|
+
...doc,
|
|
128
|
+
ipAddress: CaptchaDatabase.normaliseCompositeIp(doc.ipAddress),
|
|
129
|
+
providedIp: CaptchaDatabase.normaliseCompositeIp(doc.providedIp)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
88
132
|
async saveCaptchas(sessionEvents, imageCaptchaEvents, powCaptchaEvents) {
|
|
89
133
|
await this.connect();
|
|
90
134
|
if (sessionEvents.length) {
|
|
@@ -93,7 +137,7 @@ class CaptchaDatabase extends mongo.MongoDatabase {
|
|
|
93
137
|
const { _id, ...safeDoc } = document2;
|
|
94
138
|
return {
|
|
95
139
|
insertOne: {
|
|
96
|
-
document: safeDoc
|
|
140
|
+
document: CaptchaDatabase.normaliseDocCompositeIps(safeDoc)
|
|
97
141
|
}
|
|
98
142
|
};
|
|
99
143
|
})
|
|
@@ -110,10 +154,11 @@ class CaptchaDatabase extends mongo.MongoDatabase {
|
|
|
110
154
|
const result = await this.tables.commitment.bulkWrite(
|
|
111
155
|
imageCaptchaEvents.map((doc) => {
|
|
112
156
|
const { _id, ...safeDoc } = doc;
|
|
157
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
113
158
|
return {
|
|
114
159
|
updateOne: {
|
|
115
|
-
filter: { id:
|
|
116
|
-
update: { $set:
|
|
160
|
+
filter: { id: normalised.id },
|
|
161
|
+
update: { $set: normalised },
|
|
117
162
|
upsert: true
|
|
118
163
|
}
|
|
119
164
|
};
|
|
@@ -133,10 +178,11 @@ class CaptchaDatabase extends mongo.MongoDatabase {
|
|
|
133
178
|
const result = await this.tables.powcaptcha.bulkWrite(
|
|
134
179
|
powCaptchaEvents.map((doc) => {
|
|
135
180
|
const { _id, ...safeDoc } = doc;
|
|
181
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
136
182
|
return {
|
|
137
183
|
updateOne: {
|
|
138
|
-
filter: { challenge:
|
|
139
|
-
update: { $set:
|
|
184
|
+
filter: { challenge: normalised.challenge },
|
|
185
|
+
update: { $set: normalised },
|
|
140
186
|
upsert: true
|
|
141
187
|
}
|
|
142
188
|
};
|
|
@@ -262,7 +262,17 @@ class ProviderDatabase extends mongo.MongoDatabase {
|
|
|
262
262
|
captchaDocs.map((captchaDoc) => ({
|
|
263
263
|
updateOne: {
|
|
264
264
|
filter: { captchaId: captchaDoc.captchaId },
|
|
265
|
-
|
|
265
|
+
// `$setOnInsert` stamps `randomKey` once on the
|
|
266
|
+
// initial insert and leaves it untouched on
|
|
267
|
+
// re-imports of the same captchaId. See
|
|
268
|
+
// `getRandomCaptcha` and the
|
|
269
|
+
// providerBackfillCaptchaRandomKey playbook for
|
|
270
|
+
// the corresponding read path and one-shot
|
|
271
|
+
// backfill of pre-existing rows.
|
|
272
|
+
update: {
|
|
273
|
+
$set: captchaDoc,
|
|
274
|
+
$setOnInsert: { randomKey: Math.random() }
|
|
275
|
+
},
|
|
266
276
|
upsert: true
|
|
267
277
|
}
|
|
268
278
|
}))
|
|
@@ -368,24 +378,35 @@ class ProviderDatabase extends mongo.MongoDatabase {
|
|
|
368
378
|
});
|
|
369
379
|
}
|
|
370
380
|
const sampleSize = size ? Math.abs(Math.trunc(size)) : 1;
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
381
|
+
const projection = {
|
|
382
|
+
_id: 0,
|
|
383
|
+
datasetId: 1,
|
|
384
|
+
datasetContentId: 1,
|
|
385
|
+
captchaId: 1,
|
|
386
|
+
captchaContentId: 1,
|
|
387
|
+
items: 1,
|
|
388
|
+
target: 1
|
|
389
|
+
};
|
|
390
|
+
const baseFilter = { datasetId, solved };
|
|
391
|
+
const pivot = Math.random();
|
|
392
|
+
const head = await this.tables?.captcha.find({ ...baseFilter, randomKey: { $gte: pivot } }, projection).sort({ randomKey: 1 }).limit(sampleSize).lean() ?? [];
|
|
393
|
+
let docs = head;
|
|
394
|
+
if (docs.length < sampleSize) {
|
|
395
|
+
const tail = await this.tables?.captcha.find({ ...baseFilter, randomKey: { $lt: pivot } }, projection).sort({ randomKey: 1 }).limit(sampleSize - docs.length).lean() ?? [];
|
|
396
|
+
docs = docs.concat(tail);
|
|
397
|
+
}
|
|
398
|
+
if (docs.length === 0) {
|
|
399
|
+
const fallback = await this.tables?.captcha.aggregate([
|
|
400
|
+
{ $match: baseFilter },
|
|
401
|
+
{ $sample: { size: sampleSize } },
|
|
402
|
+
{ $project: projection }
|
|
403
|
+
]).exec() ?? [];
|
|
404
|
+
if (fallback.length > 0) {
|
|
405
|
+
return fallback.map(({ _id, ...keep }) => keep);
|
|
384
406
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
return docs.map(({ _id, ...keepAttrs }) => keepAttrs);
|
|
407
|
+
}
|
|
408
|
+
if (docs.length > 0) {
|
|
409
|
+
return docs;
|
|
389
410
|
}
|
|
390
411
|
throw new common.ProsopoDBError("DATABASE.CAPTCHA_GET_FAILED", {
|
|
391
412
|
context: {
|
|
@@ -946,26 +967,33 @@ class ProviderDatabase extends mongo.MongoDatabase {
|
|
|
946
967
|
async updatePuzzleCaptchaRecord(challenge, updates) {
|
|
947
968
|
const tables = this.getTables();
|
|
948
969
|
const timestamp = /* @__PURE__ */ new Date();
|
|
949
|
-
const
|
|
970
|
+
const baseSet = {
|
|
950
971
|
...updates,
|
|
951
972
|
pendingStage: true
|
|
952
973
|
};
|
|
974
|
+
const pipelineExprs = {};
|
|
953
975
|
if (updates.serverChecked === true) {
|
|
954
|
-
|
|
976
|
+
pipelineExprs.verifiedAtTimestamp = {
|
|
955
977
|
$ifNull: ["$verifiedAtTimestamp", timestamp]
|
|
956
978
|
};
|
|
957
979
|
}
|
|
958
980
|
if (updates.userSubmitted === true) {
|
|
959
|
-
|
|
981
|
+
pipelineExprs.submittedAtTimestamp = {
|
|
960
982
|
$ifNull: ["$submittedAtTimestamp", timestamp]
|
|
961
983
|
};
|
|
962
984
|
}
|
|
963
985
|
if (updates.result?.status === types.CaptchaStatus.disapproved) {
|
|
964
|
-
|
|
986
|
+
pipelineExprs.failedAtTimestamp = {
|
|
965
987
|
$ifNull: ["$failedAtTimestamp", timestamp]
|
|
966
988
|
};
|
|
967
989
|
}
|
|
968
|
-
|
|
990
|
+
if (Object.keys(pipelineExprs).length === 0) {
|
|
991
|
+
await tables.puzzlecaptcha.updateOne({ challenge }, { $set: baseSet });
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
await tables.puzzlecaptcha.updateOne({ challenge }, [
|
|
995
|
+
{ $set: { ...baseSet, ...pipelineExprs } }
|
|
996
|
+
]);
|
|
969
997
|
}
|
|
970
998
|
/** @description Get serverChecked Dapp User image captcha commitments from the commitments table
|
|
971
999
|
*/
|
|
@@ -1032,27 +1060,34 @@ class ProviderDatabase extends mongo.MongoDatabase {
|
|
|
1032
1060
|
async updateDappUserCommitment(commitmentId, updates) {
|
|
1033
1061
|
const filter = { id: commitmentId };
|
|
1034
1062
|
const timestamp = /* @__PURE__ */ new Date();
|
|
1035
|
-
const
|
|
1063
|
+
const baseSet = {
|
|
1036
1064
|
...updates,
|
|
1037
1065
|
lastUpdatedAtTimestamp: timestamp,
|
|
1038
1066
|
pendingStage: true
|
|
1039
1067
|
};
|
|
1068
|
+
const pipelineExprs = {};
|
|
1040
1069
|
if (updates.userSubmitted === true) {
|
|
1041
|
-
|
|
1070
|
+
pipelineExprs.submittedAtTimestamp = {
|
|
1042
1071
|
$ifNull: ["$submittedAtTimestamp", timestamp]
|
|
1043
1072
|
};
|
|
1044
1073
|
}
|
|
1045
1074
|
if (updates.serverChecked === true) {
|
|
1046
|
-
|
|
1075
|
+
pipelineExprs.verifiedAtTimestamp = {
|
|
1047
1076
|
$ifNull: ["$verifiedAtTimestamp", timestamp]
|
|
1048
1077
|
};
|
|
1049
1078
|
}
|
|
1050
1079
|
if (updates.result?.status === types.CaptchaStatus.disapproved) {
|
|
1051
|
-
|
|
1080
|
+
pipelineExprs.failedAtTimestamp = {
|
|
1052
1081
|
$ifNull: ["$failedAtTimestamp", timestamp]
|
|
1053
1082
|
};
|
|
1054
1083
|
}
|
|
1055
|
-
|
|
1084
|
+
if (Object.keys(pipelineExprs).length === 0) {
|
|
1085
|
+
await this.tables?.commitment.updateOne(filter, { $set: baseSet });
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
await this.tables?.commitment.updateOne(filter, [
|
|
1089
|
+
{ $set: { ...baseSet, ...pipelineExprs } }
|
|
1090
|
+
]);
|
|
1056
1091
|
}
|
|
1057
1092
|
/**
|
|
1058
1093
|
* @description Get Dapp User PoW captcha commitments that have not been counted towards the client's total
|
|
@@ -1194,11 +1229,41 @@ class ProviderDatabase extends mongo.MongoDatabase {
|
|
|
1194
1229
|
const filter = { sessionId };
|
|
1195
1230
|
const doc = await this.tables.session.findOne(filter, {
|
|
1196
1231
|
sessionId: 1,
|
|
1197
|
-
|
|
1232
|
+
token: 1,
|
|
1233
|
+
score: 1,
|
|
1234
|
+
threshold: 1,
|
|
1198
1235
|
scoreComponents: 1,
|
|
1236
|
+
providerSelectEntropy: 1,
|
|
1237
|
+
ipAddress: 1,
|
|
1238
|
+
ipInfo: 1,
|
|
1199
1239
|
webView: 1,
|
|
1240
|
+
iFrame: 1,
|
|
1241
|
+
decryptedHeadHash: 1,
|
|
1242
|
+
siteKey: 1,
|
|
1200
1243
|
reason: 1,
|
|
1201
|
-
|
|
1244
|
+
mode: 1,
|
|
1245
|
+
solvedImagesCount: 1,
|
|
1246
|
+
userSitekeyIpHash: 1,
|
|
1247
|
+
simdReadings: 1,
|
|
1248
|
+
dnsEvent: 1,
|
|
1249
|
+
"headers.user-agent": 1,
|
|
1250
|
+
"headers.accept": 1,
|
|
1251
|
+
"headers.accept-language": 1,
|
|
1252
|
+
"headers.accept-encoding": 1,
|
|
1253
|
+
"headers.sec-ch-ua": 1,
|
|
1254
|
+
"headers.sec-ch-ua-mobile": 1,
|
|
1255
|
+
"headers.sec-ch-ua-platform": 1,
|
|
1256
|
+
"headers.sec-ch-ua-platform-version": 1,
|
|
1257
|
+
"headers.sec-fetch-dest": 1,
|
|
1258
|
+
"headers.sec-fetch-mode": 1,
|
|
1259
|
+
"headers.sec-fetch-site": 1,
|
|
1260
|
+
"headers.sec-fetch-user": 1,
|
|
1261
|
+
"headers.referer": 1,
|
|
1262
|
+
"headers.origin": 1,
|
|
1263
|
+
"headers.prosopo-user": 1,
|
|
1264
|
+
"headers.prosopo-site-key": 1,
|
|
1265
|
+
"headers.prosopo-type": 1,
|
|
1266
|
+
"headers.x-tls-version": 1
|
|
1202
1267
|
}).lean();
|
|
1203
1268
|
return doc || void 0;
|
|
1204
1269
|
}
|
|
@@ -2052,6 +2117,7 @@ class ProviderDatabase extends mongo.MongoDatabase {
|
|
|
2052
2117
|
}
|
|
2053
2118
|
}
|
|
2054
2119
|
},
|
|
2120
|
+
{ $sample: { size: max } },
|
|
2055
2121
|
{
|
|
2056
2122
|
$lookup: {
|
|
2057
2123
|
from: "sessions",
|
|
@@ -2081,7 +2147,6 @@ class ProviderDatabase extends mongo.MongoDatabase {
|
|
|
2081
2147
|
});
|
|
2082
2148
|
}
|
|
2083
2149
|
pipeline.push(
|
|
2084
|
-
{ $limit: max },
|
|
2085
2150
|
{ $sample: { size } },
|
|
2086
2151
|
{
|
|
2087
2152
|
$project: {
|
|
@@ -15,6 +15,8 @@ export declare class CaptchaDatabase extends MongoDatabase implements ICaptchaDa
|
|
|
15
15
|
connect(): Promise<void>;
|
|
16
16
|
getTables(): Tables<TableNames>;
|
|
17
17
|
ensureIndexes(): Promise<void>;
|
|
18
|
+
private static normaliseCompositeIp;
|
|
19
|
+
private static normaliseDocCompositeIps;
|
|
18
20
|
saveCaptchas(sessionEvents: StoredSession[], imageCaptchaEvents: UserCommitmentRecord[], powCaptchaEvents: PoWCaptchaRecord[]): Promise<void>;
|
|
19
21
|
getCaptchas(filter?: RootFilterQuery<CaptchaProperties>, limit?: number): Promise<{
|
|
20
22
|
userCommitmentRecords: UserCommitmentRecord[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"captcha.d.ts","sourceRoot":"","sources":["../../src/databases/captcha.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,MAAM,EAAa,MAAM,iBAAiB,CAAC;AACzD,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAErB,KAAK,aAAa,EAGlB,KAAK,MAAM,EACX,KAAK,oBAAoB,EACzB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"captcha.d.ts","sourceRoot":"","sources":["../../src/databases/captcha.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,MAAM,EAAa,MAAM,iBAAiB,CAAC;AACzD,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EAErB,KAAK,aAAa,EAGlB,KAAK,MAAM,EACX,KAAK,oBAAoB,EACzB,MAAM,yBAAyB,CAAC;AAajC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIjD,aAAK,UAAU;IACd,iBAAiB,sBAAsB;IACvC,OAAO,YAAY;IACnB,UAAU,eAAe;IACzB,UAAU,eAAe;CACzB;AAoBD,qBAAa,eAAgB,SAAQ,aAAc,YAAW,gBAAgB;IAC7E,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAC3B,OAAO,CAAC,cAAc,CAAS;gBAG9B,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM;IAMD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAUvC,SAAS,IAAI,MAAM,CAAC,UAAU,CAAC;IAUzB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAqDpC,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAoBnC,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAajC,YAAY,CACjB,aAAa,EAAE,aAAa,EAAE,EAC9B,kBAAkB,EAAE,oBAAoB,EAAE,EAC1C,gBAAgB,EAAE,gBAAgB,EAAE;IA6E/B,WAAW,CAChB,MAAM,GAAE,eAAe,CAAC,iBAAiB,CAAM,EAC/C,KAAK,SAAM,GACT,OAAO,CAAC;QACV,qBAAqB,EAAE,oBAAoB,EAAE,CAAC;QAC9C,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;KACtC,CAAC;CAgCF"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ProsopoDBError } from "@prosopo/common";
|
|
2
2
|
import { getLogger } from "@prosopo/logger";
|
|
3
3
|
import { StoredSessionRecordSchema, StoredPoWCaptchaRecordSchema, StoredUserCommitmentRecordSchema } from "@prosopo/types-database";
|
|
4
|
+
import { Decimal128, Long } from "bson";
|
|
4
5
|
import "../base/index.js";
|
|
5
6
|
import { MongoDatabase } from "../base/mongo.js";
|
|
7
|
+
const isBsonLong = (value) => typeof value === "object" && value !== null && "_bsontype" in value && value._bsontype === "Long";
|
|
6
8
|
const logger = getLogger("info", import.meta.url);
|
|
7
9
|
var TableNames = /* @__PURE__ */ ((TableNames2) => {
|
|
8
10
|
TableNames2["frictionlessToken"] = "frictionlessToken";
|
|
@@ -82,6 +84,48 @@ class CaptchaDatabase extends MongoDatabase {
|
|
|
82
84
|
await Promise.all(indexPromises);
|
|
83
85
|
this.indexesEnsured = true;
|
|
84
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Convert a BSON `Long` (which Mongoose's Decimal128 caster rejects)
|
|
89
|
+
* into a Decimal128 with the *unsigned* numeric value — so a Long
|
|
90
|
+
* representing an IPv6 lower half with bit 63 set (signed Long shows
|
|
91
|
+
* as a negative integer) converts to a positive Decimal128 that
|
|
92
|
+
* matches what `bigint→string→Decimal128` would have produced if the
|
|
93
|
+
* schema setter had run on the original write.
|
|
94
|
+
*
|
|
95
|
+
* Production background: pipeline-form updates (`updateOne(filter,
|
|
96
|
+
* [{ $set: ... }])`) bypass Mongoose schema casting, so a `bigint`
|
|
97
|
+
* passed for an IP half is serialised by the driver as BSON Int64
|
|
98
|
+
* (Long) instead of going through the `Decimal128` setter. The
|
|
99
|
+
* central-streaming sweep below reads those docs via `.lean()` and
|
|
100
|
+
* tries to replay them with `bulkWrite`, but `bulkWrite` also skips
|
|
101
|
+
* setters — so the cast fires raw and throws. Normalising the lean
|
|
102
|
+
* doc here is the only place that's guaranteed to run before the
|
|
103
|
+
* bulkWrite sees it.
|
|
104
|
+
*/
|
|
105
|
+
static normaliseCompositeIp(ip) {
|
|
106
|
+
if (!ip) return ip;
|
|
107
|
+
const normaliseHalf = (v) => {
|
|
108
|
+
if (isBsonLong(v)) {
|
|
109
|
+
const lng = v;
|
|
110
|
+
return Decimal128.fromString(
|
|
111
|
+
Long.fromBits(lng.low, lng.high, true).toString()
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
return v;
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
...ip,
|
|
118
|
+
lower: normaliseHalf(ip.lower),
|
|
119
|
+
upper: normaliseHalf(ip.upper)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
static normaliseDocCompositeIps(doc) {
|
|
123
|
+
return {
|
|
124
|
+
...doc,
|
|
125
|
+
ipAddress: CaptchaDatabase.normaliseCompositeIp(doc.ipAddress),
|
|
126
|
+
providedIp: CaptchaDatabase.normaliseCompositeIp(doc.providedIp)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
85
129
|
async saveCaptchas(sessionEvents, imageCaptchaEvents, powCaptchaEvents) {
|
|
86
130
|
await this.connect();
|
|
87
131
|
if (sessionEvents.length) {
|
|
@@ -90,7 +134,7 @@ class CaptchaDatabase extends MongoDatabase {
|
|
|
90
134
|
const { _id, ...safeDoc } = document;
|
|
91
135
|
return {
|
|
92
136
|
insertOne: {
|
|
93
|
-
document: safeDoc
|
|
137
|
+
document: CaptchaDatabase.normaliseDocCompositeIps(safeDoc)
|
|
94
138
|
}
|
|
95
139
|
};
|
|
96
140
|
})
|
|
@@ -107,10 +151,11 @@ class CaptchaDatabase extends MongoDatabase {
|
|
|
107
151
|
const result = await this.tables.commitment.bulkWrite(
|
|
108
152
|
imageCaptchaEvents.map((doc) => {
|
|
109
153
|
const { _id, ...safeDoc } = doc;
|
|
154
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
110
155
|
return {
|
|
111
156
|
updateOne: {
|
|
112
|
-
filter: { id:
|
|
113
|
-
update: { $set:
|
|
157
|
+
filter: { id: normalised.id },
|
|
158
|
+
update: { $set: normalised },
|
|
114
159
|
upsert: true
|
|
115
160
|
}
|
|
116
161
|
};
|
|
@@ -130,10 +175,11 @@ class CaptchaDatabase extends MongoDatabase {
|
|
|
130
175
|
const result = await this.tables.powcaptcha.bulkWrite(
|
|
131
176
|
powCaptchaEvents.map((doc) => {
|
|
132
177
|
const { _id, ...safeDoc } = doc;
|
|
178
|
+
const normalised = CaptchaDatabase.normaliseDocCompositeIps(safeDoc);
|
|
133
179
|
return {
|
|
134
180
|
updateOne: {
|
|
135
|
-
filter: { challenge:
|
|
136
|
-
update: { $set:
|
|
181
|
+
filter: { challenge: normalised.challenge },
|
|
182
|
+
update: { $set: normalised },
|
|
137
183
|
upsert: true
|
|
138
184
|
}
|
|
139
185
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"captcha.js","sourceRoot":"","sources":["../../src/databases/captcha.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAe,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAIN,4BAA4B,EAE5B,yBAAyB,EACzB,gCAAgC,GAGhC,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"captcha.js","sourceRoot":"","sources":["../../src/databases/captcha.ts"],"names":[],"mappings":"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"}
|