@prosopo/database 4.5.1 → 4.6.0
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 +8 -7
- package/.turbo/turbo-build$colon$esm.log +8 -7
- package/.turbo/turbo-build$colon$tsc.log +15 -15
- package/CHANGELOG.md +25 -0
- package/dist/cjs/base/index.cjs +3 -0
- package/dist/cjs/base/mongo.cjs +5 -1
- package/dist/cjs/base/reconcileIndexes.cjs +162 -0
- package/dist/cjs/databases/captcha.cjs +20 -21
- package/dist/cjs/databases/provider.cjs +20 -22
- package/dist/cjs/index.cjs +3 -0
- package/dist/esm/base/index.js +2 -1
- package/dist/esm/base/mongo.js +5 -1
- package/dist/esm/base/reconcileIndexes.js +161 -0
- package/dist/esm/databases/captcha.js +20 -21
- package/dist/esm/databases/provider.js +20 -22
- package/dist/esm/index.js +2 -1
- package/dist/tsc/base/index.d.ts +1 -0
- package/dist/tsc/base/index.d.ts.map +1 -1
- package/dist/tsc/base/index.js +1 -0
- package/dist/tsc/base/index.js.map +1 -1
- package/dist/tsc/base/mongo.d.ts.map +1 -1
- package/dist/tsc/base/mongo.js +5 -1
- package/dist/tsc/base/mongo.js.map +1 -1
- package/dist/tsc/base/reconcileIndexes.d.ts +24 -0
- package/dist/tsc/base/reconcileIndexes.d.ts.map +1 -0
- package/dist/tsc/base/reconcileIndexes.js +177 -0
- package/dist/tsc/base/reconcileIndexes.js.map +1 -0
- package/dist/tsc/databases/captcha.d.ts.map +1 -1
- package/dist/tsc/databases/captcha.js +27 -25
- package/dist/tsc/databases/captcha.js.map +1 -1
- package/dist/tsc/databases/provider.d.ts.map +1 -1
- package/dist/tsc/databases/provider.js +27 -27
- package/dist/tsc/databases/provider.js.map +1 -1
- package/dist/tsc/tests/unit/base/mongo.unit.test.js +14 -0
- package/dist/tsc/tests/unit/base/mongo.unit.test.js.map +1 -1
- package/dist/tsc/tests/unit/base/reconcileIndexes.unit.test.d.ts +2 -0
- package/dist/tsc/tests/unit/base/reconcileIndexes.unit.test.d.ts.map +1 -0
- package/dist/tsc/tests/unit/base/reconcileIndexes.unit.test.js +350 -0
- package/dist/tsc/tests/unit/base/reconcileIndexes.unit.test.js.map +1 -0
- package/dist/types/base/index.d.ts +1 -0
- package/dist/types/base/index.d.ts.map +1 -1
- package/dist/types/base/mongo.d.ts.map +1 -1
- package/dist/types/base/reconcileIndexes.d.ts +24 -0
- package/dist/types/base/reconcileIndexes.d.ts.map +1 -0
- package/dist/types/databases/captcha.d.ts.map +1 -1
- package/dist/types/databases/provider.d.ts.map +1 -1
- package/dist/types/tests/unit/base/reconcileIndexes.unit.test.d.ts +2 -0
- package/dist/types/tests/unit/base/reconcileIndexes.unit.test.d.ts.map +1 -0
- package/package.json +7 -7
|
@@ -34,21 +34,22 @@ ViteCommonJSConfig: .
|
|
|
34
34
|
[36mvite v8.1.6 [32mbuilding ssr environment for production...[36m[39m
|
|
35
35
|
Bundle build started
|
|
36
36
|
[2K
|
|
37
|
-
✓
|
|
37
|
+
✓ 14 modules transformed.
|
|
38
38
|
rendering chunks...
|
|
39
39
|
computing gzip size...
|
|
40
|
-
dist/cjs/base/index.cjs 0.
|
|
40
|
+
dist/cjs/base/index.cjs 0.43 kB │ gzip: 0.16 kB
|
|
41
41
|
dist/cjs/base/mongoMemory.cjs 0.66 kB │ gzip: 0.32 kB
|
|
42
42
|
dist/cjs/_virtual/_rolldown/runtime.cjs 1.00 kB │ gzip: 0.50 kB
|
|
43
43
|
dist/cjs/databases/index.cjs 1.14 kB │ gzip: 0.32 kB
|
|
44
44
|
dist/cjs/databases/clientRecordCache.cjs 1.45 kB │ gzip: 0.69 kB
|
|
45
|
-
dist/cjs/index.cjs 1.
|
|
45
|
+
dist/cjs/index.cjs 1.87 kB │ gzip: 0.54 kB
|
|
46
46
|
dist/cjs/databases/client.cjs 1.96 kB │ gzip: 0.82 kB
|
|
47
47
|
dist/cjs/mongooseOptions.cjs 3.74 kB │ gzip: 1.56 kB
|
|
48
|
-
dist/cjs/base/mongo.cjs 4.
|
|
48
|
+
dist/cjs/base/mongo.cjs 4.47 kB │ gzip: 1.49 kB
|
|
49
|
+
dist/cjs/base/reconcileIndexes.cjs 5.97 kB │ gzip: 1.98 kB
|
|
49
50
|
dist/cjs/databases/centralDbStreamer.cjs 6.40 kB │ gzip: 1.52 kB
|
|
50
|
-
dist/cjs/databases/captcha.cjs 8.
|
|
51
|
+
dist/cjs/databases/captcha.cjs 8.39 kB │ gzip: 2.40 kB
|
|
51
52
|
dist/cjs/redisCache.cjs 14.30 kB │ gzip: 4.45 kB
|
|
52
|
-
dist/cjs/databases/provider.cjs 71.
|
|
53
|
+
dist/cjs/databases/provider.cjs 71.07 kB │ gzip: 13.59 kB
|
|
53
54
|
|
|
54
|
-
[32m✓ built in
|
|
55
|
+
[32m✓ built in 73ms[39m
|
|
@@ -34,21 +34,22 @@ ViteEsmConfig: .
|
|
|
34
34
|
[36mvite v8.1.6 [32mbuilding ssr environment for production...[36m[39m
|
|
35
35
|
Bundle build started
|
|
36
36
|
[2K
|
|
37
|
-
✓
|
|
37
|
+
✓ 14 modules transformed.
|
|
38
38
|
rendering chunks...
|
|
39
39
|
computing gzip size...
|
|
40
40
|
dist/esm/_virtual/_rolldown/runtime.js 0.04 kB │ gzip: 0.06 kB
|
|
41
|
-
dist/esm/base/index.js 0.
|
|
41
|
+
dist/esm/base/index.js 0.25 kB │ gzip: 0.12 kB
|
|
42
42
|
dist/esm/base/mongoMemory.js 0.60 kB │ gzip: 0.30 kB
|
|
43
43
|
dist/esm/databases/index.js 0.77 kB │ gzip: 0.27 kB
|
|
44
|
-
dist/esm/index.js 1.
|
|
44
|
+
dist/esm/index.js 1.19 kB │ gzip: 0.42 kB
|
|
45
45
|
dist/esm/databases/clientRecordCache.js 1.40 kB │ gzip: 0.68 kB
|
|
46
46
|
dist/esm/databases/client.js 1.85 kB │ gzip: 0.80 kB
|
|
47
47
|
dist/esm/mongooseOptions.js 3.58 kB │ gzip: 1.53 kB
|
|
48
|
-
dist/esm/base/mongo.js 4.
|
|
48
|
+
dist/esm/base/mongo.js 4.22 kB │ gzip: 1.40 kB
|
|
49
|
+
dist/esm/base/reconcileIndexes.js 5.93 kB │ gzip: 1.97 kB
|
|
49
50
|
dist/esm/databases/centralDbStreamer.js 6.33 kB │ gzip: 1.51 kB
|
|
50
|
-
dist/esm/databases/captcha.js 8.
|
|
51
|
+
dist/esm/databases/captcha.js 8.29 kB │ gzip: 2.40 kB
|
|
51
52
|
dist/esm/redisCache.js 14.25 kB │ gzip: 4.43 kB
|
|
52
|
-
dist/esm/databases/provider.js 69.
|
|
53
|
+
dist/esm/databases/provider.js 69.29 kB │ gzip: 13.59 kB
|
|
53
54
|
|
|
54
|
-
[32m✓ built in
|
|
55
|
+
[32m✓ built in 112ms[39m
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
$ tsc --build --verbose
|
|
2
|
-
|
|
2
|
+
10:56:34 AM - Projects in this build:
|
|
3
3
|
* ../../dev/config/tsconfig.json
|
|
4
4
|
* ../util/tsconfig.json
|
|
5
5
|
* ../locale/tsconfig.json
|
|
@@ -14,31 +14,31 @@ $ tsc --build --verbose
|
|
|
14
14
|
* ../types-database/tsconfig.json
|
|
15
15
|
* tsconfig.json
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
10:56:34 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/vite/modifyExportsFiles.test.ts' is older than output '../../dev/config/dist/tsc/tsconfig.tsbuildinfo'
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
10:56:34 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/dist/tsc/tsconfig.tsbuildinfo'
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
10:56:34 AM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/dist/tsc/tsconfig.tsbuildinfo'
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
10:56:34 AM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/dist/tsc/tsconfig.tsbuildinfo'
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
10:56:34 AM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/types.test-d.ts' is older than output '../types/dist/tsc/tsconfig.tsbuildinfo'
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
10:56:34 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/tests/errorSites.unit.test.ts' is older than output '../common/dist/tsc/tsconfig.tsbuildinfo'
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
10:56:34 AM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/dist/tsc/tsconfig.tsbuildinfo'
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
10:56:34 AM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/dist/tsc/tsconfig.tsbuildinfo'
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
10:56:34 AM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/dist/tsc/tsconfig.tsbuildinfo'
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
10:56:34 AM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/dist/tsc/tsconfig.tsbuildinfo'
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
10:56:34 AM - 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/dist/tsc/tsconfig.tsbuildinfo'
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
10:56:34 AM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/types.test-d.ts' is older than output '../types-database/dist/tsc/tsconfig.tsbuildinfo'
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
10:56:34 AM - Project 'tsconfig.json' is out of date because output file 'dist/tsc/tsconfig.tsbuildinfo' does not exist
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
10:56:34 AM - Building project '/home/runner/work/captcha/captcha/packages/database/tsconfig.json'...
|
|
44
44
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @prosopo/database
|
|
2
2
|
|
|
3
|
+
## 4.6.0
|
|
4
|
+
### Minor Changes
|
|
5
|
+
|
|
6
|
+
- c12d199: Add `reconcileIndexes`, which brings a collection's indexes in line with its schema. Mongoose's own `ensureIndexes`/autoIndex cannot change an existing index: when a declared index's key or options change (for example `pendingStage_partial` gaining `_id`, or a field becoming unique) it logs an error and leaves the old index in place, so the new one never appears. `reconcileIndexes` drops and rebuilds only those clashing indexes, puts the old one back if the rebuild fails (for example a new unique index meeting duplicates), can drop named obsolete indexes, can keep extra indexes declared outside the schema, and has a dry-run mode.
|
|
7
|
+
|
|
8
|
+
The provider's `ensureIndexes` now uses it on its own database (dropping indexes the schema no longer declares). The central captcha database uses it too, but keeps indexes it does not know about, since other services add their own there.
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- f31105a: Mongo connection URLs in logs now hide the username and password properly. The old redaction replaced the first `word:word` it found, so a password containing a `-` (which every password `generateSelfHostedEnv.ts` creates can) was logged in full or in part, with the host and port hidden instead. URLs without credentials no longer lose their host and port either.
|
|
13
|
+
- Updated dependencies [91ef450]
|
|
14
|
+
- Updated dependencies [7c58be4]
|
|
15
|
+
- Updated dependencies [cdea91f]
|
|
16
|
+
- Updated dependencies [f679760]
|
|
17
|
+
- Updated dependencies [cf9a1bb]
|
|
18
|
+
- Updated dependencies [cdea91f]
|
|
19
|
+
- Updated dependencies [ec4ed10]
|
|
20
|
+
- @prosopo/types@5.24.0
|
|
21
|
+
- @prosopo/logger@2.1.8
|
|
22
|
+
- @prosopo/user-access-policy@3.15.16
|
|
23
|
+
- @prosopo/types-database@5.16.2
|
|
24
|
+
- @prosopo/util@3.5.0
|
|
25
|
+
- @prosopo/common@3.3.2
|
|
26
|
+
- @prosopo/redis-client@1.0.45
|
|
27
|
+
|
|
3
28
|
## 4.5.1
|
|
4
29
|
### Patch Changes
|
|
5
30
|
|
package/dist/cjs/base/index.cjs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
const require_mongo = require("./mongo.cjs");
|
|
2
2
|
const require_mongoMemory = require("./mongoMemory.cjs");
|
|
3
|
+
const require_reconcileIndexes = require("./reconcileIndexes.cjs");
|
|
3
4
|
exports.MongoDatabase = require_mongo.MongoDatabase;
|
|
4
5
|
exports.MongoMemoryDatabase = require_mongoMemory.MongoMemoryDatabase;
|
|
6
|
+
exports.defaultIndexName = require_reconcileIndexes.defaultIndexName;
|
|
7
|
+
exports.reconcileIndexes = require_reconcileIndexes.reconcileIndexes;
|
package/dist/cjs/base/mongo.cjs
CHANGED
|
@@ -8,6 +8,10 @@ mongoose = require_runtime.__toESM(mongoose, 1);
|
|
|
8
8
|
//#region src/base/mongo.ts
|
|
9
9
|
mongoose.default.set("strictQuery", false);
|
|
10
10
|
var DEFAULT_ENDPOINT = "mongodb://127.0.0.1:27017";
|
|
11
|
+
var redactCredentials = (url) => {
|
|
12
|
+
const credentials = url.username || url.password ? "<Credentials>@" : "";
|
|
13
|
+
return `${url.protocol}//${credentials}${url.host}${url.pathname}${url.search}`;
|
|
14
|
+
};
|
|
11
15
|
/**
|
|
12
16
|
* Returns a generic Mongo database layer
|
|
13
17
|
* @param {string} url The database endpoint
|
|
@@ -23,7 +27,7 @@ var MongoDatabase = class {
|
|
|
23
27
|
if (dbname) parsedUrl.pathname = dbname;
|
|
24
28
|
if (authSource) parsedUrl.searchParams.set("authSource", authSource);
|
|
25
29
|
this._url = parsedUrl.toString();
|
|
26
|
-
this.safeURL =
|
|
30
|
+
this.safeURL = redactCredentials(parsedUrl);
|
|
27
31
|
this.dbname = dbname || parsedUrl.pathname.replace("/", "");
|
|
28
32
|
this.logger = (logger || (0, _prosopo_logger.getLogger)("info", "database:mongo")).with({ mongoUrl: this.safeURL });
|
|
29
33
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
//#region src/base/reconcileIndexes.ts
|
|
2
|
+
var defaultIndexName = (key) => Object.entries(key).map(([field, direction]) => `${field}_${direction}`).join("_");
|
|
3
|
+
var toDriverKey = (key) => Object.fromEntries(Object.entries(key).map(([field, direction]) => [field, direction === "ascending" || direction === "asc" ? 1 : direction === "descending" || direction === "desc" ? -1 : direction]));
|
|
4
|
+
var keyOf = (index) => index.key instanceof Map ? Object.fromEntries(index.key) : index.key;
|
|
5
|
+
var sameKey = (a, b) => {
|
|
6
|
+
const left = Object.entries(a);
|
|
7
|
+
const right = Object.entries(b);
|
|
8
|
+
return left.length === right.length && left.every(([field, direction], i) => right[i]?.[0] === field && String(right[i]?.[1]) === String(direction));
|
|
9
|
+
};
|
|
10
|
+
var listIndexesOrEmpty = async (model) => {
|
|
11
|
+
try {
|
|
12
|
+
return await model.listIndexes();
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (err.codeName === "NamespaceNotFound") return [];
|
|
15
|
+
throw err;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var toDriverOptions = ({ name, options: { unique, ...options } }) => ({
|
|
19
|
+
...options,
|
|
20
|
+
...unique !== void 0 && { unique: Array.isArray(unique) ? unique[0] : unique },
|
|
21
|
+
name
|
|
22
|
+
});
|
|
23
|
+
var errorMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
24
|
+
var specOf = (index) => {
|
|
25
|
+
const { key, v: _v, ns: _ns, ...options } = index;
|
|
26
|
+
return {
|
|
27
|
+
key,
|
|
28
|
+
options
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
var BRIDGE_SUFFIX = "_reconcile_bridge";
|
|
32
|
+
var BRIDGE_FIELD = "__reconcileBridge";
|
|
33
|
+
var bridgeOf = (index) => {
|
|
34
|
+
const { options } = specOf(index);
|
|
35
|
+
const { expireAfterSeconds: _ttl, ...bridgeOptions } = options;
|
|
36
|
+
return {
|
|
37
|
+
key: {
|
|
38
|
+
...index.key,
|
|
39
|
+
[BRIDGE_FIELD]: 1
|
|
40
|
+
},
|
|
41
|
+
options: {
|
|
42
|
+
...bridgeOptions,
|
|
43
|
+
name: `${index.name}${BRIDGE_SUFFIX}`
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
var dropIfPresent = async (model, name) => {
|
|
48
|
+
try {
|
|
49
|
+
await model.collection.dropIndex(name);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
if (err.codeName !== "IndexNotFound") throw err;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Brings a collection's indexes in line with its schema without the window of
|
|
56
|
+
* a drop-everything-then-rebuild. Indexes that already match are left alone;
|
|
57
|
+
* an existing index that clashes with a declared one (same name or same key,
|
|
58
|
+
* different key or options) is rebuilt, because `createIndex` refuses to change
|
|
59
|
+
* an index in place and mongoose's autoIndex only logs that refusal. A bridge
|
|
60
|
+
* index is built before the old one is dropped, so no query loses its index
|
|
61
|
+
* during the rebuild. If a rebuild fails (e.g. a new unique index meets
|
|
62
|
+
* duplicates) the index it replaced is restored, so a failed run never leaves
|
|
63
|
+
* a collection with less than it started with.
|
|
64
|
+
*/
|
|
65
|
+
var reconcileIndexes = async (model, options = {}) => {
|
|
66
|
+
const result = {
|
|
67
|
+
collection: model.collection.collectionName,
|
|
68
|
+
dropped: [],
|
|
69
|
+
created: [],
|
|
70
|
+
restored: [],
|
|
71
|
+
failed: []
|
|
72
|
+
};
|
|
73
|
+
const existing = await listIndexesOrEmpty(model);
|
|
74
|
+
const diff = await model.diffIndexes({ indexOptionsToCreate: true });
|
|
75
|
+
const toCreate = diff.toCreate;
|
|
76
|
+
const undeclared = diff.toDrop;
|
|
77
|
+
const wanted = toCreate.map(([key, indexOptions]) => ({
|
|
78
|
+
key,
|
|
79
|
+
options: indexOptions,
|
|
80
|
+
name: indexOptions.name ?? defaultIndexName(key)
|
|
81
|
+
}));
|
|
82
|
+
const extra = options.extra ?? [];
|
|
83
|
+
const isExtra = (index) => extra.some((e) => sameKey(keyOf(e), index.key));
|
|
84
|
+
const missingExtra = extra.filter((e) => !existing.some((index) => sameKey(keyOf(e), index.key)) && !wanted.some((w) => sameKey(keyOf(e), w.key)));
|
|
85
|
+
const obsolete = new Set(options.obsolete ?? []);
|
|
86
|
+
const clashes = (index, w) => w.name === index.name || sameKey(w.key, index.key);
|
|
87
|
+
const toDrop = existing.filter((index) => index.name !== void 0 && undeclared.includes(index.name) && !isExtra(index) && (options.dropUndeclared || obsolete.has(index.name) || wanted.some((w) => clashes(index, w))));
|
|
88
|
+
const extraName = (e) => e.name ?? defaultIndexName(keyOf(e));
|
|
89
|
+
if (options.dryRun) {
|
|
90
|
+
result.dropped = toDrop.map((index) => index.name ?? "");
|
|
91
|
+
result.created = [...wanted.map((w) => w.name), ...missingExtra.map(extraName)];
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
for (const w of wanted) {
|
|
95
|
+
const replaced = toDrop.filter((index) => clashes(index, w) && !result.dropped.includes(index.name ?? ""));
|
|
96
|
+
const bridges = [];
|
|
97
|
+
try {
|
|
98
|
+
for (const index of replaced) {
|
|
99
|
+
const { key, options: bridgeOptions } = bridgeOf(index);
|
|
100
|
+
await model.collection.createIndex(key, bridgeOptions);
|
|
101
|
+
bridges.push(bridgeOptions.name ?? "");
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
result.failed.push({
|
|
105
|
+
name: w.name,
|
|
106
|
+
error: `bridge failed: ${errorMessage(err)}`
|
|
107
|
+
});
|
|
108
|
+
for (const bridge of bridges) await dropIfPresent(model, bridge);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
for (const index of replaced) {
|
|
112
|
+
const name = index.name ?? "";
|
|
113
|
+
await dropIfPresent(model, name);
|
|
114
|
+
result.dropped.push(name);
|
|
115
|
+
}
|
|
116
|
+
let restoreFailed = false;
|
|
117
|
+
try {
|
|
118
|
+
await model.collection.createIndex(toDriverKey(w.key), toDriverOptions(w));
|
|
119
|
+
result.created.push(w.name);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
result.failed.push({
|
|
122
|
+
name: w.name,
|
|
123
|
+
error: errorMessage(err)
|
|
124
|
+
});
|
|
125
|
+
for (const index of replaced) {
|
|
126
|
+
const { key, options: replacedOptions } = specOf(index);
|
|
127
|
+
try {
|
|
128
|
+
await model.collection.createIndex(key, replacedOptions);
|
|
129
|
+
result.restored.push(index.name ?? "");
|
|
130
|
+
} catch (restoreErr) {
|
|
131
|
+
restoreFailed = true;
|
|
132
|
+
result.failed.push({
|
|
133
|
+
name: index.name ?? "",
|
|
134
|
+
error: `restore failed: ${errorMessage(restoreErr)}`
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!restoreFailed) for (const bridge of bridges) await dropIfPresent(model, bridge);
|
|
140
|
+
}
|
|
141
|
+
for (const e of missingExtra) try {
|
|
142
|
+
await model.collection.createIndexes([e]);
|
|
143
|
+
result.created.push(extraName(e));
|
|
144
|
+
} catch (err) {
|
|
145
|
+
result.failed.push({
|
|
146
|
+
name: extraName(e),
|
|
147
|
+
error: errorMessage(err)
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
for (const index of toDrop) {
|
|
151
|
+
const name = index.name ?? "";
|
|
152
|
+
if (!result.dropped.includes(name) && !wanted.some((w) => clashes(index, w))) {
|
|
153
|
+
await dropIfPresent(model, name);
|
|
154
|
+
result.dropped.push(name);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (const index of existing) if (index.name?.endsWith(BRIDGE_SUFFIX)) await dropIfPresent(model, index.name);
|
|
158
|
+
return result;
|
|
159
|
+
};
|
|
160
|
+
//#endregion
|
|
161
|
+
exports.defaultIndexName = defaultIndexName;
|
|
162
|
+
exports.reconcileIndexes = reconcileIndexes;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_mongo = require("../base/mongo.cjs");
|
|
2
|
+
const require_reconcileIndexes = require("../base/reconcileIndexes.cjs");
|
|
2
3
|
let _prosopo_common = require("@prosopo/common");
|
|
3
4
|
let _prosopo_logger = require("@prosopo/logger");
|
|
4
5
|
let _prosopo_types_database = require("@prosopo/types-database");
|
|
@@ -60,27 +61,25 @@ var CaptchaDatabase = class CaptchaDatabase extends require_mongo.MongoDatabase
|
|
|
60
61
|
return this.tables;
|
|
61
62
|
}
|
|
62
63
|
async ensureIndexes() {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
});
|
|
83
|
-
await Promise.all(indexPromises);
|
|
64
|
+
if (!this.indexesEnsured && this.connected) await Promise.all(CAPTCHA_TABLES.map(async ({ collectionName }) => {
|
|
65
|
+
try {
|
|
66
|
+
const result = await require_reconcileIndexes.reconcileIndexes(this.tables[collectionName], { dropUndeclared: false });
|
|
67
|
+
if (result.failed.length > 0) this.logger.warn(() => ({
|
|
68
|
+
msg: `Error creating indexes for collection ${collectionName}`,
|
|
69
|
+
data: result
|
|
70
|
+
}));
|
|
71
|
+
else this.logger.info(() => ({
|
|
72
|
+
msg: `Indexes ensured for collection ${collectionName}`,
|
|
73
|
+
data: result
|
|
74
|
+
}));
|
|
75
|
+
} catch (err) {
|
|
76
|
+
this.logger.warn(() => ({
|
|
77
|
+
err,
|
|
78
|
+
msg: `Error creating indexes for collection ${collectionName}`
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
else if (!this.connected) this.logger.info(() => ({ msg: "Skipping index creation as not connected" }));
|
|
84
83
|
this.indexesEnsured = true;
|
|
85
84
|
}
|
|
86
85
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_mongo = require("../base/mongo.cjs");
|
|
2
|
+
const require_reconcileIndexes = require("../base/reconcileIndexes.cjs");
|
|
2
3
|
const require_clientRecordCache = require("./clientRecordCache.cjs");
|
|
3
4
|
let _prosopo_common = require("@prosopo/common");
|
|
4
5
|
let _prosopo_types = require("@prosopo/types");
|
|
@@ -147,28 +148,25 @@ var ProviderDatabase = class ProviderDatabase extends require_mongo.MongoDatabas
|
|
|
147
148
|
return this.redisConnection;
|
|
148
149
|
}
|
|
149
150
|
async ensureIndexes() {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}));
|
|
170
|
-
});
|
|
171
|
-
await Promise.all(indexPromises);
|
|
151
|
+
if (!this.indexesEnsured && this.connected) await Promise.all(PROVIDER_TABLES.map(async ({ collectionName }) => {
|
|
152
|
+
try {
|
|
153
|
+
const result = await require_reconcileIndexes.reconcileIndexes(this.tables[collectionName], { dropUndeclared: true });
|
|
154
|
+
if (result.failed.length > 0) this.logger.warn(() => ({
|
|
155
|
+
msg: `Error creating indexes for collection ${collectionName}`,
|
|
156
|
+
data: result
|
|
157
|
+
}));
|
|
158
|
+
else this.logger.info(() => ({
|
|
159
|
+
msg: `Indexes ensured for collection ${collectionName}`,
|
|
160
|
+
data: result
|
|
161
|
+
}));
|
|
162
|
+
} catch (err) {
|
|
163
|
+
this.logger.warn(() => ({
|
|
164
|
+
err,
|
|
165
|
+
msg: `Error creating indexes for collection ${collectionName}`
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
}));
|
|
169
|
+
else if (!this.connected) this.logger.info(() => ({ msg: "Skipping index creation as not connected" }));
|
|
172
170
|
this.indexesEnsured = true;
|
|
173
171
|
}
|
|
174
172
|
getUserAccessRulesStorage() {
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_mongooseOptions = require("./mongooseOptions.cjs");
|
|
3
3
|
const require_mongo = require("./base/mongo.cjs");
|
|
4
4
|
const require_mongoMemory = require("./base/mongoMemory.cjs");
|
|
5
|
+
const require_reconcileIndexes = require("./base/reconcileIndexes.cjs");
|
|
5
6
|
const require_captcha = require("./databases/captcha.cjs");
|
|
6
7
|
const require_client = require("./databases/client.cjs");
|
|
7
8
|
const require_provider = require("./databases/provider.cjs");
|
|
@@ -23,5 +24,7 @@ exports.MongoDatabase = require_mongo.MongoDatabase;
|
|
|
23
24
|
exports.MongoMemoryDatabase = require_mongoMemory.MongoMemoryDatabase;
|
|
24
25
|
exports.ProviderDatabase = require_provider.ProviderDatabase;
|
|
25
26
|
exports.RedisWriteQueue = require_redisCache.RedisWriteQueue;
|
|
27
|
+
exports.defaultIndexName = require_reconcileIndexes.defaultIndexName;
|
|
26
28
|
exports.getMongoCompressors = require_mongooseOptions.getMongoCompressors;
|
|
27
29
|
exports.getMongoConnectionOptions = require_mongooseOptions.getMongoConnectionOptions;
|
|
30
|
+
exports.reconcileIndexes = require_reconcileIndexes.reconcileIndexes;
|
package/dist/esm/base/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { MongoDatabase } from "./mongo.js";
|
|
2
2
|
import { MongoMemoryDatabase } from "./mongoMemory.js";
|
|
3
|
-
|
|
3
|
+
import { defaultIndexName, reconcileIndexes } from "./reconcileIndexes.js";
|
|
4
|
+
export { MongoDatabase, MongoMemoryDatabase, defaultIndexName, reconcileIndexes };
|
package/dist/esm/base/mongo.js
CHANGED
|
@@ -6,6 +6,10 @@ import mongoose from "mongoose";
|
|
|
6
6
|
//#region src/base/mongo.ts
|
|
7
7
|
mongoose.set("strictQuery", false);
|
|
8
8
|
var DEFAULT_ENDPOINT = "mongodb://127.0.0.1:27017";
|
|
9
|
+
var redactCredentials = (url) => {
|
|
10
|
+
const credentials = url.username || url.password ? "<Credentials>@" : "";
|
|
11
|
+
return `${url.protocol}//${credentials}${url.host}${url.pathname}${url.search}`;
|
|
12
|
+
};
|
|
9
13
|
/**
|
|
10
14
|
* Returns a generic Mongo database layer
|
|
11
15
|
* @param {string} url The database endpoint
|
|
@@ -21,7 +25,7 @@ var MongoDatabase = class {
|
|
|
21
25
|
if (dbname) parsedUrl.pathname = dbname;
|
|
22
26
|
if (authSource) parsedUrl.searchParams.set("authSource", authSource);
|
|
23
27
|
this._url = parsedUrl.toString();
|
|
24
|
-
this.safeURL =
|
|
28
|
+
this.safeURL = redactCredentials(parsedUrl);
|
|
25
29
|
this.dbname = dbname || parsedUrl.pathname.replace("/", "");
|
|
26
30
|
this.logger = (logger || getLogger("info", "database:mongo")).with({ mongoUrl: this.safeURL });
|
|
27
31
|
}
|