@prosopo/database 3.0.13 → 3.4.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,300 @@
1
1
  # @prosopo/database
2
2
 
3
+ ## 3.4.5
4
+ ### Patch Changes
5
+
6
+ - Updated dependencies [1e3a838]
7
+ - @prosopo/config@3.1.20
8
+ - @prosopo/common@3.1.20
9
+ - @prosopo/locale@3.1.20
10
+ - @prosopo/redis-client@1.0.5
11
+ - @prosopo/types@3.5.3
12
+ - @prosopo/types-database@3.3.5
13
+ - @prosopo/user-access-policy@3.5.19
14
+
15
+ ## 3.4.4
16
+ ### Patch Changes
17
+
18
+ - 5659b24: Release 3.4.4
19
+ - Updated dependencies [f912439]
20
+ - Updated dependencies [5659b24]
21
+ - @prosopo/common@3.1.19
22
+ - @prosopo/user-access-policy@3.5.18
23
+ - @prosopo/types-database@3.3.4
24
+ - @prosopo/redis-client@1.0.4
25
+ - @prosopo/locale@3.1.19
26
+ - @prosopo/types@3.5.2
27
+ - @prosopo/config@3.1.19
28
+
29
+ ## 3.4.3
30
+ ### Patch Changes
31
+
32
+ - c72ecbd: Reduce noisy logs
33
+ - 50c4120: Release 3.4.3
34
+ - Updated dependencies [52cd544]
35
+ - Updated dependencies [b117ba3]
36
+ - Updated dependencies [50c4120]
37
+ - @prosopo/types@3.5.1
38
+ - @prosopo/user-access-policy@3.5.17
39
+ - @prosopo/types-database@3.3.3
40
+ - @prosopo/redis-client@1.0.3
41
+ - @prosopo/common@3.1.18
42
+ - @prosopo/locale@3.1.18
43
+ - @prosopo/config@3.1.18
44
+
45
+ ## 3.4.2
46
+ ### Patch Changes
47
+
48
+ - 618703f: Release 3.4.2
49
+ - Updated dependencies [618703f]
50
+ - Updated dependencies [e20ad6b]
51
+ - @prosopo/user-access-policy@3.5.16
52
+ - @prosopo/types-database@3.3.2
53
+ - @prosopo/redis-client@1.0.2
54
+ - @prosopo/common@3.1.17
55
+ - @prosopo/locale@3.1.17
56
+ - @prosopo/types@3.5.0
57
+ - @prosopo/config@3.1.17
58
+
59
+ ## 3.4.1
60
+ ### Patch Changes
61
+
62
+ - 11303d9: feat/pluggable-redis
63
+ - 11303d9: Release 3.4.0
64
+ - 18cb28b: Release 3.4.1
65
+ - 11303d9: feat/pluggable-redis
66
+ - Updated dependencies [11303d9]
67
+ - Updated dependencies [b6794f8]
68
+ - Updated dependencies [11303d9]
69
+ - Updated dependencies [bac2d91]
70
+ - Updated dependencies [18cb28b]
71
+ - Updated dependencies [11303d9]
72
+ - @prosopo/user-access-policy@3.5.15
73
+ - @prosopo/redis-client@1.0.1
74
+ - @prosopo/types-database@3.3.1
75
+ - @prosopo/common@3.1.16
76
+ - @prosopo/locale@3.1.16
77
+ - @prosopo/types@3.4.1
78
+ - @prosopo/config@3.1.16
79
+
80
+ ## 3.4.0
81
+ ### Minor Changes
82
+
83
+ - 6768f14: Update salt
84
+
85
+ ### Patch Changes
86
+
87
+ - f3f7aec: Release 3.4.0
88
+ - Updated dependencies [f3f7aec]
89
+ - Updated dependencies [6768f14]
90
+ - @prosopo/user-access-policy@3.5.14
91
+ - @prosopo/types-database@3.3.0
92
+ - @prosopo/common@3.1.15
93
+ - @prosopo/locale@3.1.15
94
+ - @prosopo/types@3.4.0
95
+ - @prosopo/config@3.1.15
96
+
97
+ ## 3.3.2
98
+ ### Patch Changes
99
+
100
+ - Release 3.3.1
101
+ - 0824221: Release 3.2.4
102
+ - Updated dependencies [97edf3f]
103
+ - Updated dependencies
104
+ - Updated dependencies [0824221]
105
+ - @prosopo/types@3.3.0
106
+ - @prosopo/user-access-policy@3.5.13
107
+ - @prosopo/types-database@3.2.2
108
+ - @prosopo/common@3.1.14
109
+ - @prosopo/locale@3.1.14
110
+ - @prosopo/config@3.1.14
111
+
112
+ ## 3.3.1
113
+ ### Patch Changes
114
+
115
+ - 5137f01: Update pow record at verify
116
+ - 008d112: Release 3.3.0
117
+ - Updated dependencies [5137f01]
118
+ - Updated dependencies [0555cd8]
119
+ - Updated dependencies [509be28]
120
+ - Updated dependencies [008d112]
121
+ - @prosopo/types-database@3.2.1
122
+ - @prosopo/types@3.2.1
123
+ - @prosopo/user-access-policy@3.5.12
124
+ - @prosopo/common@3.1.13
125
+ - @prosopo/locale@3.1.13
126
+ - @prosopo/config@3.1.13
127
+
128
+ ## 3.3.0
129
+ ### Minor Changes
130
+
131
+ - cf48565: Store additional details. Remove duplicate indexes.
132
+ - 260de39: Fix indexes so that stuff properly expires
133
+
134
+ ### Patch Changes
135
+
136
+ - 0824221: Release 3.2.4
137
+ - Updated dependencies [cf48565]
138
+ - Updated dependencies [d644c04]
139
+ - Updated dependencies [0824221]
140
+ - Updated dependencies [260de39]
141
+ - @prosopo/types-database@3.2.0
142
+ - @prosopo/types@3.2.0
143
+ - @prosopo/user-access-policy@3.5.11
144
+ - @prosopo/common@3.1.12
145
+ - @prosopo/locale@3.1.12
146
+ - @prosopo/config@3.1.12
147
+
148
+ ## 3.2.4
149
+ ### Patch Changes
150
+
151
+ - 1a23649: Release 3.2.3
152
+ - Updated dependencies [0d1a33e]
153
+ - Updated dependencies [0d1a33e]
154
+ - Updated dependencies [1a23649]
155
+ - @prosopo/types-database@3.1.5
156
+ - @prosopo/locale@3.1.11
157
+ - @prosopo/types@3.1.4
158
+ - @prosopo/user-access-policy@3.5.10
159
+ - @prosopo/common@3.1.11
160
+ - @prosopo/config@3.1.11
161
+
162
+ ## 3.2.3
163
+ ### Patch Changes
164
+
165
+ - 657a827: Release 3.2.2
166
+ - Updated dependencies [a8a9251]
167
+ - Updated dependencies [657a827]
168
+ - @prosopo/types-database@3.1.4
169
+ - @prosopo/user-access-policy@3.5.9
170
+ - @prosopo/common@3.1.10
171
+ - @prosopo/locale@3.1.10
172
+ - @prosopo/types@3.1.3
173
+ - @prosopo/config@3.1.10
174
+
175
+ ## 3.2.2
176
+ ### Patch Changes
177
+
178
+ - 4440947: fix type-only tsc compilation
179
+ - 7bdaca6: Release 3.2.1
180
+ - 1249ce0: Be more lenient with random provider selection
181
+ - Updated dependencies [4440947]
182
+ - Updated dependencies [7bdaca6]
183
+ - Updated dependencies [809b984]
184
+ - Updated dependencies [1249ce0]
185
+ - Updated dependencies [809b984]
186
+ - @prosopo/user-access-policy@3.5.8
187
+ - @prosopo/types-database@3.1.3
188
+ - @prosopo/common@3.1.9
189
+ - @prosopo/locale@3.1.9
190
+ - @prosopo/types@3.1.2
191
+ - @prosopo/config@3.1.9
192
+
193
+ ## 3.2.1
194
+ ### Patch Changes
195
+
196
+ - 6fe8570: Release 3.2.0
197
+ - Updated dependencies [1f980c4]
198
+ - Updated dependencies [6fe8570]
199
+ - @prosopo/types@3.1.1
200
+ - @prosopo/user-access-policy@3.5.7
201
+ - @prosopo/types-database@3.1.2
202
+ - @prosopo/common@3.1.8
203
+ - @prosopo/locale@3.1.8
204
+ - @prosopo/config@3.1.8
205
+
206
+ ## 3.2.0
207
+ ### Minor Changes
208
+
209
+ - 8bdc7f0: Using detector to select provider
210
+
211
+ ### Patch Changes
212
+
213
+ - f304be9: Release 3.1.13
214
+ - Updated dependencies [f304be9]
215
+ - Updated dependencies [8bdc7f0]
216
+ - @prosopo/user-access-policy@3.5.6
217
+ - @prosopo/types-database@3.1.1
218
+ - @prosopo/common@3.1.7
219
+ - @prosopo/locale@3.1.7
220
+ - @prosopo/types@3.1.0
221
+ - @prosopo/config@3.1.7
222
+
223
+ ## 3.1.0
224
+ ### Minor Changes
225
+
226
+ - 9b92339: fix/ipv6-in-captcha-flow
227
+
228
+ ### Patch Changes
229
+
230
+ - a07db04: Release 3.1.12
231
+ - Updated dependencies [9b92339]
232
+ - Updated dependencies [9eed772]
233
+ - Updated dependencies [a07db04]
234
+ - @prosopo/types-database@3.1.0
235
+ - @prosopo/config@3.1.6
236
+ - @prosopo/user-access-policy@3.5.5
237
+ - @prosopo/common@3.1.6
238
+ - @prosopo/locale@3.1.6
239
+ - @prosopo/types@3.0.10
240
+
241
+ ## 3.0.19
242
+ ### Patch Changes
243
+
244
+ - e64160c: The way mongo memory server works has changed
245
+ - Updated dependencies [553025d]
246
+ - @prosopo/user-access-policy@3.5.4
247
+ - @prosopo/types-database@3.0.19
248
+
249
+ ## 3.0.18
250
+ ### Patch Changes
251
+
252
+ - 6960643: lint detect missing and unneccessary imports
253
+ - Updated dependencies [d8e855c]
254
+ - Updated dependencies [6960643]
255
+ - @prosopo/types-database@3.0.18
256
+ - @prosopo/locale@3.1.5
257
+ - @prosopo/user-access-policy@3.5.3
258
+ - @prosopo/common@3.1.5
259
+ - @prosopo/types@3.0.9
260
+
261
+ ## 3.0.17
262
+ ### Patch Changes
263
+
264
+ - Updated dependencies [30e7d4d]
265
+ - @prosopo/config@3.1.5
266
+ - @prosopo/common@3.1.4
267
+ - @prosopo/types@3.0.8
268
+ - @prosopo/types-database@3.0.17
269
+ - @prosopo/user-access-policy@3.5.2
270
+
271
+ ## 3.0.16
272
+ ### Patch Changes
273
+
274
+ - 1f3a02f: Release 3.1.8
275
+ - Updated dependencies [1f3a02f]
276
+ - @prosopo/user-access-policy@3.5.1
277
+ - @prosopo/types-database@3.0.16
278
+
279
+ ## 3.0.15
280
+ ### Patch Changes
281
+
282
+ - Updated dependencies [e0628d9]
283
+ - @prosopo/user-access-policy@3.5.0
284
+ - @prosopo/types-database@3.0.15
285
+
286
+ ## 3.0.14
287
+ ### Patch Changes
288
+
289
+ - Updated dependencies [44ffda2]
290
+ - Updated dependencies [a49b538]
291
+ - Updated dependencies [e090e2f]
292
+ - @prosopo/config@3.1.4
293
+ - @prosopo/user-access-policy@3.4.1
294
+ - @prosopo/common@3.1.3
295
+ - @prosopo/types@3.0.7
296
+ - @prosopo/types-database@3.0.14
297
+
3
298
  ## 3.0.13
4
299
  ### Patch Changes
5
300
 
@@ -35,20 +35,20 @@ class MongoDatabase {
35
35
  * @description Connect to the database and set the various tables
36
36
  */
37
37
  async connect() {
38
- this.logger.info(() => ({
38
+ this.logger.debug(() => ({
39
39
  data: { mongoUrl: this.safeURL },
40
40
  msg: "Connecting to database"
41
41
  }));
42
42
  try {
43
43
  if (this.connected) {
44
- this.logger.info(() => ({
44
+ this.logger.debug(() => ({
45
45
  data: { mongoUrl: this.safeURL },
46
46
  msg: "Database connection already open"
47
47
  }));
48
48
  return;
49
49
  }
50
50
  if (this.connecting) {
51
- this.logger.info(() => ({
51
+ this.logger.debug(() => ({
52
52
  data: { mongoUrl: this.safeURL },
53
53
  msg: "Database connection in progress, waiting for it to finish"
54
54
  }));
@@ -60,7 +60,7 @@ class MongoDatabase {
60
60
  serverApi: ServerApiVersion.v1
61
61
  });
62
62
  const onConnected = () => {
63
- this.logger.info(() => ({
63
+ this.logger.debug(() => ({
64
64
  data: { mongoUrl: this.safeURL },
65
65
  msg: "Database connection opened"
66
66
  }));
@@ -83,28 +83,28 @@ class MongoDatabase {
83
83
  connection.once("error", onError);
84
84
  connection.on("disconnected", () => {
85
85
  this.connected = false;
86
- this.logger.info(() => ({
86
+ this.logger.debug(() => ({
87
87
  data: { mongoUrl: this.safeURL },
88
88
  msg: "Database disconnected"
89
89
  }));
90
90
  });
91
91
  connection.on("reconnected", () => {
92
92
  this.connected = true;
93
- this.logger.info(() => ({
93
+ this.logger.debug(() => ({
94
94
  data: { mongoUrl: this.safeURL },
95
95
  msg: "Database reconnected"
96
96
  }));
97
97
  });
98
98
  connection.on("close", () => {
99
99
  this.connected = false;
100
- this.logger.info(() => ({
100
+ this.logger.debug(() => ({
101
101
  data: { mongoUrl: this.safeURL },
102
102
  msg: "Database connection closed"
103
103
  }));
104
104
  });
105
105
  connection.on("fullsetup", () => {
106
106
  this.connected = true;
107
- this.logger.info(() => ({
107
+ this.logger.debug(() => ({
108
108
  data: { mongoUrl: this.safeURL },
109
109
  msg: "Database connection is fully setup"
110
110
  }));
@@ -1,26 +1,21 @@
1
1
  import { MongoMemoryServer } from "mongodb-memory-server";
2
2
  import { MongoDatabase } from "./mongo.js";
3
3
  class MongoMemoryDatabase extends MongoDatabase {
4
- constructor(url, dbname, logger, authSource) {
5
- const mongod = new MongoMemoryServer();
6
- const mongoMemoryURL = mongod.getUri();
7
- super(mongoMemoryURL, dbname, authSource, logger);
8
- this.running = false;
9
- this.mongod = mongod;
10
- this._url = mongoMemoryURL;
4
+ constructor(_url, dbname, logger, authSource) {
5
+ super("", dbname, authSource, logger);
6
+ this._url = "";
11
7
  }
12
- connect() {
13
- if (!this.running) {
14
- this.mongod?.start();
15
- this.running = true;
16
- } else {
8
+ async connect() {
9
+ if (!this.mongod) {
10
+ this.mongod = await MongoMemoryServer.create();
11
+ this._url = this.mongod.getUri();
17
12
  }
18
- return super.connect();
13
+ await super.connect();
19
14
  }
20
15
  async close() {
21
16
  await super.close();
22
17
  await this.mongod?.stop();
23
- this.running = false;
18
+ this.mongod = void 0;
24
19
  }
25
20
  }
26
21
  export {
@@ -37,20 +37,20 @@ class MongoDatabase {
37
37
  * @description Connect to the database and set the various tables
38
38
  */
39
39
  async connect() {
40
- this.logger.info(() => ({
40
+ this.logger.debug(() => ({
41
41
  data: { mongoUrl: this.safeURL },
42
42
  msg: "Connecting to database"
43
43
  }));
44
44
  try {
45
45
  if (this.connected) {
46
- this.logger.info(() => ({
46
+ this.logger.debug(() => ({
47
47
  data: { mongoUrl: this.safeURL },
48
48
  msg: "Database connection already open"
49
49
  }));
50
50
  return;
51
51
  }
52
52
  if (this.connecting) {
53
- this.logger.info(() => ({
53
+ this.logger.debug(() => ({
54
54
  data: { mongoUrl: this.safeURL },
55
55
  msg: "Database connection in progress, waiting for it to finish"
56
56
  }));
@@ -62,7 +62,7 @@ class MongoDatabase {
62
62
  serverApi: mongodb.ServerApiVersion.v1
63
63
  });
64
64
  const onConnected = () => {
65
- this.logger.info(() => ({
65
+ this.logger.debug(() => ({
66
66
  data: { mongoUrl: this.safeURL },
67
67
  msg: "Database connection opened"
68
68
  }));
@@ -85,28 +85,28 @@ class MongoDatabase {
85
85
  connection.once("error", onError);
86
86
  connection.on("disconnected", () => {
87
87
  this.connected = false;
88
- this.logger.info(() => ({
88
+ this.logger.debug(() => ({
89
89
  data: { mongoUrl: this.safeURL },
90
90
  msg: "Database disconnected"
91
91
  }));
92
92
  });
93
93
  connection.on("reconnected", () => {
94
94
  this.connected = true;
95
- this.logger.info(() => ({
95
+ this.logger.debug(() => ({
96
96
  data: { mongoUrl: this.safeURL },
97
97
  msg: "Database reconnected"
98
98
  }));
99
99
  });
100
100
  connection.on("close", () => {
101
101
  this.connected = false;
102
- this.logger.info(() => ({
102
+ this.logger.debug(() => ({
103
103
  data: { mongoUrl: this.safeURL },
104
104
  msg: "Database connection closed"
105
105
  }));
106
106
  });
107
107
  connection.on("fullsetup", () => {
108
108
  this.connected = true;
109
- this.logger.info(() => ({
109
+ this.logger.debug(() => ({
110
110
  data: { mongoUrl: this.safeURL },
111
111
  msg: "Database connection is fully setup"
112
112
  }));
@@ -3,26 +3,21 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const mongodbMemoryServer = require("mongodb-memory-server");
4
4
  const mongo = require("./mongo.cjs");
5
5
  class MongoMemoryDatabase extends mongo.MongoDatabase {
6
- constructor(url, dbname, logger, authSource) {
7
- const mongod = new mongodbMemoryServer.MongoMemoryServer();
8
- const mongoMemoryURL = mongod.getUri();
9
- super(mongoMemoryURL, dbname, authSource, logger);
10
- this.running = false;
11
- this.mongod = mongod;
12
- this._url = mongoMemoryURL;
6
+ constructor(_url, dbname, logger, authSource) {
7
+ super("", dbname, authSource, logger);
8
+ this._url = "";
13
9
  }
14
- connect() {
15
- if (!this.running) {
16
- this.mongod?.start();
17
- this.running = true;
18
- } else {
10
+ async connect() {
11
+ if (!this.mongod) {
12
+ this.mongod = await mongodbMemoryServer.MongoMemoryServer.create();
13
+ this._url = this.mongod.getUri();
19
14
  }
20
- return super.connect();
15
+ await super.connect();
21
16
  }
22
17
  async close() {
23
18
  await super.close();
24
19
  await this.mongod?.stop();
25
- this.running = false;
20
+ this.mongod = void 0;
26
21
  }
27
22
  }
28
23
  exports.MongoMemoryDatabase = MongoMemoryDatabase;
@@ -32,6 +32,7 @@ const CAPTCHA_TABLES = [
32
32
  class CaptchaDatabase extends mongo.MongoDatabase {
33
33
  constructor(url, dbname, authSource, logger2) {
34
34
  super(url, dbname, authSource, logger2);
35
+ this.indexesEnsured = false;
35
36
  this.tables = {};
36
37
  }
37
38
  async connect() {
@@ -51,6 +52,37 @@ class CaptchaDatabase extends mongo.MongoDatabase {
51
52
  }
52
53
  return this.tables;
53
54
  }
55
+ async ensureIndexes() {
56
+ const indexPromises = [];
57
+ if (!this.indexesEnsured) {
58
+ CAPTCHA_TABLES.map(({ collectionName }) => {
59
+ indexPromises.push(
60
+ new Promise((resolve) => {
61
+ if (this.connected) {
62
+ this.tables[collectionName].collection.dropIndexes().then(() => {
63
+ this.tables[collectionName].ensureIndexes().then(() => {
64
+ resolve();
65
+ }).catch((err) => {
66
+ this.logger.warn(() => ({
67
+ err,
68
+ msg: `Error creating indexes for collection ${collectionName}`
69
+ }));
70
+ resolve();
71
+ });
72
+ });
73
+ } else {
74
+ this.logger.info(() => ({
75
+ msg: `Skipping index creation for collection ${collectionName} as not connected`
76
+ }));
77
+ resolve();
78
+ }
79
+ })
80
+ );
81
+ });
82
+ }
83
+ await Promise.all(indexPromises);
84
+ this.indexesEnsured = true;
85
+ }
54
86
  async saveCaptchas(sessionEvents, imageCaptchaEvents, powCaptchaEvents) {
55
87
  await this.connect();
56
88
  if (sessionEvents.length) {
@@ -2,10 +2,10 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const is = require("@polkadot/util/is");
4
4
  const common = require("@prosopo/common");
5
+ const redisClient = require("@prosopo/redis-client");
5
6
  const types = require("@prosopo/types");
6
7
  const typesDatabase = require("@prosopo/types-database");
7
8
  const userAccessPolicy = require("@prosopo/user-access-policy");
8
- const redis = require("redis");
9
9
  const mongo = require("../base/mongo.cjs");
10
10
  var TableNames = /* @__PURE__ */ ((TableNames2) => {
11
11
  TableNames2["captcha"] = "captcha";
@@ -94,7 +94,10 @@ class ProviderDatabase extends mongo.MongoDatabase {
94
94
  );
95
95
  this.options = options;
96
96
  this.tables = {};
97
+ this.indexesEnsured = false;
97
98
  this.tables = {};
99
+ this.redisAccessRulesConnection = null;
100
+ this.redisConnection = null;
98
101
  this.userAccessRulesStorage = null;
99
102
  }
100
103
  async connect() {
@@ -103,27 +106,24 @@ class ProviderDatabase extends mongo.MongoDatabase {
103
106
  await this.setupRedis();
104
107
  }
105
108
  async setupRedis() {
106
- const redisClient = await this.createRedisClient();
107
- await userAccessPolicy.createRedisAccessRulesIndex(
108
- redisClient,
109
- this.options.redis?.indexName
109
+ this.redisConnection = redisClient.connectToRedis({
110
+ url: this.options.redis?.url,
111
+ password: this.options.redis?.password,
112
+ logger: this.logger
113
+ });
114
+ this.redisAccessRulesConnection = redisClient.setupRedisIndex(
115
+ this.redisConnection,
116
+ {
117
+ ...userAccessPolicy.redisAccessRulesIndex,
118
+ name: this.options.redis?.indexName || userAccessPolicy.redisAccessRulesIndex.name
119
+ },
120
+ this.logger
110
121
  );
111
122
  this.userAccessRulesStorage = userAccessPolicy.createRedisAccessRulesStorage(
112
- redisClient,
123
+ this.redisAccessRulesConnection,
113
124
  this.logger
114
125
  );
115
126
  }
116
- async createRedisClient() {
117
- return await redis.createClient({
118
- url: this.options.redis?.url,
119
- password: this.options.redis?.password
120
- }).on("error", (error) => {
121
- this.logger.error(() => ({
122
- err: error,
123
- msg: "Redis client error"
124
- }));
125
- }).connect();
126
- }
127
127
  loadTables() {
128
128
  const tables = {};
129
129
  PROVIDER_TABLES.map(({ collectionName, modelName, schema }) => {
@@ -142,6 +142,56 @@ class ProviderDatabase extends mongo.MongoDatabase {
142
142
  }
143
143
  return this.tables;
144
144
  }
145
+ getRedisAccessRulesConnection() {
146
+ if (null === this.redisAccessRulesConnection) {
147
+ throw new common.ProsopoDBError(
148
+ "DATABASE.REDIS_ACCESS_RULES_CONNECTION_UNDEFINED"
149
+ );
150
+ }
151
+ return this.redisAccessRulesConnection;
152
+ }
153
+ getRedisConnection() {
154
+ if (null === this.redisConnection) {
155
+ throw new common.ProsopoDBError(
156
+ "DATABASE.REDIS_ACCESS_RULES_CONNECTION_UNDEFINED"
157
+ );
158
+ }
159
+ return this.redisConnection;
160
+ }
161
+ async ensureIndexes() {
162
+ const indexPromises = [];
163
+ if (!this.indexesEnsured) {
164
+ PROVIDER_TABLES.map(({ collectionName }) => {
165
+ indexPromises.push(
166
+ new Promise((resolve) => {
167
+ if (this.connected) {
168
+ this.tables[collectionName].collection.dropIndexes().then(() => {
169
+ this.tables[collectionName].ensureIndexes().then(() => {
170
+ this.logger.info(() => ({
171
+ msg: `Indexes ensured for collection ${collectionName}`
172
+ }));
173
+ resolve();
174
+ }).catch((err) => {
175
+ this.logger.warn(() => ({
176
+ err,
177
+ msg: `Error creating indexes for collection ${collectionName}`
178
+ }));
179
+ resolve();
180
+ });
181
+ });
182
+ } else {
183
+ this.logger.info(() => ({
184
+ msg: `Skipping index creation for collection ${collectionName} as not connected`
185
+ }));
186
+ resolve();
187
+ }
188
+ })
189
+ );
190
+ });
191
+ }
192
+ await Promise.all(indexPromises);
193
+ this.indexesEnsured = true;
194
+ }
145
195
  getUserAccessRulesStorage() {
146
196
  if (null === this.userAccessRulesStorage) {
147
197
  throw new common.ProsopoDBError("DATABASE.USER_ACCESS_RULES_STORAGE_UNDEFINED");
@@ -539,7 +589,7 @@ class ProviderDatabase extends mongo.MongoDatabase {
539
589
  * @param userSignature
540
590
  * @returns {Promise<void>} A promise that resolves when the record is updated.
541
591
  */
542
- async updatePowCaptchaRecord(challenge, result, serverChecked = false, userSubmitted = false, userSignature) {
592
+ async updatePowCaptchaRecordResult(challenge, result, serverChecked = false, userSubmitted = false, userSignature) {
543
593
  const tables = this.getTables();
544
594
  const timestamp = Date.now();
545
595
  const update = {
@@ -593,6 +643,14 @@ class ProviderDatabase extends mongo.MongoDatabase {
593
643
  throw err;
594
644
  }
595
645
  }
646
+ async updatePowCaptchaRecord(challenge, updates) {
647
+ const tables = this.getTables();
648
+ await tables.powcaptcha.updateOne(
649
+ { challenge },
650
+ { $set: updates },
651
+ { upsert: false }
652
+ );
653
+ }
596
654
  /** @description Get serverChecked Dapp User image captcha commitments from the commitments table
597
655
  */
598
656
  async getCheckedDappUserCommitments() {
@@ -655,6 +713,12 @@ class ProviderDatabase extends mongo.MongoDatabase {
655
713
  { upsert: false }
656
714
  );
657
715
  }
716
+ /** @description Update an image captcha commitment
717
+ */
718
+ async updateDappUserCommitment(commitmentId, updates) {
719
+ const filter = { id: commitmentId };
720
+ await this.tables?.commitment.updateOne(filter, updates);
721
+ }
658
722
  /**
659
723
  * @description Get Dapp User PoW captcha commitments that have not been counted towards the client's total
660
724
  * @param {number} limit Maximum number of records to return
@@ -755,7 +819,7 @@ class ProviderDatabase extends mongo.MongoDatabase {
755
819
  const filter = {
756
820
  _id: { $in: tokenId }
757
821
  };
758
- return this.tables.frictionlessToken.find(filter);
822
+ return this.tables.frictionlessToken.find(filter).lean();
759
823
  }
760
824
  /**
761
825
  * Check if a frictionless token record exists.
@@ -856,7 +920,7 @@ class ProviderDatabase extends mongo.MongoDatabase {
856
920
  /** Mark a list of session records as stored */
857
921
  async markSessionRecordsStored(sessionIds) {
858
922
  const updateDoc = {
859
- storedAtTimestamp: Date.now()
923
+ storedAtTimestamp: /* @__PURE__ */ new Date()
860
924
  };
861
925
  await this.tables?.session.updateMany(
862
926
  { sessionId: { $in: sessionIds } },
@@ -864,6 +928,17 @@ class ProviderDatabase extends mongo.MongoDatabase {
864
928
  { upsert: false }
865
929
  );
866
930
  }
931
+ /** Mark a list of token records as stored */
932
+ async markFrictionlessTokenRecordsStored(tokenIds) {
933
+ const updateDoc = {
934
+ storedAtTimestamp: /* @__PURE__ */ new Date()
935
+ };
936
+ await this.tables?.frictionlessToken.updateMany(
937
+ { _id: { $in: tokenIds } },
938
+ { $set: updateDoc },
939
+ { upsert: false }
940
+ );
941
+ }
867
942
  /**
868
943
  * @description Store a Dapp User's pending record
869
944
  */
@@ -1080,13 +1155,15 @@ class ProviderDatabase extends mongo.MongoDatabase {
1080
1155
  /**
1081
1156
  * @description Approve a dapp user's solution
1082
1157
  * @param {string[]} commitmentId
1158
+ * @param coords
1083
1159
  */
1084
- async approveDappUserCommitment(commitmentId) {
1160
+ async approveDappUserCommitment(commitmentId, coords) {
1085
1161
  try {
1086
1162
  const result = { status: types.CaptchaStatus.approved };
1087
1163
  const updateDoc = {
1088
1164
  result,
1089
- lastUpdatedTimestamp: Date.now()
1165
+ lastUpdatedTimestamp: Date.now(),
1166
+ ...coords ? { coords } : {}
1090
1167
  };
1091
1168
  const filter = { id: commitmentId };
1092
1169
  await this.tables?.commitment?.findOneAndUpdate(filter, { $set: updateDoc }, { upsert: false }).lean();
@@ -1099,13 +1176,15 @@ class ProviderDatabase extends mongo.MongoDatabase {
1099
1176
  /**
1100
1177
  * @description Disapprove a dapp user's solution
1101
1178
  * @param {string} commitmentId
1179
+ * @param coords
1102
1180
  * @param reason
1103
1181
  */
1104
- async disapproveDappUserCommitment(commitmentId, reason) {
1182
+ async disapproveDappUserCommitment(commitmentId, reason, coords) {
1105
1183
  try {
1106
1184
  const updateDoc = {
1107
1185
  result: { status: types.CaptchaStatus.disapproved, reason },
1108
- lastUpdatedTimestamp: Date.now()
1186
+ lastUpdatedTimestamp: Date.now(),
1187
+ ...coords ? { coords } : {}
1109
1188
  };
1110
1189
  const filter = { id: commitmentId };
1111
1190
  await this.tables?.commitment?.findOneAndUpdate(filter, { $set: updateDoc }, { upsert: false }).lean();
@@ -1257,10 +1336,16 @@ class ProviderDatabase extends mongo.MongoDatabase {
1257
1336
  createdAt: /* @__PURE__ */ new Date()
1258
1337
  });
1259
1338
  }
1260
- /** @description Remove a detector key */
1261
- async removeDetectorKey(detectorKey) {
1339
+ /**
1340
+ * @description Remove a detector key
1341
+ * @param detectorKey The detector key to remove
1342
+ * @param expirationInSeconds Optional expiration time in seconds (default is 10 minutes)
1343
+ * */
1344
+ async removeDetectorKey(detectorKey, expirationInSeconds) {
1262
1345
  const filter = { detectorKey };
1263
- const expiresAt = new Date(Date.now() + 10 * 60 * 1e3);
1346
+ const expiresAt = new Date(
1347
+ Date.now() + (expirationInSeconds || 10 * 60) * 1e3
1348
+ );
1264
1349
  await this.tables?.detector.updateOne(filter, {
1265
1350
  $set: { expiresAt }
1266
1351
  });
@@ -30,6 +30,7 @@ const CAPTCHA_TABLES = [
30
30
  class CaptchaDatabase extends MongoDatabase {
31
31
  constructor(url, dbname, authSource, logger2) {
32
32
  super(url, dbname, authSource, logger2);
33
+ this.indexesEnsured = false;
33
34
  this.tables = {};
34
35
  }
35
36
  async connect() {
@@ -49,6 +50,37 @@ class CaptchaDatabase extends MongoDatabase {
49
50
  }
50
51
  return this.tables;
51
52
  }
53
+ async ensureIndexes() {
54
+ const indexPromises = [];
55
+ if (!this.indexesEnsured) {
56
+ CAPTCHA_TABLES.map(({ collectionName }) => {
57
+ indexPromises.push(
58
+ new Promise((resolve) => {
59
+ if (this.connected) {
60
+ this.tables[collectionName].collection.dropIndexes().then(() => {
61
+ this.tables[collectionName].ensureIndexes().then(() => {
62
+ resolve();
63
+ }).catch((err) => {
64
+ this.logger.warn(() => ({
65
+ err,
66
+ msg: `Error creating indexes for collection ${collectionName}`
67
+ }));
68
+ resolve();
69
+ });
70
+ });
71
+ } else {
72
+ this.logger.info(() => ({
73
+ msg: `Skipping index creation for collection ${collectionName} as not connected`
74
+ }));
75
+ resolve();
76
+ }
77
+ })
78
+ );
79
+ });
80
+ }
81
+ await Promise.all(indexPromises);
82
+ this.indexesEnsured = true;
83
+ }
52
84
  async saveCaptchas(sessionEvents, imageCaptchaEvents, powCaptchaEvents) {
53
85
  await this.connect();
54
86
  if (sessionEvents.length) {
@@ -1,9 +1,9 @@
1
1
  import { isHex } from "@polkadot/util/is";
2
2
  import { ProsopoDBError } from "@prosopo/common";
3
+ import { connectToRedis, setupRedisIndex } from "@prosopo/redis-client";
3
4
  import { DatasetWithIdsAndTreeSchema, StoredStatusNames, CaptchaStatus, ApiParams, CaptchaStates } from "@prosopo/types";
4
5
  import { CaptchaRecordSchema, PoWCaptchaRecordSchema, DatasetRecordSchema, SolutionRecordSchema, UserCommitmentRecordSchema, UserSolutionRecordSchema, PendingRecordSchema, ScheduledTaskRecordSchema, ClientRecordSchema, FrictionlessTokenRecordSchema, SessionRecordSchema, DetectorRecordSchema, UserCommitmentSchema, ScheduledTaskSchema } from "@prosopo/types-database";
5
- import { createRedisAccessRulesIndex, createRedisAccessRulesStorage } from "@prosopo/user-access-policy";
6
- import { createClient } from "redis";
6
+ import { redisAccessRulesIndex, createRedisAccessRulesStorage } from "@prosopo/user-access-policy";
7
7
  import { MongoDatabase } from "../base/mongo.js";
8
8
  var TableNames = /* @__PURE__ */ ((TableNames2) => {
9
9
  TableNames2["captcha"] = "captcha";
@@ -92,7 +92,10 @@ class ProviderDatabase extends MongoDatabase {
92
92
  );
93
93
  this.options = options;
94
94
  this.tables = {};
95
+ this.indexesEnsured = false;
95
96
  this.tables = {};
97
+ this.redisAccessRulesConnection = null;
98
+ this.redisConnection = null;
96
99
  this.userAccessRulesStorage = null;
97
100
  }
98
101
  async connect() {
@@ -101,27 +104,24 @@ class ProviderDatabase extends MongoDatabase {
101
104
  await this.setupRedis();
102
105
  }
103
106
  async setupRedis() {
104
- const redisClient = await this.createRedisClient();
105
- await createRedisAccessRulesIndex(
106
- redisClient,
107
- this.options.redis?.indexName
107
+ this.redisConnection = connectToRedis({
108
+ url: this.options.redis?.url,
109
+ password: this.options.redis?.password,
110
+ logger: this.logger
111
+ });
112
+ this.redisAccessRulesConnection = setupRedisIndex(
113
+ this.redisConnection,
114
+ {
115
+ ...redisAccessRulesIndex,
116
+ name: this.options.redis?.indexName || redisAccessRulesIndex.name
117
+ },
118
+ this.logger
108
119
  );
109
120
  this.userAccessRulesStorage = createRedisAccessRulesStorage(
110
- redisClient,
121
+ this.redisAccessRulesConnection,
111
122
  this.logger
112
123
  );
113
124
  }
114
- async createRedisClient() {
115
- return await createClient({
116
- url: this.options.redis?.url,
117
- password: this.options.redis?.password
118
- }).on("error", (error) => {
119
- this.logger.error(() => ({
120
- err: error,
121
- msg: "Redis client error"
122
- }));
123
- }).connect();
124
- }
125
125
  loadTables() {
126
126
  const tables = {};
127
127
  PROVIDER_TABLES.map(({ collectionName, modelName, schema }) => {
@@ -140,6 +140,56 @@ class ProviderDatabase extends MongoDatabase {
140
140
  }
141
141
  return this.tables;
142
142
  }
143
+ getRedisAccessRulesConnection() {
144
+ if (null === this.redisAccessRulesConnection) {
145
+ throw new ProsopoDBError(
146
+ "DATABASE.REDIS_ACCESS_RULES_CONNECTION_UNDEFINED"
147
+ );
148
+ }
149
+ return this.redisAccessRulesConnection;
150
+ }
151
+ getRedisConnection() {
152
+ if (null === this.redisConnection) {
153
+ throw new ProsopoDBError(
154
+ "DATABASE.REDIS_ACCESS_RULES_CONNECTION_UNDEFINED"
155
+ );
156
+ }
157
+ return this.redisConnection;
158
+ }
159
+ async ensureIndexes() {
160
+ const indexPromises = [];
161
+ if (!this.indexesEnsured) {
162
+ PROVIDER_TABLES.map(({ collectionName }) => {
163
+ indexPromises.push(
164
+ new Promise((resolve) => {
165
+ if (this.connected) {
166
+ this.tables[collectionName].collection.dropIndexes().then(() => {
167
+ this.tables[collectionName].ensureIndexes().then(() => {
168
+ this.logger.info(() => ({
169
+ msg: `Indexes ensured for collection ${collectionName}`
170
+ }));
171
+ resolve();
172
+ }).catch((err) => {
173
+ this.logger.warn(() => ({
174
+ err,
175
+ msg: `Error creating indexes for collection ${collectionName}`
176
+ }));
177
+ resolve();
178
+ });
179
+ });
180
+ } else {
181
+ this.logger.info(() => ({
182
+ msg: `Skipping index creation for collection ${collectionName} as not connected`
183
+ }));
184
+ resolve();
185
+ }
186
+ })
187
+ );
188
+ });
189
+ }
190
+ await Promise.all(indexPromises);
191
+ this.indexesEnsured = true;
192
+ }
143
193
  getUserAccessRulesStorage() {
144
194
  if (null === this.userAccessRulesStorage) {
145
195
  throw new ProsopoDBError("DATABASE.USER_ACCESS_RULES_STORAGE_UNDEFINED");
@@ -537,7 +587,7 @@ class ProviderDatabase extends MongoDatabase {
537
587
  * @param userSignature
538
588
  * @returns {Promise<void>} A promise that resolves when the record is updated.
539
589
  */
540
- async updatePowCaptchaRecord(challenge, result, serverChecked = false, userSubmitted = false, userSignature) {
590
+ async updatePowCaptchaRecordResult(challenge, result, serverChecked = false, userSubmitted = false, userSignature) {
541
591
  const tables = this.getTables();
542
592
  const timestamp = Date.now();
543
593
  const update = {
@@ -591,6 +641,14 @@ class ProviderDatabase extends MongoDatabase {
591
641
  throw err;
592
642
  }
593
643
  }
644
+ async updatePowCaptchaRecord(challenge, updates) {
645
+ const tables = this.getTables();
646
+ await tables.powcaptcha.updateOne(
647
+ { challenge },
648
+ { $set: updates },
649
+ { upsert: false }
650
+ );
651
+ }
594
652
  /** @description Get serverChecked Dapp User image captcha commitments from the commitments table
595
653
  */
596
654
  async getCheckedDappUserCommitments() {
@@ -653,6 +711,12 @@ class ProviderDatabase extends MongoDatabase {
653
711
  { upsert: false }
654
712
  );
655
713
  }
714
+ /** @description Update an image captcha commitment
715
+ */
716
+ async updateDappUserCommitment(commitmentId, updates) {
717
+ const filter = { id: commitmentId };
718
+ await this.tables?.commitment.updateOne(filter, updates);
719
+ }
656
720
  /**
657
721
  * @description Get Dapp User PoW captcha commitments that have not been counted towards the client's total
658
722
  * @param {number} limit Maximum number of records to return
@@ -753,7 +817,7 @@ class ProviderDatabase extends MongoDatabase {
753
817
  const filter = {
754
818
  _id: { $in: tokenId }
755
819
  };
756
- return this.tables.frictionlessToken.find(filter);
820
+ return this.tables.frictionlessToken.find(filter).lean();
757
821
  }
758
822
  /**
759
823
  * Check if a frictionless token record exists.
@@ -854,7 +918,7 @@ class ProviderDatabase extends MongoDatabase {
854
918
  /** Mark a list of session records as stored */
855
919
  async markSessionRecordsStored(sessionIds) {
856
920
  const updateDoc = {
857
- storedAtTimestamp: Date.now()
921
+ storedAtTimestamp: /* @__PURE__ */ new Date()
858
922
  };
859
923
  await this.tables?.session.updateMany(
860
924
  { sessionId: { $in: sessionIds } },
@@ -862,6 +926,17 @@ class ProviderDatabase extends MongoDatabase {
862
926
  { upsert: false }
863
927
  );
864
928
  }
929
+ /** Mark a list of token records as stored */
930
+ async markFrictionlessTokenRecordsStored(tokenIds) {
931
+ const updateDoc = {
932
+ storedAtTimestamp: /* @__PURE__ */ new Date()
933
+ };
934
+ await this.tables?.frictionlessToken.updateMany(
935
+ { _id: { $in: tokenIds } },
936
+ { $set: updateDoc },
937
+ { upsert: false }
938
+ );
939
+ }
865
940
  /**
866
941
  * @description Store a Dapp User's pending record
867
942
  */
@@ -1078,13 +1153,15 @@ class ProviderDatabase extends MongoDatabase {
1078
1153
  /**
1079
1154
  * @description Approve a dapp user's solution
1080
1155
  * @param {string[]} commitmentId
1156
+ * @param coords
1081
1157
  */
1082
- async approveDappUserCommitment(commitmentId) {
1158
+ async approveDappUserCommitment(commitmentId, coords) {
1083
1159
  try {
1084
1160
  const result = { status: CaptchaStatus.approved };
1085
1161
  const updateDoc = {
1086
1162
  result,
1087
- lastUpdatedTimestamp: Date.now()
1163
+ lastUpdatedTimestamp: Date.now(),
1164
+ ...coords ? { coords } : {}
1088
1165
  };
1089
1166
  const filter = { id: commitmentId };
1090
1167
  await this.tables?.commitment?.findOneAndUpdate(filter, { $set: updateDoc }, { upsert: false }).lean();
@@ -1097,13 +1174,15 @@ class ProviderDatabase extends MongoDatabase {
1097
1174
  /**
1098
1175
  * @description Disapprove a dapp user's solution
1099
1176
  * @param {string} commitmentId
1177
+ * @param coords
1100
1178
  * @param reason
1101
1179
  */
1102
- async disapproveDappUserCommitment(commitmentId, reason) {
1180
+ async disapproveDappUserCommitment(commitmentId, reason, coords) {
1103
1181
  try {
1104
1182
  const updateDoc = {
1105
1183
  result: { status: CaptchaStatus.disapproved, reason },
1106
- lastUpdatedTimestamp: Date.now()
1184
+ lastUpdatedTimestamp: Date.now(),
1185
+ ...coords ? { coords } : {}
1107
1186
  };
1108
1187
  const filter = { id: commitmentId };
1109
1188
  await this.tables?.commitment?.findOneAndUpdate(filter, { $set: updateDoc }, { upsert: false }).lean();
@@ -1255,10 +1334,16 @@ class ProviderDatabase extends MongoDatabase {
1255
1334
  createdAt: /* @__PURE__ */ new Date()
1256
1335
  });
1257
1336
  }
1258
- /** @description Remove a detector key */
1259
- async removeDetectorKey(detectorKey) {
1337
+ /**
1338
+ * @description Remove a detector key
1339
+ * @param detectorKey The detector key to remove
1340
+ * @param expirationInSeconds Optional expiration time in seconds (default is 10 minutes)
1341
+ * */
1342
+ async removeDetectorKey(detectorKey, expirationInSeconds) {
1260
1343
  const filter = { detectorKey };
1261
- const expiresAt = new Date(Date.now() + 10 * 60 * 1e3);
1344
+ const expiresAt = new Date(
1345
+ Date.now() + (expirationInSeconds || 10 * 60) * 1e3
1346
+ );
1262
1347
  await this.tables?.detector.updateOne(filter, {
1263
1348
  $set: { expiresAt }
1264
1349
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/database",
3
- "version": "3.0.13",
3
+ "version": "3.4.5",
4
4
  "description": "Prosopo database plugins for provider",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  "build": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.esm.config.ts --mode $NODE_ENV",
22
22
  "build:tsc": "tsc --build --verbose",
23
23
  "build:cjs": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV",
24
- "typecheck": "tsc --build --declaration --emitDeclarationOnly"
24
+ "typecheck": "tsc --project tsconfig.types.json"
25
25
  },
26
26
  "repository": {
27
27
  "type": "git",
@@ -34,23 +34,20 @@
34
34
  },
35
35
  "homepage": "https://github.com/prosopo/captcha#readme",
36
36
  "dependencies": {
37
- "@prosopo/common": "3.1.2",
38
- "@prosopo/config": "3.1.3",
39
- "@prosopo/types": "3.0.6",
40
- "@prosopo/types-database": "3.0.13",
41
- "@prosopo/user-access-policy": "3.4.0",
42
- "@typegoose/auto-increment": "4.13.0",
43
- "axios": "1.10.0",
44
- "esbuild": "0.25.6",
45
- "express": "4.21.2",
46
- "mongodb": "6.9.0",
37
+ "@polkadot/util": "12.6.2",
38
+ "@prosopo/common": "3.1.20",
39
+ "@prosopo/config": "3.1.20",
40
+ "@prosopo/locale": "3.1.20",
41
+ "@prosopo/types": "3.5.3",
42
+ "@prosopo/types-database": "3.3.5",
43
+ "@prosopo/user-access-policy": "3.5.19",
44
+ "mongodb": "6.15.0",
47
45
  "mongodb-memory-server": "10.0.0",
48
46
  "mongoose": "8.13.0",
49
- "openpgp": "5.11.3",
50
- "redis": "5.0.0",
51
- "webpack-dev-server": "5.2.2"
47
+ "@prosopo/redis-client": "1.0.5"
52
48
  },
53
49
  "devDependencies": {
50
+ "@types/node": "22.10.2",
54
51
  "@vitest/coverage-v8": "3.0.9",
55
52
  "concurrently": "9.0.1",
56
53
  "del-cli": "6.0.0",