@breeztech/breez-sdk-spark 0.13.10-dev → 0.13.11-dev1
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/breez-sdk-spark.tgz +0 -0
- package/bundler/breez_sdk_spark_wasm.d.ts +33 -0
- package/bundler/breez_sdk_spark_wasm.js +1 -1
- package/bundler/breez_sdk_spark_wasm_bg.js +66 -24
- package/bundler/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/bundler/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
- package/deno/breez_sdk_spark_wasm.d.ts +33 -0
- package/deno/breez_sdk_spark_wasm.js +66 -24
- package/deno/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/deno/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
- package/nodejs/breez_sdk_spark_wasm.d.ts +33 -0
- package/nodejs/breez_sdk_spark_wasm.js +67 -24
- package/nodejs/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/nodejs/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
- package/nodejs/index.js +34 -0
- package/nodejs/index.mjs +1 -0
- package/nodejs/mysql-storage/errors.cjs +19 -0
- package/nodejs/mysql-storage/index.cjs +1366 -0
- package/nodejs/mysql-storage/migrations.cjs +387 -0
- package/nodejs/mysql-storage/package.json +9 -0
- package/nodejs/mysql-token-store/errors.cjs +9 -0
- package/nodejs/mysql-token-store/index.cjs +988 -0
- package/nodejs/mysql-token-store/migrations.cjs +255 -0
- package/nodejs/mysql-token-store/package.json +9 -0
- package/nodejs/mysql-tree-store/errors.cjs +9 -0
- package/nodejs/mysql-tree-store/index.cjs +939 -0
- package/nodejs/mysql-tree-store/migrations.cjs +221 -0
- package/nodejs/mysql-tree-store/package.json +9 -0
- package/nodejs/package.json +3 -0
- package/nodejs/postgres-storage/index.cjs +147 -92
- package/nodejs/postgres-storage/migrations.cjs +85 -4
- package/nodejs/postgres-token-store/index.cjs +176 -89
- package/nodejs/postgres-token-store/migrations.cjs +92 -3
- package/nodejs/postgres-tree-store/index.cjs +168 -83
- package/nodejs/postgres-tree-store/migrations.cjs +80 -3
- package/package.json +1 -1
- package/ssr/index.js +5 -0
- package/web/breez_sdk_spark_wasm.d.ts +40 -5
- package/web/breez_sdk_spark_wasm.js +66 -24
- package/web/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/web/breez_sdk_spark_wasm_bg.wasm.d.ts +7 -5
|
@@ -63,15 +63,26 @@ const SELECT_PAYMENT_SQL = `
|
|
|
63
63
|
lrm.payment_hash AS lnurl_payment_hash,
|
|
64
64
|
pm.parent_payment_id
|
|
65
65
|
FROM payments p
|
|
66
|
-
LEFT JOIN payment_details_lightning l ON p.id = l.payment_id
|
|
67
|
-
LEFT JOIN payment_details_token t ON p.id = t.payment_id
|
|
68
|
-
LEFT JOIN payment_details_spark s ON p.id = s.payment_id
|
|
69
|
-
LEFT JOIN payment_metadata pm ON p.id = pm.payment_id
|
|
70
|
-
LEFT JOIN lnurl_receive_metadata lrm ON l.payment_hash = lrm.payment_hash`;
|
|
66
|
+
LEFT JOIN payment_details_lightning l ON p.id = l.payment_id AND p.user_id = l.user_id
|
|
67
|
+
LEFT JOIN payment_details_token t ON p.id = t.payment_id AND p.user_id = t.user_id
|
|
68
|
+
LEFT JOIN payment_details_spark s ON p.id = s.payment_id AND p.user_id = s.user_id
|
|
69
|
+
LEFT JOIN payment_metadata pm ON p.id = pm.payment_id AND p.user_id = pm.user_id
|
|
70
|
+
LEFT JOIN lnurl_receive_metadata lrm ON l.payment_hash = lrm.payment_hash AND l.user_id = lrm.user_id`;
|
|
71
71
|
|
|
72
72
|
class PostgresStorage {
|
|
73
|
-
|
|
73
|
+
/**
|
|
74
|
+
* @param {import('pg').Pool} pool - Connection pool (may be shared with other tenants).
|
|
75
|
+
* @param {Buffer|Uint8Array} identity - 33-byte secp256k1 compressed pubkey
|
|
76
|
+
* uniquely identifying this tenant. All reads and writes are scoped to it
|
|
77
|
+
* so that multiple instances with distinct identities can share one DB.
|
|
78
|
+
* @param {object} [logger]
|
|
79
|
+
*/
|
|
80
|
+
constructor(pool, identity, logger = null) {
|
|
81
|
+
if (!identity) {
|
|
82
|
+
throw new StorageError("PostgresStorage requires a tenant identity");
|
|
83
|
+
}
|
|
74
84
|
this.pool = pool;
|
|
85
|
+
this.identity = Buffer.from(identity);
|
|
75
86
|
this.logger = logger;
|
|
76
87
|
}
|
|
77
88
|
|
|
@@ -81,7 +92,7 @@ class PostgresStorage {
|
|
|
81
92
|
async initialize() {
|
|
82
93
|
try {
|
|
83
94
|
const migrationManager = new PostgresMigrationManager(this.logger);
|
|
84
|
-
await migrationManager.migrate(this.pool);
|
|
95
|
+
await migrationManager.migrate(this.pool, this.identity);
|
|
85
96
|
return this;
|
|
86
97
|
} catch (error) {
|
|
87
98
|
throw new StorageError(
|
|
@@ -127,8 +138,8 @@ class PostgresStorage {
|
|
|
127
138
|
async getCachedItem(key) {
|
|
128
139
|
try {
|
|
129
140
|
const result = await this.pool.query(
|
|
130
|
-
"SELECT value FROM settings WHERE key = $
|
|
131
|
-
[key]
|
|
141
|
+
"SELECT value FROM settings WHERE user_id = $1 AND key = $2",
|
|
142
|
+
[this.identity, key]
|
|
132
143
|
);
|
|
133
144
|
return result.rows.length > 0 ? result.rows[0].value : null;
|
|
134
145
|
} catch (error) {
|
|
@@ -142,9 +153,9 @@ class PostgresStorage {
|
|
|
142
153
|
async setCachedItem(key, value) {
|
|
143
154
|
try {
|
|
144
155
|
await this.pool.query(
|
|
145
|
-
`INSERT INTO settings (key, value) VALUES ($1, $2)
|
|
146
|
-
ON CONFLICT(key) DO UPDATE SET value = EXCLUDED.value`,
|
|
147
|
-
[key, value]
|
|
156
|
+
`INSERT INTO settings (user_id, key, value) VALUES ($1, $2, $3)
|
|
157
|
+
ON CONFLICT(user_id, key) DO UPDATE SET value = EXCLUDED.value`,
|
|
158
|
+
[this.identity, key, value]
|
|
148
159
|
);
|
|
149
160
|
} catch (error) {
|
|
150
161
|
throw new StorageError(
|
|
@@ -156,7 +167,10 @@ class PostgresStorage {
|
|
|
156
167
|
|
|
157
168
|
async deleteCachedItem(key) {
|
|
158
169
|
try {
|
|
159
|
-
await this.pool.query(
|
|
170
|
+
await this.pool.query(
|
|
171
|
+
"DELETE FROM settings WHERE user_id = $1 AND key = $2",
|
|
172
|
+
[this.identity, key]
|
|
173
|
+
);
|
|
160
174
|
} catch (error) {
|
|
161
175
|
throw new StorageError(
|
|
162
176
|
`Failed to delete cached item '${key}': ${error.message}`,
|
|
@@ -173,9 +187,10 @@ class PostgresStorage {
|
|
|
173
187
|
const actualLimit =
|
|
174
188
|
request.limit != null ? request.limit : 4294967295;
|
|
175
189
|
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
190
|
+
// Tenant scoping is always $1; dynamic filters use $2 onwards.
|
|
191
|
+
const whereClauses = ["p.user_id = $1"];
|
|
192
|
+
const params = [this.identity];
|
|
193
|
+
let paramIdx = 2;
|
|
179
194
|
|
|
180
195
|
// Filter by payment type
|
|
181
196
|
if (request.typeFilter && request.typeFilter.length > 0) {
|
|
@@ -349,9 +364,9 @@ class PostgresStorage {
|
|
|
349
364
|
const spark = payment.details?.type === "spark" ? true : null;
|
|
350
365
|
|
|
351
366
|
await client.query(
|
|
352
|
-
`INSERT INTO payments (id, payment_type, status, amount, fees, timestamp, method, withdraw_tx_id, deposit_tx_id, spark)
|
|
353
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
354
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
367
|
+
`INSERT INTO payments (user_id, id, payment_type, status, amount, fees, timestamp, method, withdraw_tx_id, deposit_tx_id, spark)
|
|
368
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
369
|
+
ON CONFLICT(user_id, id) DO UPDATE SET
|
|
355
370
|
payment_type=EXCLUDED.payment_type,
|
|
356
371
|
status=EXCLUDED.status,
|
|
357
372
|
amount=EXCLUDED.amount,
|
|
@@ -362,6 +377,7 @@ class PostgresStorage {
|
|
|
362
377
|
deposit_tx_id=EXCLUDED.deposit_tx_id,
|
|
363
378
|
spark=EXCLUDED.spark`,
|
|
364
379
|
[
|
|
380
|
+
this.identity,
|
|
365
381
|
payment.id,
|
|
366
382
|
payment.paymentType,
|
|
367
383
|
payment.status,
|
|
@@ -381,12 +397,13 @@ class PostgresStorage {
|
|
|
381
397
|
payment.details.htlcDetails != null)
|
|
382
398
|
) {
|
|
383
399
|
await client.query(
|
|
384
|
-
`INSERT INTO payment_details_spark (payment_id, invoice_details, htlc_details)
|
|
385
|
-
VALUES ($1, $2, $3)
|
|
386
|
-
ON CONFLICT(payment_id) DO UPDATE SET
|
|
400
|
+
`INSERT INTO payment_details_spark (user_id, payment_id, invoice_details, htlc_details)
|
|
401
|
+
VALUES ($1, $2, $3, $4)
|
|
402
|
+
ON CONFLICT(user_id, payment_id) DO UPDATE SET
|
|
387
403
|
invoice_details=COALESCE(EXCLUDED.invoice_details, payment_details_spark.invoice_details),
|
|
388
404
|
htlc_details=COALESCE(EXCLUDED.htlc_details, payment_details_spark.htlc_details)`,
|
|
389
405
|
[
|
|
406
|
+
this.identity,
|
|
390
407
|
payment.id,
|
|
391
408
|
payment.details.invoiceDetails
|
|
392
409
|
? JSON.stringify(payment.details.invoiceDetails)
|
|
@@ -401,9 +418,9 @@ class PostgresStorage {
|
|
|
401
418
|
if (payment.details?.type === "lightning") {
|
|
402
419
|
await client.query(
|
|
403
420
|
`INSERT INTO payment_details_lightning
|
|
404
|
-
(payment_id, invoice, payment_hash, destination_pubkey, description, preimage, htlc_status, htlc_expiry_time)
|
|
405
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
406
|
-
ON CONFLICT(payment_id) DO UPDATE SET
|
|
421
|
+
(user_id, payment_id, invoice, payment_hash, destination_pubkey, description, preimage, htlc_status, htlc_expiry_time)
|
|
422
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
423
|
+
ON CONFLICT(user_id, payment_id) DO UPDATE SET
|
|
407
424
|
invoice=EXCLUDED.invoice,
|
|
408
425
|
payment_hash=EXCLUDED.payment_hash,
|
|
409
426
|
destination_pubkey=EXCLUDED.destination_pubkey,
|
|
@@ -412,6 +429,7 @@ class PostgresStorage {
|
|
|
412
429
|
htlc_status=COALESCE(EXCLUDED.htlc_status, payment_details_lightning.htlc_status),
|
|
413
430
|
htlc_expiry_time=COALESCE(EXCLUDED.htlc_expiry_time, payment_details_lightning.htlc_expiry_time)`,
|
|
414
431
|
[
|
|
432
|
+
this.identity,
|
|
415
433
|
payment.id,
|
|
416
434
|
payment.details.invoice,
|
|
417
435
|
payment.details.htlcDetails.paymentHash,
|
|
@@ -427,14 +445,15 @@ class PostgresStorage {
|
|
|
427
445
|
if (payment.details?.type === "token") {
|
|
428
446
|
await client.query(
|
|
429
447
|
`INSERT INTO payment_details_token
|
|
430
|
-
(payment_id, metadata, tx_hash, tx_type, invoice_details)
|
|
431
|
-
VALUES ($1, $2, $3, $4, $5)
|
|
432
|
-
ON CONFLICT(payment_id) DO UPDATE SET
|
|
448
|
+
(user_id, payment_id, metadata, tx_hash, tx_type, invoice_details)
|
|
449
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
450
|
+
ON CONFLICT(user_id, payment_id) DO UPDATE SET
|
|
433
451
|
metadata=EXCLUDED.metadata,
|
|
434
452
|
tx_hash=EXCLUDED.tx_hash,
|
|
435
453
|
tx_type=EXCLUDED.tx_type,
|
|
436
454
|
invoice_details=COALESCE(EXCLUDED.invoice_details, payment_details_token.invoice_details)`,
|
|
437
455
|
[
|
|
456
|
+
this.identity,
|
|
438
457
|
payment.id,
|
|
439
458
|
JSON.stringify(payment.details.metadata),
|
|
440
459
|
payment.details.txHash,
|
|
@@ -462,8 +481,8 @@ class PostgresStorage {
|
|
|
462
481
|
}
|
|
463
482
|
|
|
464
483
|
const result = await this.pool.query(
|
|
465
|
-
`${SELECT_PAYMENT_SQL} WHERE p.id = $
|
|
466
|
-
[id]
|
|
484
|
+
`${SELECT_PAYMENT_SQL} WHERE p.user_id = $1 AND p.id = $2`,
|
|
485
|
+
[this.identity, id]
|
|
467
486
|
);
|
|
468
487
|
|
|
469
488
|
if (result.rows.length === 0) {
|
|
@@ -487,8 +506,8 @@ class PostgresStorage {
|
|
|
487
506
|
}
|
|
488
507
|
|
|
489
508
|
const result = await this.pool.query(
|
|
490
|
-
`${SELECT_PAYMENT_SQL} WHERE l.invoice = $
|
|
491
|
-
[invoice]
|
|
509
|
+
`${SELECT_PAYMENT_SQL} WHERE p.user_id = $1 AND l.invoice = $2`,
|
|
510
|
+
[this.identity, invoice]
|
|
492
511
|
);
|
|
493
512
|
|
|
494
513
|
if (result.rows.length === 0) {
|
|
@@ -511,22 +530,24 @@ class PostgresStorage {
|
|
|
511
530
|
return {};
|
|
512
531
|
}
|
|
513
532
|
|
|
514
|
-
// Early exit if no related payments exist
|
|
533
|
+
// Early exit if no related payments exist for this tenant
|
|
515
534
|
const hasRelatedResult = await this.pool.query(
|
|
516
|
-
"SELECT EXISTS(SELECT 1 FROM payment_metadata WHERE parent_payment_id IS NOT NULL LIMIT 1)"
|
|
535
|
+
"SELECT EXISTS(SELECT 1 FROM payment_metadata WHERE user_id = $1 AND parent_payment_id IS NOT NULL LIMIT 1)",
|
|
536
|
+
[this.identity]
|
|
517
537
|
);
|
|
518
538
|
if (!hasRelatedResult.rows[0].exists) {
|
|
519
539
|
return {};
|
|
520
540
|
}
|
|
521
541
|
|
|
542
|
+
// $1 is reserved for user_id; parent ids start at $2.
|
|
522
543
|
const placeholders = parentPaymentIds.map(
|
|
523
|
-
(_, i) => `$${i +
|
|
544
|
+
(_, i) => `$${i + 2}`
|
|
524
545
|
);
|
|
525
|
-
const query = `${SELECT_PAYMENT_SQL} WHERE pm.parent_payment_id IN (${placeholders.join(", ")}) ORDER BY p.timestamp ASC`;
|
|
546
|
+
const query = `${SELECT_PAYMENT_SQL} WHERE p.user_id = $1 AND pm.parent_payment_id IN (${placeholders.join(", ")}) ORDER BY p.timestamp ASC`;
|
|
526
547
|
|
|
527
548
|
const queryResult = await this.pool.query(
|
|
528
549
|
query,
|
|
529
|
-
parentPaymentIds
|
|
550
|
+
[this.identity, ...parentPaymentIds]
|
|
530
551
|
);
|
|
531
552
|
|
|
532
553
|
const result = {};
|
|
@@ -551,9 +572,9 @@ class PostgresStorage {
|
|
|
551
572
|
async insertPaymentMetadata(paymentId, metadata) {
|
|
552
573
|
try {
|
|
553
574
|
await this.pool.query(
|
|
554
|
-
`INSERT INTO payment_metadata (payment_id, parent_payment_id, lnurl_pay_info, lnurl_withdraw_info, lnurl_description, conversion_info, conversion_status)
|
|
555
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
556
|
-
ON CONFLICT(payment_id) DO UPDATE SET
|
|
575
|
+
`INSERT INTO payment_metadata (user_id, payment_id, parent_payment_id, lnurl_pay_info, lnurl_withdraw_info, lnurl_description, conversion_info, conversion_status)
|
|
576
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
577
|
+
ON CONFLICT(user_id, payment_id) DO UPDATE SET
|
|
557
578
|
parent_payment_id = COALESCE(EXCLUDED.parent_payment_id, payment_metadata.parent_payment_id),
|
|
558
579
|
lnurl_pay_info = COALESCE(EXCLUDED.lnurl_pay_info, payment_metadata.lnurl_pay_info),
|
|
559
580
|
lnurl_withdraw_info = COALESCE(EXCLUDED.lnurl_withdraw_info, payment_metadata.lnurl_withdraw_info),
|
|
@@ -561,6 +582,7 @@ class PostgresStorage {
|
|
|
561
582
|
conversion_info = COALESCE(EXCLUDED.conversion_info, payment_metadata.conversion_info),
|
|
562
583
|
conversion_status = COALESCE(EXCLUDED.conversion_status, payment_metadata.conversion_status)`,
|
|
563
584
|
[
|
|
585
|
+
this.identity,
|
|
564
586
|
paymentId,
|
|
565
587
|
metadata.parentPaymentId,
|
|
566
588
|
metadata.lnurlPayInfo
|
|
@@ -589,10 +611,10 @@ class PostgresStorage {
|
|
|
589
611
|
async addDeposit(txid, vout, amountSats, isMature) {
|
|
590
612
|
try {
|
|
591
613
|
await this.pool.query(
|
|
592
|
-
`INSERT INTO unclaimed_deposits (txid, vout, amount_sats, is_mature)
|
|
593
|
-
VALUES ($1, $2, $3, $4)
|
|
594
|
-
ON CONFLICT(txid, vout) DO UPDATE SET is_mature = EXCLUDED.is_mature, amount_sats = EXCLUDED.amount_sats`,
|
|
595
|
-
[txid, vout, amountSats, isMature]
|
|
614
|
+
`INSERT INTO unclaimed_deposits (user_id, txid, vout, amount_sats, is_mature)
|
|
615
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
616
|
+
ON CONFLICT(user_id, txid, vout) DO UPDATE SET is_mature = EXCLUDED.is_mature, amount_sats = EXCLUDED.amount_sats`,
|
|
617
|
+
[this.identity, txid, vout, amountSats, isMature]
|
|
596
618
|
);
|
|
597
619
|
} catch (error) {
|
|
598
620
|
throw new StorageError(
|
|
@@ -605,8 +627,8 @@ class PostgresStorage {
|
|
|
605
627
|
async deleteDeposit(txid, vout) {
|
|
606
628
|
try {
|
|
607
629
|
await this.pool.query(
|
|
608
|
-
"DELETE FROM unclaimed_deposits WHERE
|
|
609
|
-
[txid, vout]
|
|
630
|
+
"DELETE FROM unclaimed_deposits WHERE user_id = $1 AND txid = $2 AND vout = $3",
|
|
631
|
+
[this.identity, txid, vout]
|
|
610
632
|
);
|
|
611
633
|
} catch (error) {
|
|
612
634
|
throw new StorageError(
|
|
@@ -619,7 +641,8 @@ class PostgresStorage {
|
|
|
619
641
|
async listDeposits() {
|
|
620
642
|
try {
|
|
621
643
|
const result = await this.pool.query(
|
|
622
|
-
"SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id FROM unclaimed_deposits"
|
|
644
|
+
"SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id FROM unclaimed_deposits WHERE user_id = $1",
|
|
645
|
+
[this.identity]
|
|
623
646
|
);
|
|
624
647
|
|
|
625
648
|
return result.rows.map((row) => ({
|
|
@@ -645,15 +668,15 @@ class PostgresStorage {
|
|
|
645
668
|
await this.pool.query(
|
|
646
669
|
`UPDATE unclaimed_deposits
|
|
647
670
|
SET claim_error = $1, refund_tx = NULL, refund_tx_id = NULL
|
|
648
|
-
WHERE
|
|
649
|
-
[JSON.stringify(payload.error), txid, vout]
|
|
671
|
+
WHERE user_id = $2 AND txid = $3 AND vout = $4`,
|
|
672
|
+
[JSON.stringify(payload.error), this.identity, txid, vout]
|
|
650
673
|
);
|
|
651
674
|
} else if (payload.type === "refund") {
|
|
652
675
|
await this.pool.query(
|
|
653
676
|
`UPDATE unclaimed_deposits
|
|
654
677
|
SET refund_tx = $1, refund_tx_id = $2, claim_error = NULL
|
|
655
|
-
WHERE
|
|
656
|
-
[payload.refundTx, payload.refundTxid, txid, vout]
|
|
678
|
+
WHERE user_id = $3 AND txid = $4 AND vout = $5`,
|
|
679
|
+
[payload.refundTx, payload.refundTxid, this.identity, txid, vout]
|
|
657
680
|
);
|
|
658
681
|
} else {
|
|
659
682
|
throw new StorageError(`Unknown payload type: ${payload.type}`);
|
|
@@ -672,13 +695,14 @@ class PostgresStorage {
|
|
|
672
695
|
await this._withTransaction(async (client) => {
|
|
673
696
|
for (const item of metadata) {
|
|
674
697
|
await client.query(
|
|
675
|
-
`INSERT INTO lnurl_receive_metadata (payment_hash, nostr_zap_request, nostr_zap_receipt, sender_comment)
|
|
676
|
-
VALUES ($1, $2, $3, $4)
|
|
677
|
-
ON CONFLICT(payment_hash) DO UPDATE SET
|
|
698
|
+
`INSERT INTO lnurl_receive_metadata (user_id, payment_hash, nostr_zap_request, nostr_zap_receipt, sender_comment)
|
|
699
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
700
|
+
ON CONFLICT(user_id, payment_hash) DO UPDATE SET
|
|
678
701
|
nostr_zap_request = EXCLUDED.nostr_zap_request,
|
|
679
702
|
nostr_zap_receipt = EXCLUDED.nostr_zap_receipt,
|
|
680
703
|
sender_comment = EXCLUDED.sender_comment`,
|
|
681
704
|
[
|
|
705
|
+
this.identity,
|
|
682
706
|
item.paymentHash,
|
|
683
707
|
item.nostrZapRequest || null,
|
|
684
708
|
item.nostrZapReceipt || null,
|
|
@@ -833,9 +857,10 @@ class PostgresStorage {
|
|
|
833
857
|
const result = await this.pool.query(
|
|
834
858
|
`SELECT id, name, payment_identifier, created_at, updated_at
|
|
835
859
|
FROM contacts
|
|
860
|
+
WHERE user_id = $1
|
|
836
861
|
ORDER BY name ASC
|
|
837
|
-
LIMIT $
|
|
838
|
-
[limit, offset]
|
|
862
|
+
LIMIT $2 OFFSET $3`,
|
|
863
|
+
[this.identity, limit, offset]
|
|
839
864
|
);
|
|
840
865
|
|
|
841
866
|
return result.rows.map((row) => ({
|
|
@@ -858,8 +883,8 @@ class PostgresStorage {
|
|
|
858
883
|
const result = await this.pool.query(
|
|
859
884
|
`SELECT id, name, payment_identifier, created_at, updated_at
|
|
860
885
|
FROM contacts
|
|
861
|
-
WHERE id = $
|
|
862
|
-
[id]
|
|
886
|
+
WHERE user_id = $1 AND id = $2`,
|
|
887
|
+
[this.identity, id]
|
|
863
888
|
);
|
|
864
889
|
|
|
865
890
|
if (result.rows.length === 0) {
|
|
@@ -885,13 +910,14 @@ class PostgresStorage {
|
|
|
885
910
|
async insertContact(contact) {
|
|
886
911
|
try {
|
|
887
912
|
await this.pool.query(
|
|
888
|
-
`INSERT INTO contacts (id, name, payment_identifier, created_at, updated_at)
|
|
889
|
-
VALUES ($1, $2, $3, $4, $5)
|
|
890
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
913
|
+
`INSERT INTO contacts (user_id, id, name, payment_identifier, created_at, updated_at)
|
|
914
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
915
|
+
ON CONFLICT(user_id, id) DO UPDATE SET
|
|
891
916
|
name = EXCLUDED.name,
|
|
892
917
|
payment_identifier = EXCLUDED.payment_identifier,
|
|
893
918
|
updated_at = EXCLUDED.updated_at`,
|
|
894
919
|
[
|
|
920
|
+
this.identity,
|
|
895
921
|
contact.id,
|
|
896
922
|
contact.name,
|
|
897
923
|
contact.paymentIdentifier,
|
|
@@ -909,7 +935,10 @@ class PostgresStorage {
|
|
|
909
935
|
|
|
910
936
|
async deleteContact(id) {
|
|
911
937
|
try {
|
|
912
|
-
await this.pool.query(
|
|
938
|
+
await this.pool.query(
|
|
939
|
+
"DELETE FROM contacts WHERE user_id = $1 AND id = $2",
|
|
940
|
+
[this.identity, id]
|
|
941
|
+
);
|
|
913
942
|
} catch (error) {
|
|
914
943
|
throw new StorageError(
|
|
915
944
|
`Failed to delete contact: ${error.message}`,
|
|
@@ -923,21 +952,25 @@ class PostgresStorage {
|
|
|
923
952
|
async syncAddOutgoingChange(record) {
|
|
924
953
|
try {
|
|
925
954
|
return await this._withTransaction(async (client) => {
|
|
955
|
+
// Local queue revision is per-tenant — two tenants don't share a queue.
|
|
926
956
|
const revisionResult = await client.query(
|
|
927
|
-
"SELECT COALESCE(MAX(revision), 0) + 1 AS revision FROM sync_outgoing"
|
|
957
|
+
"SELECT COALESCE(MAX(revision), 0) + 1 AS revision FROM sync_outgoing WHERE user_id = $1",
|
|
958
|
+
[this.identity]
|
|
928
959
|
);
|
|
929
960
|
const revision = BigInt(revisionResult.rows[0].revision);
|
|
930
961
|
|
|
931
962
|
await client.query(
|
|
932
963
|
`INSERT INTO sync_outgoing (
|
|
964
|
+
user_id,
|
|
933
965
|
record_type,
|
|
934
966
|
data_id,
|
|
935
967
|
schema_version,
|
|
936
968
|
commit_time,
|
|
937
969
|
updated_fields_json,
|
|
938
970
|
revision
|
|
939
|
-
) VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
971
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
940
972
|
[
|
|
973
|
+
this.identity,
|
|
941
974
|
record.id.type,
|
|
942
975
|
record.id.dataId,
|
|
943
976
|
record.schemaVersion,
|
|
@@ -962,8 +995,9 @@ class PostgresStorage {
|
|
|
962
995
|
try {
|
|
963
996
|
await this._withTransaction(async (client) => {
|
|
964
997
|
const deleteResult = await client.query(
|
|
965
|
-
"DELETE FROM sync_outgoing WHERE
|
|
998
|
+
"DELETE FROM sync_outgoing WHERE user_id = $1 AND record_type = $2 AND data_id = $3 AND revision = $4",
|
|
966
999
|
[
|
|
1000
|
+
this.identity,
|
|
967
1001
|
record.id.type,
|
|
968
1002
|
record.id.dataId,
|
|
969
1003
|
localRevision.toString(),
|
|
@@ -981,19 +1015,21 @@ class PostgresStorage {
|
|
|
981
1015
|
|
|
982
1016
|
await client.query(
|
|
983
1017
|
`INSERT INTO sync_state (
|
|
1018
|
+
user_id,
|
|
984
1019
|
record_type,
|
|
985
1020
|
data_id,
|
|
986
1021
|
revision,
|
|
987
1022
|
schema_version,
|
|
988
1023
|
commit_time,
|
|
989
1024
|
data
|
|
990
|
-
) VALUES ($1, $2, $3, $4, $5, $6)
|
|
991
|
-
ON CONFLICT(record_type, data_id) DO UPDATE SET
|
|
1025
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
1026
|
+
ON CONFLICT(user_id, record_type, data_id) DO UPDATE SET
|
|
992
1027
|
schema_version = EXCLUDED.schema_version,
|
|
993
1028
|
commit_time = EXCLUDED.commit_time,
|
|
994
1029
|
data = EXCLUDED.data,
|
|
995
1030
|
revision = EXCLUDED.revision`,
|
|
996
1031
|
[
|
|
1032
|
+
this.identity,
|
|
997
1033
|
record.id.type,
|
|
998
1034
|
record.id.dataId,
|
|
999
1035
|
record.revision.toString(),
|
|
@@ -1003,9 +1039,11 @@ class PostgresStorage {
|
|
|
1003
1039
|
]
|
|
1004
1040
|
);
|
|
1005
1041
|
|
|
1042
|
+
// Upsert this tenant's revision row; fresh tenants without a row get one.
|
|
1006
1043
|
await client.query(
|
|
1007
|
-
|
|
1008
|
-
|
|
1044
|
+
`INSERT INTO sync_revision (user_id, revision) VALUES ($1, $2)
|
|
1045
|
+
ON CONFLICT (user_id) DO UPDATE SET revision = GREATEST(sync_revision.revision, EXCLUDED.revision)`,
|
|
1046
|
+
[this.identity, record.revision.toString()]
|
|
1009
1047
|
);
|
|
1010
1048
|
});
|
|
1011
1049
|
} catch (error) {
|
|
@@ -1034,10 +1072,12 @@ class PostgresStorage {
|
|
|
1034
1072
|
FROM sync_outgoing o
|
|
1035
1073
|
LEFT JOIN sync_state e ON
|
|
1036
1074
|
o.record_type = e.record_type AND
|
|
1037
|
-
o.data_id = e.data_id
|
|
1075
|
+
o.data_id = e.data_id AND
|
|
1076
|
+
o.user_id = e.user_id
|
|
1077
|
+
WHERE o.user_id = $1
|
|
1038
1078
|
ORDER BY o.revision ASC
|
|
1039
|
-
LIMIT $
|
|
1040
|
-
[limit]
|
|
1079
|
+
LIMIT $2`,
|
|
1080
|
+
[this.identity, limit]
|
|
1041
1081
|
);
|
|
1042
1082
|
|
|
1043
1083
|
return result.rows.map((row) => {
|
|
@@ -1082,8 +1122,10 @@ class PostgresStorage {
|
|
|
1082
1122
|
|
|
1083
1123
|
async syncGetLastRevision() {
|
|
1084
1124
|
try {
|
|
1125
|
+
// A tenant that hasn't synced anything yet may have no row; treat as 0.
|
|
1085
1126
|
const result = await this.pool.query(
|
|
1086
|
-
"SELECT revision FROM sync_revision"
|
|
1127
|
+
"SELECT revision FROM sync_revision WHERE user_id = $1",
|
|
1128
|
+
[this.identity]
|
|
1087
1129
|
);
|
|
1088
1130
|
return result.rows.length > 0
|
|
1089
1131
|
? BigInt(result.rows[0].revision)
|
|
@@ -1106,18 +1148,20 @@ class PostgresStorage {
|
|
|
1106
1148
|
for (const record of records) {
|
|
1107
1149
|
await client.query(
|
|
1108
1150
|
`INSERT INTO sync_incoming (
|
|
1151
|
+
user_id,
|
|
1109
1152
|
record_type,
|
|
1110
1153
|
data_id,
|
|
1111
1154
|
schema_version,
|
|
1112
1155
|
commit_time,
|
|
1113
1156
|
data,
|
|
1114
1157
|
revision
|
|
1115
|
-
) VALUES ($1, $2, $3, $4, $5, $6)
|
|
1116
|
-
ON CONFLICT(record_type, data_id, revision) DO UPDATE SET
|
|
1158
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
1159
|
+
ON CONFLICT(user_id, record_type, data_id, revision) DO UPDATE SET
|
|
1117
1160
|
schema_version = EXCLUDED.schema_version,
|
|
1118
1161
|
commit_time = EXCLUDED.commit_time,
|
|
1119
1162
|
data = EXCLUDED.data`,
|
|
1120
1163
|
[
|
|
1164
|
+
this.identity,
|
|
1121
1165
|
record.id.type,
|
|
1122
1166
|
record.id.dataId,
|
|
1123
1167
|
record.schemaVersion,
|
|
@@ -1141,10 +1185,11 @@ class PostgresStorage {
|
|
|
1141
1185
|
try {
|
|
1142
1186
|
await this.pool.query(
|
|
1143
1187
|
`DELETE FROM sync_incoming
|
|
1144
|
-
WHERE
|
|
1145
|
-
AND
|
|
1146
|
-
AND
|
|
1147
|
-
|
|
1188
|
+
WHERE user_id = $1
|
|
1189
|
+
AND record_type = $2
|
|
1190
|
+
AND data_id = $3
|
|
1191
|
+
AND revision = $4`,
|
|
1192
|
+
[this.identity, record.id.type, record.id.dataId, record.revision.toString()]
|
|
1148
1193
|
);
|
|
1149
1194
|
} catch (error) {
|
|
1150
1195
|
throw new StorageError(
|
|
@@ -1167,10 +1212,11 @@ class PostgresStorage {
|
|
|
1167
1212
|
e.data AS existing_data,
|
|
1168
1213
|
e.revision AS existing_revision
|
|
1169
1214
|
FROM sync_incoming i
|
|
1170
|
-
LEFT JOIN sync_state e ON i.record_type = e.record_type AND i.data_id = e.data_id
|
|
1215
|
+
LEFT JOIN sync_state e ON i.record_type = e.record_type AND i.data_id = e.data_id AND i.user_id = e.user_id
|
|
1216
|
+
WHERE i.user_id = $1
|
|
1171
1217
|
ORDER BY i.revision ASC
|
|
1172
|
-
LIMIT $
|
|
1173
|
-
[limit]
|
|
1218
|
+
LIMIT $2`,
|
|
1219
|
+
[this.identity, limit]
|
|
1174
1220
|
);
|
|
1175
1221
|
|
|
1176
1222
|
return result.rows.map((row) => {
|
|
@@ -1230,9 +1276,12 @@ class PostgresStorage {
|
|
|
1230
1276
|
FROM sync_outgoing o
|
|
1231
1277
|
LEFT JOIN sync_state e ON
|
|
1232
1278
|
o.record_type = e.record_type AND
|
|
1233
|
-
o.data_id = e.data_id
|
|
1279
|
+
o.data_id = e.data_id AND
|
|
1280
|
+
o.user_id = e.user_id
|
|
1281
|
+
WHERE o.user_id = $1
|
|
1234
1282
|
ORDER BY o.revision DESC
|
|
1235
|
-
LIMIT 1
|
|
1283
|
+
LIMIT 1`,
|
|
1284
|
+
[this.identity]
|
|
1236
1285
|
);
|
|
1237
1286
|
|
|
1238
1287
|
if (result.rows.length === 0) {
|
|
@@ -1284,19 +1333,21 @@ class PostgresStorage {
|
|
|
1284
1333
|
await this._withTransaction(async (client) => {
|
|
1285
1334
|
await client.query(
|
|
1286
1335
|
`INSERT INTO sync_state (
|
|
1336
|
+
user_id,
|
|
1287
1337
|
record_type,
|
|
1288
1338
|
data_id,
|
|
1289
1339
|
revision,
|
|
1290
1340
|
schema_version,
|
|
1291
1341
|
commit_time,
|
|
1292
1342
|
data
|
|
1293
|
-
) VALUES ($1, $2, $3, $4, $5, $6)
|
|
1294
|
-
ON CONFLICT(record_type, data_id) DO UPDATE SET
|
|
1343
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
1344
|
+
ON CONFLICT(user_id, record_type, data_id) DO UPDATE SET
|
|
1295
1345
|
schema_version = EXCLUDED.schema_version,
|
|
1296
1346
|
commit_time = EXCLUDED.commit_time,
|
|
1297
1347
|
data = EXCLUDED.data,
|
|
1298
1348
|
revision = EXCLUDED.revision`,
|
|
1299
1349
|
[
|
|
1350
|
+
this.identity,
|
|
1300
1351
|
record.id.type,
|
|
1301
1352
|
record.id.dataId,
|
|
1302
1353
|
record.revision.toString(),
|
|
@@ -1307,8 +1358,9 @@ class PostgresStorage {
|
|
|
1307
1358
|
);
|
|
1308
1359
|
|
|
1309
1360
|
await client.query(
|
|
1310
|
-
|
|
1311
|
-
|
|
1361
|
+
`INSERT INTO sync_revision (user_id, revision) VALUES ($1, $2)
|
|
1362
|
+
ON CONFLICT (user_id) DO UPDATE SET revision = GREATEST(sync_revision.revision, EXCLUDED.revision)`,
|
|
1363
|
+
[this.identity, record.revision.toString()]
|
|
1312
1364
|
);
|
|
1313
1365
|
});
|
|
1314
1366
|
} catch (error) {
|
|
@@ -1350,12 +1402,14 @@ function defaultPostgresStorageConfig(connectionString) {
|
|
|
1350
1402
|
* @param {number} config.maxPoolSize - Maximum number of connections in the pool
|
|
1351
1403
|
* @param {number} config.createTimeoutSecs - Timeout in seconds for establishing a new connection
|
|
1352
1404
|
* @param {number} config.recycleTimeoutSecs - Timeout in seconds before recycling an idle connection
|
|
1405
|
+
* @param {Buffer|Uint8Array} identity - 33-byte secp256k1 compressed pubkey
|
|
1406
|
+
* uniquely identifying this tenant.
|
|
1353
1407
|
* @param {object} [logger] - Optional logger
|
|
1354
1408
|
* @returns {Promise<PostgresStorage>}
|
|
1355
1409
|
*/
|
|
1356
|
-
async function createPostgresStorage(config, logger = null) {
|
|
1410
|
+
async function createPostgresStorage(config, identity, logger = null) {
|
|
1357
1411
|
const pool = createPostgresPool(config);
|
|
1358
|
-
return createPostgresStorageWithPool(pool, logger);
|
|
1412
|
+
return createPostgresStorageWithPool(pool, identity, logger);
|
|
1359
1413
|
}
|
|
1360
1414
|
|
|
1361
1415
|
/**
|
|
@@ -1378,11 +1432,12 @@ function createPostgresPool(config) {
|
|
|
1378
1432
|
* Create a PostgresStorage instance from an existing pg.Pool.
|
|
1379
1433
|
*
|
|
1380
1434
|
* @param {pg.Pool} pool - An existing connection pool
|
|
1435
|
+
* @param {Buffer|Uint8Array} identity - 33-byte tenant identity (secp256k1 pubkey).
|
|
1381
1436
|
* @param {object} [logger] - Optional logger
|
|
1382
1437
|
* @returns {Promise<PostgresStorage>}
|
|
1383
1438
|
*/
|
|
1384
|
-
async function createPostgresStorageWithPool(pool, logger = null) {
|
|
1385
|
-
const storage = new PostgresStorage(pool, logger);
|
|
1439
|
+
async function createPostgresStorageWithPool(pool, identity, logger = null) {
|
|
1440
|
+
const storage = new PostgresStorage(pool, identity, logger);
|
|
1386
1441
|
await storage.initialize();
|
|
1387
1442
|
return storage;
|
|
1388
1443
|
}
|