@prosopo/database 2.1.1 → 2.1.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.
Files changed (40) hide show
  1. package/package.json +5 -5
  2. package/dist/base/index.d.ts +0 -3
  3. package/dist/base/index.d.ts.map +0 -1
  4. package/dist/base/index.js +0 -3
  5. package/dist/base/index.js.map +0 -1
  6. package/dist/base/mongo.d.ts +0 -16
  7. package/dist/base/mongo.d.ts.map +0 -1
  8. package/dist/base/mongo.js +0 -89
  9. package/dist/base/mongo.js.map +0 -1
  10. package/dist/base/mongoMemory.d.ts +0 -11
  11. package/dist/base/mongoMemory.d.ts.map +0 -1
  12. package/dist/base/mongoMemory.js +0 -27
  13. package/dist/base/mongoMemory.js.map +0 -1
  14. package/dist/cjs/base/index.cjs +0 -6
  15. package/dist/cjs/base/mongo.cjs +0 -97
  16. package/dist/cjs/base/mongoMemory.cjs +0 -28
  17. package/dist/cjs/databases/captcha.cjs +0 -82
  18. package/dist/cjs/databases/client.cjs +0 -46
  19. package/dist/cjs/databases/index.cjs +0 -19
  20. package/dist/cjs/databases/provider.cjs +0 -949
  21. package/dist/cjs/index.cjs +0 -15
  22. package/dist/databases/captcha.d.ts +0 -16
  23. package/dist/databases/captcha.d.ts.map +0 -1
  24. package/dist/databases/captcha.js +0 -75
  25. package/dist/databases/captcha.js.map +0 -1
  26. package/dist/databases/client.d.ts +0 -12
  27. package/dist/databases/client.d.ts.map +0 -1
  28. package/dist/databases/client.js +0 -42
  29. package/dist/databases/client.js.map +0 -1
  30. package/dist/databases/index.d.ts +0 -16
  31. package/dist/databases/index.d.ts.map +0 -1
  32. package/dist/databases/index.js +0 -17
  33. package/dist/databases/index.js.map +0 -1
  34. package/dist/databases/provider.d.ts +0 -70
  35. package/dist/databases/provider.d.ts.map +0 -1
  36. package/dist/databases/provider.js +0 -792
  37. package/dist/databases/provider.js.map +0 -1
  38. package/dist/index.d.ts +0 -3
  39. package/dist/index.d.ts.map +0 -1
  40. package/dist/index.js.map +0 -1
@@ -1,949 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const is = require("@polkadot/util/is");
4
- const common = require("@prosopo/common");
5
- const types = require("@prosopo/types");
6
- const typesDatabase = require("@prosopo/types-database");
7
- const mongo = require("../base/mongo.cjs");
8
- var TableNames = /* @__PURE__ */ ((TableNames2) => {
9
- TableNames2["captcha"] = "captcha";
10
- TableNames2["dataset"] = "dataset";
11
- TableNames2["solution"] = "solution";
12
- TableNames2["commitment"] = "commitment";
13
- TableNames2["usersolution"] = "usersolution";
14
- TableNames2["pending"] = "pending";
15
- TableNames2["scheduler"] = "scheduler";
16
- TableNames2["powcaptcha"] = "powcaptcha";
17
- TableNames2["client"] = "client";
18
- return TableNames2;
19
- })(TableNames || {});
20
- const PROVIDER_TABLES = [
21
- {
22
- collectionName: "captcha",
23
- modelName: "Captcha",
24
- schema: typesDatabase.CaptchaRecordSchema
25
- },
26
- {
27
- collectionName: "powcaptcha",
28
- modelName: "PowCaptcha",
29
- schema: typesDatabase.PoWCaptchaRecordSchema
30
- },
31
- {
32
- collectionName: "dataset",
33
- modelName: "Dataset",
34
- schema: typesDatabase.DatasetRecordSchema
35
- },
36
- {
37
- collectionName: "solution",
38
- modelName: "Solution",
39
- schema: typesDatabase.SolutionRecordSchema
40
- },
41
- {
42
- collectionName: "commitment",
43
- modelName: "UserCommitment",
44
- schema: typesDatabase.UserCommitmentRecordSchema
45
- },
46
- {
47
- collectionName: "usersolution",
48
- modelName: "UserSolution",
49
- schema: typesDatabase.UserSolutionRecordSchema
50
- },
51
- {
52
- collectionName: "pending",
53
- modelName: "Pending",
54
- schema: typesDatabase.PendingRecordSchema
55
- },
56
- {
57
- collectionName: "scheduler",
58
- modelName: "Scheduler",
59
- schema: typesDatabase.ScheduledTaskRecordSchema
60
- },
61
- {
62
- collectionName: "client",
63
- modelName: "Client",
64
- schema: typesDatabase.ClientRecordSchema
65
- }
66
- ];
67
- class ProviderDatabase extends mongo.MongoDatabase {
68
- constructor(url, dbname, authSource, logger) {
69
- super(url, dbname, authSource, logger);
70
- this.tables = {};
71
- this.tables = {};
72
- }
73
- async connect() {
74
- await super.connect();
75
- this.loadTables();
76
- }
77
- loadTables() {
78
- const tables = {};
79
- PROVIDER_TABLES.map(({ collectionName, modelName, schema }) => {
80
- if (this.connection) {
81
- tables[collectionName] = this.connection.model(modelName, schema);
82
- }
83
- });
84
- this.tables = tables;
85
- }
86
- getTables() {
87
- if (!this.tables) {
88
- throw new common.ProsopoDBError("DATABASE.TABLES_UNDEFINED", {
89
- context: { failedFuncName: this.getTables.name },
90
- logger: this.logger
91
- });
92
- }
93
- return this.tables;
94
- }
95
- /**
96
- * @description Load a dataset to the database
97
- * @param {Dataset} dataset
98
- */
99
- async storeDataset(dataset) {
100
- try {
101
- this.logger.debug("Storing dataset in database");
102
- const parsedDataset = types.DatasetWithIdsAndTreeSchema.parse(dataset);
103
- const datasetDoc = {
104
- datasetId: parsedDataset.datasetId,
105
- datasetContentId: parsedDataset.datasetContentId,
106
- format: parsedDataset.format,
107
- contentTree: parsedDataset.contentTree,
108
- solutionTree: parsedDataset.solutionTree
109
- };
110
- await this.tables.dataset?.updateOne(
111
- { datasetId: parsedDataset.datasetId },
112
- { $set: datasetDoc },
113
- { upsert: true }
114
- );
115
- const captchaDocs = parsedDataset.captchas.map(
116
- ({ solution, ...captcha }, index) => ({
117
- ...captcha,
118
- datasetId: parsedDataset.datasetId,
119
- datasetContentId: parsedDataset.datasetContentId,
120
- index,
121
- solved: !!solution?.length
122
- })
123
- );
124
- this.logger.debug("Inserting captcha records");
125
- if (captchaDocs.length) {
126
- await this.tables?.captcha.bulkWrite(
127
- captchaDocs.map((captchaDoc) => ({
128
- updateOne: {
129
- filter: { captchaId: captchaDoc.captchaId },
130
- update: { $set: captchaDoc },
131
- upsert: true
132
- }
133
- }))
134
- );
135
- }
136
- const captchaSolutionDocs = parsedDataset.captchas.filter(({ solution }) => solution?.length).map((captcha) => ({
137
- captchaId: captcha.captchaId,
138
- captchaContentId: captcha.captchaContentId,
139
- solution: captcha.solution,
140
- salt: captcha.salt,
141
- datasetId: parsedDataset.datasetId,
142
- datasetContentId: parsedDataset.datasetContentId
143
- }));
144
- this.logger.debug("Inserting solution records");
145
- if (captchaSolutionDocs.length) {
146
- await this.tables?.solution.bulkWrite(
147
- captchaSolutionDocs.map((captchaSolutionDoc) => ({
148
- updateOne: {
149
- filter: { captchaId: captchaSolutionDoc.captchaId },
150
- update: { $set: captchaSolutionDoc },
151
- upsert: true
152
- }
153
- }))
154
- );
155
- }
156
- this.logger.debug("Dataset stored in database");
157
- } catch (err) {
158
- throw new common.ProsopoDBError("DATABASE.DATASET_LOAD_FAILED", {
159
- context: { failedFuncName: this.storeDataset.name, error: err },
160
- logger: this.logger
161
- });
162
- }
163
- }
164
- /** @description Get solutions for a dataset
165
- * @param {string} datasetId
166
- */
167
- async getSolutions(datasetId) {
168
- const docs = await this.tables?.solution.find({ datasetId }).lean();
169
- return docs ? docs : [];
170
- }
171
- /** @description Get a dataset from the database
172
- * @param {string} datasetId
173
- */
174
- async getDataset(datasetId) {
175
- const datasetDoc = await this.tables?.dataset.findOne({ datasetId }).lean();
176
- if (datasetDoc) {
177
- const { datasetContentId, format, contentTree, solutionTree } = datasetDoc;
178
- const captchas = await this.tables?.captcha.find({ datasetId }).lean() || [];
179
- const solutions = await this.tables?.solution.find({ datasetId }).lean() || [];
180
- const solutionsKeyed = {};
181
- for (const solution of solutions) {
182
- solutionsKeyed[solution.captchaId] = solution;
183
- }
184
- return {
185
- datasetId,
186
- datasetContentId,
187
- format,
188
- contentTree: contentTree || [],
189
- solutionTree: solutionTree || [],
190
- captchas: captchas.map((captchaDoc) => {
191
- const { captchaId, captchaContentId, items, target, salt, solved } = captchaDoc;
192
- const solution = solutionsKeyed[captchaId];
193
- return {
194
- captchaId,
195
- captchaContentId,
196
- solved: !!solved,
197
- salt,
198
- items,
199
- target,
200
- solution: solved && solution ? solution.solution : []
201
- };
202
- })
203
- };
204
- }
205
- throw new common.ProsopoDBError("DATABASE.DATASET_GET_FAILED", {
206
- context: { failedFuncName: this.getDataset.name, datasetId }
207
- });
208
- }
209
- /**
210
- * @description Get random captchas that are solved or not solved
211
- * @param {boolean} solved `true` when captcha is solved
212
- * @param {string} datasetId the id of the data set
213
- * @param {number} size the number of records to be returned
214
- */
215
- async getRandomCaptcha(solved, datasetId, size) {
216
- if (!is.isHex(datasetId)) {
217
- throw new common.ProsopoDBError("DATABASE.INVALID_HASH", {
218
- context: { failedFuncName: this.getRandomCaptcha.name, datasetId }
219
- });
220
- }
221
- const sampleSize = size ? Math.abs(Math.trunc(size)) : 1;
222
- const cursor = this.tables?.captcha.aggregate([
223
- { $match: { datasetId, solved } },
224
- { $sample: { size: sampleSize } },
225
- {
226
- $project: {
227
- datasetId: 1,
228
- datasetContentId: 1,
229
- captchaId: 1,
230
- captchaContentId: 1,
231
- items: 1,
232
- target: 1
233
- }
234
- }
235
- ]);
236
- const docs = await cursor;
237
- if (docs?.length) {
238
- return docs.map(({ _id, ...keepAttrs }) => keepAttrs);
239
- }
240
- throw new common.ProsopoDBError("DATABASE.CAPTCHA_GET_FAILED", {
241
- context: {
242
- failedFuncName: this.getRandomCaptcha.name,
243
- solved,
244
- datasetId,
245
- size
246
- }
247
- });
248
- }
249
- /**
250
- * @description Get captchas by id
251
- * @param {string[]} captchaId
252
- */
253
- async getCaptchaById(captchaId) {
254
- const cursor = this.tables?.captcha.find({ captchaId: { $in: captchaId } }).lean();
255
- const docs = await cursor;
256
- if (docs?.length) {
257
- return docs.map(({ _id, ...keepAttrs }) => keepAttrs);
258
- }
259
- throw new common.ProsopoDBError("DATABASE.CAPTCHA_GET_FAILED", {
260
- context: { failedFuncName: this.getCaptchaById.name, captchaId }
261
- });
262
- }
263
- /**
264
- * @description Update a captcha
265
- * @param {Captcha} captcha
266
- * @param {string} datasetId the id of the data set
267
- */
268
- async updateCaptcha(captcha, datasetId) {
269
- if (!is.isHex(datasetId)) {
270
- throw new common.ProsopoDBError("DATABASE.INVALID_HASH", {
271
- context: { failedFuncName: this.updateCaptcha.name, datasetId }
272
- });
273
- }
274
- try {
275
- await this.tables?.captcha.updateOne(
276
- { datasetId },
277
- { $set: captcha },
278
- { upsert: false }
279
- );
280
- } catch (err) {
281
- throw new common.ProsopoDBError("DATABASE.CAPTCHA_UPDATE_FAILED", {
282
- context: { failedFuncName: this.getDatasetDetails.name, error: err }
283
- });
284
- }
285
- }
286
- /**
287
- * @description Remove captchas
288
- */
289
- async removeCaptchas(captchaIds) {
290
- await this.tables?.captcha.deleteMany({ captchaId: { $in: captchaIds } });
291
- }
292
- /**
293
- * @description Get a dataset by Id
294
- */
295
- async getDatasetDetails(datasetId) {
296
- if (!is.isHex(datasetId)) {
297
- throw new common.ProsopoDBError("DATABASE.INVALID_HASH", {
298
- context: { failedFuncName: this.getDatasetDetails.name, datasetId }
299
- });
300
- }
301
- const doc = await this.tables?.dataset.findOne({ datasetId }).lean();
302
- if (doc) {
303
- return doc;
304
- }
305
- throw new common.ProsopoDBError("DATABASE.DATASET_GET_FAILED", {
306
- context: {
307
- failedFuncName: this.getDatasetDetails.name,
308
- datasetId
309
- }
310
- });
311
- }
312
- /**
313
- * @description Store a Dapp User's captcha solution commitment
314
- */
315
- async storeDappUserSolution(captchas, commit) {
316
- const commitmentRecord = typesDatabase.UserCommitmentSchema.parse({
317
- ...commit,
318
- lastUpdatedTimestamp: Date.now()
319
- });
320
- if (captchas.length) {
321
- await this.tables?.commitment.updateOne(
322
- {
323
- id: commit.id
324
- },
325
- commitmentRecord,
326
- { upsert: true }
327
- );
328
- const ops = captchas.map((captcha) => ({
329
- updateOne: {
330
- filter: { commitmentId: commit.id, captchaId: captcha.captchaId },
331
- update: {
332
- $set: {
333
- captchaId: captcha.captchaId,
334
- captchaContentId: captcha.captchaContentId,
335
- salt: captcha.salt,
336
- solution: captcha.solution,
337
- commitmentId: commit.id,
338
- processed: false
339
- }
340
- },
341
- upsert: true
342
- }
343
- }));
344
- await this.tables?.usersolution.bulkWrite(ops);
345
- }
346
- }
347
- /**
348
- * @description Adds a new PoW Captcha record to the database.
349
- * @param {string} challenge The challenge string for the captcha.
350
- * @param components The components of the PoW challenge.
351
- * @param difficulty
352
- * @param providerSignature
353
- * @param ipAddress
354
- * @param serverChecked
355
- * @param userSubmitted
356
- * @param storedStatus
357
- * @param userSignature
358
- * @returns {Promise<void>} A promise that resolves when the record is added.
359
- */
360
- async storePowCaptchaRecord(challenge, components, difficulty, providerSignature, ipAddress, headers, serverChecked = false, userSubmitted = false, storedStatus = typesDatabase.StoredStatusNames.notStored, userSignature) {
361
- const tables = this.getTables();
362
- const powCaptchaRecord = {
363
- challenge,
364
- ...components,
365
- ipAddress,
366
- headers,
367
- result: { status: types.CaptchaStatus.pending },
368
- userSubmitted,
369
- serverChecked,
370
- difficulty,
371
- providerSignature,
372
- userSignature,
373
- lastUpdatedTimestamp: Date.now()
374
- };
375
- try {
376
- await tables.powcaptcha.create(powCaptchaRecord);
377
- this.logger.info("PowCaptcha record added successfully", {
378
- challenge,
379
- userSubmitted,
380
- serverChecked,
381
- storedStatus
382
- });
383
- } catch (error) {
384
- this.logger.error("Failed to add PowCaptcha record", {
385
- error,
386
- challenge,
387
- userSubmitted,
388
- serverChecked,
389
- storedStatus
390
- });
391
- throw new common.ProsopoDBError("DATABASE.CAPTCHA_UPDATE_FAILED", {
392
- context: {
393
- error,
394
- challenge,
395
- userSubmitted,
396
- serverChecked,
397
- storedStatus
398
- },
399
- logger: this.logger
400
- });
401
- }
402
- }
403
- /**
404
- * @description Retrieves a PoW Captcha record by its challenge string.
405
- * @param {string} challenge The challenge string to search for.
406
- * @returns {Promise<PoWCaptchaRecord | null>} A promise that resolves with the found record or null if not found.
407
- */
408
- async getPowCaptchaRecordByChallenge(challenge) {
409
- if (!this.tables) {
410
- throw new common.ProsopoEnvError("DATABASE.DATABASE_UNDEFINED", {
411
- context: { failedFuncName: this.getPowCaptchaRecordByChallenge.name },
412
- logger: this.logger
413
- });
414
- }
415
- try {
416
- const record = await this.tables.powcaptcha.findOne({ challenge }).lean();
417
- if (record) {
418
- this.logger.info("PowCaptcha record retrieved successfully", {
419
- challenge
420
- });
421
- return record;
422
- }
423
- this.logger.info("No PowCaptcha record found", { challenge });
424
- return null;
425
- } catch (error) {
426
- this.logger.error("Failed to retrieve PowCaptcha record", {
427
- error,
428
- challenge
429
- });
430
- throw new common.ProsopoDBError("DATABASE.CAPTCHA_GET_FAILED", {
431
- context: { error, challenge },
432
- logger: this.logger
433
- });
434
- }
435
- }
436
- /**
437
- * @description Updates a PoW Captcha record in the database.
438
- * @param {string} challenge The challenge string of the captcha to be updated.
439
- * @param result
440
- * @param serverChecked
441
- * @param userSubmitted
442
- * @param userSignature
443
- * @returns {Promise<void>} A promise that resolves when the record is updated.
444
- */
445
- async updatePowCaptchaRecord(challenge, result, serverChecked = false, userSubmitted = false, userSignature) {
446
- const tables = this.getTables();
447
- const timestamp = Date.now();
448
- const update = {
449
- result,
450
- serverChecked,
451
- userSubmitted,
452
- userSignature,
453
- lastUpdatedTimestamp: timestamp
454
- };
455
- try {
456
- const updateResult = await tables.powcaptcha.updateOne(
457
- { challenge },
458
- {
459
- $set: update
460
- }
461
- );
462
- if (updateResult.matchedCount === 0) {
463
- this.logger.info("No PowCaptcha record found to update", {
464
- challenge,
465
- ...update
466
- });
467
- throw new common.ProsopoDBError("DATABASE.CAPTCHA_GET_FAILED", {
468
- context: {
469
- challenge,
470
- ...update
471
- },
472
- logger: this.logger
473
- });
474
- }
475
- this.logger.info("PowCaptcha record updated successfully", {
476
- challenge,
477
- ...update
478
- });
479
- } catch (error) {
480
- this.logger.error("Failed to update PowCaptcha record", {
481
- error,
482
- challenge,
483
- ...update
484
- });
485
- throw new common.ProsopoDBError("DATABASE.CAPTCHA_UPDATE_FAILED", {
486
- context: {
487
- error,
488
- challenge,
489
- ...update
490
- },
491
- logger: this.logger
492
- });
493
- }
494
- }
495
- /** @description Get processed Dapp User captcha solutions from the user solution table
496
- */
497
- async getProcessedDappUserSolutions() {
498
- const docs = await this.tables?.usersolution.find({ processed: true }).lean();
499
- return docs || [];
500
- }
501
- /** @description Get processed Dapp User image captcha commitments from the commitments table
502
- */
503
- async getProcessedDappUserCommitments() {
504
- const docs = await this.tables?.commitment.find({ processed: true }).lean();
505
- return docs || [];
506
- }
507
- /** @description Get serverChecked Dapp User image captcha commitments from the commitments table
508
- */
509
- async getCheckedDappUserCommitments() {
510
- const docs = await this.tables?.commitment.find({ [typesDatabase.StoredStatusNames.serverChecked]: true }).lean();
511
- return docs || [];
512
- }
513
- /** @description Get Dapp User captcha commitments from the commitments table that have not been counted towards the
514
- * client's total
515
- */
516
- async getUnstoredDappUserCommitments() {
517
- const docs = await this.tables?.commitment.find({
518
- $or: [
519
- { storedStatus: { $ne: typesDatabase.StoredStatusNames.stored } },
520
- { storedStatus: { $exists: false } }
521
- ]
522
- }).lean();
523
- return docs || [];
524
- }
525
- /** @description Mark a list of captcha commits as stored
526
- */
527
- async markDappUserCommitmentsStored(commitmentIds) {
528
- const updateDoc = {
529
- storedAtTimestamp: Date.now()
530
- };
531
- await this.tables?.commitment.updateMany(
532
- { id: { $in: commitmentIds } },
533
- { $set: updateDoc },
534
- { upsert: false }
535
- );
536
- }
537
- /** @description Mark a list of captcha commits as checked
538
- */
539
- async markDappUserCommitmentsChecked(commitmentIds) {
540
- const updateDoc = {
541
- [typesDatabase.StoredStatusNames.serverChecked]: true,
542
- lastUpdatedTimestamp: Date.now()
543
- };
544
- await this.tables?.commitment.updateMany(
545
- { id: { $in: commitmentIds } },
546
- { $set: updateDoc },
547
- { upsert: false }
548
- );
549
- }
550
- /** @description Get Dapp User PoW captcha commitments that have not been counted towards the client's total
551
- */
552
- async getUnstoredDappUserPoWCommitments() {
553
- const docs = await this.tables?.powcaptcha.find({
554
- $or: [
555
- { storedStatus: { $ne: typesDatabase.StoredStatusNames.stored } },
556
- { storedStatus: { $exists: false } }
557
- ]
558
- }).lean();
559
- return docs || [];
560
- }
561
- /** @description Mark a list of PoW captcha commits as stored
562
- */
563
- async markDappUserPoWCommitmentsStored(challenges) {
564
- const updateDoc = {
565
- storedAtTimestamp: Date.now()
566
- };
567
- await this.tables?.powcaptcha.updateMany(
568
- { challenge: { $in: challenges } },
569
- { $set: updateDoc },
570
- { upsert: false }
571
- );
572
- }
573
- /** @description Mark a list of PoW captcha commits as checked by the server
574
- */
575
- async markDappUserPoWCommitmentsChecked(challenges) {
576
- const updateDoc = {
577
- [typesDatabase.StoredStatusNames.serverChecked]: true,
578
- lastUpdatedTimestamp: Date.now()
579
- };
580
- await this.tables?.powcaptcha.updateMany(
581
- { challenge: { $in: challenges } },
582
- {
583
- $set: updateDoc
584
- },
585
- { upsert: false }
586
- );
587
- }
588
- /** @description Remove processed Dapp User captcha solutions from the user solution table
589
- */
590
- async removeProcessedDappUserSolutions(commitmentIds) {
591
- return this.tables?.usersolution.deleteMany({
592
- processed: true,
593
- commitmentId: { $in: commitmentIds }
594
- });
595
- }
596
- /** @description Remove processed Dapp User captcha commitments from the user commitments table
597
- */
598
- async removeProcessedDappUserCommitments(commitmentIds) {
599
- return this.tables?.commitment.deleteMany({
600
- processed: true,
601
- id: { $in: commitmentIds }
602
- });
603
- }
604
- /**
605
- * @description Store a Dapp User's pending record
606
- */
607
- async storeDappUserPending(userAccount, requestHash, salt, deadlineTimestamp, requestedAtTimestamp, ipAddress) {
608
- if (!is.isHex(requestHash)) {
609
- throw new common.ProsopoDBError("DATABASE.INVALID_HASH", {
610
- context: {
611
- failedFuncName: this.storeDappUserPending.name,
612
- requestHash
613
- }
614
- });
615
- }
616
- const pendingRecord = {
617
- accountId: userAccount,
618
- pending: true,
619
- salt,
620
- requestHash,
621
- deadlineTimestamp,
622
- requestedAtTimestamp,
623
- ipAddress
624
- };
625
- await this.tables?.pending.updateOne(
626
- { requestHash },
627
- { $set: pendingRecord },
628
- { upsert: true }
629
- );
630
- }
631
- /**
632
- * @description Get a Dapp user's pending record
633
- */
634
- async getDappUserPending(requestHash) {
635
- if (!is.isHex(requestHash)) {
636
- throw new common.ProsopoEnvError("DATABASE.INVALID_HASH", {
637
- context: { failedFuncName: this.getDappUserPending.name, requestHash }
638
- });
639
- }
640
- const doc = await this.tables?.pending.findOne({ requestHash }).lean();
641
- if (doc) {
642
- return doc;
643
- }
644
- throw new common.ProsopoEnvError("DATABASE.PENDING_RECORD_NOT_FOUND", {
645
- context: { failedFuncName: this.getDappUserPending.name, requestHash }
646
- });
647
- }
648
- /**
649
- * @description Mark a pending request as used
650
- */
651
- async updateDappUserPendingStatus(requestHash) {
652
- if (!is.isHex(requestHash)) {
653
- throw new common.ProsopoEnvError("DATABASE.INVALID_HASH", {
654
- context: {
655
- failedFuncName: this.updateDappUserPendingStatus.name,
656
- requestHash
657
- }
658
- });
659
- }
660
- await this.tables?.pending.updateOne(
661
- { requestHash },
662
- {
663
- $set: {
664
- [types.CaptchaStatus.pending]: false
665
- }
666
- },
667
- { upsert: true }
668
- );
669
- }
670
- /**
671
- * @description Get all unsolved captchas
672
- */
673
- async getAllCaptchasByDatasetId(datasetId, state) {
674
- const cursor = this.tables?.captcha.find({
675
- datasetId,
676
- solved: state === types.CaptchaStates.Solved
677
- }).lean();
678
- const docs = await cursor;
679
- if (docs) {
680
- return docs.map(({ _id, ...keepAttrs }) => keepAttrs);
681
- }
682
- throw new common.ProsopoEnvError("DATABASE.CAPTCHA_GET_FAILED");
683
- }
684
- /**
685
- * @description Get all dapp user solutions by captchaIds
686
- */
687
- async getAllDappUserSolutions(captchaId) {
688
- const cursor = this.tables?.usersolution?.find({ captchaId: { $in: captchaId } }).lean();
689
- const docs = await cursor;
690
- if (docs) {
691
- return docs.map(
692
- ({ _id, ...keepAttrs }) => keepAttrs
693
- );
694
- }
695
- throw new common.ProsopoEnvError("DATABASE.SOLUTION_GET_FAILED");
696
- }
697
- async getDatasetIdWithSolvedCaptchasOfSizeN(solvedCaptchaCount) {
698
- const cursor = this.tables?.solution.aggregate([
699
- {
700
- $match: {}
701
- },
702
- {
703
- $group: {
704
- _id: "$datasetId",
705
- count: { $sum: 1 }
706
- }
707
- },
708
- {
709
- $match: {
710
- count: { $gte: solvedCaptchaCount }
711
- }
712
- },
713
- {
714
- $sample: { size: 1 }
715
- }
716
- ]);
717
- const docs = await cursor;
718
- if (docs?.length) {
719
- return docs[0]._id;
720
- }
721
- throw new common.ProsopoDBError("DATABASE.DATASET_WITH_SOLUTIONS_GET_FAILED");
722
- }
723
- async getRandomSolvedCaptchasFromSingleDataset(datasetId, size) {
724
- if (!is.isHex(datasetId)) {
725
- throw new common.ProsopoDBError("DATABASE.INVALID_HASH", {
726
- context: {
727
- failedFuncName: this.getRandomSolvedCaptchasFromSingleDataset.name,
728
- datasetId
729
- }
730
- });
731
- }
732
- const sampleSize = size ? Math.abs(Math.trunc(size)) : 1;
733
- const cursor = this.tables?.solution.aggregate([
734
- { $match: { datasetId } },
735
- { $sample: { size: sampleSize } },
736
- {
737
- $project: {
738
- captchaId: 1,
739
- captchaContentId: 1,
740
- solution: 1
741
- }
742
- }
743
- ]);
744
- const docs = await cursor;
745
- if (docs?.length) {
746
- return docs;
747
- }
748
- throw new common.ProsopoDBError("DATABASE.SOLUTION_GET_FAILED", {
749
- context: {
750
- failedFuncName: this.getRandomSolvedCaptchasFromSingleDataset.name,
751
- datasetId,
752
- size
753
- }
754
- });
755
- }
756
- /**
757
- * @description Get dapp user solution by ID
758
- * @param {string[]} commitmentId
759
- */
760
- async getDappUserSolutionById(commitmentId) {
761
- const cursor = this.tables?.usersolution?.findOne(
762
- {
763
- commitmentId
764
- },
765
- { projection: { _id: 0 } }
766
- ).lean();
767
- const doc = await cursor;
768
- if (doc) {
769
- return doc;
770
- }
771
- throw new common.ProsopoDBError("DATABASE.SOLUTION_GET_FAILED", {
772
- context: { failedFuncName: this.getCaptchaById.name, commitmentId }
773
- });
774
- }
775
- /**
776
- * @description Get dapp user commitment by user account
777
- * @param commitmentId
778
- */
779
- async getDappUserCommitmentById(commitmentId) {
780
- const commitmentCursor = this.tables?.commitment?.findOne({ id: commitmentId }).lean();
781
- const doc = await commitmentCursor;
782
- return doc ? doc : void 0;
783
- }
784
- /**
785
- * @description Get dapp user commitment by user account
786
- * @param {string} userAccount
787
- * @param {string} dappAccount
788
- */
789
- async getDappUserCommitmentByAccount(userAccount, dappAccount) {
790
- const docs = await this.tables?.commitment?.find({ userAccount, dappAccount }, { _id: 0 }, { sort: { _id: -1 } }).lean();
791
- return docs ? docs : [];
792
- }
793
- /**
794
- * @description Approve a dapp user's solution
795
- * @param {string[]} commitmentId
796
- */
797
- async approveDappUserCommitment(commitmentId) {
798
- try {
799
- const result = { status: types.CaptchaStatus.approved };
800
- const updateDoc = {
801
- result,
802
- lastUpdatedTimestamp: Date.now()
803
- };
804
- await this.tables?.commitment?.findOneAndUpdate(
805
- { id: commitmentId },
806
- { $set: updateDoc },
807
- { upsert: false }
808
- ).lean();
809
- } catch (err) {
810
- throw new common.ProsopoDBError("DATABASE.SOLUTION_APPROVE_FAILED", {
811
- context: { error: err, commitmentId }
812
- });
813
- }
814
- }
815
- /**
816
- * @description Disapprove a dapp user's solution
817
- * @param {string} commitmentId
818
- * @param reason
819
- */
820
- async disapproveDappUserCommitment(commitmentId, reason) {
821
- try {
822
- const updateDoc = {
823
- result: { status: types.CaptchaStatus.disapproved, reason },
824
- lastUpdatedTimestamp: Date.now()
825
- };
826
- await this.tables?.commitment?.findOneAndUpdate(
827
- { id: commitmentId },
828
- { $set: updateDoc },
829
- { upsert: false }
830
- ).lean();
831
- } catch (err) {
832
- throw new common.ProsopoDBError("DATABASE.SOLUTION_APPROVE_FAILED", {
833
- context: { error: err, commitmentId }
834
- });
835
- }
836
- }
837
- /**
838
- * @description Flag a dapp user's solutions as used by calculated solution
839
- * @param {string[]} captchaIds
840
- */
841
- async flagProcessedDappUserSolutions(captchaIds) {
842
- try {
843
- await this.tables?.usersolution?.updateMany(
844
- { captchaId: { $in: captchaIds } },
845
- { $set: { processed: true } },
846
- { upsert: false }
847
- ).lean();
848
- } catch (err) {
849
- throw new common.ProsopoDBError("DATABASE.SOLUTION_FLAG_FAILED", {
850
- context: { error: err, captchaIds }
851
- });
852
- }
853
- }
854
- /**
855
- * @description Flag dapp users' commitments as used by calculated solution
856
- * @param {string[]} commitmentIds
857
- */
858
- async flagProcessedDappUserCommitments(commitmentIds) {
859
- try {
860
- const distinctCommitmentIds = [...new Set(commitmentIds)];
861
- await this.tables?.commitment?.updateMany(
862
- { id: { $in: distinctCommitmentIds } },
863
- { $set: { processed: true } },
864
- { upsert: false }
865
- ).lean();
866
- } catch (err) {
867
- throw new common.ProsopoDBError("DATABASE.COMMITMENT_FLAG_FAILED", {
868
- context: { error: err, commitmentIds }
869
- });
870
- }
871
- }
872
- /**
873
- * @description Get a scheduled task status record by task ID and status
874
- */
875
- async getScheduledTaskStatus(taskId, status) {
876
- const cursor = await this.tables?.scheduler?.findOne({ taskId, status }).lean();
877
- return cursor ? cursor : void 0;
878
- }
879
- /**
880
- * @description Get the most recent scheduled task status record for a given task
881
- */
882
- async getLastScheduledTaskStatus(task, status) {
883
- const lookup = { processName: task };
884
- if (status) {
885
- lookup.status = status;
886
- }
887
- const cursor = await this.tables?.scheduler?.findOne(lookup).sort({ datetime: -1 }).limit(1).lean();
888
- return cursor ? cursor : void 0;
889
- }
890
- /**
891
- * @description Create the status of a scheduled task
892
- */
893
- async createScheduledTaskStatus(taskName, status) {
894
- const now = (/* @__PURE__ */ new Date()).getTime();
895
- const doc = typesDatabase.ScheduledTaskSchema.parse({
896
- processName: taskName,
897
- datetime: now,
898
- status
899
- });
900
- const taskRecord = await this.tables?.scheduler.create(doc);
901
- return taskRecord._id;
902
- }
903
- /**
904
- * @description Update the status of a scheduled task and an optional result
905
- */
906
- async updateScheduledTaskStatus(taskId, status, result) {
907
- const update = {
908
- status,
909
- updated: (/* @__PURE__ */ new Date()).getTime(),
910
- ...result && { result }
911
- };
912
- await this.tables?.scheduler.updateOne(
913
- { _id: taskId },
914
- { $set: update },
915
- {
916
- upsert: false
917
- }
918
- );
919
- }
920
- /**
921
- * @description Update the client records
922
- */
923
- async updateClientRecords(clientRecords) {
924
- const ops = clientRecords.map((record) => {
925
- const clientRecord = {
926
- account: record.account,
927
- settings: record.settings
928
- };
929
- return {
930
- updateOne: {
931
- filter: { account: record.account },
932
- update: {
933
- $set: clientRecord
934
- },
935
- upsert: true
936
- }
937
- };
938
- });
939
- await this.tables?.client.bulkWrite(ops);
940
- }
941
- /**
942
- * @description Get a client record
943
- */
944
- async getClientRecord(account) {
945
- const doc = await this.tables?.client.findOne({ account }).lean();
946
- return doc ? doc : void 0;
947
- }
948
- }
949
- exports.ProviderDatabase = ProviderDatabase;