@bopen-io/wallet-toolbox 2.15.0-bopen.1 → 2.15.0-bopen.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 (62) hide show
  1. package/CHANGELOG.md +2 -47
  2. package/README.md +7 -73
  3. package/docs/client.md +2729 -3020
  4. package/docs/monitor.md +153 -164
  5. package/docs/services.md +710 -856
  6. package/docs/setup.md +1 -46
  7. package/docs/storage.md +1636 -1654
  8. package/docs/wallet.md +1842 -2090
  9. package/out/src/Setup.d.ts +0 -10
  10. package/out/src/Setup.d.ts.map +1 -1
  11. package/out/src/Setup.js +0 -24
  12. package/out/src/Setup.js.map +1 -1
  13. package/out/src/monitor/MonitorDaemon.d.ts +0 -2
  14. package/out/src/monitor/MonitorDaemon.d.ts.map +1 -1
  15. package/out/src/monitor/MonitorDaemon.js +1 -9
  16. package/out/src/monitor/MonitorDaemon.js.map +1 -1
  17. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.d.ts +0 -5
  18. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.d.ts.map +1 -1
  19. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.js +3 -11
  20. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.js.map +1 -1
  21. package/out/src/storage/StorageKnex.d.ts +0 -3
  22. package/out/src/storage/StorageKnex.d.ts.map +1 -1
  23. package/out/src/storage/StorageKnex.js +207 -115
  24. package/out/src/storage/StorageKnex.js.map +1 -1
  25. package/out/src/storage/adminServer/adminServer.d.ts.map +1 -1
  26. package/out/src/storage/adminServer/adminServer.js +41 -3
  27. package/out/src/storage/adminServer/adminServer.js.map +1 -1
  28. package/out/src/storage/index.all.d.ts +0 -1
  29. package/out/src/storage/index.all.d.ts.map +1 -1
  30. package/out/src/storage/index.all.js +0 -1
  31. package/out/src/storage/index.all.js.map +1 -1
  32. package/out/src/storage/methods/listActionsKnex.d.ts.map +1 -1
  33. package/out/src/storage/methods/listActionsKnex.js +1 -0
  34. package/out/src/storage/methods/listActionsKnex.js.map +1 -1
  35. package/out/src/storage/methods/listOutputsKnex.d.ts.map +1 -1
  36. package/out/src/storage/methods/listOutputsKnex.js +1 -0
  37. package/out/src/storage/methods/listOutputsKnex.js.map +1 -1
  38. package/out/src/storage/methods/purgeData.d.ts.map +1 -1
  39. package/out/src/storage/methods/purgeData.js +3 -0
  40. package/out/src/storage/methods/purgeData.js.map +1 -1
  41. package/out/src/storage/methods/reviewStatus.d.ts.map +1 -1
  42. package/out/src/storage/methods/reviewStatus.js +3 -0
  43. package/out/src/storage/methods/reviewStatus.js.map +1 -1
  44. package/out/src/storage/portable/index.js +12 -38
  45. package/out/src/storage/portable/index.js.map +1 -1
  46. package/out/src/storage/remoting/StorageServer.d.ts +4 -17
  47. package/out/src/storage/remoting/StorageServer.d.ts.map +1 -1
  48. package/out/src/storage/remoting/StorageServer.js +13 -22
  49. package/out/src/storage/remoting/StorageServer.js.map +1 -1
  50. package/out/src/storage/schema/KnexMigrations.d.ts +2 -7
  51. package/out/src/storage/schema/KnexMigrations.d.ts.map +1 -1
  52. package/out/src/storage/schema/KnexMigrations.js +60 -162
  53. package/out/src/storage/schema/KnexMigrations.js.map +1 -1
  54. package/package.json +4 -5
  55. package/out/src/storage/adminServer/reqReviewQuery.d.ts +0 -41
  56. package/out/src/storage/adminServer/reqReviewQuery.d.ts.map +0 -1
  57. package/out/src/storage/adminServer/reqReviewQuery.js +0 -67
  58. package/out/src/storage/adminServer/reqReviewQuery.js.map +0 -1
  59. package/out/src/storage/knexPostgres.d.ts +0 -16
  60. package/out/src/storage/knexPostgres.d.ts.map +0 -1
  61. package/out/src/storage/knexPostgres.js +0 -34
  62. package/out/src/storage/knexPostgres.js.map +0 -1
@@ -12,7 +12,6 @@ const WERR_errors_1 = require("../sdk/WERR_errors");
12
12
  const utilityHelpers_1 = require("../utility/utilityHelpers");
13
13
  const managedChange_1 = require("./methods/managedChange");
14
14
  const preparedBeef_1 = require("./methods/preparedBeef");
15
- const knexPostgres_1 = require("./knexPostgres");
16
15
  // Keep bulk statements below conservative SQLite/MySQL bind-parameter
17
16
  // ceilings. The surrounding transaction still makes a complete pack atomic.
18
17
  const ACTION_BATCH_BLOB_SQL_CHUNK = 500;
@@ -48,7 +47,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
48
47
  if (options.knex == null)
49
48
  throw new WERR_errors_1.WERR_INVALID_PARAMETER('options.knex', 'valid');
50
49
  this.knex = options.knex;
51
- (0, knexPostgres_1.usePostgresInt8Numbers)(this.knex);
50
+ parsePostgresInt8AsNumber(this.knex);
52
51
  this.preparedBeefPolicy = (0, preparedBeef_1.validatePreparedBeefPolicy)(options.preparedBeef);
53
52
  this.preparedBeefCoordinator = new preparedBeef_1.PreparedBeefCoordinator(this);
54
53
  if (this.telemetry.enabled) {
@@ -261,6 +260,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
261
260
  .whereNull('pb.preparedBeefId')
262
261
  .select('o.userId', 'o.txid as rootTxid')
263
262
  .groupBy('o.userId', 'o.txid')
263
+ // ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
264
264
  .orderByRaw('MIN(??)', ['o.outputId'])
265
265
  .limit(limit));
266
266
  return rows;
@@ -387,6 +387,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
387
387
  void this.whereExists(k
388
388
  .select('*')
389
389
  .from('transactions')
390
+ // ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
390
391
  .whereRaw('?? = ?? and ?? = ?', [
391
392
  'proven_txs.provenTxId',
392
393
  'transactions.provenTxId',
@@ -414,6 +415,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
414
415
  void this.whereExists(k
415
416
  .select('*')
416
417
  .from('transactions')
418
+ // ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
417
419
  .whereRaw('proven_tx_reqs.txid = transactions.txid and ?? = ?', ['transactions.userId', args.userId]));
418
420
  });
419
421
  if (args.paged != null) {
@@ -435,6 +437,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
435
437
  let q = k('tx_labels_map').whereExists(k
436
438
  .select('*')
437
439
  .from('tx_labels')
440
+ // ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
438
441
  .whereRaw('?? = ?? and ?? = ?', [
439
442
  'tx_labels.txLabelId',
440
443
  'tx_labels_map.txLabelId',
@@ -460,6 +463,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
460
463
  let q = k('output_tags_map').whereExists(k
461
464
  .select('*')
462
465
  .from('output_tags')
466
+ // ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
463
467
  .whereRaw('?? = ?? and ?? = ?', [
464
468
  'output_tags.outputTagId',
465
469
  'output_tags_map.outputTagId',
@@ -532,20 +536,16 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
532
536
  /**
533
537
  * Insert one row and return its auto-increment primary key. MySQL and SQLite
534
538
  * report the id as the insert result; Postgres requires a RETURNING clause.
535
- *
536
- * `recoverable` marks inserts that the findOrInsert helpers retry as a find
537
- * after a duplicate-key error, in the same transaction; see `inSavepoint`.
538
539
  */
539
- async insertReturningId(table, idColumn, row, trx, recoverable = false) {
540
+ async insertReturningId(table, idColumn, row, trx) {
540
541
  if (this.dbtype !== 'Postgres') {
541
542
  const [id] = await this.toDb(trx)(table).insert(row);
542
543
  return id;
543
544
  }
544
- const insert = async (db) => {
545
+ return await this.inSavepoint(trx, async (db) => {
545
546
  const [r] = await db(table).insert(row).returning(idColumn);
546
547
  return r[idColumn];
547
- };
548
- return recoverable ? await this.inSavepoint(trx, insert) : await insert(this.toDb(trx));
548
+ });
549
549
  }
550
550
  /**
551
551
  * The findOrInsert helpers catch a failed insert and find the row again in
@@ -562,7 +562,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
562
562
  const e = await this.validateEntityForInsert(tx, trx);
563
563
  if (e.provenTxId === 0)
564
564
  delete e.provenTxId;
565
- const id = await this.insertReturningId('proven_txs', 'provenTxId', e, trx, true);
565
+ const id = await this.insertReturningId('proven_txs', 'provenTxId', e, trx);
566
566
  tx.provenTxId = id;
567
567
  return tx.provenTxId;
568
568
  }
@@ -690,7 +690,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
690
690
  const e = await this.validateEntityForInsert(tx, trx);
691
691
  if (e.provenTxReqId === 0)
692
692
  delete e.provenTxReqId;
693
- const id = await this.insertReturningId('proven_tx_reqs', 'provenTxReqId', e, trx, true);
693
+ const id = await this.insertReturningId('proven_tx_reqs', 'provenTxReqId', e, trx);
694
694
  tx.provenTxReqId = id;
695
695
  return tx.provenTxReqId;
696
696
  }
@@ -737,7 +737,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
737
737
  const e = await this.validateEntityForInsert(basket, trx, undefined, ['isDeleted']);
738
738
  if (e.basketId === 0)
739
739
  delete e.basketId;
740
- const id = await this.insertReturningId('output_baskets', 'basketId', e, trx, true);
740
+ const id = await this.insertReturningId('output_baskets', 'basketId', e, trx);
741
741
  basket.basketId = id;
742
742
  return basket.basketId;
743
743
  }
@@ -745,7 +745,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
745
745
  const e = await this.validateEntityForInsert(tx, trx);
746
746
  if (e.transactionId === 0)
747
747
  delete e.transactionId;
748
- const id = await this.insertReturningId('transactions', 'transactionId', e, trx, true);
748
+ const id = await this.insertReturningId('transactions', 'transactionId', e, trx);
749
749
  tx.transactionId = id;
750
750
  return tx.transactionId;
751
751
  }
@@ -782,7 +782,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
782
782
  const e = await this.validateEntityForInsert(tag, trx, undefined, ['isDeleted']);
783
783
  if (e.outputTagId === 0)
784
784
  delete e.outputTagId;
785
- const id = await this.insertReturningId('output_tags', 'outputTagId', e, trx, true);
785
+ const id = await this.insertReturningId('output_tags', 'outputTagId', e, trx);
786
786
  tag.outputTagId = id;
787
787
  return tag.outputTagId;
788
788
  }
@@ -794,7 +794,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
794
794
  const e = await this.validateEntityForInsert(label, trx, undefined, ['isDeleted']);
795
795
  if (e.txLabelId === 0)
796
796
  delete e.txLabelId;
797
- const id = await this.insertReturningId('tx_labels', 'txLabelId', e, trx, true);
797
+ const id = await this.insertReturningId('tx_labels', 'txLabelId', e, trx);
798
798
  label.txLabelId = id;
799
799
  return label.txLabelId;
800
800
  }
@@ -1364,27 +1364,13 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
1364
1364
  if (isSQLite) {
1365
1365
  await this.knex.raw('PRAGMA foreign_keys = OFF;');
1366
1366
  }
1367
- const isPostgres = this.knex.client.dialect === 'postgresql';
1368
1367
  try {
1369
1368
  const config = {
1370
- migrationSource: new KnexMigrations_1.KnexMigrations(this.chain, storageName, storageIdentityKey, 1024, isPostgres ? 'Postgres' : undefined),
1369
+ migrationSource: new KnexMigrations_1.KnexMigrations(this.chain, storageName, storageIdentityKey, 1024),
1371
1370
  // Keep DDL and its migration journal entry in the same transaction.
1372
1371
  disableTransactions: false
1373
1372
  };
1374
- if (isPostgres) {
1375
- // Postgres index migrations run outside a transaction (CREATE INDEX
1376
- // CONCURRENTLY). When any pending migration does, knex's latest()
1377
- // journals every migration of the batch outside its transaction, so
1378
- // run one migration per up() to keep the others atomic with their journal row.
1379
- for (;;) {
1380
- const [, applied] = (await this.knex.migrate.up(config));
1381
- if (applied.length === 0)
1382
- break;
1383
- }
1384
- }
1385
- else {
1386
- await this.knex.migrate.latest(config);
1387
- }
1373
+ await this.knex.migrate.latest(config);
1388
1374
  return await this.knex.migrate.currentVersion(config);
1389
1375
  }
1390
1376
  finally {
@@ -1619,6 +1605,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
1619
1605
  void this.select(1)
1620
1606
  .from('action_batch_outputs as abo')
1621
1607
  .join('action_batches as ab', 'abo.actionBatchId', 'ab.actionBatchId')
1608
+ // ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
1622
1609
  .whereRaw('?? = ??', ['abo.outputId', 'o.outputId'])
1623
1610
  .whereIn('ab.status', ['active', 'prepared'])
1624
1611
  .where('ab.expiresAt', '>', now)
@@ -1653,6 +1640,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
1653
1640
  void this.select(1)
1654
1641
  .from('action_batch_outputs as abo')
1655
1642
  .join('action_batches as ab', 'abo.actionBatchId', 'ab.actionBatchId')
1643
+ // ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
1656
1644
  .whereRaw('?? = ??', ['abo.outputId', 'o.outputId'])
1657
1645
  .whereIn('ab.status', ['active', 'prepared'])
1658
1646
  .where('ab.expiresAt', '>', now)
@@ -1687,6 +1675,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
1687
1675
  void this.select(1)
1688
1676
  .from('action_batch_outputs as abo')
1689
1677
  .join('action_batches as ab', 'abo.actionBatchId', 'ab.actionBatchId')
1678
+ // ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
1690
1679
  .whereRaw('?? = ??', ['abo.outputId', 'o.outputId'])
1691
1680
  .whereIn('ab.status', ['active', 'prepared'])
1692
1681
  .where('ab.expiresAt', '>', now)
@@ -1876,6 +1865,7 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
1876
1865
  const status = ['completed', 'unproven'];
1877
1866
  if (!excludeSending)
1878
1867
  status.push('sending');
1868
+ let reservedConcurrently = false;
1879
1869
  const r = await this.knex.transaction(async (trx) => {
1880
1870
  const baseQuery = () => trx('outputs as o')
1881
1871
  .join('transactions as t', 'o.transactionId', 't.transactionId')
@@ -1894,42 +1884,36 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
1894
1884
  .whereNot('o.derivationSuffix', '')
1895
1885
  .whereNull('o.spentBy')
1896
1886
  .whereNotExists(function () {
1887
+ // ?? quotes identifiers: Postgres lowercases unquoted names, so camelCase columns must be quoted.
1897
1888
  void this.select(1).from('action_batch_outputs as abo').whereRaw('?? = ??', ['abo.outputId', 'o.outputId']);
1898
1889
  })
1899
1890
  .whereIn('t.status', status)
1900
1891
  .select('o.*')
1901
1892
  .forUpdate();
1902
- const excluded = [];
1903
- const candidates = () => excluded.length > 0 ? baseQuery().whereNotIn('o.outputId', excluded) : baseQuery();
1904
- const selectOutput = async () => {
1905
- let output;
1906
- if (exactSatoshis !== undefined) {
1907
- output = await candidates().where('o.satoshis', exactSatoshis).orderBy('o.outputId', 'asc').first();
1908
- }
1909
- output ??= await candidates()
1910
- .where('o.satoshis', '>=', targetSatoshis)
1911
- .orderBy('o.satoshis', 'asc')
1912
- .orderBy('o.outputId', 'asc')
1913
- .first();
1914
- output ??= await candidates()
1915
- .where('o.satoshis', '<', targetSatoshis)
1916
- .orderBy('o.satoshis', 'desc')
1917
- .orderBy('o.outputId', 'desc')
1918
- .first();
1919
- return output;
1920
- };
1921
- let output = await selectOutput();
1893
+ let output;
1894
+ if (exactSatoshis !== undefined) {
1895
+ output = await baseQuery().where('o.satoshis', exactSatoshis).orderBy('o.outputId', 'asc').first();
1896
+ }
1897
+ output ??= await baseQuery()
1898
+ .where('o.satoshis', '>=', targetSatoshis)
1899
+ .orderBy('o.satoshis', 'asc')
1900
+ .orderBy('o.outputId', 'asc')
1901
+ .first();
1902
+ output ??= await baseQuery()
1903
+ .where('o.satoshis', '<', targetSatoshis)
1904
+ .orderBy('o.satoshis', 'desc')
1905
+ .orderBy('o.outputId', 'desc')
1906
+ .first();
1907
+ if (output == null)
1908
+ return undefined;
1922
1909
  // Under Postgres READ COMMITTED the NOT EXISTS above uses the statement's
1923
1910
  // snapshot, so an action batch reservation committed while the statement
1924
- // waited for the row lock is not visible to it. Re-check with a new statement.
1925
- while (output != null &&
1926
- this.dbtype === 'Postgres' &&
1911
+ // waited for the row lock is not visible to it. Retry in a new transaction.
1912
+ if (this.dbtype === 'Postgres' &&
1927
1913
  (await this.findReservedActionBatchOutputIds([output.outputId], trx)).length > 0) {
1928
- excluded.push(output.outputId);
1929
- output = await selectOutput();
1930
- }
1931
- if (output == null)
1914
+ reservedConcurrently = true;
1932
1915
  return undefined;
1916
+ }
1933
1917
  await this.updateOutput(output.outputId, {
1934
1918
  spendable: false,
1935
1919
  spentBy: transactionId
@@ -1941,6 +1925,8 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
1941
1925
  output.spentBy = transactionId;
1942
1926
  return output;
1943
1927
  });
1928
+ if (reservedConcurrently)
1929
+ return await this.allocateChangeInput(userId, basketId, targetSatoshis, exactSatoshis, excludeSending, transactionId);
1944
1930
  return r;
1945
1931
  }
1946
1932
  async markChangeInputsSpent(outputIds, transactionId, trx) {
@@ -2006,8 +1992,8 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
2006
1992
  return entities;
2007
1993
  }
2008
1994
  async adminStats(adminIdentityKey) {
2009
- if (this.dbtype !== 'MySQL' && this.dbtype !== 'Postgres')
2010
- throw new WERR_errors_1.WERR_NOT_IMPLEMENTED('adminStats, only MySQL and Postgres are supported');
1995
+ if (this.dbtype !== 'MySQL')
1996
+ throw new WERR_errors_1.WERR_NOT_IMPLEMENTED('adminStats, only MySQL is supported');
2011
1997
  const monitorEvent = (0, utilityHelpers_1.verifyOneOrNone)(await this.findMonitorEvents({
2012
1998
  partial: { event: 'MonitorCallHistory' },
2013
1999
  orderDescending: true,
@@ -2015,66 +2001,153 @@ class StorageKnex extends StorageProvider_1.StorageProvider {
2015
2001
  }));
2016
2002
  const monitorStats = monitorEvent != null ? JSON.parse(monitorEvent.details) : undefined;
2017
2003
  const servicesStats = this.getServices().getServicesCallHistory(true);
2018
- const day = 24 * 60 * 60 * 1000;
2019
- const now = Date.now();
2020
- const periods = [
2021
- ['Day', new Date(now - day)],
2022
- ['Week', new Date(now - 7 * day)],
2023
- ['Month', new Date(now - 30 * day)],
2024
- ['Total', undefined]
2025
- ];
2026
- const k = this.knex;
2027
- const count = (table, createdAt = 'created_at') => (since) => {
2028
- const q = k(table).count('*');
2029
- if (since != null)
2030
- void q.where(createdAt, '>', this.validateDateForWhere(since));
2031
- return q;
2032
- };
2033
- const txCount = (status) => (since) => count('transactions')(since).where({ status });
2034
- const satoshis = (change) => (since) => {
2035
- const q = k('outputs as o')
2036
- .join('transactions as t', 'o.transactionId', 't.transactionId')
2037
- .where({ 't.status': 'completed', 'o.spendable': true, 'o.change': change })
2038
- .sum('o.satoshis');
2039
- if (!change)
2040
- void q.whereNotNull('o.basketId');
2041
- if (since != null)
2042
- void q.where('o.created_at', '>', this.validateDateForWhere(since));
2043
- return q;
2044
- };
2045
- const stats = [
2046
- ['users', count('users')],
2047
- ['transactions', count('transactions')],
2048
- ['txCompleted', txCount('completed')],
2049
- ['txFailed', since => txCount('failed')(since).whereNotNull('txid')],
2050
- ['txAbandoned', since => txCount('failed')(since).whereNull('txid')],
2051
- ['txUnprocessed', txCount('unprocessed')],
2052
- ['txSending', txCount('sending')],
2053
- ['txUnproven', txCount('unproven')],
2054
- ['txUnsigned', txCount('unsigned')],
2055
- ['txNosend', txCount('nosend')],
2056
- ['txNonfinal', txCount('nonfinal')],
2057
- ['txUnfail', txCount('unfail')],
2058
- ['satoshisDefault', satoshis(true)],
2059
- ['satoshisOther', satoshis(false)],
2060
- ['baskets', count('output_baskets')],
2061
- ['labels', count('tx_labels')],
2062
- ['tags', count('output_tags')]
2063
- ];
2064
- // One statement of scalar subqueries; knex quotes the camelCase identifiers per dialect.
2065
- const columns = {};
2066
- for (const [name, query] of stats) {
2067
- for (const [period, since] of periods)
2068
- columns[`${name}${period}`] = query(since);
2069
- }
2070
- const [row] = (await k.select(columns));
2071
- const values = Object.fromEntries(Object.keys(columns).map(name => [name, Number(row[name] ?? 0)]));
2004
+ const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
2005
+ const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
2006
+ const oneMonthAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
2007
+ const [[{ usersDay, usersMonth, usersWeek, usersTotal, transactionsDay, transactionsMonth, transactionsWeek, transactionsTotal, txCompletedDay, txCompletedMonth, txCompletedWeek, txCompletedTotal, txFailedDay, txFailedMonth, txFailedWeek, txFailedTotal, txAbandonedDay, txAbandonedMonth, txAbandonedWeek, txAbandonedTotal, txUnprocessedDay, txUnprocessedMonth, txUnprocessedWeek, txUnprocessedTotal, txSendingDay, txSendingMonth, txSendingWeek, txSendingTotal, txUnprovenDay, txUnprovenMonth, txUnprovenWeek, txUnprovenTotal, txUnsignedDay, txUnsignedMonth, txUnsignedWeek, txUnsignedTotal, txNosendDay, txNosendMonth, txNosendWeek, txNosendTotal, txNonfinalDay, txNonfinalMonth, txNonfinalWeek, txNonfinalTotal, txUnfailDay, txUnfailMonth, txUnfailWeek, txUnfailTotal, satoshisDefaultDay, satoshisDefaultMonth, satoshisDefaultWeek, satoshisDefaultTotal, satoshisOtherDay, satoshisOtherMonth, satoshisOtherWeek, satoshisOtherTotal, basketsDay, basketsMonth, basketsWeek, basketsTotal, labelsDay, labelsMonth, labelsWeek, labelsTotal, tagsDay, tagsMonth, tagsWeek, tagsTotal }]] = await this.knex.raw(`
2008
+ select
2009
+ (select count(*) from users where created_at > '${oneDayAgo}') as usersDay,
2010
+ (select count(*) from users where created_at > '${oneWeekAgo}') as usersWeek,
2011
+ (select count(*) from users where created_at > '${oneMonthAgo}') as usersMonth,
2012
+ (select count(*) from users) as usersTotal,
2013
+ (select count(*) from transactions where created_at > '${oneDayAgo}') as transactionsDay,
2014
+ (select count(*) from transactions where created_at > '${oneWeekAgo}') as transactionsWeek,
2015
+ (select count(*) from transactions where created_at > '${oneMonthAgo}') as transactionsMonth,
2016
+ (select count(*) from transactions) as transactionsTotal,
2017
+ (select count(*) from transactions where status = 'completed' and created_at > '${oneDayAgo}') as txCompletedDay,
2018
+ (select count(*) from transactions where status = 'completed' and created_at > '${oneWeekAgo}') as txCompletedWeek,
2019
+ (select count(*) from transactions where status = 'completed' and created_at > '${oneMonthAgo}') as txCompletedMonth,
2020
+ (select count(*) from transactions where status = 'completed') as txCompletedTotal,
2021
+ (select count(*) from transactions where status = 'failed' and not txid is null and created_at > '${oneDayAgo}') as txFailedDay,
2022
+ (select count(*) from transactions where status = 'failed' and not txid is null and created_at > '${oneWeekAgo}') as txFailedWeek,
2023
+ (select count(*) from transactions where status = 'failed' and not txid is null and created_at > '${oneMonthAgo}') as txFailedMonth,
2024
+ (select count(*) from transactions where status = 'failed' and not txid is null) as txFailedTotal,
2025
+ (select count(*) from transactions where status = 'failed' and txid is null and created_at > '${oneDayAgo}') as txAbandonedDay,
2026
+ (select count(*) from transactions where status = 'failed' and txid is null and created_at > '${oneWeekAgo}') as txAbandonedWeek,
2027
+ (select count(*) from transactions where status = 'failed' and txid is null and created_at > '${oneMonthAgo}') as txAbandonedMonth,
2028
+ (select count(*) from transactions where status = 'failed' and txid is null) as txAbandonedTotal,
2029
+ (select count(*) from transactions where status = 'unprocessed' and created_at > '${oneDayAgo}') as txUnprocessedDay,
2030
+ (select count(*) from transactions where status = 'unprocessed' and created_at > '${oneWeekAgo}') as txUnprocessedWeek,
2031
+ (select count(*) from transactions where status = 'unprocessed' and created_at > '${oneMonthAgo}') as txUnprocessedMonth,
2032
+ (select count(*) from transactions where status = 'unprocessed') as txUnprocessedTotal,
2033
+ (select count(*) from transactions where status = 'sending' and created_at > '${oneDayAgo}') as txSendingDay,
2034
+ (select count(*) from transactions where status = 'sending' and created_at > '${oneWeekAgo}') as txSendingWeek,
2035
+ (select count(*) from transactions where status = 'sending' and created_at > '${oneMonthAgo}') as txSendingMonth,
2036
+ (select count(*) from transactions where status = 'sending') as txSendingTotal,
2037
+ (select count(*) from transactions where status = 'unproven' and created_at > '${oneDayAgo}') as txUnprovenDay,
2038
+ (select count(*) from transactions where status = 'unproven' and created_at > '${oneWeekAgo}') as txUnprovenWeek,
2039
+ (select count(*) from transactions where status = 'unproven' and created_at > '${oneMonthAgo}') as txUnprovenMonth,
2040
+ (select count(*) from transactions where status = 'unproven') as txUnprovenTotal,
2041
+ (select count(*) from transactions where status = 'unsigned' and created_at > '${oneDayAgo}') as txUnsignedDay,
2042
+ (select count(*) from transactions where status = 'unsigned' and created_at > '${oneWeekAgo}') as txUnsignedWeek,
2043
+ (select count(*) from transactions where status = 'unsigned' and created_at > '${oneMonthAgo}') as txUnsignedMonth,
2044
+ (select count(*) from transactions where status = 'unsigned') as txUnsignedTotal,
2045
+ (select count(*) from transactions where status = 'nosend' and created_at > '${oneDayAgo}') as txNosendDay,
2046
+ (select count(*) from transactions where status = 'nosend' and created_at > '${oneWeekAgo}') as txNosendWeek,
2047
+ (select count(*) from transactions where status = 'nosend' and created_at > '${oneMonthAgo}') as txNosendMonth,
2048
+ (select count(*) from transactions where status = 'nosend') as txNosendTotal,
2049
+ (select count(*) from transactions where status = 'nonfinal' and created_at > '${oneDayAgo}') as txNonfinalDay,
2050
+ (select count(*) from transactions where status = 'nonfinal' and created_at > '${oneWeekAgo}') as txNonfinalWeek,
2051
+ (select count(*) from transactions where status = 'nonfinal' and created_at > '${oneMonthAgo}') as txNonfinalMonth,
2052
+ (select count(*) from transactions where status = 'nonfinal') as txNonfinalTotal,
2053
+ (select count(*) from transactions where status = 'unfail' and created_at > '${oneDayAgo}') as txUnfailDay,
2054
+ (select count(*) from transactions where status = 'unfail' and created_at > '${oneWeekAgo}') as txUnfailWeek,
2055
+ (select count(*) from transactions where status = 'unfail' and created_at > '${oneMonthAgo}') as txUnfailMonth,
2056
+ (select count(*) from transactions where status = 'unfail') as txUnfailTotal,
2057
+ (select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 1 and o.created_at > '${oneDayAgo}') as satoshisDefaultDay,
2058
+ (select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 1 and o.created_at > '${oneWeekAgo}') as satoshisDefaultWeek,
2059
+ (select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 1 and o.created_at > '${oneMonthAgo}') as satoshisDefaultMonth,
2060
+ (select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 1) as satoshisDefaultTotal,
2061
+ (select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 0 and not o.basketId is null and o.created_at > '${oneDayAgo}') as satoshisOtherDay,
2062
+ (select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 0 and not o.basketId is null and o.created_at > '${oneWeekAgo}') as satoshisOtherWeek,
2063
+ (select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 0 and not o.basketId is null and o.created_at > '${oneMonthAgo}') as satoshisOtherMonth,
2064
+ (select sum(o.satoshis) from outputs o, transactions t where o.transactionId = t.transactionId and t.status = 'completed' and o.spendable = 1 and o.change = 0 and not o.basketId is null) as satoshisOtherTotal,
2065
+ (select count(*) from output_baskets where created_at > '${oneDayAgo}') as basketsDay,
2066
+ (select count(*) from output_baskets where created_at > '${oneWeekAgo}') as basketsWeek,
2067
+ (select count(*) from output_baskets where created_at > '${oneMonthAgo}') as basketsMonth,
2068
+ (select count(*) from output_baskets) as basketsTotal,
2069
+ (select count(*) from tx_labels where created_at > '${oneDayAgo}') as labelsDay,
2070
+ (select count(*) from tx_labels where created_at > '${oneWeekAgo}') as labelsWeek,
2071
+ (select count(*) from tx_labels where created_at > '${oneMonthAgo}') as labelsMonth,
2072
+ (select count(*) from tx_labels) as labelsTotal,
2073
+ (select count(*) from output_tags where created_at > '${oneDayAgo}') as tagsDay,
2074
+ (select count(*) from output_tags where created_at > '${oneWeekAgo}') as tagsWeek,
2075
+ (select count(*) from output_tags where created_at > '${oneMonthAgo}') as tagsMonth,
2076
+ (select count(*) from output_tags) as tagsTotal
2077
+ `);
2072
2078
  const r = {
2073
2079
  monitorStats,
2074
2080
  servicesStats,
2075
2081
  requestedBy: adminIdentityKey,
2076
2082
  when: new Date().toISOString(),
2077
- ...values
2083
+ usersDay,
2084
+ usersWeek,
2085
+ usersMonth,
2086
+ usersTotal,
2087
+ transactionsDay,
2088
+ transactionsWeek,
2089
+ transactionsMonth,
2090
+ transactionsTotal,
2091
+ txCompletedDay,
2092
+ txCompletedWeek,
2093
+ txCompletedMonth,
2094
+ txCompletedTotal,
2095
+ txFailedDay,
2096
+ txFailedWeek,
2097
+ txFailedMonth,
2098
+ txFailedTotal,
2099
+ txAbandonedDay,
2100
+ txAbandonedWeek,
2101
+ txAbandonedMonth,
2102
+ txAbandonedTotal,
2103
+ txUnprocessedDay,
2104
+ txUnprocessedWeek,
2105
+ txUnprocessedMonth,
2106
+ txUnprocessedTotal,
2107
+ txSendingDay,
2108
+ txSendingWeek,
2109
+ txSendingMonth,
2110
+ txSendingTotal,
2111
+ txUnprovenDay,
2112
+ txUnprovenWeek,
2113
+ txUnprovenMonth,
2114
+ txUnprovenTotal,
2115
+ txUnsignedDay,
2116
+ txUnsignedWeek,
2117
+ txUnsignedMonth,
2118
+ txUnsignedTotal,
2119
+ txNosendDay,
2120
+ txNosendWeek,
2121
+ txNosendMonth,
2122
+ txNosendTotal,
2123
+ txNonfinalDay,
2124
+ txNonfinalWeek,
2125
+ txNonfinalMonth,
2126
+ txNonfinalTotal,
2127
+ txUnfailDay,
2128
+ txUnfailWeek,
2129
+ txUnfailMonth,
2130
+ txUnfailTotal,
2131
+ satoshisDefaultDay: Number(satoshisDefaultDay),
2132
+ satoshisDefaultWeek: Number(satoshisDefaultWeek),
2133
+ satoshisDefaultMonth: Number(satoshisDefaultMonth),
2134
+ satoshisDefaultTotal: Number(satoshisDefaultTotal),
2135
+ satoshisOtherDay: Number(satoshisOtherDay),
2136
+ satoshisOtherWeek: Number(satoshisOtherWeek),
2137
+ satoshisOtherMonth: Number(satoshisOtherMonth),
2138
+ satoshisOtherTotal: Number(satoshisOtherTotal),
2139
+ basketsDay,
2140
+ basketsWeek,
2141
+ basketsMonth,
2142
+ basketsTotal,
2143
+ labelsDay,
2144
+ labelsWeek,
2145
+ labelsMonth,
2146
+ labelsTotal,
2147
+ tagsDay,
2148
+ tagsWeek,
2149
+ tagsMonth,
2150
+ tagsTotal
2078
2151
  };
2079
2152
  return r;
2080
2153
  }
@@ -2124,4 +2197,23 @@ function pagingKey(table) {
2124
2197
  return [];
2125
2198
  }
2126
2199
  }
2200
+ const pgInt8Parsed = Symbol('pgInt8Parsed');
2201
+ /**
2202
+ * node-postgres returns int8 values (bigint columns such as satoshis, `count(*)`)
2203
+ * as strings. Set a parser on each connection this knex acquires so they come
2204
+ * back as numbers, as with mysql2 and better-sqlite3. The process-wide `pg.types`
2205
+ * defaults are left unchanged.
2206
+ */
2207
+ function parsePostgresInt8AsNumber(knex) {
2208
+ const client = knex.client;
2209
+ if (client?.dialect !== 'postgresql' || client[pgInt8Parsed] === true)
2210
+ return;
2211
+ client[pgInt8Parsed] = true;
2212
+ const acquireConnection = client.acquireConnection.bind(client);
2213
+ client.acquireConnection = async () => {
2214
+ const connection = await acquireConnection();
2215
+ connection.setTypeParser(20, (value) => Number.parseInt(value, 10));
2216
+ return connection;
2217
+ };
2218
+ }
2127
2219
  //# sourceMappingURL=StorageKnex.js.map