commit
fb17b05441
49 changed files with 10182 additions and 7105 deletions
|
|
@ -15,6 +15,7 @@ import { LspOrder } from "./build/src/services/storage/entity/LspOrder.js"
|
|||
import { LndNodeInfo } from "./build/src/services/storage/entity/LndNodeInfo.js"
|
||||
import { TrackedProvider } from "./build/src/services/storage/entity/TrackedProvider.js"
|
||||
import { InviteToken } from "./build/src/services/storage/entity/InviteToken.js"
|
||||
import { DebitAccess } from "./build/src/services/storage/entity/DebitAccess.js"
|
||||
|
||||
import { Initial1703170309875 } from './build/src/services/storage/migrations/1703170309875-initial.js'
|
||||
import { LspOrder1718387847693 } from './build/src/services/storage/migrations/1718387847693-lsp_order.js'
|
||||
|
|
@ -22,13 +23,17 @@ import { LndNodeInfo1720187506189 } from './build/src/services/storage/migration
|
|||
import { LiquidityProvider1719335699480 } from './build/src/services/storage/migrations/1719335699480-liquidity_provider.js'
|
||||
import { CreateInviteTokenTable1721751414878 } from './build/src/services/storage/migrations/1721751414878-create_invite_token_table.js'
|
||||
import { PaymentIndex1721760297610 } from './build/src/services/storage/migrations/1721760297610-payment_index.js'
|
||||
import { DebitAccess1726496225078 } from './build/src/services/storage/migrations/1726496225078-debit_access.js'
|
||||
import { DebitAccessFixes1726685229264 } from './build/src/services/storage/migrations/1726685229264-debit_access_fixes.js'
|
||||
import { DebitToPub1727105758354 } from './build/src/services/storage/migrations/1727105758354-debit_to_pub.js'
|
||||
import { UserCbUrl1727112281043 } from './build/src/services/storage/migrations/1727112281043-user_cb_url.js'
|
||||
export default new DataSource({
|
||||
type: "sqlite",
|
||||
database: "db.sqlite",
|
||||
// logging: true,
|
||||
migrations: [Initial1703170309875, LspOrder1718387847693, LiquidityProvider1719335699480, LndNodeInfo1720187506189, CreateInviteTokenTable1721751414878, PaymentIndex1721760297610],
|
||||
migrations: [Initial1703170309875, LspOrder1718387847693, LiquidityProvider1719335699480, LndNodeInfo1720187506189, CreateInviteTokenTable1721751414878, PaymentIndex1721760297610, DebitAccess1726496225078, DebitAccessFixes1726685229264, DebitToPub1727105758354, UserCbUrl1727112281043],
|
||||
entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment,
|
||||
UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment, LspOrder, LndNodeInfo, TrackedProvider, InviteToken],
|
||||
UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment, LspOrder, LndNodeInfo, TrackedProvider, InviteToken, DebitAccess],
|
||||
// synchronize: true,
|
||||
})
|
||||
//npx typeorm migration:generate ./src/services/storage/migrations/lnd_node_info -d ./datasource.js
|
||||
//npx typeorm migration:generate ./src/services/storage/migrations/usert_cb_url -d ./datasource.js
|
||||
173
package-lock.json
generated
173
package-lock.json
generated
|
|
@ -32,7 +32,7 @@
|
|||
"grpc-tools": "^1.12.4",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"nostr-tools": "^1.9.0",
|
||||
"nostr-tools": "github:shocknet/nostr-tools#19271c4bcc9ff9bf18f9208e2d9fd6870e5f350c",
|
||||
"pg": "^8.4.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
@ -46,7 +46,8 @@
|
|||
"uuid": "^8.3.2",
|
||||
"websocket": "^1.0.34",
|
||||
"websocket-polyfill": "^0.0.3",
|
||||
"why-is-node-running": "^3.2.0"
|
||||
"why-is-node-running": "^3.2.0",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.4",
|
||||
|
|
@ -59,6 +60,7 @@
|
|||
"@types/node-fetch": "^2.6.3",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/websocket": "^1.0.6",
|
||||
"@types/ws": "^8.5.12",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-node": "10.7.0",
|
||||
"typescript": "5.5.4"
|
||||
|
|
@ -191,30 +193,50 @@
|
|||
"node-pre-gyp": "bin/node-pre-gyp"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz",
|
||||
"integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"node_modules/@noble/ciphers": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
|
||||
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.0"
|
||||
"@noble/hashes": "1.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves/node_modules/@noble/hashes": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz",
|
||||
"integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
|
|
@ -417,37 +439,46 @@
|
|||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
]
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@scure/bip32": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.0.tgz",
|
||||
"integrity": "sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
|
||||
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/curves": "~1.1.0",
|
||||
"@noble/hashes": "~1.3.1",
|
||||
"@scure/base": "~1.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
},
|
||||
"node_modules/@scure/bip32/node_modules/@noble/curves": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/curves": "~1.0.0",
|
||||
"@noble/hashes": "~1.3.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
"@noble/hashes": "1.3.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip39": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz",
|
||||
"integrity": "sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
|
||||
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "~1.3.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@sqltools/formatter": {
|
||||
|
|
@ -571,6 +602,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/eccrypto/-/eccrypto-1.1.6.tgz",
|
||||
"integrity": "sha512-rsmcX5LdDZ3xN2W3al6+YR+XNmiQWlXSwVhsU184QOwNQNJ83YpwvAt8a7cT7y3RpVWkKWmXoIFdanI/z38rNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/expect": "^1.20.4",
|
||||
"@types/node": "*"
|
||||
|
|
@ -719,6 +751,16 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz",
|
||||
"integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@uphold/request-logger": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@uphold/request-logger/-/request-logger-2.0.0.tgz",
|
||||
|
|
@ -3760,16 +3802,36 @@
|
|||
}
|
||||
},
|
||||
"node_modules/nostr-tools": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.9.0.tgz",
|
||||
"integrity": "sha512-ZvFf1uiBqWLWhLBHD2nY0KsdSdNWKb3PrQUmYMWxSzfT4k48cDrDJu2qgULkOhQbFX7oty8IpaKnLvixhqefqA==",
|
||||
"version": "2.8.0",
|
||||
"resolved": "git+ssh://git@github.com/shocknet/nostr-tools.git#19271c4bcc9ff9bf18f9208e2d9fd6870e5f350c",
|
||||
"integrity": "sha512-NWYu4yx9UELd4M333r6Eirg0lKK9vyEiUW/8DDorZtxSlBdH4B9FqH8w7VJrG2LfPJABC89EH6+VNLz7RENCfg==",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@noble/curves": "1.0.0",
|
||||
"@noble/hashes": "1.3.0",
|
||||
"@noble/ciphers": "^0.5.1",
|
||||
"@noble/curves": "1.2.0",
|
||||
"@noble/hashes": "1.3.1",
|
||||
"@scure/base": "1.1.1",
|
||||
"@scure/bip32": "1.3.0",
|
||||
"@scure/bip39": "1.2.0"
|
||||
"@scure/bip32": "1.3.1",
|
||||
"@scure/bip39": "1.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"nostr-wasm": "v0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/nostr-wasm": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz",
|
||||
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/npmlog": {
|
||||
"version": "5.0.1",
|
||||
|
|
@ -5706,6 +5768,27 @@
|
|||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
"test": "npm run clean && tsc && node build/src/tests/testRunner.js",
|
||||
"start": "npm run clean && tsc && node build/src/index.js",
|
||||
"start:ci": "git reset --hard && git pull && npm run start",
|
||||
"gen": "cd proto && rimraf autogenerated && export PATH=$PATH:~/Lightning.Pub/proto && protoc -I ./service --pub_out=. service/*",
|
||||
"build_autogenerated": "cd proto && rimraf autogenerated && protoc -I ./service --pub_out=. service/*",
|
||||
"build_lnd_client_1": "cd proto && protoc -I ./others --plugin=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_out=./lnd --ts_proto_opt=esModuleInterop=true others/* ",
|
||||
"build_lnd_client": "cd proto && rimraf lnd/* && npx protoc --ts_out ./lnd --ts_opt long_type_string --proto_path others others/* ",
|
||||
|
|
@ -48,7 +49,7 @@
|
|||
"grpc-tools": "^1.12.4",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"nostr-tools": "^1.9.0",
|
||||
"nostr-tools": "github:shocknet/nostr-tools#19271c4bcc9ff9bf18f9208e2d9fd6870e5f350c",
|
||||
"pg": "^8.4.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
@ -62,7 +63,8 @@
|
|||
"uuid": "^8.3.2",
|
||||
"websocket": "^1.0.34",
|
||||
"websocket-polyfill": "^0.0.3",
|
||||
"why-is-node-running": "^3.2.0"
|
||||
"why-is-node-running": "^3.2.0",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.4",
|
||||
|
|
@ -75,6 +77,7 @@
|
|||
"@types/node-fetch": "^2.6.3",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/websocket": "^1.0.6",
|
||||
"@types/ws": "^8.5.12",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-node": "10.7.0",
|
||||
"typescript": "5.5.4"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [AuthAppRequest](#AuthAppRequest)
|
||||
- output: [AuthApp](#AuthApp)
|
||||
|
||||
- AuthorizeDebit
|
||||
- auth type: __User__
|
||||
- input: [DebitAuthorizationRequest](#DebitAuthorizationRequest)
|
||||
- output: [DebitAuthorization](#DebitAuthorization)
|
||||
|
||||
- BanDebit
|
||||
- auth type: __User__
|
||||
- input: [DebitOperation](#DebitOperation)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- BanUser
|
||||
- auth type: __Admin__
|
||||
- input: [BanUserRequest](#BanUserRequest)
|
||||
|
|
@ -48,6 +58,11 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [DecodeInvoiceRequest](#DecodeInvoiceRequest)
|
||||
- output: [DecodeInvoiceResponse](#DecodeInvoiceResponse)
|
||||
|
||||
- EditDebit
|
||||
- auth type: __User__
|
||||
- input: [DebitAuthorizationRequest](#DebitAuthorizationRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- EnrollAdminToken
|
||||
- auth type: __User__
|
||||
- input: [EnrollAdminTokenRequest](#EnrollAdminTokenRequest)
|
||||
|
|
@ -58,6 +73,11 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [AppsMetricsRequest](#AppsMetricsRequest)
|
||||
- output: [AppsMetrics](#AppsMetrics)
|
||||
|
||||
- GetDebitAuthorizations
|
||||
- auth type: __User__
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [DebitAuthorizations](#DebitAuthorizations)
|
||||
|
||||
- GetHttpCreds
|
||||
- auth type: __User__
|
||||
- This methods has an __empty__ __request__ body
|
||||
|
|
@ -73,6 +93,11 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- This methods has an __empty__ __request__ body
|
||||
- output: [LnurlLinkResponse](#LnurlLinkResponse)
|
||||
|
||||
- GetLiveDebitRequests
|
||||
- auth type: __User__
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [LiveDebitRequest](#LiveDebitRequest)
|
||||
|
||||
- GetLiveUserOperations
|
||||
- auth type: __User__
|
||||
- This methods has an __empty__ __request__ body
|
||||
|
|
@ -170,6 +195,21 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [PayInvoiceRequest](#PayInvoiceRequest)
|
||||
- output: [PayInvoiceResponse](#PayInvoiceResponse)
|
||||
|
||||
- ResetDebit
|
||||
- auth type: __User__
|
||||
- input: [DebitOperation](#DebitOperation)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- RespondToDebit
|
||||
- auth type: __User__
|
||||
- input: [DebitResponse](#DebitResponse)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- UpdateCallbackUrl
|
||||
- auth type: __User__
|
||||
- input: [CallbackUrl](#CallbackUrl)
|
||||
- output: [CallbackUrl](#CallbackUrl)
|
||||
|
||||
- UseInviteLink
|
||||
- auth type: __GuestWithPub__
|
||||
- input: [UseInviteLinkRequest](#UseInviteLinkRequest)
|
||||
|
|
@ -256,6 +296,20 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [AuthAppRequest](#AuthAppRequest)
|
||||
- output: [AuthApp](#AuthApp)
|
||||
|
||||
- AuthorizeDebit
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/debit/authorize__
|
||||
- input: [DebitAuthorizationRequest](#DebitAuthorizationRequest)
|
||||
- output: [DebitAuthorization](#DebitAuthorization)
|
||||
|
||||
- BanDebit
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/debit/ban__
|
||||
- input: [DebitOperation](#DebitOperation)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- BanUser
|
||||
- auth type: __Admin__
|
||||
- http method: __post__
|
||||
|
|
@ -284,6 +338,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [DecodeInvoiceRequest](#DecodeInvoiceRequest)
|
||||
- output: [DecodeInvoiceResponse](#DecodeInvoiceResponse)
|
||||
|
||||
- EditDebit
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/debit/edit__
|
||||
- input: [DebitAuthorizationRequest](#DebitAuthorizationRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- EncryptionExchange
|
||||
- auth type: __Guest__
|
||||
- http method: __post__
|
||||
|
|
@ -326,6 +387,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [AppsMetricsRequest](#AppsMetricsRequest)
|
||||
- output: [AppsMetrics](#AppsMetrics)
|
||||
|
||||
- GetDebitAuthorizations
|
||||
- auth type: __User__
|
||||
- http method: __get__
|
||||
- http route: __/api/user/debit/get__
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [DebitAuthorizations](#DebitAuthorizations)
|
||||
|
||||
- GetHttpCreds
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
|
|
@ -347,6 +415,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- This methods has an __empty__ __request__ body
|
||||
- output: [LnurlLinkResponse](#LnurlLinkResponse)
|
||||
|
||||
- GetLiveDebitRequests
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/debit/sub__
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [LiveDebitRequest](#LiveDebitRequest)
|
||||
|
||||
- GetLiveUserOperations
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
|
|
@ -552,6 +627,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [RequestNPubLinkingTokenRequest](#RequestNPubLinkingTokenRequest)
|
||||
- output: [RequestNPubLinkingTokenResponse](#RequestNPubLinkingTokenResponse)
|
||||
|
||||
- ResetDebit
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/debit/reset__
|
||||
- input: [DebitOperation](#DebitOperation)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- ResetNPubLinkingToken
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
|
|
@ -559,6 +641,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [RequestNPubLinkingTokenRequest](#RequestNPubLinkingTokenRequest)
|
||||
- output: [RequestNPubLinkingTokenResponse](#RequestNPubLinkingTokenResponse)
|
||||
|
||||
- RespondToDebit
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/debit/finish__
|
||||
- input: [DebitResponse](#DebitResponse)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- SendAppUserToAppPayment
|
||||
- auth type: __App__
|
||||
- http method: __post__
|
||||
|
|
@ -594,6 +683,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [SetMockInvoiceAsPaidRequest](#SetMockInvoiceAsPaidRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- UpdateCallbackUrl
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/cb/update__
|
||||
- input: [CallbackUrl](#CallbackUrl)
|
||||
- output: [CallbackUrl](#CallbackUrl)
|
||||
|
||||
- UseInviteLink
|
||||
- auth type: __GuestWithPub__
|
||||
- http method: __post__
|
||||
|
|
@ -688,6 +784,9 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __nostr_pub__: _string_
|
||||
- __user_identifier__: _string_
|
||||
|
||||
### CallbackUrl
|
||||
- __url__: _string_
|
||||
|
||||
### ClosedChannel
|
||||
- __capacity__: _number_
|
||||
- __channel_id__: _string_
|
||||
|
|
@ -702,6 +801,34 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
### CreateOneTimeInviteLinkResponse
|
||||
- __invitation_link__: _string_
|
||||
|
||||
### DebitAuthorization
|
||||
- __authorized__: _boolean_
|
||||
- __debit_id__: _string_
|
||||
- __npub__: _string_
|
||||
- __rules__: ARRAY of: _[DebitRule](#DebitRule)_
|
||||
|
||||
### DebitAuthorizationRequest
|
||||
- __authorize_npub__: _string_
|
||||
- __request_id__: _string_ *this field is optional
|
||||
- __rules__: ARRAY of: _[DebitRule](#DebitRule)_
|
||||
|
||||
### DebitAuthorizations
|
||||
- __debits__: ARRAY of: _[DebitAuthorization](#DebitAuthorization)_
|
||||
|
||||
### DebitExpirationRule
|
||||
- __expires_at_unix__: _number_
|
||||
|
||||
### DebitOperation
|
||||
- __npub__: _string_
|
||||
|
||||
### DebitResponse
|
||||
- __npub__: _string_
|
||||
- __request_id__: _string_
|
||||
- __response__: _[DebitResponse_response](#DebitResponse_response)_
|
||||
|
||||
### DebitRule
|
||||
- __rule__: _[DebitRule_rule](#DebitRule_rule)_
|
||||
|
||||
### DecodeInvoiceRequest
|
||||
- __invoice__: _string_
|
||||
|
||||
|
|
@ -717,6 +844,11 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
### EnrollAdminTokenRequest
|
||||
- __admin_token__: _string_
|
||||
|
||||
### FrequencyRule
|
||||
- __amount__: _number_
|
||||
- __interval__: _[IntervalType](#IntervalType)_
|
||||
- __number_of_intervals__: _number_
|
||||
|
||||
### GetAppUserLNURLInfoRequest
|
||||
- __base_url_override__: _string_
|
||||
- __user_identifier__: _string_
|
||||
|
|
@ -768,6 +900,11 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
### LinkNPubThroughTokenRequest
|
||||
- __token__: _string_
|
||||
|
||||
### LiveDebitRequest
|
||||
- __debit__: _[LiveDebitRequest_debit](#LiveDebitRequest_debit)_
|
||||
- __npub__: _string_
|
||||
- __request_id__: _string_
|
||||
|
||||
### LiveUserOperation
|
||||
- __operation__: _[UserOperation](#UserOperation)_
|
||||
|
||||
|
|
@ -874,11 +1011,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
|
||||
### PayAppUserInvoiceRequest
|
||||
- __amount__: _number_
|
||||
- __debit_npub__: _string_ *this field is optional
|
||||
- __invoice__: _string_
|
||||
- __user_identifier__: _string_
|
||||
|
||||
### PayInvoiceRequest
|
||||
- __amount__: _number_
|
||||
- __debit_npub__: _string_ *this field is optional
|
||||
- __invoice__: _string_
|
||||
|
||||
### PayInvoiceResponse
|
||||
|
|
@ -963,7 +1102,9 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
### UserInfo
|
||||
- __balance__: _number_
|
||||
- __bridge_url__: _string_
|
||||
- __callback_url__: _string_
|
||||
- __max_withdrawable__: _number_
|
||||
- __ndebit__: _string_
|
||||
- __network_max_fee_bps__: _number_
|
||||
- __network_max_fee_fixed__: _number_
|
||||
- __noffer__: _string_
|
||||
|
|
@ -1004,6 +1145,11 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __TAPROOT_PUBKEY__
|
||||
- __WITNESS_PUBKEY_HASH__
|
||||
|
||||
### IntervalType
|
||||
- __DAY__
|
||||
- __MONTH__
|
||||
- __WEEK__
|
||||
|
||||
### UserOperationType
|
||||
- __INCOMING_INVOICE__
|
||||
- __INCOMING_TX__
|
||||
|
|
|
|||
1656
proto/autogenerated/go/http_client.go
Normal file
1656
proto/autogenerated/go/http_client.go
Normal file
File diff suppressed because it is too large
Load diff
544
proto/autogenerated/go/types.go
Normal file
544
proto/autogenerated/go/types.go
Normal file
|
|
@ -0,0 +1,544 @@
|
|||
// This file was autogenerated from a .proto file, DO NOT EDIT!
|
||||
|
||||
package lightning_pub
|
||||
|
||||
type ResultError struct {
|
||||
Status string `json:"status"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
type AdminContext struct {
|
||||
Admin_id string `json:"admin_id"`
|
||||
}
|
||||
type AppContext struct {
|
||||
App_id string `json:"app_id"`
|
||||
}
|
||||
type GuestContext struct {
|
||||
}
|
||||
type GuestWithPubContext struct {
|
||||
App_id string `json:"app_id"`
|
||||
Pub string `json:"pub"`
|
||||
}
|
||||
type MetricsContext struct {
|
||||
Operator_id string `json:"operator_id"`
|
||||
}
|
||||
type UserContext struct {
|
||||
App_id string `json:"app_id"`
|
||||
App_user_id string `json:"app_user_id"`
|
||||
User_id string `json:"user_id"`
|
||||
}
|
||||
type GetLnurlPayInfo_Query struct {
|
||||
K1 *string `json:"k1,omitempty"`
|
||||
}
|
||||
type GetLnurlWithdrawInfo_Query struct {
|
||||
K1 *string `json:"k1,omitempty"`
|
||||
}
|
||||
type HandleLnurlAddress_RouteParams struct {
|
||||
Address_name string `json:"address_name"`
|
||||
}
|
||||
type HandleLnurlPay_Query struct {
|
||||
Amount *string `json:"amount,omitempty"`
|
||||
K1 *string `json:"k1,omitempty"`
|
||||
Lnurl *string `json:"lnurl,omitempty"`
|
||||
Nostr *string `json:"nostr,omitempty"`
|
||||
}
|
||||
type HandleLnurlWithdraw_Query struct {
|
||||
K1 *string `json:"k1,omitempty"`
|
||||
Pr *string `json:"pr,omitempty"`
|
||||
}
|
||||
type NewProductInvoice_Query struct {
|
||||
Id *string `json:"id,omitempty"`
|
||||
}
|
||||
type AddressType string
|
||||
|
||||
const (
|
||||
NESTED_PUBKEY_HASH AddressType = "NESTED_PUBKEY_HASH"
|
||||
TAPROOT_PUBKEY AddressType = "TAPROOT_PUBKEY"
|
||||
WITNESS_PUBKEY_HASH AddressType = "WITNESS_PUBKEY_HASH"
|
||||
)
|
||||
|
||||
type IntervalType string
|
||||
|
||||
const (
|
||||
DAY IntervalType = "DAY"
|
||||
MONTH IntervalType = "MONTH"
|
||||
WEEK IntervalType = "WEEK"
|
||||
)
|
||||
|
||||
type UserOperationType string
|
||||
|
||||
const (
|
||||
INCOMING_INVOICE UserOperationType = "INCOMING_INVOICE"
|
||||
INCOMING_TX UserOperationType = "INCOMING_TX"
|
||||
INCOMING_USER_TO_USER UserOperationType = "INCOMING_USER_TO_USER"
|
||||
OUTGOING_INVOICE UserOperationType = "OUTGOING_INVOICE"
|
||||
OUTGOING_TX UserOperationType = "OUTGOING_TX"
|
||||
OUTGOING_USER_TO_USER UserOperationType = "OUTGOING_USER_TO_USER"
|
||||
)
|
||||
|
||||
type AddAppInvoiceRequest struct {
|
||||
Http_callback_url string `json:"http_callback_url"`
|
||||
Invoice_req *NewInvoiceRequest `json:"invoice_req"`
|
||||
Payer_identifier string `json:"payer_identifier"`
|
||||
}
|
||||
type AddAppRequest struct {
|
||||
Allow_user_creation bool `json:"allow_user_creation"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
type AddAppUserInvoiceRequest struct {
|
||||
Http_callback_url string `json:"http_callback_url"`
|
||||
Invoice_req *NewInvoiceRequest `json:"invoice_req"`
|
||||
Payer_identifier string `json:"payer_identifier"`
|
||||
Receiver_identifier string `json:"receiver_identifier"`
|
||||
}
|
||||
type AddAppUserRequest struct {
|
||||
Balance int64 `json:"balance"`
|
||||
Fail_if_exists bool `json:"fail_if_exists"`
|
||||
Identifier string `json:"identifier"`
|
||||
}
|
||||
type AddProductRequest struct {
|
||||
Name string `json:"name"`
|
||||
Price_sats int64 `json:"price_sats"`
|
||||
}
|
||||
type AppMetrics struct {
|
||||
App *Application `json:"app"`
|
||||
Available int64 `json:"available"`
|
||||
Fees int64 `json:"fees"`
|
||||
Invoices int64 `json:"invoices"`
|
||||
Operations []UserOperation `json:"operations"`
|
||||
Received int64 `json:"received"`
|
||||
Spent int64 `json:"spent"`
|
||||
Total_fees int64 `json:"total_fees"`
|
||||
Users *UsersInfo `json:"users"`
|
||||
}
|
||||
type AppUser struct {
|
||||
Identifier string `json:"identifier"`
|
||||
Info *UserInfo `json:"info"`
|
||||
Max_withdrawable int64 `json:"max_withdrawable"`
|
||||
}
|
||||
type Application struct {
|
||||
Balance int64 `json:"balance"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Npub string `json:"npub"`
|
||||
}
|
||||
type AppsMetrics struct {
|
||||
Apps []AppMetrics `json:"apps"`
|
||||
}
|
||||
type AppsMetricsRequest struct {
|
||||
From_unix int64 `json:"from_unix"`
|
||||
Include_operations bool `json:"include_operations"`
|
||||
To_unix int64 `json:"to_unix"`
|
||||
}
|
||||
type AuthApp struct {
|
||||
App *Application `json:"app"`
|
||||
Auth_token string `json:"auth_token"`
|
||||
}
|
||||
type AuthAppRequest struct {
|
||||
Allow_user_creation bool `json:"allow_user_creation"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
type BanUserRequest struct {
|
||||
User_id string `json:"user_id"`
|
||||
}
|
||||
type BanUserResponse struct {
|
||||
Balance_sats int64 `json:"balance_sats"`
|
||||
Banned_app_users []BannedAppUser `json:"banned_app_users"`
|
||||
}
|
||||
type BannedAppUser struct {
|
||||
App_id string `json:"app_id"`
|
||||
App_name string `json:"app_name"`
|
||||
Nostr_pub string `json:"nostr_pub"`
|
||||
User_identifier string `json:"user_identifier"`
|
||||
}
|
||||
type CallbackUrl struct {
|
||||
Url string `json:"url"`
|
||||
}
|
||||
type ClosedChannel struct {
|
||||
Capacity int64 `json:"capacity"`
|
||||
Channel_id string `json:"channel_id"`
|
||||
Closed_height int64 `json:"closed_height"`
|
||||
}
|
||||
type ClosureMigration struct {
|
||||
Closes_at_unix int64 `json:"closes_at_unix"`
|
||||
}
|
||||
type CreateOneTimeInviteLinkRequest struct {
|
||||
Sats int64 `json:"sats"`
|
||||
}
|
||||
type CreateOneTimeInviteLinkResponse struct {
|
||||
Invitation_link string `json:"invitation_link"`
|
||||
}
|
||||
type DebitAuthorization struct {
|
||||
Authorized bool `json:"authorized"`
|
||||
Debit_id string `json:"debit_id"`
|
||||
Npub string `json:"npub"`
|
||||
Rules []DebitRule `json:"rules"`
|
||||
}
|
||||
type DebitAuthorizationRequest struct {
|
||||
Authorize_npub string `json:"authorize_npub"`
|
||||
Request_id string `json:"request_id"`
|
||||
Rules []DebitRule `json:"rules"`
|
||||
}
|
||||
type DebitAuthorizations struct {
|
||||
Debits []DebitAuthorization `json:"debits"`
|
||||
}
|
||||
type DebitExpirationRule struct {
|
||||
Expires_at_unix int64 `json:"expires_at_unix"`
|
||||
}
|
||||
type DebitOperation struct {
|
||||
Npub string `json:"npub"`
|
||||
}
|
||||
type DebitResponse struct {
|
||||
Npub string `json:"npub"`
|
||||
Request_id string `json:"request_id"`
|
||||
Response *DebitResponse_response `json:"response"`
|
||||
}
|
||||
type DebitRule struct {
|
||||
Rule *DebitRule_rule `json:"rule"`
|
||||
}
|
||||
type DecodeInvoiceRequest struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
type DecodeInvoiceResponse struct {
|
||||
Amount int64 `json:"amount"`
|
||||
}
|
||||
type Empty struct {
|
||||
}
|
||||
type EncryptionExchangeRequest struct {
|
||||
Deviceid string `json:"deviceId"`
|
||||
Publickey string `json:"publicKey"`
|
||||
}
|
||||
type EnrollAdminTokenRequest struct {
|
||||
Admin_token string `json:"admin_token"`
|
||||
}
|
||||
type FrequencyRule struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Interval IntervalType `json:"interval"`
|
||||
Number_of_intervals int64 `json:"number_of_intervals"`
|
||||
}
|
||||
type GetAppUserLNURLInfoRequest struct {
|
||||
Base_url_override string `json:"base_url_override"`
|
||||
User_identifier string `json:"user_identifier"`
|
||||
}
|
||||
type GetAppUserRequest struct {
|
||||
User_identifier string `json:"user_identifier"`
|
||||
}
|
||||
type GetInviteTokenStateRequest struct {
|
||||
Invite_token string `json:"invite_token"`
|
||||
}
|
||||
type GetInviteTokenStateResponse struct {
|
||||
Used bool `json:"used"`
|
||||
}
|
||||
type GetPaymentStateRequest struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
type GetProductBuyLinkResponse struct {
|
||||
Link string `json:"link"`
|
||||
}
|
||||
type GetUserOperationsRequest struct {
|
||||
Latestincominginvoice int64 `json:"latestIncomingInvoice"`
|
||||
Latestincomingtx int64 `json:"latestIncomingTx"`
|
||||
Latestincomingusertouserpayment int64 `json:"latestIncomingUserToUserPayment"`
|
||||
Latestoutgoinginvoice int64 `json:"latestOutgoingInvoice"`
|
||||
Latestoutgoingtx int64 `json:"latestOutgoingTx"`
|
||||
Latestoutgoingusertouserpayment int64 `json:"latestOutgoingUserToUserPayment"`
|
||||
Max_size int64 `json:"max_size"`
|
||||
}
|
||||
type GetUserOperationsResponse struct {
|
||||
Latestincominginvoiceoperations *UserOperations `json:"latestIncomingInvoiceOperations"`
|
||||
Latestincomingtxoperations *UserOperations `json:"latestIncomingTxOperations"`
|
||||
Latestincomingusertouserpayemnts *UserOperations `json:"latestIncomingUserToUserPayemnts"`
|
||||
Latestoutgoinginvoiceoperations *UserOperations `json:"latestOutgoingInvoiceOperations"`
|
||||
Latestoutgoingtxoperations *UserOperations `json:"latestOutgoingTxOperations"`
|
||||
Latestoutgoingusertouserpayemnts *UserOperations `json:"latestOutgoingUserToUserPayemnts"`
|
||||
}
|
||||
type GraphPoint struct {
|
||||
X int64 `json:"x"`
|
||||
Y int64 `json:"y"`
|
||||
}
|
||||
type HandleLnurlPayResponse struct {
|
||||
Pr string `json:"pr"`
|
||||
Routes []Empty `json:"routes"`
|
||||
}
|
||||
type HttpCreds struct {
|
||||
Token string `json:"token"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
type LinkNPubThroughTokenRequest struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
type LiveDebitRequest struct {
|
||||
Debit *LiveDebitRequest_debit `json:"debit"`
|
||||
Npub string `json:"npub"`
|
||||
Request_id string `json:"request_id"`
|
||||
}
|
||||
type LiveUserOperation struct {
|
||||
Operation *UserOperation `json:"operation"`
|
||||
}
|
||||
type LndChannels struct {
|
||||
Open_channels []OpenChannel `json:"open_channels"`
|
||||
}
|
||||
type LndGetInfoRequest struct {
|
||||
Nodeid int64 `json:"nodeId"`
|
||||
}
|
||||
type LndGetInfoResponse struct {
|
||||
Alias string `json:"alias"`
|
||||
}
|
||||
type LndMetrics struct {
|
||||
Nodes []LndNodeMetrics `json:"nodes"`
|
||||
}
|
||||
type LndMetricsRequest struct {
|
||||
From_unix int64 `json:"from_unix"`
|
||||
To_unix int64 `json:"to_unix"`
|
||||
}
|
||||
type LndNodeMetrics struct {
|
||||
Chain_balance []GraphPoint `json:"chain_balance"`
|
||||
Channel_balance []GraphPoint `json:"channel_balance"`
|
||||
Closed_channels []ClosedChannel `json:"closed_channels"`
|
||||
Closing_channels int64 `json:"closing_channels"`
|
||||
External_balance []GraphPoint `json:"external_balance"`
|
||||
Forwarding_events int64 `json:"forwarding_events"`
|
||||
Forwarding_fees int64 `json:"forwarding_fees"`
|
||||
Offline_channels int64 `json:"offline_channels"`
|
||||
Online_channels int64 `json:"online_channels"`
|
||||
Open_channels []OpenChannel `json:"open_channels"`
|
||||
Pending_channels int64 `json:"pending_channels"`
|
||||
}
|
||||
type LndSeed struct {
|
||||
Seed []string `json:"seed"`
|
||||
}
|
||||
type LnurlLinkResponse struct {
|
||||
K1 string `json:"k1"`
|
||||
Lnurl string `json:"lnurl"`
|
||||
}
|
||||
type LnurlPayInfoResponse struct {
|
||||
Allowsnostr bool `json:"allowsNostr"`
|
||||
Callback string `json:"callback"`
|
||||
Maxsendable int64 `json:"maxSendable"`
|
||||
Metadata string `json:"metadata"`
|
||||
Minsendable int64 `json:"minSendable"`
|
||||
Nostrpubkey string `json:"nostrPubkey"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
type LnurlWithdrawInfoResponse struct {
|
||||
Balancecheck string `json:"balanceCheck"`
|
||||
Callback string `json:"callback"`
|
||||
Defaultdescription string `json:"defaultDescription"`
|
||||
K1 string `json:"k1"`
|
||||
Maxwithdrawable int64 `json:"maxWithdrawable"`
|
||||
Minwithdrawable int64 `json:"minWithdrawable"`
|
||||
Paylink string `json:"payLink"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
type MigrationUpdate struct {
|
||||
Closure *ClosureMigration `json:"closure"`
|
||||
Relays *RelaysMigration `json:"relays"`
|
||||
}
|
||||
type NewAddressRequest struct {
|
||||
Addresstype AddressType `json:"addressType"`
|
||||
}
|
||||
type NewAddressResponse struct {
|
||||
Address string `json:"address"`
|
||||
}
|
||||
type NewInvoiceRequest struct {
|
||||
Amountsats int64 `json:"amountSats"`
|
||||
Memo string `json:"memo"`
|
||||
}
|
||||
type NewInvoiceResponse struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
type OpenChannel struct {
|
||||
Active bool `json:"active"`
|
||||
Capacity int64 `json:"capacity"`
|
||||
Channel_id string `json:"channel_id"`
|
||||
Label string `json:"label"`
|
||||
Lifetime int64 `json:"lifetime"`
|
||||
Local_balance int64 `json:"local_balance"`
|
||||
Remote_balance int64 `json:"remote_balance"`
|
||||
}
|
||||
type OpenChannelRequest struct {
|
||||
Closeaddress string `json:"closeAddress"`
|
||||
Destination string `json:"destination"`
|
||||
Fundingamount int64 `json:"fundingAmount"`
|
||||
Pushamount int64 `json:"pushAmount"`
|
||||
}
|
||||
type OpenChannelResponse struct {
|
||||
Channelid string `json:"channelId"`
|
||||
}
|
||||
type PayAddressRequest struct {
|
||||
Address string `json:"address"`
|
||||
Amoutsats int64 `json:"amoutSats"`
|
||||
Satspervbyte int64 `json:"satsPerVByte"`
|
||||
}
|
||||
type PayAddressResponse struct {
|
||||
Network_fee int64 `json:"network_fee"`
|
||||
Operation_id string `json:"operation_id"`
|
||||
Service_fee int64 `json:"service_fee"`
|
||||
Txid string `json:"txId"`
|
||||
}
|
||||
type PayAppUserInvoiceRequest struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Debit_npub string `json:"debit_npub"`
|
||||
Invoice string `json:"invoice"`
|
||||
User_identifier string `json:"user_identifier"`
|
||||
}
|
||||
type PayInvoiceRequest struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Debit_npub string `json:"debit_npub"`
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
type PayInvoiceResponse struct {
|
||||
Amount_paid int64 `json:"amount_paid"`
|
||||
Network_fee int64 `json:"network_fee"`
|
||||
Operation_id string `json:"operation_id"`
|
||||
Preimage string `json:"preimage"`
|
||||
Service_fee int64 `json:"service_fee"`
|
||||
}
|
||||
type PaymentState struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Network_fee int64 `json:"network_fee"`
|
||||
Paid_at_unix int64 `json:"paid_at_unix"`
|
||||
Service_fee int64 `json:"service_fee"`
|
||||
}
|
||||
type Product struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Noffer string `json:"noffer"`
|
||||
Price_sats int64 `json:"price_sats"`
|
||||
}
|
||||
type RelaysMigration struct {
|
||||
Relays []string `json:"relays"`
|
||||
}
|
||||
type RequestNPubLinkingTokenRequest struct {
|
||||
User_identifier string `json:"user_identifier"`
|
||||
}
|
||||
type RequestNPubLinkingTokenResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
type RoutingEvent struct {
|
||||
Event_type string `json:"event_type"`
|
||||
Failure_string string `json:"failure_string"`
|
||||
Forward_fail_event bool `json:"forward_fail_event"`
|
||||
Incoming_amt_msat int64 `json:"incoming_amt_msat"`
|
||||
Incoming_channel_id int64 `json:"incoming_channel_id"`
|
||||
Incoming_htlc_id int64 `json:"incoming_htlc_id"`
|
||||
Offchain bool `json:"offchain"`
|
||||
Outgoing_amt_msat int64 `json:"outgoing_amt_msat"`
|
||||
Outgoing_channel_id int64 `json:"outgoing_channel_id"`
|
||||
Outgoing_htlc_id int64 `json:"outgoing_htlc_id"`
|
||||
Settled bool `json:"settled"`
|
||||
Timestamp_ns int64 `json:"timestamp_ns"`
|
||||
}
|
||||
type SendAppUserToAppPaymentRequest struct {
|
||||
Amount int64 `json:"amount"`
|
||||
From_user_identifier string `json:"from_user_identifier"`
|
||||
}
|
||||
type SendAppUserToAppUserPaymentRequest struct {
|
||||
Amount int64 `json:"amount"`
|
||||
From_user_identifier string `json:"from_user_identifier"`
|
||||
To_user_identifier string `json:"to_user_identifier"`
|
||||
}
|
||||
type SetMockAppBalanceRequest struct {
|
||||
Amount int64 `json:"amount"`
|
||||
}
|
||||
type SetMockAppUserBalanceRequest struct {
|
||||
Amount int64 `json:"amount"`
|
||||
User_identifier string `json:"user_identifier"`
|
||||
}
|
||||
type SetMockInvoiceAsPaidRequest struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
type UsageMetric struct {
|
||||
Auth_in_nano int64 `json:"auth_in_nano"`
|
||||
Batch bool `json:"batch"`
|
||||
Batch_size int64 `json:"batch_size"`
|
||||
Handle_in_nano int64 `json:"handle_in_nano"`
|
||||
Nostr bool `json:"nostr"`
|
||||
Parsed_in_nano int64 `json:"parsed_in_nano"`
|
||||
Processed_at_ms int64 `json:"processed_at_ms"`
|
||||
Rpc_name string `json:"rpc_name"`
|
||||
Validate_in_nano int64 `json:"validate_in_nano"`
|
||||
}
|
||||
type UsageMetrics struct {
|
||||
Metrics []UsageMetric `json:"metrics"`
|
||||
}
|
||||
type UseInviteLinkRequest struct {
|
||||
Invite_token string `json:"invite_token"`
|
||||
}
|
||||
type UserInfo struct {
|
||||
Balance int64 `json:"balance"`
|
||||
Bridge_url string `json:"bridge_url"`
|
||||
Callback_url string `json:"callback_url"`
|
||||
Max_withdrawable int64 `json:"max_withdrawable"`
|
||||
Ndebit string `json:"ndebit"`
|
||||
Network_max_fee_bps int64 `json:"network_max_fee_bps"`
|
||||
Network_max_fee_fixed int64 `json:"network_max_fee_fixed"`
|
||||
Noffer string `json:"noffer"`
|
||||
Service_fee_bps int64 `json:"service_fee_bps"`
|
||||
Userid string `json:"userId"`
|
||||
User_identifier string `json:"user_identifier"`
|
||||
}
|
||||
type UserOperation struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Confirmed bool `json:"confirmed"`
|
||||
Identifier string `json:"identifier"`
|
||||
Inbound bool `json:"inbound"`
|
||||
Internal bool `json:"internal"`
|
||||
Network_fee int64 `json:"network_fee"`
|
||||
Operationid string `json:"operationId"`
|
||||
Paidatunix int64 `json:"paidAtUnix"`
|
||||
Service_fee int64 `json:"service_fee"`
|
||||
Tx_hash string `json:"tx_hash"`
|
||||
Type UserOperationType `json:"type"`
|
||||
}
|
||||
type UserOperations struct {
|
||||
Fromindex int64 `json:"fromIndex"`
|
||||
Operations []UserOperation `json:"operations"`
|
||||
Toindex int64 `json:"toIndex"`
|
||||
}
|
||||
type UsersInfo struct {
|
||||
Always_been_inactive int64 `json:"always_been_inactive"`
|
||||
Balance_avg int64 `json:"balance_avg"`
|
||||
Balance_median int64 `json:"balance_median"`
|
||||
Negative_balance int64 `json:"negative_balance"`
|
||||
No_balance int64 `json:"no_balance"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
type DebitResponse_response_type string
|
||||
|
||||
const (
|
||||
DENIED DebitResponse_response_type = "denied"
|
||||
INVOICE DebitResponse_response_type = "invoice"
|
||||
)
|
||||
|
||||
type DebitResponse_response struct {
|
||||
Type DebitResponse_response_type `json:"type"`
|
||||
Denied *Empty `json:"denied"`
|
||||
Invoice *string `json:"invoice"`
|
||||
}
|
||||
type DebitRule_rule_type string
|
||||
|
||||
const (
|
||||
EXPIRATION_RULE DebitRule_rule_type = "expiration_rule"
|
||||
FREQUENCY_RULE DebitRule_rule_type = "frequency_rule"
|
||||
)
|
||||
|
||||
type DebitRule_rule struct {
|
||||
Type DebitRule_rule_type `json:"type"`
|
||||
Expiration_rule *DebitExpirationRule `json:"expiration_rule"`
|
||||
Frequency_rule *FrequencyRule `json:"frequency_rule"`
|
||||
}
|
||||
type LiveDebitRequest_debit_type string
|
||||
|
||||
const (
|
||||
FREQUENCY LiveDebitRequest_debit_type = "frequency"
|
||||
FULL_ACCESS LiveDebitRequest_debit_type = "full_access"
|
||||
INVOICE LiveDebitRequest_debit_type = "invoice"
|
||||
)
|
||||
|
||||
type LiveDebitRequest_debit struct {
|
||||
Type LiveDebitRequest_debit_type `json:"type"`
|
||||
Frequency *FrequencyRule `json:"frequency"`
|
||||
Full_access *Empty `json:"full_access"`
|
||||
Invoice *string `json:"invoice"`
|
||||
}
|
||||
|
|
@ -166,6 +166,50 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.AuthorizeDebit) throw new Error('method: AuthorizeDebit is not implemented')
|
||||
app.post('/api/user/debit/authorize', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'AuthorizeDebit', batch: false, nostr: false, batchSize: 0}
|
||||
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||
let authCtx: Types.AuthContext = {}
|
||||
try {
|
||||
if (!methods.AuthorizeDebit) throw new Error('method: AuthorizeDebit is not implemented')
|
||||
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
|
||||
authCtx = authContext
|
||||
stats.guard = process.hrtime.bigint()
|
||||
const request = req.body
|
||||
const error = Types.DebitAuthorizationRequestValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.AuthorizeDebit({rpcName:'AuthorizeDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK', ...response})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.BanDebit) throw new Error('method: BanDebit is not implemented')
|
||||
app.post('/api/user/debit/ban', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'BanDebit', batch: false, nostr: false, batchSize: 0}
|
||||
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||
let authCtx: Types.AuthContext = {}
|
||||
try {
|
||||
if (!methods.BanDebit) throw new Error('method: BanDebit is not implemented')
|
||||
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
|
||||
authCtx = authContext
|
||||
stats.guard = process.hrtime.bigint()
|
||||
const request = req.body
|
||||
const error = Types.DebitOperationValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.BanDebit({rpcName:'BanDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK'})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.BanUser) throw new Error('method: BanUser is not implemented')
|
||||
app.post('/api/admin/user/ban', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'BanUser', batch: false, nostr: false, batchSize: 0}
|
||||
|
|
@ -221,6 +265,30 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'AuthorizeDebit':
|
||||
if (!methods.AuthorizeDebit) {
|
||||
throw new Error('method AuthorizeDebit not found' )
|
||||
} else {
|
||||
const error = Types.DebitAuthorizationRequestValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
const res = await methods.AuthorizeDebit({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'BanDebit':
|
||||
if (!methods.BanDebit) {
|
||||
throw new Error('method BanDebit not found' )
|
||||
} else {
|
||||
const error = Types.DebitOperationValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.BanDebit({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'DecodeInvoice':
|
||||
if (!methods.DecodeInvoice) {
|
||||
throw new Error('method DecodeInvoice not found' )
|
||||
|
|
@ -233,6 +301,18 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'EditDebit':
|
||||
if (!methods.EditDebit) {
|
||||
throw new Error('method EditDebit not found' )
|
||||
} else {
|
||||
const error = Types.DebitAuthorizationRequestValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.EditDebit({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'EnrollAdminToken':
|
||||
if (!methods.EnrollAdminToken) {
|
||||
throw new Error('method EnrollAdminToken not found' )
|
||||
|
|
@ -245,6 +325,16 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetDebitAuthorizations':
|
||||
if (!methods.GetDebitAuthorizations) {
|
||||
throw new Error('method GetDebitAuthorizations not found' )
|
||||
} else {
|
||||
opStats.validate = opStats.guard
|
||||
const res = await methods.GetDebitAuthorizations({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetLNURLChannelLink':
|
||||
if (!methods.GetLNURLChannelLink) {
|
||||
throw new Error('method GetLNURLChannelLink not found' )
|
||||
|
|
@ -379,6 +469,42 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'ResetDebit':
|
||||
if (!methods.ResetDebit) {
|
||||
throw new Error('method ResetDebit not found' )
|
||||
} else {
|
||||
const error = Types.DebitOperationValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.ResetDebit({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'RespondToDebit':
|
||||
if (!methods.RespondToDebit) {
|
||||
throw new Error('method RespondToDebit not found' )
|
||||
} else {
|
||||
const error = Types.DebitResponseValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.RespondToDebit({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'UpdateCallbackUrl':
|
||||
if (!methods.UpdateCallbackUrl) {
|
||||
throw new Error('method UpdateCallbackUrl not found' )
|
||||
} else {
|
||||
const error = Types.CallbackUrlValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
const res = await methods.UpdateCallbackUrl({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'UserHealth':
|
||||
if (!methods.UserHealth) {
|
||||
throw new Error('method UserHealth not found' )
|
||||
|
|
@ -443,6 +569,28 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.EditDebit) throw new Error('method: EditDebit is not implemented')
|
||||
app.post('/api/user/debit/edit', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'EditDebit', batch: false, nostr: false, batchSize: 0}
|
||||
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||
let authCtx: Types.AuthContext = {}
|
||||
try {
|
||||
if (!methods.EditDebit) throw new Error('method: EditDebit is not implemented')
|
||||
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
|
||||
authCtx = authContext
|
||||
stats.guard = process.hrtime.bigint()
|
||||
const request = req.body
|
||||
const error = Types.DebitAuthorizationRequestValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.EditDebit({rpcName:'EditDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK'})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.EncryptionExchange) throw new Error('method: EncryptionExchange is not implemented')
|
||||
app.post('/api/encryption/exchange', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'EncryptionExchange', batch: false, nostr: false, batchSize: 0}
|
||||
|
|
@ -572,6 +720,25 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.GetDebitAuthorizations) throw new Error('method: GetDebitAuthorizations is not implemented')
|
||||
app.get('/api/user/debit/get', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'GetDebitAuthorizations', batch: false, nostr: false, batchSize: 0}
|
||||
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||
let authCtx: Types.AuthContext = {}
|
||||
try {
|
||||
if (!methods.GetDebitAuthorizations) throw new Error('method: GetDebitAuthorizations is not implemented')
|
||||
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
|
||||
authCtx = authContext
|
||||
stats.guard = process.hrtime.bigint()
|
||||
stats.validate = stats.guard
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.GetDebitAuthorizations({rpcName:'GetDebitAuthorizations', ctx:authContext })
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK', ...response})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.GetInviteLinkState) throw new Error('method: GetInviteLinkState is not implemented')
|
||||
app.post('/api/admin/app/invite/get', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'GetInviteLinkState', batch: false, nostr: false, batchSize: 0}
|
||||
|
|
@ -1124,6 +1291,28 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.ResetDebit) throw new Error('method: ResetDebit is not implemented')
|
||||
app.post('/api/user/debit/reset', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'ResetDebit', batch: false, nostr: false, batchSize: 0}
|
||||
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||
let authCtx: Types.AuthContext = {}
|
||||
try {
|
||||
if (!methods.ResetDebit) throw new Error('method: ResetDebit is not implemented')
|
||||
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
|
||||
authCtx = authContext
|
||||
stats.guard = process.hrtime.bigint()
|
||||
const request = req.body
|
||||
const error = Types.DebitOperationValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.ResetDebit({rpcName:'ResetDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK'})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.ResetNPubLinkingToken) throw new Error('method: ResetNPubLinkingToken is not implemented')
|
||||
app.post('/api/app/user/npub/token/reset', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'ResetNPubLinkingToken', batch: false, nostr: false, batchSize: 0}
|
||||
|
|
@ -1146,6 +1335,28 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.RespondToDebit) throw new Error('method: RespondToDebit is not implemented')
|
||||
app.post('/api/user/debit/finish', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'RespondToDebit', batch: false, nostr: false, batchSize: 0}
|
||||
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||
let authCtx: Types.AuthContext = {}
|
||||
try {
|
||||
if (!methods.RespondToDebit) throw new Error('method: RespondToDebit is not implemented')
|
||||
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
|
||||
authCtx = authContext
|
||||
stats.guard = process.hrtime.bigint()
|
||||
const request = req.body
|
||||
const error = Types.DebitResponseValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
await methods.RespondToDebit({rpcName:'RespondToDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK'})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.SendAppUserToAppPayment) throw new Error('method: SendAppUserToAppPayment is not implemented')
|
||||
app.post('/api/app/internal/pay', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'SendAppUserToAppPayment', batch: false, nostr: false, batchSize: 0}
|
||||
|
|
@ -1256,6 +1467,28 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.UpdateCallbackUrl) throw new Error('method: UpdateCallbackUrl is not implemented')
|
||||
app.post('/api/user/cb/update', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'UpdateCallbackUrl', batch: false, nostr: false, batchSize: 0}
|
||||
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||
let authCtx: Types.AuthContext = {}
|
||||
try {
|
||||
if (!methods.UpdateCallbackUrl) throw new Error('method: UpdateCallbackUrl is not implemented')
|
||||
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
|
||||
authCtx = authContext
|
||||
stats.guard = process.hrtime.bigint()
|
||||
const request = req.body
|
||||
const error = Types.CallbackUrlValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
|
||||
const query = req.query
|
||||
const params = req.params
|
||||
const response = await methods.UpdateCallbackUrl({rpcName:'UpdateCallbackUrl', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK', ...response})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
})
|
||||
if (!opts.allowNotImplementedMethods && !methods.UseInviteLink) throw new Error('method: UseInviteLink is not implemented')
|
||||
app.post('/api/guest/invite', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'UseInviteLink', batch: false, nostr: false, batchSize: 0}
|
||||
|
|
|
|||
|
|
@ -101,6 +101,31 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AuthorizeDebit: async (request: Types.DebitAuthorizationRequest): Promise<ResultError | ({ status: 'OK' }& Types.DebitAuthorization)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/debit/authorize'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.DebitAuthorizationValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
BanDebit: async (request: Types.DebitOperation): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/debit/ban'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
BanUser: async (request: Types.BanUserRequest): Promise<ResultError | ({ status: 'OK' }& Types.BanUserResponse)> => {
|
||||
const auth = await params.retrieveAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveAdminAuth() returned null')
|
||||
|
|
@ -154,6 +179,17 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
EditDebit: async (request: Types.DebitAuthorizationRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/debit/edit'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
EncryptionExchange: async (request: Types.EncryptionExchangeRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveGuestAuth()
|
||||
if (auth === null) throw new Error('retrieveGuestAuth() returned null')
|
||||
|
|
@ -232,6 +268,20 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetDebitAuthorizations: async (): Promise<ResultError | ({ status: 'OK' }& Types.DebitAuthorizations)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/debit/get'
|
||||
const { data } = await axios.get(params.baseUrl + finalRoute, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.DebitAuthorizationsValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetHttpCreds: async (cb: (v:ResultError | ({ status: 'OK' }& Types.HttpCreds)) => void): Promise<void> => { throw new Error('http streams are not supported')},
|
||||
GetInviteLinkState: async (request: Types.GetInviteTokenStateRequest): Promise<ResultError | ({ status: 'OK' }& Types.GetInviteTokenStateResponse)> => {
|
||||
const auth = await params.retrieveAdminAuth()
|
||||
|
|
@ -261,6 +311,7 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetLiveDebitRequests: async (cb: (v:ResultError | ({ status: 'OK' }& Types.LiveDebitRequest)) => void): Promise<void> => { throw new Error('http streams are not supported')},
|
||||
GetLiveUserOperations: async (cb: (v:ResultError | ({ status: 'OK' }& Types.LiveUserOperation)) => void): Promise<void> => { throw new Error('http streams are not supported')},
|
||||
GetLndMetrics: async (request: Types.LndMetricsRequest): Promise<ResultError | ({ status: 'OK' }& Types.LndMetrics)> => {
|
||||
const auth = await params.retrieveMetricsAuth()
|
||||
|
|
@ -615,6 +666,17 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
ResetDebit: async (request: Types.DebitOperation): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/debit/reset'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
ResetNPubLinkingToken: async (request: Types.RequestNPubLinkingTokenRequest): Promise<ResultError | ({ status: 'OK' }& Types.RequestNPubLinkingTokenResponse)> => {
|
||||
const auth = await params.retrieveAppAuth()
|
||||
if (auth === null) throw new Error('retrieveAppAuth() returned null')
|
||||
|
|
@ -629,6 +691,17 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
RespondToDebit: async (request: Types.DebitResponse): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/debit/finish'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
SendAppUserToAppPayment: async (request: Types.SendAppUserToAppPaymentRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveAppAuth()
|
||||
if (auth === null) throw new Error('retrieveAppAuth() returned null')
|
||||
|
|
@ -684,6 +757,20 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
UpdateCallbackUrl: async (request: Types.CallbackUrl): Promise<ResultError | ({ status: 'OK' }& Types.CallbackUrl)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/cb/update'
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.CallbackUrlValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
UseInviteLink: async (request: Types.UseInviteLinkRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveGuestWithPubAuth()
|
||||
if (auth === null) throw new Error('retrieveGuestWithPubAuth() returned null')
|
||||
|
|
|
|||
|
|
@ -57,6 +57,33 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AuthorizeDebit: async (request: Types.DebitAuthorizationRequest): Promise<ResultError | ({ status: 'OK' }& Types.DebitAuthorization)> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
nostrRequest.body = request
|
||||
const data = await send(params.pubDestination, {rpcName:'AuthorizeDebit',authIdentifier:auth, ...nostrRequest })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.DebitAuthorizationValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
BanDebit: async (request: Types.DebitOperation): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
nostrRequest.body = request
|
||||
const data = await send(params.pubDestination, {rpcName:'BanDebit',authIdentifier:auth, ...nostrRequest })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
BanUser: async (request: Types.BanUserRequest): Promise<ResultError | ({ status: 'OK' }& Types.BanUserResponse)> => {
|
||||
const auth = await params.retrieveNostrAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrAdminAuth() returned null')
|
||||
|
|
@ -113,6 +140,18 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
EditDebit: async (request: Types.DebitAuthorizationRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
nostrRequest.body = request
|
||||
const data = await send(params.pubDestination, {rpcName:'EditDebit',authIdentifier:auth, ...nostrRequest })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
EnrollAdminToken: async (request: Types.EnrollAdminTokenRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
|
|
@ -140,6 +179,20 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetDebitAuthorizations: async (): Promise<ResultError | ({ status: 'OK' }& Types.DebitAuthorizations)> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
const data = await send(params.pubDestination, {rpcName:'GetDebitAuthorizations',authIdentifier:auth, ...nostrRequest })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.DebitAuthorizationsValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetHttpCreds: async (cb: (res:ResultError | ({ status: 'OK' }& Types.HttpCreds)) => void): Promise<void> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
|
|
@ -184,6 +237,21 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetLiveDebitRequests: async (cb: (res:ResultError | ({ status: 'OK' }& Types.LiveDebitRequest)) => void): Promise<void> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
subscribe(params.pubDestination, {rpcName:'GetLiveDebitRequests',authIdentifier:auth, ...nostrRequest }, (data) => {
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return cb(data)
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return cb({ status: 'OK', ...result })
|
||||
const error = Types.LiveDebitRequestValidate(result)
|
||||
if (error === null) { return cb({ status: 'OK', ...result }) } else return cb({ status: 'ERROR', reason: error.message })
|
||||
}
|
||||
return cb({ status: 'ERROR', reason: 'invalid response' })
|
||||
})
|
||||
},
|
||||
GetLiveUserOperations: async (cb: (res:ResultError | ({ status: 'OK' }& Types.LiveUserOperation)) => void): Promise<void> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
|
|
@ -460,6 +528,45 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
ResetDebit: async (request: Types.DebitOperation): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
nostrRequest.body = request
|
||||
const data = await send(params.pubDestination, {rpcName:'ResetDebit',authIdentifier:auth, ...nostrRequest })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
RespondToDebit: async (request: Types.DebitResponse): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
nostrRequest.body = request
|
||||
const data = await send(params.pubDestination, {rpcName:'RespondToDebit',authIdentifier:auth, ...nostrRequest })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
UpdateCallbackUrl: async (request: Types.CallbackUrl): Promise<ResultError | ({ status: 'OK' }& Types.CallbackUrl)> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
nostrRequest.body = request
|
||||
const data = await send(params.pubDestination, {rpcName:'UpdateCallbackUrl',authIdentifier:auth, ...nostrRequest })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
const result = data
|
||||
if(!params.checkResult) return { status: 'OK', ...result }
|
||||
const error = Types.CallbackUrlValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
UseInviteLink: async (request: Types.UseInviteLinkRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveNostrGuestWithPubAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrGuestWithPubAuth() returned null')
|
||||
|
|
|
|||
|
|
@ -80,6 +80,38 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'AuthorizeDebit':
|
||||
try {
|
||||
if (!methods.AuthorizeDebit) throw new Error('method: AuthorizeDebit is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
|
||||
stats.guard = process.hrtime.bigint()
|
||||
authCtx = authContext
|
||||
const request = req.body
|
||||
const error = Types.DebitAuthorizationRequestValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
|
||||
const response = await methods.AuthorizeDebit({rpcName:'AuthorizeDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res({status: 'OK', ...response})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'BanDebit':
|
||||
try {
|
||||
if (!methods.BanDebit) throw new Error('method: BanDebit is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
|
||||
stats.guard = process.hrtime.bigint()
|
||||
authCtx = authContext
|
||||
const request = req.body
|
||||
const error = Types.DebitOperationValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
|
||||
await methods.BanDebit({rpcName:'BanDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res({status: 'OK'})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'BanUser':
|
||||
try {
|
||||
if (!methods.BanUser) throw new Error('method: BanUser is not implemented')
|
||||
|
|
@ -127,6 +159,30 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'AuthorizeDebit':
|
||||
if (!methods.AuthorizeDebit) {
|
||||
throw new Error('method not defined: AuthorizeDebit')
|
||||
} else {
|
||||
const error = Types.DebitAuthorizationRequestValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
const res = await methods.AuthorizeDebit({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'BanDebit':
|
||||
if (!methods.BanDebit) {
|
||||
throw new Error('method not defined: BanDebit')
|
||||
} else {
|
||||
const error = Types.DebitOperationValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.BanDebit({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'DecodeInvoice':
|
||||
if (!methods.DecodeInvoice) {
|
||||
throw new Error('method not defined: DecodeInvoice')
|
||||
|
|
@ -139,6 +195,18 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'EditDebit':
|
||||
if (!methods.EditDebit) {
|
||||
throw new Error('method not defined: EditDebit')
|
||||
} else {
|
||||
const error = Types.DebitAuthorizationRequestValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.EditDebit({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'EnrollAdminToken':
|
||||
if (!methods.EnrollAdminToken) {
|
||||
throw new Error('method not defined: EnrollAdminToken')
|
||||
|
|
@ -151,6 +219,16 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetDebitAuthorizations':
|
||||
if (!methods.GetDebitAuthorizations) {
|
||||
throw new Error('method not defined: GetDebitAuthorizations')
|
||||
} else {
|
||||
opStats.validate = opStats.guard
|
||||
const res = await methods.GetDebitAuthorizations({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetLNURLChannelLink':
|
||||
if (!methods.GetLNURLChannelLink) {
|
||||
throw new Error('method not defined: GetLNURLChannelLink')
|
||||
|
|
@ -285,6 +363,42 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'ResetDebit':
|
||||
if (!methods.ResetDebit) {
|
||||
throw new Error('method not defined: ResetDebit')
|
||||
} else {
|
||||
const error = Types.DebitOperationValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.ResetDebit({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'RespondToDebit':
|
||||
if (!methods.RespondToDebit) {
|
||||
throw new Error('method not defined: RespondToDebit')
|
||||
} else {
|
||||
const error = Types.DebitResponseValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.RespondToDebit({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'UpdateCallbackUrl':
|
||||
if (!methods.UpdateCallbackUrl) {
|
||||
throw new Error('method not defined: UpdateCallbackUrl')
|
||||
} else {
|
||||
const error = Types.CallbackUrlValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
const res = await methods.UpdateCallbackUrl({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'UserHealth':
|
||||
if (!methods.UserHealth) {
|
||||
throw new Error('method not defined: UserHealth')
|
||||
|
|
@ -337,6 +451,22 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'EditDebit':
|
||||
try {
|
||||
if (!methods.EditDebit) throw new Error('method: EditDebit is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
|
||||
stats.guard = process.hrtime.bigint()
|
||||
authCtx = authContext
|
||||
const request = req.body
|
||||
const error = Types.DebitAuthorizationRequestValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
|
||||
await methods.EditDebit({rpcName:'EditDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res({status: 'OK'})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'EnrollAdminToken':
|
||||
try {
|
||||
if (!methods.EnrollAdminToken) throw new Error('method: EnrollAdminToken is not implemented')
|
||||
|
|
@ -369,6 +499,19 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'GetDebitAuthorizations':
|
||||
try {
|
||||
if (!methods.GetDebitAuthorizations) throw new Error('method: GetDebitAuthorizations is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
|
||||
stats.guard = process.hrtime.bigint()
|
||||
authCtx = authContext
|
||||
stats.validate = stats.guard
|
||||
const response = await methods.GetDebitAuthorizations({rpcName:'GetDebitAuthorizations', ctx:authContext })
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res({status: 'OK', ...response})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'GetHttpCreds':
|
||||
try {
|
||||
if (!methods.GetHttpCreds) throw new Error('method: GetHttpCreds is not implemented')
|
||||
|
|
@ -411,6 +554,19 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'GetLiveDebitRequests':
|
||||
try {
|
||||
if (!methods.GetLiveDebitRequests) throw new Error('method: GetLiveDebitRequests is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
|
||||
stats.guard = process.hrtime.bigint()
|
||||
authCtx = authContext
|
||||
stats.validate = stats.guard
|
||||
methods.GetLiveDebitRequests({rpcName:'GetLiveDebitRequests', ctx:authContext ,cb: (response, err) => {
|
||||
stats.handle = process.hrtime.bigint()
|
||||
if (err) { logErrorAndReturnResponse(err, err.message, res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)} else { res({status: 'OK', ...response});opts.metricsCallback([{ ...info, ...stats, ...authContext }])}
|
||||
}})
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'GetLiveUserOperations':
|
||||
try {
|
||||
if (!methods.GetLiveUserOperations) throw new Error('method: GetLiveUserOperations is not implemented')
|
||||
|
|
@ -688,6 +844,54 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'ResetDebit':
|
||||
try {
|
||||
if (!methods.ResetDebit) throw new Error('method: ResetDebit is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
|
||||
stats.guard = process.hrtime.bigint()
|
||||
authCtx = authContext
|
||||
const request = req.body
|
||||
const error = Types.DebitOperationValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
|
||||
await methods.ResetDebit({rpcName:'ResetDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res({status: 'OK'})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'RespondToDebit':
|
||||
try {
|
||||
if (!methods.RespondToDebit) throw new Error('method: RespondToDebit is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
|
||||
stats.guard = process.hrtime.bigint()
|
||||
authCtx = authContext
|
||||
const request = req.body
|
||||
const error = Types.DebitResponseValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
|
||||
await methods.RespondToDebit({rpcName:'RespondToDebit', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res({status: 'OK'})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'UpdateCallbackUrl':
|
||||
try {
|
||||
if (!methods.UpdateCallbackUrl) throw new Error('method: UpdateCallbackUrl is not implemented')
|
||||
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
|
||||
stats.guard = process.hrtime.bigint()
|
||||
authCtx = authContext
|
||||
const request = req.body
|
||||
const error = Types.CallbackUrlValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
|
||||
const response = await methods.UpdateCallbackUrl({rpcName:'UpdateCallbackUrl', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res({status: 'OK', ...response})
|
||||
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||
break
|
||||
case 'UseInviteLink':
|
||||
try {
|
||||
if (!methods.UseInviteLink) throw new Error('method: UseInviteLink is not implemented')
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ export type UserContext = {
|
|||
app_user_id: string
|
||||
user_id: string
|
||||
}
|
||||
export type UserMethodInputs = AddProduct_Input | DecodeInvoice_Input | EnrollAdminToken_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetPaymentState_Input | GetUserInfo_Input | GetUserOperations_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | OpenChannel_Input | PayAddress_Input | PayInvoice_Input | UserHealth_Input
|
||||
export type UserMethodOutputs = AddProduct_Output | DecodeInvoice_Output | EnrollAdminToken_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetPaymentState_Output | GetUserInfo_Output | GetUserOperations_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | OpenChannel_Output | PayAddress_Output | PayInvoice_Output | UserHealth_Output
|
||||
export type UserMethodInputs = AddProduct_Input | AuthorizeDebit_Input | BanDebit_Input | DecodeInvoice_Input | EditDebit_Input | EnrollAdminToken_Input | GetDebitAuthorizations_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetPaymentState_Input | GetUserInfo_Input | GetUserOperations_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | OpenChannel_Input | PayAddress_Input | PayInvoice_Input | ResetDebit_Input | RespondToDebit_Input | UpdateCallbackUrl_Input | UserHealth_Input
|
||||
export type UserMethodOutputs = AddProduct_Output | AuthorizeDebit_Output | BanDebit_Output | DecodeInvoice_Output | EditDebit_Output | EnrollAdminToken_Output | GetDebitAuthorizations_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetPaymentState_Output | GetUserInfo_Output | GetUserOperations_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | OpenChannel_Output | PayAddress_Output | PayInvoice_Output | ResetDebit_Output | RespondToDebit_Output | UpdateCallbackUrl_Output | UserHealth_Output
|
||||
export type AuthContext = AdminContext | AppContext | GuestContext | GuestWithPubContext | MetricsContext | UserContext
|
||||
|
||||
export type AddApp_Input = {rpcName:'AddApp', req: AddAppRequest}
|
||||
|
|
@ -56,6 +56,12 @@ export type AddProduct_Output = ResultError | ({ status: 'OK' } & Product)
|
|||
export type AuthApp_Input = {rpcName:'AuthApp', req: AuthAppRequest}
|
||||
export type AuthApp_Output = ResultError | ({ status: 'OK' } & AuthApp)
|
||||
|
||||
export type AuthorizeDebit_Input = {rpcName:'AuthorizeDebit', req: DebitAuthorizationRequest}
|
||||
export type AuthorizeDebit_Output = ResultError | ({ status: 'OK' } & DebitAuthorization)
|
||||
|
||||
export type BanDebit_Input = {rpcName:'BanDebit', req: DebitOperation}
|
||||
export type BanDebit_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type BanUser_Input = {rpcName:'BanUser', req: BanUserRequest}
|
||||
export type BanUser_Output = ResultError | ({ status: 'OK' } & BanUserResponse)
|
||||
|
||||
|
|
@ -68,6 +74,9 @@ export type CreateOneTimeInviteLink_Output = ResultError | ({ status: 'OK' } & C
|
|||
export type DecodeInvoice_Input = {rpcName:'DecodeInvoice', req: DecodeInvoiceRequest}
|
||||
export type DecodeInvoice_Output = ResultError | ({ status: 'OK' } & DecodeInvoiceResponse)
|
||||
|
||||
export type EditDebit_Input = {rpcName:'EditDebit', req: DebitAuthorizationRequest}
|
||||
export type EditDebit_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type EncryptionExchange_Input = {rpcName:'EncryptionExchange', req: EncryptionExchangeRequest}
|
||||
export type EncryptionExchange_Output = ResultError | { status: 'OK' }
|
||||
|
||||
|
|
@ -86,6 +95,9 @@ export type GetAppUserLNURLInfo_Output = ResultError | ({ status: 'OK' } & Lnurl
|
|||
export type GetAppsMetrics_Input = {rpcName:'GetAppsMetrics', req: AppsMetricsRequest}
|
||||
export type GetAppsMetrics_Output = ResultError | ({ status: 'OK' } & AppsMetrics)
|
||||
|
||||
export type GetDebitAuthorizations_Input = {rpcName:'GetDebitAuthorizations'}
|
||||
export type GetDebitAuthorizations_Output = ResultError | ({ status: 'OK' } & DebitAuthorizations)
|
||||
|
||||
export type GetHttpCreds_Input = {rpcName:'GetHttpCreds', cb:(res: HttpCreds, err:Error|null)=> void}
|
||||
export type GetHttpCreds_Output = ResultError | { status: 'OK' }
|
||||
|
||||
|
|
@ -95,6 +107,9 @@ export type GetInviteLinkState_Output = ResultError | ({ status: 'OK' } & GetInv
|
|||
export type GetLNURLChannelLink_Input = {rpcName:'GetLNURLChannelLink'}
|
||||
export type GetLNURLChannelLink_Output = ResultError | ({ status: 'OK' } & LnurlLinkResponse)
|
||||
|
||||
export type GetLiveDebitRequests_Input = {rpcName:'GetLiveDebitRequests', cb:(res: LiveDebitRequest, err:Error|null)=> void}
|
||||
export type GetLiveDebitRequests_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type GetLiveUserOperations_Input = {rpcName:'GetLiveUserOperations', cb:(res: LiveUserOperation, err:Error|null)=> void}
|
||||
export type GetLiveUserOperations_Output = ResultError | { status: 'OK' }
|
||||
|
||||
|
|
@ -198,9 +213,15 @@ export type PayInvoice_Output = ResultError | ({ status: 'OK' } & PayInvoiceResp
|
|||
export type RequestNPubLinkingToken_Input = {rpcName:'RequestNPubLinkingToken', req: RequestNPubLinkingTokenRequest}
|
||||
export type RequestNPubLinkingToken_Output = ResultError | ({ status: 'OK' } & RequestNPubLinkingTokenResponse)
|
||||
|
||||
export type ResetDebit_Input = {rpcName:'ResetDebit', req: DebitOperation}
|
||||
export type ResetDebit_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type ResetNPubLinkingToken_Input = {rpcName:'ResetNPubLinkingToken', req: RequestNPubLinkingTokenRequest}
|
||||
export type ResetNPubLinkingToken_Output = ResultError | ({ status: 'OK' } & RequestNPubLinkingTokenResponse)
|
||||
|
||||
export type RespondToDebit_Input = {rpcName:'RespondToDebit', req: DebitResponse}
|
||||
export type RespondToDebit_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type SendAppUserToAppPayment_Input = {rpcName:'SendAppUserToAppPayment', req: SendAppUserToAppPaymentRequest}
|
||||
export type SendAppUserToAppPayment_Output = ResultError | { status: 'OK' }
|
||||
|
||||
|
|
@ -216,6 +237,9 @@ export type SetMockAppUserBalance_Output = ResultError | { status: 'OK' }
|
|||
export type SetMockInvoiceAsPaid_Input = {rpcName:'SetMockInvoiceAsPaid', req: SetMockInvoiceAsPaidRequest}
|
||||
export type SetMockInvoiceAsPaid_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type UpdateCallbackUrl_Input = {rpcName:'UpdateCallbackUrl', req: CallbackUrl}
|
||||
export type UpdateCallbackUrl_Output = ResultError | ({ status: 'OK' } & CallbackUrl)
|
||||
|
||||
export type UseInviteLink_Input = {rpcName:'UseInviteLink', req: UseInviteLinkRequest}
|
||||
export type UseInviteLink_Output = ResultError | { status: 'OK' }
|
||||
|
||||
|
|
@ -229,18 +253,23 @@ export type ServerMethods = {
|
|||
AddAppUserInvoice?: (req: AddAppUserInvoice_Input & {ctx: AppContext }) => Promise<NewInvoiceResponse>
|
||||
AddProduct?: (req: AddProduct_Input & {ctx: UserContext }) => Promise<Product>
|
||||
AuthApp?: (req: AuthApp_Input & {ctx: AdminContext }) => Promise<AuthApp>
|
||||
AuthorizeDebit?: (req: AuthorizeDebit_Input & {ctx: UserContext }) => Promise<DebitAuthorization>
|
||||
BanDebit?: (req: BanDebit_Input & {ctx: UserContext }) => Promise<void>
|
||||
BanUser?: (req: BanUser_Input & {ctx: AdminContext }) => Promise<BanUserResponse>
|
||||
CreateOneTimeInviteLink?: (req: CreateOneTimeInviteLink_Input & {ctx: AdminContext }) => Promise<CreateOneTimeInviteLinkResponse>
|
||||
DecodeInvoice?: (req: DecodeInvoice_Input & {ctx: UserContext }) => Promise<DecodeInvoiceResponse>
|
||||
EditDebit?: (req: EditDebit_Input & {ctx: UserContext }) => Promise<void>
|
||||
EncryptionExchange?: (req: EncryptionExchange_Input & {ctx: GuestContext }) => Promise<void>
|
||||
EnrollAdminToken?: (req: EnrollAdminToken_Input & {ctx: UserContext }) => Promise<void>
|
||||
GetApp?: (req: GetApp_Input & {ctx: AppContext }) => Promise<Application>
|
||||
GetAppUser?: (req: GetAppUser_Input & {ctx: AppContext }) => Promise<AppUser>
|
||||
GetAppUserLNURLInfo?: (req: GetAppUserLNURLInfo_Input & {ctx: AppContext }) => Promise<LnurlPayInfoResponse>
|
||||
GetAppsMetrics?: (req: GetAppsMetrics_Input & {ctx: MetricsContext }) => Promise<AppsMetrics>
|
||||
GetDebitAuthorizations?: (req: GetDebitAuthorizations_Input & {ctx: UserContext }) => Promise<DebitAuthorizations>
|
||||
GetHttpCreds?: (req: GetHttpCreds_Input & {ctx: UserContext }) => Promise<void>
|
||||
GetInviteLinkState?: (req: GetInviteLinkState_Input & {ctx: AdminContext }) => Promise<GetInviteTokenStateResponse>
|
||||
GetLNURLChannelLink?: (req: GetLNURLChannelLink_Input & {ctx: UserContext }) => Promise<LnurlLinkResponse>
|
||||
GetLiveDebitRequests?: (req: GetLiveDebitRequests_Input & {ctx: UserContext }) => Promise<void>
|
||||
GetLiveUserOperations?: (req: GetLiveUserOperations_Input & {ctx: UserContext }) => Promise<void>
|
||||
GetLndMetrics?: (req: GetLndMetrics_Input & {ctx: MetricsContext }) => Promise<LndMetrics>
|
||||
GetLnurlPayInfo?: (req: GetLnurlPayInfo_Input & {ctx: GuestContext }) => Promise<LnurlPayInfoResponse>
|
||||
|
|
@ -268,12 +297,15 @@ export type ServerMethods = {
|
|||
PayAppUserInvoice?: (req: PayAppUserInvoice_Input & {ctx: AppContext }) => Promise<PayInvoiceResponse>
|
||||
PayInvoice?: (req: PayInvoice_Input & {ctx: UserContext }) => Promise<PayInvoiceResponse>
|
||||
RequestNPubLinkingToken?: (req: RequestNPubLinkingToken_Input & {ctx: AppContext }) => Promise<RequestNPubLinkingTokenResponse>
|
||||
ResetDebit?: (req: ResetDebit_Input & {ctx: UserContext }) => Promise<void>
|
||||
ResetNPubLinkingToken?: (req: ResetNPubLinkingToken_Input & {ctx: AppContext }) => Promise<RequestNPubLinkingTokenResponse>
|
||||
RespondToDebit?: (req: RespondToDebit_Input & {ctx: UserContext }) => Promise<void>
|
||||
SendAppUserToAppPayment?: (req: SendAppUserToAppPayment_Input & {ctx: AppContext }) => Promise<void>
|
||||
SendAppUserToAppUserPayment?: (req: SendAppUserToAppUserPayment_Input & {ctx: AppContext }) => Promise<void>
|
||||
SetMockAppBalance?: (req: SetMockAppBalance_Input & {ctx: AppContext }) => Promise<void>
|
||||
SetMockAppUserBalance?: (req: SetMockAppUserBalance_Input & {ctx: AppContext }) => Promise<void>
|
||||
SetMockInvoiceAsPaid?: (req: SetMockInvoiceAsPaid_Input & {ctx: GuestContext }) => Promise<void>
|
||||
UpdateCallbackUrl?: (req: UpdateCallbackUrl_Input & {ctx: UserContext }) => Promise<CallbackUrl>
|
||||
UseInviteLink?: (req: UseInviteLink_Input & {ctx: GuestWithPubContext }) => Promise<void>
|
||||
UserHealth?: (req: UserHealth_Input & {ctx: UserContext }) => Promise<void>
|
||||
}
|
||||
|
|
@ -287,6 +319,15 @@ export const enumCheckAddressType = (e?: AddressType): boolean => {
|
|||
for (const v in AddressType) if (e === v) return true
|
||||
return false
|
||||
}
|
||||
export enum IntervalType {
|
||||
DAY = 'DAY',
|
||||
MONTH = 'MONTH',
|
||||
WEEK = 'WEEK',
|
||||
}
|
||||
export const enumCheckIntervalType = (e?: IntervalType): boolean => {
|
||||
for (const v in IntervalType) if (e === v) return true
|
||||
return false
|
||||
}
|
||||
export enum UserOperationType {
|
||||
INCOMING_INVOICE = 'INCOMING_INVOICE',
|
||||
INCOMING_TX = 'INCOMING_TX',
|
||||
|
|
@ -747,6 +788,24 @@ export const BannedAppUserValidate = (o?: BannedAppUser, opts: BannedAppUserOpti
|
|||
return null
|
||||
}
|
||||
|
||||
export type CallbackUrl = {
|
||||
url: string
|
||||
}
|
||||
export const CallbackUrlOptionalFields: [] = []
|
||||
export type CallbackUrlOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
url_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
export const CallbackUrlValidate = (o?: CallbackUrl, opts: CallbackUrlOptions = {}, path: string = 'CallbackUrl::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
if (typeof o.url !== 'string') return new Error(`${path}.url: is not a string`)
|
||||
if (opts.url_CustomCheck && !opts.url_CustomCheck(o.url)) return new Error(`${path}.url: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type ClosedChannel = {
|
||||
capacity: number
|
||||
channel_id: string
|
||||
|
|
@ -830,6 +889,185 @@ export const CreateOneTimeInviteLinkResponseValidate = (o?: CreateOneTimeInviteL
|
|||
return null
|
||||
}
|
||||
|
||||
export type DebitAuthorization = {
|
||||
authorized: boolean
|
||||
debit_id: string
|
||||
npub: string
|
||||
rules: DebitRule[]
|
||||
}
|
||||
export const DebitAuthorizationOptionalFields: [] = []
|
||||
export type DebitAuthorizationOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
authorized_CustomCheck?: (v: boolean) => boolean
|
||||
debit_id_CustomCheck?: (v: string) => boolean
|
||||
npub_CustomCheck?: (v: string) => boolean
|
||||
rules_ItemOptions?: DebitRuleOptions
|
||||
rules_CustomCheck?: (v: DebitRule[]) => boolean
|
||||
}
|
||||
export const DebitAuthorizationValidate = (o?: DebitAuthorization, opts: DebitAuthorizationOptions = {}, path: string = 'DebitAuthorization::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
if (typeof o.authorized !== 'boolean') return new Error(`${path}.authorized: is not a boolean`)
|
||||
if (opts.authorized_CustomCheck && !opts.authorized_CustomCheck(o.authorized)) return new Error(`${path}.authorized: custom check failed`)
|
||||
|
||||
if (typeof o.debit_id !== 'string') return new Error(`${path}.debit_id: is not a string`)
|
||||
if (opts.debit_id_CustomCheck && !opts.debit_id_CustomCheck(o.debit_id)) return new Error(`${path}.debit_id: custom check failed`)
|
||||
|
||||
if (typeof o.npub !== 'string') return new Error(`${path}.npub: is not a string`)
|
||||
if (opts.npub_CustomCheck && !opts.npub_CustomCheck(o.npub)) return new Error(`${path}.npub: custom check failed`)
|
||||
|
||||
if (!Array.isArray(o.rules)) return new Error(`${path}.rules: is not an array`)
|
||||
for (let index = 0; index < o.rules.length; index++) {
|
||||
const rulesErr = DebitRuleValidate(o.rules[index], opts.rules_ItemOptions, `${path}.rules[${index}]`)
|
||||
if (rulesErr !== null) return rulesErr
|
||||
}
|
||||
if (opts.rules_CustomCheck && !opts.rules_CustomCheck(o.rules)) return new Error(`${path}.rules: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type DebitAuthorizationRequest = {
|
||||
authorize_npub: string
|
||||
request_id?: string
|
||||
rules: DebitRule[]
|
||||
}
|
||||
export type DebitAuthorizationRequestOptionalField = 'request_id'
|
||||
export const DebitAuthorizationRequestOptionalFields: DebitAuthorizationRequestOptionalField[] = ['request_id']
|
||||
export type DebitAuthorizationRequestOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: DebitAuthorizationRequestOptionalField[]
|
||||
authorize_npub_CustomCheck?: (v: string) => boolean
|
||||
request_id_CustomCheck?: (v?: string) => boolean
|
||||
rules_ItemOptions?: DebitRuleOptions
|
||||
rules_CustomCheck?: (v: DebitRule[]) => boolean
|
||||
}
|
||||
export const DebitAuthorizationRequestValidate = (o?: DebitAuthorizationRequest, opts: DebitAuthorizationRequestOptions = {}, path: string = 'DebitAuthorizationRequest::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
if (typeof o.authorize_npub !== 'string') return new Error(`${path}.authorize_npub: is not a string`)
|
||||
if (opts.authorize_npub_CustomCheck && !opts.authorize_npub_CustomCheck(o.authorize_npub)) return new Error(`${path}.authorize_npub: custom check failed`)
|
||||
|
||||
if ((o.request_id || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('request_id')) && typeof o.request_id !== 'string') return new Error(`${path}.request_id: is not a string`)
|
||||
if (opts.request_id_CustomCheck && !opts.request_id_CustomCheck(o.request_id)) return new Error(`${path}.request_id: custom check failed`)
|
||||
|
||||
if (!Array.isArray(o.rules)) return new Error(`${path}.rules: is not an array`)
|
||||
for (let index = 0; index < o.rules.length; index++) {
|
||||
const rulesErr = DebitRuleValidate(o.rules[index], opts.rules_ItemOptions, `${path}.rules[${index}]`)
|
||||
if (rulesErr !== null) return rulesErr
|
||||
}
|
||||
if (opts.rules_CustomCheck && !opts.rules_CustomCheck(o.rules)) return new Error(`${path}.rules: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type DebitAuthorizations = {
|
||||
debits: DebitAuthorization[]
|
||||
}
|
||||
export const DebitAuthorizationsOptionalFields: [] = []
|
||||
export type DebitAuthorizationsOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
debits_ItemOptions?: DebitAuthorizationOptions
|
||||
debits_CustomCheck?: (v: DebitAuthorization[]) => boolean
|
||||
}
|
||||
export const DebitAuthorizationsValidate = (o?: DebitAuthorizations, opts: DebitAuthorizationsOptions = {}, path: string = 'DebitAuthorizations::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
if (!Array.isArray(o.debits)) return new Error(`${path}.debits: is not an array`)
|
||||
for (let index = 0; index < o.debits.length; index++) {
|
||||
const debitsErr = DebitAuthorizationValidate(o.debits[index], opts.debits_ItemOptions, `${path}.debits[${index}]`)
|
||||
if (debitsErr !== null) return debitsErr
|
||||
}
|
||||
if (opts.debits_CustomCheck && !opts.debits_CustomCheck(o.debits)) return new Error(`${path}.debits: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type DebitExpirationRule = {
|
||||
expires_at_unix: number
|
||||
}
|
||||
export const DebitExpirationRuleOptionalFields: [] = []
|
||||
export type DebitExpirationRuleOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
expires_at_unix_CustomCheck?: (v: number) => boolean
|
||||
}
|
||||
export const DebitExpirationRuleValidate = (o?: DebitExpirationRule, opts: DebitExpirationRuleOptions = {}, path: string = 'DebitExpirationRule::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
if (typeof o.expires_at_unix !== 'number') return new Error(`${path}.expires_at_unix: is not a number`)
|
||||
if (opts.expires_at_unix_CustomCheck && !opts.expires_at_unix_CustomCheck(o.expires_at_unix)) return new Error(`${path}.expires_at_unix: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type DebitOperation = {
|
||||
npub: string
|
||||
}
|
||||
export const DebitOperationOptionalFields: [] = []
|
||||
export type DebitOperationOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
npub_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
export const DebitOperationValidate = (o?: DebitOperation, opts: DebitOperationOptions = {}, path: string = 'DebitOperation::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
if (typeof o.npub !== 'string') return new Error(`${path}.npub: is not a string`)
|
||||
if (opts.npub_CustomCheck && !opts.npub_CustomCheck(o.npub)) return new Error(`${path}.npub: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type DebitResponse = {
|
||||
npub: string
|
||||
request_id: string
|
||||
response: DebitResponse_response
|
||||
}
|
||||
export const DebitResponseOptionalFields: [] = []
|
||||
export type DebitResponseOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
npub_CustomCheck?: (v: string) => boolean
|
||||
request_id_CustomCheck?: (v: string) => boolean
|
||||
response_Options?: DebitResponse_responseOptions
|
||||
}
|
||||
export const DebitResponseValidate = (o?: DebitResponse, opts: DebitResponseOptions = {}, path: string = 'DebitResponse::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
if (typeof o.npub !== 'string') return new Error(`${path}.npub: is not a string`)
|
||||
if (opts.npub_CustomCheck && !opts.npub_CustomCheck(o.npub)) return new Error(`${path}.npub: custom check failed`)
|
||||
|
||||
if (typeof o.request_id !== 'string') return new Error(`${path}.request_id: is not a string`)
|
||||
if (opts.request_id_CustomCheck && !opts.request_id_CustomCheck(o.request_id)) return new Error(`${path}.request_id: custom check failed`)
|
||||
|
||||
const responseErr = DebitResponse_responseValidate(o.response, opts.response_Options, `${path}.response`)
|
||||
if (responseErr !== null) return responseErr
|
||||
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type DebitRule = {
|
||||
rule: DebitRule_rule
|
||||
}
|
||||
export const DebitRuleOptionalFields: [] = []
|
||||
export type DebitRuleOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
rule_Options?: DebitRule_ruleOptions
|
||||
}
|
||||
export const DebitRuleValidate = (o?: DebitRule, opts: DebitRuleOptions = {}, path: string = 'DebitRule::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
const ruleErr = DebitRule_ruleValidate(o.rule, opts.rule_Options, `${path}.rule`)
|
||||
if (ruleErr !== null) return ruleErr
|
||||
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type DecodeInvoiceRequest = {
|
||||
invoice: string
|
||||
}
|
||||
|
|
@ -920,6 +1158,34 @@ export const EnrollAdminTokenRequestValidate = (o?: EnrollAdminTokenRequest, opt
|
|||
return null
|
||||
}
|
||||
|
||||
export type FrequencyRule = {
|
||||
amount: number
|
||||
interval: IntervalType
|
||||
number_of_intervals: number
|
||||
}
|
||||
export const FrequencyRuleOptionalFields: [] = []
|
||||
export type FrequencyRuleOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
amount_CustomCheck?: (v: number) => boolean
|
||||
interval_CustomCheck?: (v: IntervalType) => boolean
|
||||
number_of_intervals_CustomCheck?: (v: number) => boolean
|
||||
}
|
||||
export const FrequencyRuleValidate = (o?: FrequencyRule, opts: FrequencyRuleOptions = {}, path: string = 'FrequencyRule::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
if (typeof o.amount !== 'number') return new Error(`${path}.amount: is not a number`)
|
||||
if (opts.amount_CustomCheck && !opts.amount_CustomCheck(o.amount)) return new Error(`${path}.amount: custom check failed`)
|
||||
|
||||
if (!enumCheckIntervalType(o.interval)) return new Error(`${path}.interval: is not a valid IntervalType`)
|
||||
if (opts.interval_CustomCheck && !opts.interval_CustomCheck(o.interval)) return new Error(`${path}.interval: custom check failed`)
|
||||
|
||||
if (typeof o.number_of_intervals !== 'number') return new Error(`${path}.number_of_intervals: is not a number`)
|
||||
if (opts.number_of_intervals_CustomCheck && !opts.number_of_intervals_CustomCheck(o.number_of_intervals)) return new Error(`${path}.number_of_intervals: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type GetAppUserLNURLInfoRequest = {
|
||||
base_url_override: string
|
||||
user_identifier: string
|
||||
|
|
@ -1222,6 +1488,35 @@ export const LinkNPubThroughTokenRequestValidate = (o?: LinkNPubThroughTokenRequ
|
|||
return null
|
||||
}
|
||||
|
||||
export type LiveDebitRequest = {
|
||||
debit: LiveDebitRequest_debit
|
||||
npub: string
|
||||
request_id: string
|
||||
}
|
||||
export const LiveDebitRequestOptionalFields: [] = []
|
||||
export type LiveDebitRequestOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
debit_Options?: LiveDebitRequest_debitOptions
|
||||
npub_CustomCheck?: (v: string) => boolean
|
||||
request_id_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
export const LiveDebitRequestValidate = (o?: LiveDebitRequest, opts: LiveDebitRequestOptions = {}, path: string = 'LiveDebitRequest::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
const debitErr = LiveDebitRequest_debitValidate(o.debit, opts.debit_Options, `${path}.debit`)
|
||||
if (debitErr !== null) return debitErr
|
||||
|
||||
|
||||
if (typeof o.npub !== 'string') return new Error(`${path}.npub: is not a string`)
|
||||
if (opts.npub_CustomCheck && !opts.npub_CustomCheck(o.npub)) return new Error(`${path}.npub: custom check failed`)
|
||||
|
||||
if (typeof o.request_id !== 'string') return new Error(`${path}.request_id: is not a string`)
|
||||
if (opts.request_id_CustomCheck && !opts.request_id_CustomCheck(o.request_id)) return new Error(`${path}.request_id: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type LiveUserOperation = {
|
||||
operation: UserOperation
|
||||
}
|
||||
|
|
@ -1854,13 +2149,16 @@ export const PayAddressResponseValidate = (o?: PayAddressResponse, opts: PayAddr
|
|||
|
||||
export type PayAppUserInvoiceRequest = {
|
||||
amount: number
|
||||
debit_npub?: string
|
||||
invoice: string
|
||||
user_identifier: string
|
||||
}
|
||||
export const PayAppUserInvoiceRequestOptionalFields: [] = []
|
||||
export type PayAppUserInvoiceRequestOptionalField = 'debit_npub'
|
||||
export const PayAppUserInvoiceRequestOptionalFields: PayAppUserInvoiceRequestOptionalField[] = ['debit_npub']
|
||||
export type PayAppUserInvoiceRequestOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
checkOptionalsAreSet?: PayAppUserInvoiceRequestOptionalField[]
|
||||
amount_CustomCheck?: (v: number) => boolean
|
||||
debit_npub_CustomCheck?: (v?: string) => boolean
|
||||
invoice_CustomCheck?: (v: string) => boolean
|
||||
user_identifier_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
|
|
@ -1871,6 +2169,9 @@ export const PayAppUserInvoiceRequestValidate = (o?: PayAppUserInvoiceRequest, o
|
|||
if (typeof o.amount !== 'number') return new Error(`${path}.amount: is not a number`)
|
||||
if (opts.amount_CustomCheck && !opts.amount_CustomCheck(o.amount)) return new Error(`${path}.amount: custom check failed`)
|
||||
|
||||
if ((o.debit_npub || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('debit_npub')) && typeof o.debit_npub !== 'string') return new Error(`${path}.debit_npub: is not a string`)
|
||||
if (opts.debit_npub_CustomCheck && !opts.debit_npub_CustomCheck(o.debit_npub)) return new Error(`${path}.debit_npub: custom check failed`)
|
||||
|
||||
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
|
||||
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
|
||||
|
||||
|
|
@ -1882,12 +2183,15 @@ export const PayAppUserInvoiceRequestValidate = (o?: PayAppUserInvoiceRequest, o
|
|||
|
||||
export type PayInvoiceRequest = {
|
||||
amount: number
|
||||
debit_npub?: string
|
||||
invoice: string
|
||||
}
|
||||
export const PayInvoiceRequestOptionalFields: [] = []
|
||||
export type PayInvoiceRequestOptionalField = 'debit_npub'
|
||||
export const PayInvoiceRequestOptionalFields: PayInvoiceRequestOptionalField[] = ['debit_npub']
|
||||
export type PayInvoiceRequestOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
checkOptionalsAreSet?: PayInvoiceRequestOptionalField[]
|
||||
amount_CustomCheck?: (v: number) => boolean
|
||||
debit_npub_CustomCheck?: (v?: string) => boolean
|
||||
invoice_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
export const PayInvoiceRequestValidate = (o?: PayInvoiceRequest, opts: PayInvoiceRequestOptions = {}, path: string = 'PayInvoiceRequest::root.'): Error | null => {
|
||||
|
|
@ -1897,6 +2201,9 @@ export const PayInvoiceRequestValidate = (o?: PayInvoiceRequest, opts: PayInvoic
|
|||
if (typeof o.amount !== 'number') return new Error(`${path}.amount: is not a number`)
|
||||
if (opts.amount_CustomCheck && !opts.amount_CustomCheck(o.amount)) return new Error(`${path}.amount: custom check failed`)
|
||||
|
||||
if ((o.debit_npub || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('debit_npub')) && typeof o.debit_npub !== 'string') return new Error(`${path}.debit_npub: is not a string`)
|
||||
if (opts.debit_npub_CustomCheck && !opts.debit_npub_CustomCheck(o.debit_npub)) return new Error(`${path}.debit_npub: custom check failed`)
|
||||
|
||||
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
|
||||
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
|
||||
|
||||
|
|
@ -2354,7 +2661,9 @@ export const UseInviteLinkRequestValidate = (o?: UseInviteLinkRequest, opts: Use
|
|||
export type UserInfo = {
|
||||
balance: number
|
||||
bridge_url: string
|
||||
callback_url: string
|
||||
max_withdrawable: number
|
||||
ndebit: string
|
||||
network_max_fee_bps: number
|
||||
network_max_fee_fixed: number
|
||||
noffer: string
|
||||
|
|
@ -2367,7 +2676,9 @@ export type UserInfoOptions = OptionsBaseMessage & {
|
|||
checkOptionalsAreSet?: []
|
||||
balance_CustomCheck?: (v: number) => boolean
|
||||
bridge_url_CustomCheck?: (v: string) => boolean
|
||||
callback_url_CustomCheck?: (v: string) => boolean
|
||||
max_withdrawable_CustomCheck?: (v: number) => boolean
|
||||
ndebit_CustomCheck?: (v: string) => boolean
|
||||
network_max_fee_bps_CustomCheck?: (v: number) => boolean
|
||||
network_max_fee_fixed_CustomCheck?: (v: number) => boolean
|
||||
noffer_CustomCheck?: (v: string) => boolean
|
||||
|
|
@ -2385,9 +2696,15 @@ export const UserInfoValidate = (o?: UserInfo, opts: UserInfoOptions = {}, path:
|
|||
if (typeof o.bridge_url !== 'string') return new Error(`${path}.bridge_url: is not a string`)
|
||||
if (opts.bridge_url_CustomCheck && !opts.bridge_url_CustomCheck(o.bridge_url)) return new Error(`${path}.bridge_url: custom check failed`)
|
||||
|
||||
if (typeof o.callback_url !== 'string') return new Error(`${path}.callback_url: is not a string`)
|
||||
if (opts.callback_url_CustomCheck && !opts.callback_url_CustomCheck(o.callback_url)) return new Error(`${path}.callback_url: custom check failed`)
|
||||
|
||||
if (typeof o.max_withdrawable !== 'number') return new Error(`${path}.max_withdrawable: is not a number`)
|
||||
if (opts.max_withdrawable_CustomCheck && !opts.max_withdrawable_CustomCheck(o.max_withdrawable)) return new Error(`${path}.max_withdrawable: custom check failed`)
|
||||
|
||||
if (typeof o.ndebit !== 'string') return new Error(`${path}.ndebit: is not a string`)
|
||||
if (opts.ndebit_CustomCheck && !opts.ndebit_CustomCheck(o.ndebit)) return new Error(`${path}.ndebit: custom check failed`)
|
||||
|
||||
if (typeof o.network_max_fee_bps !== 'number') return new Error(`${path}.network_max_fee_bps: is not a number`)
|
||||
if (opts.network_max_fee_bps_CustomCheck && !opts.network_max_fee_bps_CustomCheck(o.network_max_fee_bps)) return new Error(`${path}.network_max_fee_bps: custom check failed`)
|
||||
|
||||
|
|
@ -2553,3 +2870,121 @@ export const UsersInfoValidate = (o?: UsersInfo, opts: UsersInfoOptions = {}, pa
|
|||
return null
|
||||
}
|
||||
|
||||
export enum DebitResponse_response_type {
|
||||
DENIED = 'denied',
|
||||
INVOICE = 'invoice',
|
||||
}
|
||||
export const enumCheckDebitResponse_response_type = (e?: DebitResponse_response_type): boolean => {
|
||||
for (const v in DebitResponse_response_type) if (e === v) return true
|
||||
return false
|
||||
}
|
||||
export type DebitResponse_response =
|
||||
{type:DebitResponse_response_type.DENIED, denied:Empty}|
|
||||
{type:DebitResponse_response_type.INVOICE, invoice:string}
|
||||
|
||||
export type DebitResponse_responseOptions = {
|
||||
denied_Options?: EmptyOptions
|
||||
invoice_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
export const DebitResponse_responseValidate = (o?: DebitResponse_response, opts:DebitResponse_responseOptions = {}, path: string = 'DebitResponse_response::root.'): Error | null => {
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
const stringType: string = o.type
|
||||
switch (o.type) {
|
||||
case DebitResponse_response_type.DENIED:
|
||||
const deniedErr = EmptyValidate(o.denied, opts.denied_Options, `${path}.denied`)
|
||||
if (deniedErr !== null) return deniedErr
|
||||
|
||||
|
||||
break
|
||||
case DebitResponse_response_type.INVOICE:
|
||||
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
|
||||
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
|
||||
|
||||
break
|
||||
default:
|
||||
return new Error(path + ': unknown type '+ stringType)
|
||||
}
|
||||
return null
|
||||
}
|
||||
export enum DebitRule_rule_type {
|
||||
EXPIRATION_RULE = 'expiration_rule',
|
||||
FREQUENCY_RULE = 'frequency_rule',
|
||||
}
|
||||
export const enumCheckDebitRule_rule_type = (e?: DebitRule_rule_type): boolean => {
|
||||
for (const v in DebitRule_rule_type) if (e === v) return true
|
||||
return false
|
||||
}
|
||||
export type DebitRule_rule =
|
||||
{type:DebitRule_rule_type.EXPIRATION_RULE, expiration_rule:DebitExpirationRule}|
|
||||
{type:DebitRule_rule_type.FREQUENCY_RULE, frequency_rule:FrequencyRule}
|
||||
|
||||
export type DebitRule_ruleOptions = {
|
||||
expiration_rule_Options?: DebitExpirationRuleOptions
|
||||
frequency_rule_Options?: FrequencyRuleOptions
|
||||
}
|
||||
export const DebitRule_ruleValidate = (o?: DebitRule_rule, opts:DebitRule_ruleOptions = {}, path: string = 'DebitRule_rule::root.'): Error | null => {
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
const stringType: string = o.type
|
||||
switch (o.type) {
|
||||
case DebitRule_rule_type.EXPIRATION_RULE:
|
||||
const expiration_ruleErr = DebitExpirationRuleValidate(o.expiration_rule, opts.expiration_rule_Options, `${path}.expiration_rule`)
|
||||
if (expiration_ruleErr !== null) return expiration_ruleErr
|
||||
|
||||
|
||||
break
|
||||
case DebitRule_rule_type.FREQUENCY_RULE:
|
||||
const frequency_ruleErr = FrequencyRuleValidate(o.frequency_rule, opts.frequency_rule_Options, `${path}.frequency_rule`)
|
||||
if (frequency_ruleErr !== null) return frequency_ruleErr
|
||||
|
||||
|
||||
break
|
||||
default:
|
||||
return new Error(path + ': unknown type '+ stringType)
|
||||
}
|
||||
return null
|
||||
}
|
||||
export enum LiveDebitRequest_debit_type {
|
||||
FREQUENCY = 'frequency',
|
||||
FULL_ACCESS = 'full_access',
|
||||
INVOICE = 'invoice',
|
||||
}
|
||||
export const enumCheckLiveDebitRequest_debit_type = (e?: LiveDebitRequest_debit_type): boolean => {
|
||||
for (const v in LiveDebitRequest_debit_type) if (e === v) return true
|
||||
return false
|
||||
}
|
||||
export type LiveDebitRequest_debit =
|
||||
{type:LiveDebitRequest_debit_type.FREQUENCY, frequency:FrequencyRule}|
|
||||
{type:LiveDebitRequest_debit_type.FULL_ACCESS, full_access:Empty}|
|
||||
{type:LiveDebitRequest_debit_type.INVOICE, invoice:string}
|
||||
|
||||
export type LiveDebitRequest_debitOptions = {
|
||||
frequency_Options?: FrequencyRuleOptions
|
||||
full_access_Options?: EmptyOptions
|
||||
invoice_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
export const LiveDebitRequest_debitValidate = (o?: LiveDebitRequest_debit, opts:LiveDebitRequest_debitOptions = {}, path: string = 'LiveDebitRequest_debit::root.'): Error | null => {
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
const stringType: string = o.type
|
||||
switch (o.type) {
|
||||
case LiveDebitRequest_debit_type.FREQUENCY:
|
||||
const frequencyErr = FrequencyRuleValidate(o.frequency, opts.frequency_Options, `${path}.frequency`)
|
||||
if (frequencyErr !== null) return frequencyErr
|
||||
|
||||
|
||||
break
|
||||
case LiveDebitRequest_debit_type.FULL_ACCESS:
|
||||
const full_accessErr = EmptyValidate(o.full_access, opts.full_access_Options, `${path}.full_access`)
|
||||
if (full_accessErr !== null) return full_accessErr
|
||||
|
||||
|
||||
break
|
||||
case LiveDebitRequest_debit_type.INVOICE:
|
||||
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
|
||||
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
|
||||
|
||||
break
|
||||
default:
|
||||
return new Error(path + ': unknown type '+ stringType)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -343,6 +343,12 @@ service LightningPub {
|
|||
option (http_route) = "/api/user/info";
|
||||
option (nostr) = true;
|
||||
}
|
||||
rpc UpdateCallbackUrl(structs.CallbackUrl)returns(structs.CallbackUrl){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/cb/update";
|
||||
option (nostr) = true;
|
||||
}
|
||||
|
||||
rpc AddProduct(structs.AddProductRequest) returns (structs.Product){
|
||||
option (auth_type) = "User";
|
||||
|
|
@ -435,6 +441,48 @@ service LightningPub {
|
|||
option (http_route) = "/api/user/lnurl_channel/url";
|
||||
option (nostr) = true;
|
||||
}
|
||||
rpc GetDebitAuthorizations(structs.Empty) returns (structs.DebitAuthorizations){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "get";
|
||||
option (http_route) = "/api/user/debit/get";
|
||||
option (nostr) = true;
|
||||
}
|
||||
rpc AuthorizeDebit(structs.DebitAuthorizationRequest) returns (structs.DebitAuthorization){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/debit/authorize";
|
||||
option (nostr) = true;
|
||||
}
|
||||
rpc EditDebit(structs.DebitAuthorizationRequest) returns (structs.Empty){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/debit/edit";
|
||||
option (nostr) = true;
|
||||
}
|
||||
rpc BanDebit(structs.DebitOperation) returns (structs.Empty){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/debit/ban";
|
||||
option (nostr) = true;
|
||||
}
|
||||
rpc ResetDebit(structs.DebitOperation) returns (structs.Empty){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/debit/reset";
|
||||
option (nostr) = true;
|
||||
}
|
||||
rpc RespondToDebit(structs.DebitResponse) returns (structs.Empty){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/debit/finish";
|
||||
option (nostr) = true;
|
||||
}
|
||||
rpc GetLiveDebitRequests(structs.Empty) returns (stream structs.LiveDebitRequest){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/debit/sub";
|
||||
option (nostr) = true;
|
||||
}
|
||||
rpc GetLiveUserOperations(structs.Empty) returns (stream structs.LiveUserOperation){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
|
|
|
|||
|
|
@ -215,6 +215,7 @@ message PayAppUserInvoiceRequest {
|
|||
string user_identifier = 1;
|
||||
string invoice = 2;
|
||||
int64 amount = 3;
|
||||
optional string debit_npub = 4;
|
||||
}
|
||||
|
||||
message SendAppUserToAppUserPaymentRequest {
|
||||
|
|
@ -283,6 +284,7 @@ message DecodeInvoiceResponse{
|
|||
message PayInvoiceRequest{
|
||||
string invoice = 1;
|
||||
int64 amount = 2;
|
||||
optional string debit_npub = 3;
|
||||
}
|
||||
|
||||
message PayInvoiceResponse{
|
||||
|
|
@ -345,6 +347,10 @@ message HandleLnurlPayResponse {
|
|||
repeated Empty routes = 2;
|
||||
}
|
||||
|
||||
message CallbackUrl {
|
||||
string url = 1;
|
||||
}
|
||||
|
||||
message UserInfo{
|
||||
string userId = 1;
|
||||
int64 balance = 2;
|
||||
|
|
@ -354,9 +360,10 @@ message UserInfo{
|
|||
int64 network_max_fee_bps = 6;
|
||||
int64 network_max_fee_fixed = 7;
|
||||
string noffer = 8;
|
||||
string bridge_url = 9;
|
||||
string ndebit = 9;
|
||||
string callback_url = 10;
|
||||
string bridge_url = 11;
|
||||
}
|
||||
|
||||
message GetUserOperationsRequest{
|
||||
int64 latestIncomingInvoice = 1;
|
||||
int64 latestOutgoingInvoice = 2;
|
||||
|
|
@ -478,3 +485,66 @@ message GetInviteTokenStateRequest {
|
|||
message GetInviteTokenStateResponse {
|
||||
bool used = 1;
|
||||
}
|
||||
|
||||
message DebitOperation {
|
||||
string npub = 1;
|
||||
}
|
||||
|
||||
message DebitAuthorizationRequest {
|
||||
string authorize_npub = 1;
|
||||
repeated DebitRule rules = 2;
|
||||
optional string request_id = 3;
|
||||
}
|
||||
|
||||
message DebitAuthorization {
|
||||
string debit_id = 1;
|
||||
bool authorized = 2;
|
||||
string npub = 3;
|
||||
repeated DebitRule rules = 4;
|
||||
|
||||
}
|
||||
|
||||
message DebitAuthorizations {
|
||||
repeated DebitAuthorization debits = 1;
|
||||
}
|
||||
|
||||
message DebitExpirationRule {
|
||||
int64 expires_at_unix = 1;
|
||||
}
|
||||
|
||||
enum IntervalType {
|
||||
DAY = 0;
|
||||
WEEK = 1;
|
||||
MONTH = 2;
|
||||
}
|
||||
message FrequencyRule {
|
||||
int64 number_of_intervals = 1;
|
||||
IntervalType interval = 2;
|
||||
int64 amount = 3;
|
||||
}
|
||||
|
||||
message DebitRule {
|
||||
oneof rule {
|
||||
DebitExpirationRule expiration_rule = 1;
|
||||
FrequencyRule frequency_rule = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message LiveDebitRequest {
|
||||
string request_id = 1;
|
||||
string npub = 2;
|
||||
oneof debit {
|
||||
string invoice = 3;
|
||||
FrequencyRule frequency = 4;
|
||||
Empty full_access = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message DebitResponse {
|
||||
string request_id = 1;
|
||||
string npub = 2;
|
||||
oneof response {
|
||||
Empty denied = 3;
|
||||
string invoice = 4;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
This file contains functions that deal with encoding and decoding nprofiles,
|
||||
but with he addition of bridge urls in the nprofile.
|
||||
These functions are basically the same functions from nostr-tools package
|
||||
but with some tweaks to allow for the bridge inclusion.
|
||||
*/
|
||||
import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils';
|
||||
import { bech32 } from 'bech32';
|
||||
import { LoadNosrtSettingsFromEnv } from './services/nostr/index.js';
|
||||
|
||||
export const utf8Decoder = new TextDecoder('utf-8')
|
||||
export const utf8Encoder = new TextEncoder()
|
||||
|
||||
|
||||
export type CustomProfilePointer = {
|
||||
pubkey: string
|
||||
relays?: string[]
|
||||
bridge?: string[] // one bridge
|
||||
}
|
||||
|
||||
export type OfferPointer = {
|
||||
pubkey: string,
|
||||
relay: string,
|
||||
offer: string
|
||||
priceType: PriceType,
|
||||
price?: number
|
||||
}
|
||||
export enum PriceType {
|
||||
fixed = 0,
|
||||
variable = 1,
|
||||
spontaneous = 2,
|
||||
}
|
||||
|
||||
|
||||
type TLV = { [t: number]: Uint8Array[] }
|
||||
|
||||
|
||||
const encodeTLV = (tlv: TLV): Uint8Array => {
|
||||
const entries: Uint8Array[] = []
|
||||
|
||||
Object.entries(tlv)
|
||||
/*
|
||||
the original function does a reverse() here,
|
||||
but here it causes the nprofile string to be different,
|
||||
even though it would still decode to the correct original inputs
|
||||
*/
|
||||
//.reverse()
|
||||
.forEach(([t, vs]) => {
|
||||
vs.forEach(v => {
|
||||
const entry = new Uint8Array(v.length + 2)
|
||||
entry.set([parseInt(t)], 0)
|
||||
entry.set([v.length], 1)
|
||||
entry.set(v, 2)
|
||||
entries.push(entry)
|
||||
})
|
||||
})
|
||||
return concatBytes(...entries);
|
||||
}
|
||||
|
||||
export const encodeNprofile = (profile: CustomProfilePointer): string => {
|
||||
const data = encodeTLV({
|
||||
0: [hexToBytes(profile.pubkey)],
|
||||
1: (profile.relays || []).map(url => utf8Encoder.encode(url)),
|
||||
2: (profile.bridge || []).map(url => utf8Encoder.encode(url))
|
||||
});
|
||||
const words = bech32.toWords(data)
|
||||
return bech32.encode("nprofile", words, 5000);
|
||||
}
|
||||
|
||||
export const encodeNoffer = (offer: OfferPointer): string => {
|
||||
let relay = offer.relay
|
||||
if (!relay) {
|
||||
const settings = LoadNosrtSettingsFromEnv()
|
||||
relay = settings.relays[0]
|
||||
}
|
||||
const o: TLV = {
|
||||
0: [hexToBytes(offer.pubkey)],
|
||||
1: [utf8Encoder.encode(relay)],
|
||||
2: [utf8Encoder.encode(offer.offer)],
|
||||
3: [new Uint8Array([Number(offer.priceType)])],
|
||||
}
|
||||
if (offer.price) {
|
||||
o[4] = [new Uint8Array(new BigUint64Array([BigInt(offer.price)]).buffer)]
|
||||
}
|
||||
const data = encodeTLV(o);
|
||||
const words = bech32.toWords(data)
|
||||
return bech32.encode("noffer", words, 5000);
|
||||
}
|
||||
|
||||
const parseTLV = (data: Uint8Array): TLV => {
|
||||
const result: TLV = {}
|
||||
let rest = data
|
||||
while (rest.length > 0) {
|
||||
const t = rest[0]
|
||||
const l = rest[1]
|
||||
const v = rest.slice(2, 2 + l)
|
||||
rest = rest.slice(2 + l)
|
||||
if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)
|
||||
result[t] = result[t] || []
|
||||
result[t].push(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export const decodeNoffer = (noffer: string): OfferPointer => {
|
||||
const { prefix, words } = bech32.decode(noffer, 5000)
|
||||
if (prefix !== "noffer") {
|
||||
throw new Error("Expected nprofile prefix");
|
||||
}
|
||||
const data = new Uint8Array(bech32.fromWords(words))
|
||||
|
||||
const tlv = parseTLV(data);
|
||||
if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for noffer')
|
||||
if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')
|
||||
if (!tlv[1]?.[0]) throw new Error('missing TLV 1 for noffer')
|
||||
if (!tlv[2]?.[0]) throw new Error('missing TLV 2 for noffer')
|
||||
if (!tlv[3]?.[0]) throw new Error('missing TLV 3 for noffer')
|
||||
return {
|
||||
pubkey: bytesToHex(tlv[0][0]),
|
||||
relay: utf8Decoder.decode(tlv[1][0]),
|
||||
offer: utf8Decoder.decode(tlv[2][0]),
|
||||
priceType: tlv[3][0][0],
|
||||
price: tlv[4] ? Number(new BigUint64Array(tlv[4][0])[0]) : undefined
|
||||
}
|
||||
}
|
||||
|
||||
export const decodeNprofile = (nprofile: string): CustomProfilePointer => {
|
||||
const { prefix, words } = bech32.decode(nprofile, 5000)
|
||||
if (prefix !== "nprofile") {
|
||||
throw new Error("Expected nprofile prefix");
|
||||
}
|
||||
const data = new Uint8Array(bech32.fromWords(words))
|
||||
|
||||
const tlv = parseTLV(data);
|
||||
if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile')
|
||||
if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes')
|
||||
|
||||
return {
|
||||
pubkey: bytesToHex(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],
|
||||
bridge: tlv[2] ? tlv[2].map(d => utf8Decoder.decode(d)) : []
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,9 @@ import nostrMiddleware from './nostrMiddleware.js'
|
|||
import { getLogger } from './services/helpers/logger.js';
|
||||
import { initMainHandler } from './services/main/init.js';
|
||||
import { LoadMainSettingsFromEnv } from './services/main/settings.js';
|
||||
import { encodeNprofile } from './custom-nip19.js';
|
||||
import { nip19 } from 'nostr-tools'
|
||||
//@ts-ignore
|
||||
const { nprofileEncode } = nip19
|
||||
|
||||
const start = async () => {
|
||||
const log = getLogger({})
|
||||
|
|
@ -29,7 +31,7 @@ const start = async () => {
|
|||
log("starting server")
|
||||
mainHandler.attachNostrSend(Send)
|
||||
mainHandler.StartBeacons()
|
||||
const appNprofile = encodeNprofile({ pubkey: liquidityProviderInfo.publicKey, relays: nostrSettings.relays })
|
||||
const appNprofile = nprofileEncode({ pubkey: liquidityProviderInfo.publicKey, relays: nostrSettings.relays })
|
||||
if (wizard) {
|
||||
wizard.AddConnectInfo(appNprofile, nostrSettings.relays)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import { NostrEvent, NostrSend, NostrSettings } from "./services/nostr/handler.j
|
|||
import * as Types from '../proto/autogenerated/ts/types.js'
|
||||
import NewNostrTransport, { NostrRequest } from '../proto/autogenerated/ts/nostr_transport.js';
|
||||
import { ERROR, getLogger } from "./services/helpers/logger.js";
|
||||
import { UnsignedEvent } from "./services/nostr/tools/event.js";
|
||||
import { defaultInvoiceExpiry } from "./services/storage/paymentStorage.js";
|
||||
import { Application } from "./services/storage/entity/Application.js";
|
||||
import { NdebitData } from "nostr-tools/lib/types/nip68.js";
|
||||
|
||||
export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSettings: NostrSettings, onClientEvent: (e: { requestId: string }, fromPub: string) => void): { Stop: () => void, Send: NostrSend } => {
|
||||
const log = getLogger({})
|
||||
|
|
@ -52,6 +50,10 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
|||
const offerReq = j as NofferData
|
||||
mainHandler.handleNip69Noffer(offerReq, event)
|
||||
return
|
||||
} else if (event.kind === 21002) {
|
||||
const debitReq = j as NdebitData
|
||||
mainHandler.debitManager.handleNip68Debit(debitReq, event)
|
||||
return
|
||||
}
|
||||
if (!j.rpcName) {
|
||||
onClientEvent(j as { requestId: string }, event.pub)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@ import * as Types from '../../../proto/autogenerated/ts/types.js'
|
|||
|
||||
import { MainSettings } from './settings.js'
|
||||
import ApplicationManager from './applicationManager.js'
|
||||
import { encodeNoffer, PriceType } from '../../custom-nip19.js'
|
||||
import { nip19 } from 'nostr-tools'
|
||||
import { LoadNosrtSettingsFromEnv } from '../nostr/index.js'
|
||||
const { ndebitEncode, nofferEncode, OfferPriceType } = nip19
|
||||
export default class {
|
||||
|
||||
storage: Storage
|
||||
settings: MainSettings
|
||||
applicationManager: ApplicationManager
|
||||
|
|
@ -50,9 +53,12 @@ export default class {
|
|||
const user = await this.storage.userStorage.GetUser(ctx.user_id)
|
||||
const app = await this.storage.applicationStorage.GetApplication(ctx.app_id)
|
||||
const appUser = await this.storage.applicationStorage.GetAppUserFromUser(app, user.user_id)
|
||||
console.log("User Identifier/pointer here", appUser?.identifier)
|
||||
|
||||
if (!appUser) {
|
||||
throw new Error(`app user ${ctx.user_id} not found`) // TODO: fix logs doxing
|
||||
}
|
||||
const nostrSettings = LoadNosrtSettingsFromEnv()
|
||||
return {
|
||||
userId: ctx.user_id,
|
||||
balance: user.balance_sats,
|
||||
|
|
@ -61,11 +67,19 @@ export default class {
|
|||
network_max_fee_bps: this.settings.lndSettings.feeRateBps,
|
||||
network_max_fee_fixed: this.settings.lndSettings.feeFixedLimit,
|
||||
service_fee_bps: this.settings.outgoingAppUserInvoiceFeeBps,
|
||||
noffer: encodeNoffer({ pubkey: app.nostr_public_key!, offer: appUser.identifier, priceType: PriceType.spontaneous, relay: "" }),
|
||||
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: appUser.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
||||
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: appUser.identifier, relay: nostrSettings.relays[0] }),
|
||||
callback_url: appUser.callback_url,
|
||||
bridge_url: this.settings.bridgeUrl
|
||||
}
|
||||
}
|
||||
|
||||
async UpdateCallbackUrl(ctx: Types.UserContext, req: Types.CallbackUrl): Promise<Types.CallbackUrl> {
|
||||
const app = await this.storage.applicationStorage.GetApplication(ctx.app_id)
|
||||
await this.storage.applicationStorage.UpdateUserCallbackUrl(app, ctx.app_user_id, req.url)
|
||||
return { url: req.url }
|
||||
}
|
||||
|
||||
async NewInvoice(ctx: Types.UserContext, req: Types.NewInvoiceRequest): Promise<Types.NewInvoiceResponse> {
|
||||
return this.applicationManager.AddAppUserInvoice(ctx.app_id, {
|
||||
http_callback_url: "",
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ import { ApplicationUser } from '../storage/entity/ApplicationUser.js'
|
|||
import { PubLogger, getLogger } from '../helpers/logger.js'
|
||||
import crypto from 'crypto'
|
||||
import { Application } from '../storage/entity/Application.js'
|
||||
import { encodeNoffer, PriceType } from '../../custom-nip19.js'
|
||||
|
||||
import { nip69, nip19 } from 'nostr-tools'
|
||||
import { LoadNosrtSettingsFromEnv } from '../nostr/index.js'
|
||||
const { SendNofferRequest } = nip69
|
||||
const { nofferEncode, ndebitEncode, OfferPriceType } = nip19
|
||||
const TOKEN_EXPIRY_TIME = 2 * 60 * 1000 // 2 minutes, in milliseconds
|
||||
|
||||
type NsecLinkingData = {
|
||||
|
|
@ -149,6 +151,7 @@ export default class {
|
|||
u = user
|
||||
if (created) log(u.identifier, u.user.user_id, "user created")
|
||||
}
|
||||
const nostrSettings = LoadNosrtSettingsFromEnv()
|
||||
return {
|
||||
identifier: u.identifier,
|
||||
info: {
|
||||
|
|
@ -159,7 +162,9 @@ export default class {
|
|||
network_max_fee_bps: this.settings.lndSettings.feeRateBps,
|
||||
network_max_fee_fixed: this.settings.lndSettings.feeFixedLimit,
|
||||
service_fee_bps: this.settings.outgoingAppUserInvoiceFeeBps,
|
||||
noffer: encodeNoffer({ pubkey: app.nostr_public_key!, offer: u.identifier, priceType: PriceType.spontaneous, relay: "" }),
|
||||
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: u.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
||||
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: u.identifier, relay: nostrSettings.relays[0] }),
|
||||
callback_url: u.callback_url,
|
||||
bridge_url: this.settings.bridgeUrl
|
||||
|
||||
},
|
||||
|
|
@ -181,7 +186,8 @@ export default class {
|
|||
const log = getLogger({ appName: app.name })
|
||||
const receiver = await this.storage.applicationStorage.GetApplicationUser(app, req.receiver_identifier)
|
||||
const { user: payer } = await this.storage.applicationStorage.GetOrCreateApplicationUser(app, req.payer_identifier, 0)
|
||||
const opts: InboundOptionals = { callbackUrl: req.http_callback_url, expiry: defaultInvoiceExpiry, expectedPayer: payer.user, linkedApplication: app }
|
||||
const cbUrl = req.http_callback_url || receiver.callback_url || ""
|
||||
const opts: InboundOptionals = { callbackUrl: cbUrl, expiry: defaultInvoiceExpiry, expectedPayer: payer.user, linkedApplication: app }
|
||||
const appUserInvoice = await this.paymentManager.NewInvoice(receiver.user.user_id, req.invoice_req, opts)
|
||||
return {
|
||||
invoice: appUserInvoice.invoice
|
||||
|
|
@ -192,6 +198,7 @@ export default class {
|
|||
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||
const user = await this.storage.applicationStorage.GetApplicationUser(app, req.user_identifier)
|
||||
const max = this.paymentManager.GetMaxPayableInvoice(user.user.balance_sats, true)
|
||||
const nostrSettings = LoadNosrtSettingsFromEnv()
|
||||
return {
|
||||
max_withdrawable: max, identifier: req.user_identifier, info: {
|
||||
userId: user.user.user_id, balance: user.user.balance_sats,
|
||||
|
|
@ -200,7 +207,9 @@ export default class {
|
|||
network_max_fee_bps: this.settings.lndSettings.feeRateBps,
|
||||
network_max_fee_fixed: this.settings.lndSettings.feeFixedLimit,
|
||||
service_fee_bps: this.settings.outgoingAppUserInvoiceFeeBps,
|
||||
noffer: encodeNoffer({ pubkey: app.nostr_public_key!, offer: user.identifier, priceType: PriceType.spontaneous, relay: "" }),
|
||||
noffer: nofferEncode({ pubkey: app.nostr_public_key!, offer: user.identifier, priceType: OfferPriceType.Spontaneous, relay: nostrSettings.relays[0] }),
|
||||
ndebit: ndebitEncode({ pubkey: app.nostr_public_key!, pointer: user.identifier, relay: nostrSettings.relays[0] }),
|
||||
callback_url: user.callback_url,
|
||||
bridge_url: this.settings.bridgeUrl
|
||||
},
|
||||
}
|
||||
|
|
|
|||
395
src/services/main/debitManager.ts
Normal file
395
src/services/main/debitManager.ts
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
import crypto from 'crypto';
|
||||
import * as Types from "../../../proto/autogenerated/ts/types.js";
|
||||
import ApplicationManager from "./applicationManager.js";
|
||||
import Storage from '../storage/index.js'
|
||||
import LND from "../lnd/lnd.js"
|
||||
import { ERROR, getLogger } from "../helpers/logger.js";
|
||||
import { DebitAccess, DebitAccessRules } from '../storage/entity/DebitAccess.js';
|
||||
import paymentManager from './paymentManager.js';
|
||||
import { Application } from '../storage/entity/Application.js';
|
||||
import { ApplicationUser } from '../storage/entity/ApplicationUser.js';
|
||||
import { NostrEvent, NostrSend, SendData, SendInitiator } from '../nostr/handler.js';
|
||||
import { UnsignedEvent } from 'nostr-tools';
|
||||
import { BudgetFrequency, NdebitData, NdebitFailure, NdebitSuccess, NdebitSuccessPayment, RecurringDebitTimeUnit } from 'nostr-tools/lib/types/nip68.js';
|
||||
export const expirationRuleName = 'expiration'
|
||||
export const frequencyRuleName = 'frequency'
|
||||
const unitToIntervalType = (unit: RecurringDebitTimeUnit) => {
|
||||
switch (unit) {
|
||||
case 'day': return Types.IntervalType.DAY
|
||||
case 'week': return Types.IntervalType.WEEK
|
||||
case 'month': return Types.IntervalType.MONTH
|
||||
default: throw new Error("invalid unit")
|
||||
}
|
||||
}
|
||||
const intervalTypeToUnit = (interval: Types.IntervalType): RecurringDebitTimeUnit => {
|
||||
switch (interval) {
|
||||
case Types.IntervalType.DAY: return 'day'
|
||||
case Types.IntervalType.WEEK: return 'week'
|
||||
case Types.IntervalType.MONTH: return 'month'
|
||||
default: throw new Error("invalid interval")
|
||||
}
|
||||
}
|
||||
const IntervalTypeToSeconds = (interval: Types.IntervalType) => {
|
||||
switch (interval) {
|
||||
case Types.IntervalType.DAY: return 24 * 60 * 60
|
||||
case Types.IntervalType.WEEK: return 7 * 24 * 60 * 60
|
||||
case Types.IntervalType.MONTH: return 30 * 24 * 60 * 60
|
||||
default: throw new Error("invalid interval")
|
||||
}
|
||||
}
|
||||
const debitRulesToDebitAccessRules = (rule: Types.DebitRule[]): DebitAccessRules | undefined => {
|
||||
let rules: DebitAccessRules | undefined = undefined
|
||||
rule.forEach(r => {
|
||||
if (!rules) {
|
||||
rules = {}
|
||||
}
|
||||
const { rule } = r
|
||||
switch (rule.type) {
|
||||
case Types.DebitRule_rule_type.EXPIRATION_RULE:
|
||||
|
||||
rules[expirationRuleName] = [rule.expiration_rule.expires_at_unix.toString()]
|
||||
break
|
||||
case Types.DebitRule_rule_type.FREQUENCY_RULE:
|
||||
const intervals = rule.frequency_rule.number_of_intervals.toString()
|
||||
const unit = intervalTypeToUnit(rule.frequency_rule.interval)
|
||||
rules[frequencyRuleName] = [intervals, unit, rule.frequency_rule.amount.toString()];
|
||||
break
|
||||
default:
|
||||
throw new Error("invalid rule")
|
||||
}
|
||||
})
|
||||
return rules
|
||||
}
|
||||
|
||||
const debitAccessRulesToDebitRules = (rules: DebitAccessRules | null): Types.DebitRule[] => {
|
||||
if (!rules) {
|
||||
return []
|
||||
}
|
||||
return Object.entries(rules).map(([key, val]) => {
|
||||
switch (key) {
|
||||
case expirationRuleName:
|
||||
return {
|
||||
rule: {
|
||||
type: Types.DebitRule_rule_type.EXPIRATION_RULE,
|
||||
expiration_rule: {
|
||||
expires_at_unix: +val[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
case frequencyRuleName:
|
||||
return {
|
||||
rule: {
|
||||
type: Types.DebitRule_rule_type.FREQUENCY_RULE,
|
||||
frequency_rule: {
|
||||
number_of_intervals: +val[0],
|
||||
interval: unitToIntervalType(val[1] as RecurringDebitTimeUnit),
|
||||
amount: +val[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new Error("invalid rule")
|
||||
}
|
||||
})
|
||||
}
|
||||
const nip68errs = {
|
||||
1: "Request Denied Warning",
|
||||
2: "Temporary Failure",
|
||||
3: "Expired Request",
|
||||
4: "Rate Limited",
|
||||
5: "Invalid Amount",
|
||||
6: "Invalid Request",
|
||||
}
|
||||
type HandleNdebitRes = { status: 'fail', debitRes: NdebitFailure }
|
||||
| { status: 'invoicePaid', op: Types.UserOperation, app: Application, appUser: ApplicationUser, debitRes: NdebitSuccessPayment }
|
||||
| { status: 'authRequired', liveDebitReq: Types.LiveDebitRequest, app: Application, appUser: ApplicationUser }
|
||||
| { status: 'authOk', debitRes: NdebitSuccess }
|
||||
export class DebitManager {
|
||||
|
||||
|
||||
_nostrSend: NostrSend | null = null
|
||||
|
||||
applicationManager: ApplicationManager
|
||||
|
||||
storage: Storage
|
||||
lnd: LND
|
||||
logger = getLogger({ component: 'DebitManager' })
|
||||
constructor(storage: Storage, lnd: LND, applicationManager: ApplicationManager) {
|
||||
this.storage = storage
|
||||
this.lnd = lnd
|
||||
this.applicationManager = applicationManager
|
||||
}
|
||||
|
||||
attachNostrSend = (nostrSend: NostrSend) => {
|
||||
this._nostrSend = nostrSend
|
||||
}
|
||||
nostrSend: NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => {
|
||||
if (!this._nostrSend) {
|
||||
throw new Error("No nostrSend attached")
|
||||
}
|
||||
this._nostrSend(initiator, data, relays)
|
||||
}
|
||||
|
||||
AuthorizeDebit = async (ctx: Types.UserContext, req: Types.DebitAuthorizationRequest): Promise<Types.DebitAuthorization> => {
|
||||
const access = await this.storage.debitStorage.AddDebitAccess(ctx.app_user_id, {
|
||||
authorize: true,
|
||||
npub: req.authorize_npub,
|
||||
rules: debitRulesToDebitAccessRules(req.rules)
|
||||
})
|
||||
if (req.request_id) {
|
||||
this.sendDebitResponse({ res: 'ok' }, { pub: req.authorize_npub, id: req.request_id, appId: ctx.app_id })
|
||||
}
|
||||
return {
|
||||
debit_id: access.serial_id.toString(),
|
||||
npub: req.authorize_npub,
|
||||
authorized: true,
|
||||
rules: req.rules
|
||||
}
|
||||
}
|
||||
|
||||
GetDebitAuthorizations = async (ctx: Types.UserContext): Promise<Types.DebitAuthorizations> => {
|
||||
const allDebitsAccesses = await this.storage.debitStorage.GetAllUserDebitAccess(ctx.app_user_id)
|
||||
const debits: Types.DebitAuthorization[] = allDebitsAccesses.map(access => ({
|
||||
debit_id: access.serial_id.toString(),
|
||||
authorized: access.authorized,
|
||||
npub: access.npub,
|
||||
rules: debitAccessRulesToDebitRules(access.rules)
|
||||
}))
|
||||
return { debits }
|
||||
}
|
||||
|
||||
EditDebit = async (ctx: Types.UserContext, req: Types.DebitAuthorizationRequest): Promise<void> => {
|
||||
const access = await this.storage.debitStorage.GetDebitAccess(ctx.app_user_id, req.authorize_npub);
|
||||
if (!access) {
|
||||
throw new Error("Debit does not exist")
|
||||
}
|
||||
await this.storage.debitStorage.UpdateDebitAccessRules(ctx.app_user_id, req.authorize_npub, debitRulesToDebitAccessRules(req.rules));
|
||||
}
|
||||
|
||||
BanDebit = async (ctx: Types.UserContext, req: Types.DebitOperation): Promise<void> => {
|
||||
await this.storage.debitStorage.DenyDebitAccess(ctx.app_user_id, req.npub)
|
||||
}
|
||||
ResetDebit = async (ctx: Types.UserContext, req: Types.DebitOperation): Promise<void> => {
|
||||
await this.storage.debitStorage.RemoveDebitAccess(ctx.app_user_id, req.npub)
|
||||
}
|
||||
|
||||
RespondToDebit = async (ctx: Types.UserContext, req: Types.DebitResponse): Promise<void> => {
|
||||
switch (req.response.type) {
|
||||
case Types.DebitResponse_response_type.DENIED:
|
||||
this.sendDebitResponse({ res: 'GFY', error: nip68errs[1], code: 1 }, { pub: req.npub, id: req.request_id, appId: ctx.app_id })
|
||||
return
|
||||
case Types.DebitResponse_response_type.INVOICE:
|
||||
const app = await this.storage.applicationStorage.GetApplication(ctx.app_id)
|
||||
const appUser = await this.storage.applicationStorage.GetApplicationUser(app, ctx.app_user_id)
|
||||
const { op, payment } = await this.sendDebitPayment(ctx.app_id, ctx.app_user_id, req.npub, req.response.invoice)
|
||||
const debitRes: NdebitSuccessPayment = { res: 'ok', preimage: payment.preimage }
|
||||
this.notifyPaymentSuccess(appUser, debitRes, op, { appId: ctx.app_id, pub: req.npub, id: req.request_id })
|
||||
return
|
||||
default:
|
||||
throw new Error("invalid response type")
|
||||
}
|
||||
}
|
||||
|
||||
handleNip68Debit = async (pointerdata: NdebitData, event: NostrEvent) => {
|
||||
if (!this._nostrSend) {
|
||||
throw new Error("No nostrSend attached")
|
||||
}
|
||||
console.log({ pointerdata, event })
|
||||
const res = await this.payNdebitInvoice(event, pointerdata)
|
||||
console.log({ debitRes: res })
|
||||
if (res.status === 'fail' || res.status === 'authOk') {
|
||||
const e = newNdebitResponse(JSON.stringify(res.debitRes), event)
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||
return
|
||||
}
|
||||
const { appUser } = res
|
||||
if (res.status === 'authRequired') {
|
||||
const message: Types.LiveDebitRequest & { requestId: string, status: 'OK' } = { ...res.liveDebitReq, requestId: "GetLiveDebitRequests", status: 'OK' }
|
||||
if (appUser.nostr_public_key) {// TODO - fix before support for http streams
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'content', content: JSON.stringify(message), pub: appUser.nostr_public_key })
|
||||
}
|
||||
return
|
||||
}
|
||||
const { op, debitRes } = res
|
||||
this.notifyPaymentSuccess(appUser, debitRes, op, event)
|
||||
}
|
||||
|
||||
notifyPaymentSuccess = (appUser: ApplicationUser, debitRes: NdebitSuccessPayment, op: Types.UserOperation, event: { pub: string, id: string, appId: string }) => {
|
||||
const message: Types.LiveUserOperation & { requestId: string, status: 'OK' } = { operation: op, requestId: "GetLiveUserOperations", status: 'OK' }
|
||||
if (appUser.nostr_public_key) { // TODO - fix before support for http streams
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'content', content: JSON.stringify(message), pub: appUser.nostr_public_key })
|
||||
}
|
||||
this.sendDebitResponse(debitRes, event)
|
||||
}
|
||||
|
||||
sendDebitResponse = (debitRes: NdebitFailure | NdebitSuccess | NdebitSuccessPayment, event: { pub: string, id: string, appId: string }) => {
|
||||
const e = newNdebitResponse(JSON.stringify(debitRes), event)
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||
}
|
||||
|
||||
payNdebitInvoice = async (event: NostrEvent, pointerdata: NdebitData): Promise<HandleNdebitRes> => {
|
||||
try {
|
||||
return await this.doNdebit(event, pointerdata)
|
||||
} catch (e: any) {
|
||||
this.logger(ERROR, e.message || e)
|
||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } }
|
||||
}
|
||||
}
|
||||
|
||||
doNdebit = async (event: NostrEvent, pointerdata: NdebitData): Promise<HandleNdebitRes> => {
|
||||
const { appId, pub: requestorPub } = event
|
||||
const { amount_sats, pointer, bolt11, frequency } = pointerdata
|
||||
if (!pointer) {
|
||||
// TODO: debit from app owner balance
|
||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } }
|
||||
}
|
||||
const appUserId = pointer
|
||||
const app = await this.storage.applicationStorage.GetApplication(appId)
|
||||
const appUser = await this.storage.applicationStorage.GetApplicationUser(app, appUserId)
|
||||
let decodedAmount = null
|
||||
if (bolt11) {
|
||||
const decoded = await this.lnd.DecodeInvoice(bolt11)
|
||||
decodedAmount = decoded.numSatoshis
|
||||
}
|
||||
if (frequency) {
|
||||
const amt = amount_sats || decodedAmount
|
||||
if (!amt) {
|
||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[5], code: 5 } }
|
||||
}
|
||||
const debitAccess = await this.storage.debitStorage.GetDebitAccess(appUserId, requestorPub)
|
||||
if (!debitAccess) {
|
||||
return {
|
||||
status: 'authRequired', app, appUser, liveDebitReq: {
|
||||
request_id: event.id,
|
||||
npub: requestorPub,
|
||||
debit: {
|
||||
type: Types.LiveDebitRequest_debit_type.FREQUENCY,
|
||||
frequency: {
|
||||
interval: unitToIntervalType(frequency.unit),
|
||||
number_of_intervals: frequency.number,
|
||||
amount: amt,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!debitAccess.authorized) {
|
||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } }
|
||||
}
|
||||
return { status: 'authOk', debitRes: { res: 'ok' } }
|
||||
}
|
||||
|
||||
if (!bolt11) {
|
||||
if (!amount_sats) {
|
||||
const debitAccess = await this.storage.debitStorage.GetDebitAccess(appUserId, requestorPub)
|
||||
if (!debitAccess) {
|
||||
return {
|
||||
status: 'authRequired', app, appUser, liveDebitReq: {
|
||||
request_id: event.id,
|
||||
npub: requestorPub,
|
||||
debit: {
|
||||
type: Types.LiveDebitRequest_debit_type.FULL_ACCESS,
|
||||
full_access: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!debitAccess.authorized) {
|
||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } }
|
||||
}
|
||||
return { status: 'authOk', debitRes: { res: 'ok' } }
|
||||
}
|
||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[6], code: 6 } }
|
||||
}
|
||||
|
||||
if (!decodedAmount) {
|
||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[6], code: 6 } }
|
||||
}
|
||||
if (amount_sats && amount_sats !== decodedAmount) {
|
||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[5], code: 5 } }
|
||||
}
|
||||
|
||||
const authorization = await this.storage.debitStorage.GetDebitAccess(appUserId, requestorPub)
|
||||
if (!authorization) {
|
||||
return {
|
||||
status: 'authRequired', app, appUser, liveDebitReq: {
|
||||
request_id: event.id,
|
||||
npub: requestorPub,
|
||||
debit: {
|
||||
type: Types.LiveDebitRequest_debit_type.INVOICE,
|
||||
invoice: bolt11
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!authorization.authorized) {
|
||||
return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } }
|
||||
}
|
||||
await this.validateAccessRules(authorization, app, appUser)
|
||||
const { op, payment } = await this.sendDebitPayment(appId, appUserId, requestorPub, bolt11)
|
||||
return { status: 'invoicePaid', op, app, appUser, debitRes: { res: 'ok', preimage: payment.preimage } }
|
||||
}
|
||||
|
||||
sendDebitPayment = async (appId: string, appUserId: string, requestorPub: string, bolt11: string) => {
|
||||
const payment = await this.applicationManager.PayAppUserInvoice(appId, { amount: 0, invoice: bolt11, user_identifier: appUserId, debit_npub: requestorPub })
|
||||
await this.storage.debitStorage.IncrementDebitAccess(appUserId, requestorPub, payment.amount_paid + payment.service_fee + payment.network_fee)
|
||||
const op = this.newPaymentOperation(payment, bolt11)
|
||||
return { payment, op }
|
||||
}
|
||||
|
||||
validateAccessRules = async (access: DebitAccess, app: Application, appUser: ApplicationUser): Promise<boolean> => {
|
||||
const { rules } = access
|
||||
if (!rules) {
|
||||
return true
|
||||
}
|
||||
if (rules[expirationRuleName]) {
|
||||
const [expiration] = rules[expirationRuleName]
|
||||
if (+expiration < Date.now()) {
|
||||
await this.storage.debitStorage.RemoveDebitAccess(access.app_user_id, access.npub)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (rules[frequencyRuleName]) {
|
||||
const [number, unit, max] = rules[frequencyRuleName]
|
||||
const intervalType = unitToIntervalType(unit as RecurringDebitTimeUnit)
|
||||
const seconds = IntervalTypeToSeconds(intervalType) * (+number)
|
||||
const sinceUnix = Math.floor(Date.now() / 1000) * seconds
|
||||
const payments = await this.storage.paymentStorage.GetUserDebitPayments(appUser.user.user_id, sinceUnix, access.npub)
|
||||
let total = 0
|
||||
for (const payment of payments) {
|
||||
total += payment.paid_amount
|
||||
}
|
||||
if (total > +max) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
newPaymentOperation = (payment: Types.PayInvoiceResponse, bolt11: string) => {
|
||||
return {
|
||||
amount: payment.amount_paid,
|
||||
paidAtUnix: Math.floor(Date.now() / 1000),
|
||||
inbound: false,
|
||||
type: Types.UserOperationType.OUTGOING_INVOICE,
|
||||
identifier: bolt11,
|
||||
operationId: payment.operation_id,
|
||||
network_fee: payment.network_fee,
|
||||
service_fee: payment.service_fee,
|
||||
confirmed: true,
|
||||
tx_hash: "",
|
||||
internal: payment.network_fee === 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const newNdebitResponse = (content: string, event: { pub: string, id: string }): UnsignedEvent => {
|
||||
return {
|
||||
content,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 21002,
|
||||
pubkey: "",
|
||||
tags: [
|
||||
['p', event.pub],
|
||||
['e', event.id],
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ import { ERROR, getLogger, PubLogger } from "../helpers/logger.js"
|
|||
import AppUserManager from "./appUserManager.js"
|
||||
import { Application } from '../storage/entity/Application.js'
|
||||
import { UserReceivingInvoice } from '../storage/entity/UserReceivingInvoice.js'
|
||||
import { UnsignedEvent } from '../nostr/tools/event.js'
|
||||
import { UnsignedEvent } from 'nostr-tools'
|
||||
import { NostrEvent, NostrSend } from '../nostr/handler.js'
|
||||
import MetricsManager from '../metrics/index.js'
|
||||
import { LoggedEvent } from '../storage/eventsLog.js'
|
||||
|
|
@ -22,6 +22,7 @@ import { RugPullTracker } from "./rugPullTracker.js"
|
|||
import { AdminManager } from "./adminManager.js"
|
||||
import { Unlocker } from "./unlocker.js"
|
||||
import { defaultInvoiceExpiry } from "../storage/paymentStorage.js"
|
||||
import { DebitManager } from "./debitManager.js"
|
||||
|
||||
type UserOperationsSub = {
|
||||
id: string
|
||||
|
|
@ -32,6 +33,7 @@ type UserOperationsSub = {
|
|||
}
|
||||
const appTag = "Lightning.Pub"
|
||||
export type NofferData = { offer: string, amount?: number }
|
||||
|
||||
export default class {
|
||||
storage: Storage
|
||||
lnd: LND
|
||||
|
|
@ -46,6 +48,7 @@ export default class {
|
|||
metricsManager: MetricsManager
|
||||
liquidityManager: LiquidityManager
|
||||
liquidityProvider: LiquidityProvider
|
||||
debitManager: DebitManager
|
||||
utils: Utils
|
||||
rugPullTracker: RugPullTracker
|
||||
unlocker: Unlocker
|
||||
|
|
@ -67,6 +70,7 @@ export default class {
|
|||
this.productManager = new ProductManager(this.storage, this.paymentManager, this.settings)
|
||||
this.applicationManager = new ApplicationManager(this.storage, this.settings, this.paymentManager)
|
||||
this.appUserManager = new AppUserManager(this.storage, this.settings, this.applicationManager)
|
||||
this.debitManager = new DebitManager(this.storage, this.lnd, this.applicationManager)
|
||||
}
|
||||
|
||||
Stop() {
|
||||
|
|
@ -84,6 +88,7 @@ export default class {
|
|||
attachNostrSend(f: NostrSend) {
|
||||
this.nostrSend = f
|
||||
this.liquidityProvider.attachNostrSend(f)
|
||||
this.debitManager.attachNostrSend(f)
|
||||
}
|
||||
|
||||
htlcCb: HtlcCb = (e) => {
|
||||
|
|
@ -223,7 +228,8 @@ export default class {
|
|||
return
|
||||
}
|
||||
try {
|
||||
await fetch(url + "&ok=true")
|
||||
const symbol = url.includes('?') ? "&" : "?"
|
||||
await fetch(url + symbol + "ok=true")
|
||||
} catch (err: any) {
|
||||
log(ERROR, "error sending paid callback for invoice", err.message || "")
|
||||
}
|
||||
|
|
@ -313,6 +319,8 @@ export default class {
|
|||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
const codeToMessage = (code: number) => {
|
||||
|
|
@ -338,3 +346,5 @@ const newNofferResponse = (content: string, event: NostrEvent): UnsignedEvent =>
|
|||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { LoadMainSettingsFromEnv, MainSettings } from "./settings.js"
|
|||
import { Utils } from "../helpers/utilsWrapper.js"
|
||||
import { Wizard } from "../wizard/index.js"
|
||||
import { AdminManager } from "./adminManager.js"
|
||||
import { encodeNprofile } from "../../custom-nip19.js"
|
||||
export type AppData = {
|
||||
privateKey: string;
|
||||
publicKey: string;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import newNostrClient from '../../../proto/autogenerated/ts/nostr_client.js'
|
||||
import { NostrRequest } from '../../../proto/autogenerated/ts/nostr_transport.js'
|
||||
import * as Types from '../../../proto/autogenerated/ts/types.js'
|
||||
import { decodeNprofile } from '../../custom-nip19.js'
|
||||
import { getLogger } from '../helpers/logger.js'
|
||||
import { Utils } from '../helpers/utilsWrapper.js'
|
||||
import { NostrEvent, NostrSend } from '../nostr/handler.js'
|
||||
import { relayInit } from '../nostr/tools/relay.js'
|
||||
import { InvoicePaidCb } from '../lnd/settings.js'
|
||||
import Storage from '../storage/index.js'
|
||||
export type LiquidityRequest = { action: 'spend' | 'receive', amount: number }
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { UserReceivingAddress } from '../storage/entity/UserReceivingAddress.js'
|
|||
import { AddressPaidCb, InvoicePaidCb, PaidInvoice } from '../lnd/settings.js'
|
||||
import { UserReceivingInvoice, ZapInfo } from '../storage/entity/UserReceivingInvoice.js'
|
||||
import { Payment_PaymentStatus, SendCoinsResponse } from '../../../proto/lnd/lightning.js'
|
||||
import { Event, verifiedSymbol, verifySignature } from '../nostr/tools/event.js'
|
||||
import { Event, verifiedSymbol, verifyEvent } from 'nostr-tools'
|
||||
import { AddressReceivingTransaction } from '../storage/entity/AddressReceivingTransaction.js'
|
||||
import { UserTransactionPayment } from '../storage/entity/UserTransactionPayment.js'
|
||||
import { Watchdog } from './watchdog.js'
|
||||
|
|
@ -286,9 +286,9 @@ export default class {
|
|||
}
|
||||
let paymentInfo = { preimage: "", amtPaid: 0, networkFee: 0, serialId: 0 }
|
||||
if (internalInvoice) {
|
||||
paymentInfo = await this.PayInternalInvoice(userId, internalInvoice, { payAmount, serviceFee }, linkedApplication)
|
||||
paymentInfo = await this.PayInternalInvoice(userId, internalInvoice, { payAmount, serviceFee }, linkedApplication, req.debit_npub)
|
||||
} else {
|
||||
paymentInfo = await this.PayExternalInvoice(userId, req.invoice, { payAmount, serviceFee, amountForLnd: req.amount }, linkedApplication)
|
||||
paymentInfo = await this.PayExternalInvoice(userId, req.invoice, { payAmount, serviceFee, amountForLnd: req.amount }, linkedApplication, req.debit_npub)
|
||||
}
|
||||
if (isAppUserPayment && serviceFee > 0) {
|
||||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, serviceFee, "fees")
|
||||
|
|
@ -304,7 +304,7 @@ export default class {
|
|||
}
|
||||
}
|
||||
|
||||
async PayExternalInvoice(userId: string, invoice: string, amounts: { payAmount: number, serviceFee: number, amountForLnd: number }, linkedApplication: Application) {
|
||||
async PayExternalInvoice(userId: string, invoice: string, amounts: { payAmount: number, serviceFee: number, amountForLnd: number }, linkedApplication: Application, debitNpub?: string) {
|
||||
if (this.settings.disableExternalPayments) {
|
||||
throw new Error("something went wrong sending payment, please try again later")
|
||||
}
|
||||
|
|
@ -325,7 +325,7 @@ export default class {
|
|||
const pendingPayment = await this.storage.txQueue.PushToQueue({
|
||||
dbTx: true, description: "payment started", exec: async tx => {
|
||||
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + routingFeeLimit, invoice, tx)
|
||||
return await this.storage.paymentStorage.AddPendingExternalPayment(userId, invoice, { payAmount, serviceFee, networkFee: routingFeeLimit }, linkedApplication, provider, tx)
|
||||
return await this.storage.paymentStorage.AddPendingExternalPayment(userId, invoice, { payAmount, serviceFee, networkFee: routingFeeLimit }, linkedApplication, provider, tx, debitNpub)
|
||||
}
|
||||
})
|
||||
this.log("ready to pay")
|
||||
|
|
@ -348,7 +348,7 @@ export default class {
|
|||
}
|
||||
}
|
||||
|
||||
async PayInternalInvoice(userId: string, internalInvoice: UserReceivingInvoice, amounts: { payAmount: number, serviceFee: number }, linkedApplication: Application) {
|
||||
async PayInternalInvoice(userId: string, internalInvoice: UserReceivingInvoice, amounts: { payAmount: number, serviceFee: number }, linkedApplication: Application, debitNpub?: string) {
|
||||
if (internalInvoice.paid_at_unix > 0) {
|
||||
throw new Error("this invoice was already paid")
|
||||
}
|
||||
|
|
@ -357,7 +357,7 @@ export default class {
|
|||
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement, internalInvoice.invoice)
|
||||
try {
|
||||
await this.invoicePaidCb(internalInvoice.invoice, payAmount, 'internal')
|
||||
const newPayment = await this.storage.paymentStorage.AddInternalPayment(userId, internalInvoice.invoice, payAmount, serviceFee, linkedApplication)
|
||||
const newPayment = await this.storage.paymentStorage.AddInternalPayment(userId, internalInvoice.invoice, payAmount, serviceFee, linkedApplication, debitNpub)
|
||||
this.utils.stateBundler.AddTxPoint('paidAnInvoice', payAmount, { used: 'internal', from: 'user' })
|
||||
return { preimage: "", amtPaid: payAmount, networkFee: 0, serialId: newPayment.serial_id }
|
||||
} catch (err) {
|
||||
|
|
@ -567,7 +567,7 @@ export default class {
|
|||
validateZapEvent(event: string, amt: number): ZapInfo {
|
||||
const nostrEvent = JSON.parse(event) as Event
|
||||
delete nostrEvent[verifiedSymbol]
|
||||
const verified = verifySignature(nostrEvent)
|
||||
const verified = verifyEvent(nostrEvent)
|
||||
if (!verified) {
|
||||
throw new Error("nostr event not valid")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import * as Types from '../../../proto/autogenerated/ts/types.js'
|
|||
import { MainSettings } from './settings.js'
|
||||
import PaymentManager from './paymentManager.js'
|
||||
import { defaultInvoiceExpiry } from '../storage/paymentStorage.js'
|
||||
import { encodeNoffer, PriceType } from '../../custom-nip19.js'
|
||||
import { nip19 } from 'nostr-tools'
|
||||
const { nofferEncode, OfferPriceType } = nip19
|
||||
|
||||
export default class {
|
||||
storage: Storage
|
||||
|
|
@ -26,7 +27,7 @@ export default class {
|
|||
id: newProduct.product_id,
|
||||
name: newProduct.name,
|
||||
price_sats: newProduct.price_sats,
|
||||
noffer: encodeNoffer({ pubkey: user.user_id, offer: offer, priceType: PriceType.fixed, price: newProduct.price_sats, relay: "" })
|
||||
noffer: nofferEncode({ pubkey: user.user_id, offer: offer, priceType: OfferPriceType.Fixed, price: newProduct.price_sats, relay: "" })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
//import { SimplePool, Sub, Event, UnsignedEvent, getEventHash, signEvent } from 'nostr-tools'
|
||||
import { SimplePool, Sub, Event, UnsignedEvent, getEventHash, finishEvent, relayInit } from './tools/index.js'
|
||||
import { encryptData, decryptData, getSharedSecret, decodePayload, encodePayload, EncryptedData } from './nip44.js'
|
||||
import WebSocket from 'ws'
|
||||
Object.assign(global, { WebSocket: WebSocket });
|
||||
import { SimplePool, Event, UnsignedEvent, getEventHash, finalizeEvent, Relay, nip44 } from 'nostr-tools'
|
||||
//import { encryptData, decryptData, getSharedSecret, decodePayload, encodePayload, EncryptedData, nip44 } from 'nostr-tools'
|
||||
import { ERROR, getLogger } from '../helpers/logger.js'
|
||||
import { encodeNprofile } from '../../custom-nip19.js'
|
||||
import { nip19 } from 'nostr-tools'
|
||||
import { encrypt as encryptV1, decrypt as decryptV1, getSharedSecret as getConversationKeyV1 } from './nip44v1.js'
|
||||
const { nprofileEncode } = nip19
|
||||
const { v2 } = nip44
|
||||
const { encrypt: encryptV2, decrypt: decryptV2, utils } = v2
|
||||
const { getConversationKey: getConversationKeyV2 } = utils
|
||||
const handledEvents: string[] = [] // TODO: - big memory leak here, add TTL
|
||||
type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string }
|
||||
type ClientInfo = { clientId: string, publicKey: string, privateKey: string, name: string }
|
||||
|
|
@ -90,11 +97,10 @@ const sendToNostr: NostrSend = (initiator, data, relays) => {
|
|||
subProcessHandler.Send(initiator, data, relays)
|
||||
}
|
||||
send({ type: 'ready' })
|
||||
const supportedKinds = [21000, 21001]
|
||||
const supportedKinds = [21000, 21001, 21002]
|
||||
export default class Handler {
|
||||
pool = new SimplePool()
|
||||
settings: NostrSettings
|
||||
subs: Sub[] = []
|
||||
apps: Record<string, AppInfo> = {}
|
||||
eventCallback: (event: NostrEvent) => void
|
||||
log = getLogger({ component: "nostrMiddleware" })
|
||||
|
|
@ -102,7 +108,7 @@ export default class Handler {
|
|||
this.settings = settings
|
||||
this.log("connecting to relays:", settings.relays)
|
||||
this.settings.apps.forEach(app => {
|
||||
this.log("appId:", app.appId, "pubkey:", app.publicKey, "nprofile:", encodeNprofile({ pubkey: app.publicKey, relays: settings.relays }))
|
||||
this.log("appId:", app.appId, "pubkey:", app.publicKey, "nprofile:", nprofileEncode({ pubkey: app.publicKey, relays: settings.relays }))
|
||||
})
|
||||
this.eventCallback = eventCallback
|
||||
this.settings.apps.forEach(app => {
|
||||
|
|
@ -114,9 +120,13 @@ export default class Handler {
|
|||
async Connect() {
|
||||
const log = getLogger({})
|
||||
log("conneting to relay...", this.settings.relays[0])
|
||||
const relay = relayInit(this.settings.relays[0]) // TODO: create multiple conns for multiple relays
|
||||
let relay: Relay | null = null
|
||||
//const relay = relayInit(this.settings.relays[0]) // TODO: create multiple conns for multiple relays
|
||||
try {
|
||||
await relay.connect()
|
||||
relay = await Relay.connect(this.settings.relays[0])
|
||||
if (!relay.connected) {
|
||||
throw new Error("failed to connect to relay")
|
||||
}
|
||||
} catch (err) {
|
||||
log("failed to connect to relay, will try again in 2 seconds")
|
||||
setTimeout(() => {
|
||||
|
|
@ -124,23 +134,24 @@ export default class Handler {
|
|||
}, 2000)
|
||||
return
|
||||
}
|
||||
|
||||
log("connected, subbing...")
|
||||
relay.on('disconnect', () => {
|
||||
relay.onclose = (() => {
|
||||
log("relay disconnected, will try to reconnect")
|
||||
relay.close()
|
||||
this.Connect()
|
||||
})
|
||||
const sub = relay.sub([
|
||||
const sub = relay.subscribe([
|
||||
{
|
||||
since: Math.ceil(Date.now() / 1000),
|
||||
kinds: supportedKinds,
|
||||
'#p': Object.keys(this.apps),
|
||||
}
|
||||
])
|
||||
sub.on('eose', () => {
|
||||
], {
|
||||
oneose: () => {
|
||||
log("up to date with nostr events")
|
||||
})
|
||||
sub.on('event', async (e) => {
|
||||
},
|
||||
onevent: async (e) => {
|
||||
if (!supportedKinds.includes(e.kind) || !e.pubkey) {
|
||||
return
|
||||
}
|
||||
|
|
@ -153,6 +164,7 @@ export default class Handler {
|
|||
await this.processEvent(e, app)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -167,8 +179,11 @@ export default class Handler {
|
|||
const startAtNano = process.hrtime.bigint().toString()
|
||||
let content = ""
|
||||
try {
|
||||
const decoded = decodePayload(e.content)
|
||||
content = await decryptData(decoded, getSharedSecret(app.privateKey, e.pubkey))
|
||||
if (e.kind === 21000) {
|
||||
content = decryptV1(e.content, getConversationKeyV1(app.privateKey, e.pubkey))
|
||||
} else {
|
||||
content = decryptV2(e.content, getConversationKeyV2(Buffer.from(app.privateKey, 'hex'), e.pubkey))
|
||||
}
|
||||
} catch (e: any) {
|
||||
this.log(ERROR, "failed to decrypt event", e.message, e.content)
|
||||
return
|
||||
|
|
@ -179,12 +194,12 @@ export default class Handler {
|
|||
|
||||
async Send(initiator: SendInitiator, data: SendData, relays?: string[]) {
|
||||
const keys = this.GetSendKeys(initiator)
|
||||
const privateKey = Buffer.from(keys.privateKey, 'hex')
|
||||
let toSign: UnsignedEvent
|
||||
if (data.type === 'content') {
|
||||
let content: string
|
||||
try {
|
||||
const decoded = await encryptData(data.content, getSharedSecret(keys.privateKey, data.pub))
|
||||
content = encodePayload(decoded)
|
||||
content = encryptV1(data.content, getConversationKeyV1(keys.privateKey, data.pub))
|
||||
} catch (e: any) {
|
||||
this.log(ERROR, "failed to encrypt content", e.message, data.content)
|
||||
return
|
||||
|
|
@ -197,11 +212,11 @@ export default class Handler {
|
|||
tags: [['p', data.pub]],
|
||||
}
|
||||
} else {
|
||||
console.log(data)
|
||||
toSign = data.event
|
||||
if (data.encrypt) {
|
||||
try {
|
||||
const content = await encryptData(data.event.content, getSharedSecret(keys.privateKey, data.encrypt.toPub))
|
||||
toSign.content = encodePayload(content)
|
||||
toSign.content = encryptV2(data.event.content, getConversationKeyV2(Buffer.from(keys.privateKey, 'hex'), data.encrypt.toPub))
|
||||
} catch (e: any) {
|
||||
this.log(ERROR, "failed to encrypt content", e.message)
|
||||
return
|
||||
|
|
@ -212,7 +227,7 @@ export default class Handler {
|
|||
}
|
||||
}
|
||||
|
||||
const signed = finishEvent(toSign, keys.privateKey)
|
||||
const signed = finalizeEvent(toSign, Buffer.from(keys.privateKey, 'hex'))
|
||||
let sent = false
|
||||
const log = getLogger({ appName: keys.name })
|
||||
await Promise.all(this.pool.publish(relays || this.settings.relays, signed).map(async p => {
|
||||
|
|
|
|||
|
|
@ -12,17 +12,15 @@ export const getSharedSecret = (privateKey: string, publicKey: string) => {
|
|||
return sha256(key.slice(1, 33));
|
||||
}
|
||||
|
||||
export const encryptData = (content: string, sharedSecret: Uint8Array) => {
|
||||
export const encrypt = (content: string, sharedSecret: Uint8Array) => {
|
||||
const nonce = randomBytes(24);
|
||||
const plaintext = new TextEncoder().encode(content);
|
||||
const ciphertext = xchacha20(sharedSecret, nonce, plaintext, plaintext);
|
||||
return {
|
||||
ciphertext: Uint8Array.from(ciphertext),
|
||||
nonce: nonce,
|
||||
} as EncryptedData;
|
||||
return encodePayload({ ciphertext, nonce });
|
||||
}
|
||||
|
||||
export const decryptData = (payload: EncryptedData, sharedSecret: Uint8Array) => {
|
||||
export const decrypt = (content: string, sharedSecret: Uint8Array) => {
|
||||
const payload = decodePayload(content);
|
||||
const dst = xchacha20(sharedSecret, payload.nonce, payload.ciphertext, payload.ciphertext);
|
||||
const decoded = new TextDecoder().decode(dst);
|
||||
return decoded;
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
import { schnorr } from '@noble/curves/secp256k1'
|
||||
import { sha256 } from '@noble/hashes/sha256'
|
||||
import { bytesToHex } from '@noble/hashes/utils'
|
||||
|
||||
import { getPublicKey } from './keys.js'
|
||||
import { utf8Encoder } from './utils.js'
|
||||
|
||||
/** Designates a verified event signature. */
|
||||
export const verifiedSymbol = Symbol('verified')
|
||||
|
||||
/** @deprecated Use numbers instead. */
|
||||
/* eslint-disable no-unused-vars */
|
||||
export enum Kind {
|
||||
Metadata = 0,
|
||||
Text = 1,
|
||||
RecommendRelay = 2,
|
||||
Contacts = 3,
|
||||
EncryptedDirectMessage = 4,
|
||||
EventDeletion = 5,
|
||||
Repost = 6,
|
||||
Reaction = 7,
|
||||
BadgeAward = 8,
|
||||
ChannelCreation = 40,
|
||||
ChannelMetadata = 41,
|
||||
ChannelMessage = 42,
|
||||
ChannelHideMessage = 43,
|
||||
ChannelMuteUser = 44,
|
||||
Blank = 255,
|
||||
Report = 1984,
|
||||
ZapRequest = 9734,
|
||||
Zap = 9735,
|
||||
RelayList = 10002,
|
||||
ClientAuth = 22242,
|
||||
HttpAuth = 27235,
|
||||
ProfileBadge = 30008,
|
||||
BadgeDefinition = 30009,
|
||||
Article = 30023,
|
||||
FileMetadata = 1063,
|
||||
}
|
||||
|
||||
export interface Event<K extends number = number> {
|
||||
kind: K
|
||||
tags: string[][]
|
||||
content: string
|
||||
created_at: number
|
||||
pubkey: string
|
||||
id: string
|
||||
sig: string
|
||||
[verifiedSymbol]?: boolean
|
||||
}
|
||||
|
||||
export type EventTemplate<K extends number = number> = Pick<Event<K>, 'kind' | 'tags' | 'content' | 'created_at'>
|
||||
export type UnsignedEvent<K extends number = number> = Pick<
|
||||
Event<K>,
|
||||
'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'
|
||||
>
|
||||
|
||||
/** An event whose signature has been verified. */
|
||||
export interface VerifiedEvent<K extends number = number> extends Event<K> {
|
||||
[verifiedSymbol]: true
|
||||
}
|
||||
|
||||
export function getBlankEvent(): EventTemplate<Kind.Blank>
|
||||
export function getBlankEvent<K extends number>(kind: K): EventTemplate<K>
|
||||
export function getBlankEvent<K>(kind: K | Kind.Blank = Kind.Blank) {
|
||||
return {
|
||||
kind,
|
||||
content: '',
|
||||
tags: [],
|
||||
created_at: 0,
|
||||
}
|
||||
}
|
||||
|
||||
export function finishEvent<K extends number = number>(t: EventTemplate<K>, privateKey: string): VerifiedEvent<K> {
|
||||
const event = t as VerifiedEvent<K>
|
||||
event.pubkey = getPublicKey(privateKey)
|
||||
event.id = getEventHash(event)
|
||||
event.sig = getSignature(event, privateKey)
|
||||
event[verifiedSymbol] = true
|
||||
return event
|
||||
}
|
||||
|
||||
export function serializeEvent(evt: UnsignedEvent<number>): string {
|
||||
if (!validateEvent(evt)) throw new Error("can't serialize event with wrong or missing properties")
|
||||
|
||||
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])
|
||||
}
|
||||
|
||||
export function getEventHash(event: UnsignedEvent<number>): string {
|
||||
let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))
|
||||
return bytesToHex(eventHash)
|
||||
}
|
||||
|
||||
const isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object
|
||||
|
||||
export function validateEvent<T>(event: T): event is T & UnsignedEvent<number> {
|
||||
if (!isRecord(event)) return false
|
||||
if (typeof event.kind !== 'number') return false
|
||||
if (typeof event.content !== 'string') return false
|
||||
if (typeof event.created_at !== 'number') return false
|
||||
if (typeof event.pubkey !== 'string') return false
|
||||
if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false
|
||||
|
||||
if (!Array.isArray(event.tags)) return false
|
||||
for (let i = 0; i < event.tags.length; i++) {
|
||||
let tag = event.tags[i]
|
||||
if (!Array.isArray(tag)) return false
|
||||
for (let j = 0; j < tag.length; j++) {
|
||||
if (typeof tag[j] === 'object') return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/** Verify the event's signature. This function mutates the event with a `verified` symbol, making it idempotent. */
|
||||
export function verifySignature<K extends number>(event: Event<K>): event is VerifiedEvent<K> {
|
||||
//@ts-ignore
|
||||
if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]
|
||||
|
||||
const hash = getEventHash(event)
|
||||
if (hash !== event.id) {
|
||||
return (event[verifiedSymbol] = false)
|
||||
}
|
||||
|
||||
try {
|
||||
return (event[verifiedSymbol] = schnorr.verify(event.sig, hash, event.pubkey))
|
||||
} catch (err) {
|
||||
return (event[verifiedSymbol] = false)
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Use `getSignature` instead. */
|
||||
export function signEvent(event: UnsignedEvent<number>, key: string): string {
|
||||
console.warn(
|
||||
'nostr-tools: `signEvent` is deprecated and will be removed or changed in the future. Please use `getSignature` instead.',
|
||||
)
|
||||
return getSignature(event, key)
|
||||
}
|
||||
|
||||
/** Calculate the signature for an event. */
|
||||
export function getSignature(event: UnsignedEvent<number>, key: string): string {
|
||||
return bytesToHex(schnorr.sign(getEventHash(event), key))
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
export function getHex64(json: string, field: string): string {
|
||||
let len = field.length + 3
|
||||
let idx = json.indexOf(`"${field}":`) + len
|
||||
let s = json.slice(idx).indexOf(`"`) + idx + 1
|
||||
return json.slice(s, s + 64)
|
||||
}
|
||||
|
||||
export function getInt(json: string, field: string): number {
|
||||
let len = field.length
|
||||
let idx = json.indexOf(`"${field}":`) + len + 3
|
||||
let sliced = json.slice(idx)
|
||||
let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))
|
||||
return parseInt(sliced.slice(0, end), 10)
|
||||
}
|
||||
|
||||
export function getSubscriptionId(json: string): string | null {
|
||||
let idx = json.slice(0, 22).indexOf(`"EVENT"`)
|
||||
if (idx === -1) return null
|
||||
|
||||
let pstart = json.slice(idx + 7 + 1).indexOf(`"`)
|
||||
if (pstart === -1) return null
|
||||
let start = idx + 7 + 1 + pstart
|
||||
|
||||
let pend = json.slice(start + 1, 80).indexOf(`"`)
|
||||
if (pend === -1) return null
|
||||
let end = start + 1 + pend
|
||||
|
||||
return json.slice(start + 1, end)
|
||||
}
|
||||
|
||||
export function matchEventId(json: string, id: string): boolean {
|
||||
return id === getHex64(json, 'id')
|
||||
}
|
||||
|
||||
export function matchEventPubkey(json: string, pubkey: string): boolean {
|
||||
return pubkey === getHex64(json, 'pubkey')
|
||||
}
|
||||
|
||||
export function matchEventKind(json: string, kind: number): boolean {
|
||||
return kind === getInt(json, 'kind')
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
import { Event } from './event.js'
|
||||
|
||||
export type Filter<K extends number = number> = {
|
||||
ids?: string[]
|
||||
kinds?: K[]
|
||||
authors?: string[]
|
||||
since?: number
|
||||
until?: number
|
||||
limit?: number
|
||||
search?: string
|
||||
[key: `#${string}`]: string[] | undefined
|
||||
}
|
||||
|
||||
export function matchFilter(filter: Filter<number>, event: Event<number>): boolean {
|
||||
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
|
||||
if (!filter.ids.some(prefix => event.id.startsWith(prefix))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false
|
||||
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
|
||||
if (!filter.authors.some(prefix => event.pubkey.startsWith(prefix))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for (let f in filter) {
|
||||
if (f[0] === '#') {
|
||||
let tagName = f.slice(1)
|
||||
let values = filter[`#${tagName}`]
|
||||
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false
|
||||
}
|
||||
}
|
||||
|
||||
if (filter.since && event.created_at < filter.since) return false
|
||||
if (filter.until && event.created_at > filter.until) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export function matchFilters(filters: Filter<number>[], event: Event<number>): boolean {
|
||||
for (let i = 0; i < filters.length; i++) {
|
||||
if (matchFilter(filters[i], event)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function mergeFilters(...filters: Filter<number>[]): Filter<number> {
|
||||
let result: Filter<number> = {}
|
||||
for (let i = 0; i < filters.length; i++) {
|
||||
let filter = filters[i]
|
||||
Object.entries(filter).forEach(([property, values]) => {
|
||||
if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {
|
||||
// @ts-ignore
|
||||
result[property] = result[property] || []
|
||||
// @ts-ignore
|
||||
for (let v = 0; v < values.length; v++) {
|
||||
// @ts-ignore
|
||||
let value = values[v]
|
||||
// @ts-ignore
|
||||
if (!result[property].includes(value)) result[property].push(value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit
|
||||
if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until
|
||||
if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
export * from './event.js'
|
||||
export * from './fakejson.js'
|
||||
export * from './filter.js'
|
||||
export * from './keys.js'
|
||||
export * from './pool.js'
|
||||
export * from './relay.js'
|
||||
export * from './utils.js'
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import { schnorr } from '@noble/curves/secp256k1'
|
||||
import { bytesToHex } from '@noble/hashes/utils'
|
||||
|
||||
export function generatePrivateKey(): string {
|
||||
return bytesToHex(schnorr.utils.randomPrivateKey())
|
||||
}
|
||||
|
||||
export function getPublicKey(privateKey: string): string {
|
||||
return bytesToHex(schnorr.getPublicKey(privateKey))
|
||||
}
|
||||
|
|
@ -1,249 +0,0 @@
|
|||
import { relayInit, eventsGenerator, type Relay, type Sub, type SubscriptionOptions } from './relay.js'
|
||||
import { normalizeURL } from './utils.js'
|
||||
|
||||
import type { Event } from './event.js'
|
||||
import { matchFilters, type Filter } from './filter.js'
|
||||
|
||||
type BatchedRequest = {
|
||||
filters: Filter<any>[]
|
||||
relays: string[]
|
||||
resolve: (events: Event<any>[]) => void
|
||||
events: Event<any>[]
|
||||
}
|
||||
|
||||
export class SimplePool {
|
||||
private _conn: { [url: string]: Relay }
|
||||
private _seenOn: { [id: string]: Set<string> } = {} // a map of all events we've seen in each relay
|
||||
private batchedByKey: { [batchKey: string]: BatchedRequest[] } = {}
|
||||
|
||||
private eoseSubTimeout: number
|
||||
private getTimeout: number
|
||||
private seenOnEnabled: boolean = true
|
||||
private batchInterval: number = 100
|
||||
|
||||
constructor(
|
||||
options: {
|
||||
eoseSubTimeout?: number
|
||||
getTimeout?: number
|
||||
seenOnEnabled?: boolean
|
||||
batchInterval?: number
|
||||
} = {},
|
||||
) {
|
||||
this._conn = {}
|
||||
this.eoseSubTimeout = options.eoseSubTimeout || 3400
|
||||
this.getTimeout = options.getTimeout || 3400
|
||||
this.seenOnEnabled = options.seenOnEnabled !== false
|
||||
this.batchInterval = options.batchInterval || 100
|
||||
}
|
||||
|
||||
close(relays: string[]): void {
|
||||
relays.forEach(url => {
|
||||
let relay = this._conn[normalizeURL(url)]
|
||||
if (relay) relay.close()
|
||||
})
|
||||
}
|
||||
|
||||
async ensureRelay(url: string): Promise<Relay> {
|
||||
const nm = normalizeURL(url)
|
||||
|
||||
if (!this._conn[nm]) {
|
||||
this._conn[nm] = relayInit(nm, {
|
||||
getTimeout: this.getTimeout * 0.9,
|
||||
listTimeout: this.getTimeout * 0.9,
|
||||
})
|
||||
}
|
||||
|
||||
const relay = this._conn[nm]
|
||||
await relay.connect()
|
||||
return relay
|
||||
}
|
||||
|
||||
sub<K extends number = number>(relays: string[], filters: Filter<K>[], opts?: SubscriptionOptions): Sub<K> {
|
||||
let _knownIds: Set<string> = new Set()
|
||||
let modifiedOpts = { ...(opts || {}) }
|
||||
modifiedOpts.alreadyHaveEvent = (id, url) => {
|
||||
if (opts?.alreadyHaveEvent?.(id, url)) {
|
||||
return true
|
||||
}
|
||||
if (this.seenOnEnabled) {
|
||||
let set = this._seenOn[id] || new Set()
|
||||
set.add(url)
|
||||
this._seenOn[id] = set
|
||||
}
|
||||
return _knownIds.has(id)
|
||||
}
|
||||
|
||||
let subs: Sub[] = []
|
||||
let eventListeners: Set<any> = new Set()
|
||||
let eoseListeners: Set<() => void> = new Set()
|
||||
let eosesMissing = relays.length
|
||||
|
||||
let eoseSent = false
|
||||
let eoseTimeout = setTimeout(
|
||||
() => {
|
||||
eoseSent = true
|
||||
for (let cb of eoseListeners.values()) cb()
|
||||
},
|
||||
opts?.eoseSubTimeout || this.eoseSubTimeout,
|
||||
)
|
||||
|
||||
relays
|
||||
.filter((r, i, a) => a.indexOf(r) === i)
|
||||
.forEach(async relay => {
|
||||
let r
|
||||
try {
|
||||
r = await this.ensureRelay(relay)
|
||||
} catch (err) {
|
||||
handleEose()
|
||||
return
|
||||
}
|
||||
if (!r) return
|
||||
let s = r.sub(filters, modifiedOpts)
|
||||
s.on('event', event => {
|
||||
_knownIds.add(event.id as string)
|
||||
for (let cb of eventListeners.values()) cb(event)
|
||||
})
|
||||
s.on('eose', () => {
|
||||
if (eoseSent) return
|
||||
handleEose()
|
||||
})
|
||||
subs.push(s)
|
||||
|
||||
function handleEose() {
|
||||
eosesMissing--
|
||||
if (eosesMissing === 0) {
|
||||
clearTimeout(eoseTimeout)
|
||||
for (let cb of eoseListeners.values()) cb()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let greaterSub: Sub<K> = {
|
||||
sub(filters, opts) {
|
||||
subs.forEach(sub => sub.sub(filters, opts))
|
||||
return greaterSub as any
|
||||
},
|
||||
unsub() {
|
||||
subs.forEach(sub => sub.unsub())
|
||||
},
|
||||
on(type, cb) {
|
||||
if (type === 'event') {
|
||||
eventListeners.add(cb)
|
||||
} else if (type === 'eose') {
|
||||
eoseListeners.add(cb as () => void | Promise<void>)
|
||||
}
|
||||
},
|
||||
off(type, cb) {
|
||||
if (type === 'event') {
|
||||
eventListeners.delete(cb)
|
||||
} else if (type === 'eose') eoseListeners.delete(cb as () => void | Promise<void>)
|
||||
},
|
||||
get events() {
|
||||
return eventsGenerator(greaterSub)
|
||||
},
|
||||
}
|
||||
|
||||
return greaterSub
|
||||
}
|
||||
|
||||
get<K extends number = number>(
|
||||
relays: string[],
|
||||
filter: Filter<K>,
|
||||
opts?: SubscriptionOptions,
|
||||
): Promise<Event<K> | null> {
|
||||
return new Promise(resolve => {
|
||||
let sub = this.sub(relays, [filter], opts)
|
||||
let timeout = setTimeout(() => {
|
||||
sub.unsub()
|
||||
resolve(null)
|
||||
}, this.getTimeout)
|
||||
sub.on('event', event => {
|
||||
resolve(event)
|
||||
clearTimeout(timeout)
|
||||
sub.unsub()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
list<K extends number = number>(
|
||||
relays: string[],
|
||||
filters: Filter<K>[],
|
||||
opts?: SubscriptionOptions,
|
||||
): Promise<Event<K>[]> {
|
||||
return new Promise(resolve => {
|
||||
let events: Event<K>[] = []
|
||||
let sub = this.sub(relays, filters, opts)
|
||||
|
||||
sub.on('event', event => {
|
||||
events.push(event)
|
||||
})
|
||||
|
||||
// we can rely on an eose being emitted here because pool.sub() will fake one
|
||||
sub.on('eose', () => {
|
||||
sub.unsub()
|
||||
resolve(events)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
batchedList<K extends number = number>(
|
||||
batchKey: string,
|
||||
relays: string[],
|
||||
filters: Filter<K>[],
|
||||
): Promise<Event<K>[]> {
|
||||
return new Promise(resolve => {
|
||||
if (!this.batchedByKey[batchKey]) {
|
||||
this.batchedByKey[batchKey] = [
|
||||
{
|
||||
filters,
|
||||
relays,
|
||||
resolve,
|
||||
events: [],
|
||||
},
|
||||
]
|
||||
|
||||
setTimeout(() => {
|
||||
Object.keys(this.batchedByKey).forEach(async batchKey => {
|
||||
const batchedRequests = this.batchedByKey[batchKey]
|
||||
|
||||
const filters = [] as Filter[]
|
||||
const relays = [] as string[]
|
||||
batchedRequests.forEach(br => {
|
||||
filters.push(...br.filters)
|
||||
relays.push(...br.relays)
|
||||
})
|
||||
|
||||
const sub = this.sub(relays, filters)
|
||||
sub.on('event', event => {
|
||||
batchedRequests.forEach(br => matchFilters(br.filters, event) && br.events.push(event))
|
||||
})
|
||||
sub.on('eose', () => {
|
||||
sub.unsub()
|
||||
batchedRequests.forEach(br => br.resolve(br.events))
|
||||
})
|
||||
|
||||
delete this.batchedByKey[batchKey]
|
||||
})
|
||||
}, this.batchInterval)
|
||||
} else {
|
||||
this.batchedByKey[batchKey].push({
|
||||
filters,
|
||||
relays,
|
||||
resolve,
|
||||
events: [],
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
publish(relays: string[], event: Event<number>): Promise<void>[] {
|
||||
return relays.map(async relay => {
|
||||
let r = await this.ensureRelay(relay)
|
||||
return r.publish(event)
|
||||
})
|
||||
}
|
||||
|
||||
seenOn(id: string): string[] {
|
||||
return Array.from(this._seenOn[id]?.values?.() || [])
|
||||
}
|
||||
}
|
||||
|
|
@ -1,402 +0,0 @@
|
|||
/* global WebSocket */
|
||||
import "websocket-polyfill"
|
||||
import { verifySignature, validateEvent, type Event } from './event.js'
|
||||
import { matchFilters, type Filter } from './filter.js'
|
||||
import { getHex64, getSubscriptionId } from './fakejson.js'
|
||||
import { MessageQueue } from './utils.js'
|
||||
|
||||
type RelayEvent = {
|
||||
connect: () => void | Promise<void>
|
||||
disconnect: () => void | Promise<void>
|
||||
error: () => void | Promise<void>
|
||||
notice: (msg: string) => void | Promise<void>
|
||||
auth: (challenge: string) => void | Promise<void>
|
||||
}
|
||||
export type CountPayload = {
|
||||
count: number
|
||||
}
|
||||
export type SubEvent<K extends number> = {
|
||||
event: (event: Event<K>) => void | Promise<void>
|
||||
count: (payload: CountPayload) => void | Promise<void>
|
||||
eose: () => void | Promise<void>
|
||||
}
|
||||
export type Relay = {
|
||||
url: string
|
||||
status: number
|
||||
connect: () => Promise<void>
|
||||
close: () => void
|
||||
sub: <K extends number = number>(filters: Filter<K>[], opts?: SubscriptionOptions) => Sub<K>
|
||||
list: <K extends number = number>(filters: Filter<K>[], opts?: SubscriptionOptions) => Promise<Event<K>[]>
|
||||
get: <K extends number = number>(filter: Filter<K>, opts?: SubscriptionOptions) => Promise<Event<K> | null>
|
||||
count: (filters: Filter[], opts?: SubscriptionOptions) => Promise<CountPayload | null>
|
||||
publish: (event: Event<number>) => Promise<void>
|
||||
auth: (event: Event<number>) => Promise<void>
|
||||
off: <T extends keyof RelayEvent, U extends RelayEvent[T]>(event: T, listener: U) => void
|
||||
on: <T extends keyof RelayEvent, U extends RelayEvent[T]>(event: T, listener: U) => void
|
||||
}
|
||||
export type Sub<K extends number = number> = {
|
||||
sub: <K extends number = number>(filters: Filter<K>[], opts: SubscriptionOptions) => Sub<K>
|
||||
unsub: () => void
|
||||
on: <T extends keyof SubEvent<K>, U extends SubEvent<K>[T]>(event: T, listener: U) => void
|
||||
off: <T extends keyof SubEvent<K>, U extends SubEvent<K>[T]>(event: T, listener: U) => void
|
||||
events: AsyncGenerator<Event<K>, void, unknown>
|
||||
}
|
||||
|
||||
export type SubscriptionOptions = {
|
||||
id?: string
|
||||
verb?: 'REQ' | 'COUNT'
|
||||
skipVerification?: boolean
|
||||
alreadyHaveEvent?: null | ((id: string, relay: string) => boolean)
|
||||
eoseSubTimeout?: number
|
||||
}
|
||||
|
||||
const newListeners = (): { [TK in keyof RelayEvent]: RelayEvent[TK][] } => ({
|
||||
connect: [],
|
||||
disconnect: [],
|
||||
error: [],
|
||||
notice: [],
|
||||
auth: [],
|
||||
})
|
||||
|
||||
export function relayInit(
|
||||
url: string,
|
||||
options: {
|
||||
getTimeout?: number
|
||||
listTimeout?: number
|
||||
countTimeout?: number
|
||||
} = {},
|
||||
): Relay {
|
||||
let { listTimeout = 3000, getTimeout = 3000, countTimeout = 3000 } = options
|
||||
|
||||
var ws: WebSocket
|
||||
var openSubs: { [id: string]: { filters: Filter[] } & SubscriptionOptions } = {}
|
||||
var listeners = newListeners()
|
||||
var subListeners: {
|
||||
[subid: string]: { [TK in keyof SubEvent<any>]: SubEvent<any>[TK][] }
|
||||
} = {}
|
||||
var pubListeners: {
|
||||
[eventid: string]: {
|
||||
resolve: (_: unknown) => void
|
||||
reject: (err: Error) => void
|
||||
}
|
||||
} = {}
|
||||
|
||||
var connectionPromise: Promise<void> | undefined
|
||||
async function connectRelay(): Promise<void> {
|
||||
if (connectionPromise) return connectionPromise
|
||||
connectionPromise = new Promise((resolve, reject) => {
|
||||
try {
|
||||
ws = new WebSocket(url)
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
ws.onopen = () => {
|
||||
listeners.connect.forEach(cb => cb())
|
||||
resolve()
|
||||
}
|
||||
ws.onerror = () => {
|
||||
connectionPromise = undefined
|
||||
listeners.error.forEach(cb => cb())
|
||||
reject()
|
||||
}
|
||||
ws.onclose = async () => {
|
||||
connectionPromise = undefined
|
||||
listeners.disconnect.forEach(cb => cb())
|
||||
}
|
||||
|
||||
let incomingMessageQueue: MessageQueue = new MessageQueue()
|
||||
let handleNextInterval: any
|
||||
|
||||
ws.onmessage = e => {
|
||||
incomingMessageQueue.enqueue(e.data)
|
||||
if (!handleNextInterval) {
|
||||
handleNextInterval = setInterval(handleNext, 0)
|
||||
}
|
||||
}
|
||||
|
||||
function handleNext() {
|
||||
if (incomingMessageQueue.size === 0) {
|
||||
clearInterval(handleNextInterval)
|
||||
handleNextInterval = null
|
||||
return
|
||||
}
|
||||
|
||||
var json = incomingMessageQueue.dequeue()
|
||||
if (!json) return
|
||||
|
||||
let subid = getSubscriptionId(json)
|
||||
if (subid) {
|
||||
let so = openSubs[subid]
|
||||
if (so && so.alreadyHaveEvent && so.alreadyHaveEvent(getHex64(json, 'id'), url)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let data = JSON.parse(json)
|
||||
|
||||
// we won't do any checks against the data since all failures (i.e. invalid messages from relays)
|
||||
// will naturally be caught by the encompassing try..catch block
|
||||
|
||||
switch (data[0]) {
|
||||
case 'EVENT': {
|
||||
let id = data[1]
|
||||
let event = data[2]
|
||||
if (
|
||||
validateEvent(event) &&
|
||||
openSubs[id] &&
|
||||
(openSubs[id].skipVerification || verifySignature(event)) &&
|
||||
matchFilters(openSubs[id].filters, event)
|
||||
) {
|
||||
openSubs[id]
|
||||
; (subListeners[id]?.event || []).forEach(cb => cb(event))
|
||||
}
|
||||
return
|
||||
}
|
||||
case 'COUNT':
|
||||
let id = data[1]
|
||||
let payload = data[2]
|
||||
if (openSubs[id]) {
|
||||
; (subListeners[id]?.count || []).forEach(cb => cb(payload))
|
||||
}
|
||||
return
|
||||
case 'EOSE': {
|
||||
let id = data[1]
|
||||
if (id in subListeners) {
|
||||
subListeners[id].eose.forEach(cb => cb())
|
||||
subListeners[id].eose = [] // 'eose' only happens once per sub, so stop listeners here
|
||||
}
|
||||
return
|
||||
}
|
||||
case 'OK': {
|
||||
let id: string = data[1]
|
||||
let ok: boolean = data[2]
|
||||
let reason: string = data[3] || ''
|
||||
if (id in pubListeners) {
|
||||
let { resolve, reject } = pubListeners[id]
|
||||
if (ok) resolve(null)
|
||||
else reject(new Error(reason))
|
||||
}
|
||||
return
|
||||
}
|
||||
case 'NOTICE':
|
||||
let notice = data[1]
|
||||
listeners.notice.forEach(cb => cb(notice))
|
||||
return
|
||||
case 'AUTH': {
|
||||
let challenge = data[1]
|
||||
listeners.auth?.forEach(cb => cb(challenge))
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return connectionPromise
|
||||
}
|
||||
|
||||
function connected() {
|
||||
return ws?.readyState === 1
|
||||
}
|
||||
|
||||
async function connect(): Promise<void> {
|
||||
if (connected()) return // ws already open
|
||||
await connectRelay()
|
||||
}
|
||||
|
||||
async function trySend(params: [string, ...any]) {
|
||||
let msg = JSON.stringify(params)
|
||||
if (!connected()) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
if (!connected()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
try {
|
||||
ws.send(msg)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
const sub = <K extends number = number>(
|
||||
filters: Filter<K>[],
|
||||
{
|
||||
verb = 'REQ',
|
||||
skipVerification = false,
|
||||
alreadyHaveEvent = null,
|
||||
id = Math.random().toString().slice(2),
|
||||
}: SubscriptionOptions = {},
|
||||
): Sub<K> => {
|
||||
let subid = id
|
||||
|
||||
openSubs[subid] = {
|
||||
id: subid,
|
||||
filters,
|
||||
skipVerification,
|
||||
alreadyHaveEvent,
|
||||
}
|
||||
trySend([verb, subid, ...filters])
|
||||
|
||||
let subscription: Sub<K> = {
|
||||
sub: (newFilters, newOpts = {}) =>
|
||||
sub(newFilters || filters, {
|
||||
skipVerification: newOpts.skipVerification || skipVerification,
|
||||
alreadyHaveEvent: newOpts.alreadyHaveEvent || alreadyHaveEvent,
|
||||
id: subid,
|
||||
}),
|
||||
unsub: () => {
|
||||
delete openSubs[subid]
|
||||
delete subListeners[subid]
|
||||
trySend(['CLOSE', subid])
|
||||
},
|
||||
on: (type, cb) => {
|
||||
subListeners[subid] = subListeners[subid] || {
|
||||
event: [],
|
||||
count: [],
|
||||
eose: [],
|
||||
}
|
||||
//@ts-ignore
|
||||
subListeners[subid][type].push(cb)
|
||||
},
|
||||
off: (type, cb): void => {
|
||||
let listeners = subListeners[subid]
|
||||
//@ts-ignore
|
||||
let idx = listeners[type].indexOf(cb)
|
||||
if (idx >= 0) listeners[type].splice(idx, 1)
|
||||
},
|
||||
get events() {
|
||||
return eventsGenerator(subscription)
|
||||
},
|
||||
}
|
||||
|
||||
return subscription
|
||||
}
|
||||
|
||||
function _publishEvent(event: Event<number>, type: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!event.id) {
|
||||
reject(new Error(`event ${event} has no id`))
|
||||
return
|
||||
}
|
||||
|
||||
let id = event.id
|
||||
trySend([type, event])
|
||||
pubListeners[id] = { resolve, reject }
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
url,
|
||||
sub,
|
||||
on: <T extends keyof RelayEvent, U extends RelayEvent[T]>(type: T, cb: U): void => {
|
||||
//@ts-ignore
|
||||
listeners[type].push(cb)
|
||||
if (type === 'connect' && ws?.readyState === 1) {
|
||||
// i would love to know why we need this
|
||||
; (cb as () => void)()
|
||||
}
|
||||
},
|
||||
off: <T extends keyof RelayEvent, U extends RelayEvent[T]>(type: T, cb: U): void => {
|
||||
//@ts-ignore
|
||||
let index = listeners[type].indexOf(cb)
|
||||
if (index !== -1) listeners[type].splice(index, 1)
|
||||
},
|
||||
list: (filters, opts?: SubscriptionOptions) =>
|
||||
new Promise(resolve => {
|
||||
let s = sub(filters, opts)
|
||||
let events: Event<any>[] = []
|
||||
let timeout = setTimeout(() => {
|
||||
s.unsub()
|
||||
resolve(events)
|
||||
}, listTimeout)
|
||||
s.on('eose', () => {
|
||||
s.unsub()
|
||||
clearTimeout(timeout)
|
||||
resolve(events)
|
||||
})
|
||||
s.on('event', event => {
|
||||
events.push(event)
|
||||
})
|
||||
}),
|
||||
get: (filter, opts?: SubscriptionOptions) =>
|
||||
new Promise(resolve => {
|
||||
let s = sub([filter], opts)
|
||||
let timeout = setTimeout(() => {
|
||||
s.unsub()
|
||||
resolve(null)
|
||||
}, getTimeout)
|
||||
s.on('event', event => {
|
||||
s.unsub()
|
||||
clearTimeout(timeout)
|
||||
resolve(event)
|
||||
})
|
||||
}),
|
||||
count: (filters: Filter[]): Promise<CountPayload | null> =>
|
||||
new Promise(resolve => {
|
||||
let s = sub(filters, { ...sub, verb: 'COUNT' })
|
||||
let timeout = setTimeout(() => {
|
||||
s.unsub()
|
||||
resolve(null)
|
||||
}, countTimeout)
|
||||
s.on('count', (event: CountPayload) => {
|
||||
s.unsub()
|
||||
clearTimeout(timeout)
|
||||
resolve(event)
|
||||
})
|
||||
}),
|
||||
async publish(event): Promise<void> {
|
||||
await _publishEvent(event, 'EVENT')
|
||||
},
|
||||
async auth(event): Promise<void> {
|
||||
await _publishEvent(event, 'AUTH')
|
||||
},
|
||||
connect,
|
||||
close(): void {
|
||||
listeners = newListeners()
|
||||
subListeners = {}
|
||||
pubListeners = {}
|
||||
if (ws?.readyState === WebSocket.OPEN) {
|
||||
ws.close()
|
||||
}
|
||||
},
|
||||
get status() {
|
||||
return ws?.readyState ?? 3
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function* eventsGenerator<K extends number>(sub: Sub<K>): AsyncGenerator<Event<K>, void, unknown> {
|
||||
let nextResolve: ((event: Event<K>) => void) | undefined
|
||||
const eventQueue: Event<K>[] = []
|
||||
|
||||
const pushToQueue = (event: Event<K>) => {
|
||||
if (nextResolve) {
|
||||
nextResolve(event)
|
||||
nextResolve = undefined
|
||||
} else {
|
||||
eventQueue.push(event)
|
||||
}
|
||||
}
|
||||
|
||||
sub.on('event', pushToQueue)
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
if (eventQueue.length > 0) {
|
||||
yield eventQueue.shift()!
|
||||
} else {
|
||||
const event = await new Promise<Event<K>>(resolve => {
|
||||
nextResolve = resolve
|
||||
})
|
||||
yield event
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
sub.off('event', pushToQueue)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
import type { Event } from './event.js'
|
||||
|
||||
export const utf8Decoder = new TextDecoder('utf-8')
|
||||
export const utf8Encoder = new TextEncoder()
|
||||
|
||||
export function normalizeURL(url: string): string {
|
||||
let p = new URL(url)
|
||||
p.pathname = p.pathname.replace(/\/+/g, '/')
|
||||
if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)
|
||||
if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''
|
||||
p.searchParams.sort()
|
||||
p.hash = ''
|
||||
return p.toString()
|
||||
}
|
||||
|
||||
//
|
||||
// fast insert-into-sorted-array functions adapted from https://github.com/terrymorse58/fast-sorted-array
|
||||
//
|
||||
export function insertEventIntoDescendingList(sortedArray: Event<number>[], event: Event<number>) {
|
||||
let start = 0
|
||||
let end = sortedArray.length - 1
|
||||
let midPoint
|
||||
let position = start
|
||||
|
||||
if (end < 0) {
|
||||
position = 0
|
||||
} else if (event.created_at < sortedArray[end].created_at) {
|
||||
position = end + 1
|
||||
} else if (event.created_at >= sortedArray[start].created_at) {
|
||||
position = start
|
||||
} else
|
||||
while (true) {
|
||||
if (end <= start + 1) {
|
||||
position = end
|
||||
break
|
||||
}
|
||||
midPoint = Math.floor(start + (end - start) / 2)
|
||||
if (sortedArray[midPoint].created_at > event.created_at) {
|
||||
start = midPoint
|
||||
} else if (sortedArray[midPoint].created_at < event.created_at) {
|
||||
end = midPoint
|
||||
} else {
|
||||
// aMidPoint === num
|
||||
position = midPoint
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// insert when num is NOT already in (no duplicates)
|
||||
if (sortedArray[position]?.id !== event.id) {
|
||||
return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)]
|
||||
}
|
||||
|
||||
return sortedArray
|
||||
}
|
||||
|
||||
export function insertEventIntoAscendingList(sortedArray: Event<number>[], event: Event<number>) {
|
||||
let start = 0
|
||||
let end = sortedArray.length - 1
|
||||
let midPoint
|
||||
let position = start
|
||||
|
||||
if (end < 0) {
|
||||
position = 0
|
||||
} else if (event.created_at > sortedArray[end].created_at) {
|
||||
position = end + 1
|
||||
} else if (event.created_at <= sortedArray[start].created_at) {
|
||||
position = start
|
||||
} else
|
||||
while (true) {
|
||||
if (end <= start + 1) {
|
||||
position = end
|
||||
break
|
||||
}
|
||||
midPoint = Math.floor(start + (end - start) / 2)
|
||||
if (sortedArray[midPoint].created_at < event.created_at) {
|
||||
start = midPoint
|
||||
} else if (sortedArray[midPoint].created_at > event.created_at) {
|
||||
end = midPoint
|
||||
} else {
|
||||
// aMidPoint === num
|
||||
position = midPoint
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// insert when num is NOT already in (no duplicates)
|
||||
if (sortedArray[position]?.id !== event.id) {
|
||||
return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)]
|
||||
}
|
||||
|
||||
return sortedArray
|
||||
}
|
||||
|
||||
export class MessageNode {
|
||||
private _value: string
|
||||
private _next: MessageNode | null
|
||||
|
||||
public get value(): string {
|
||||
return this._value
|
||||
}
|
||||
public set value(message: string) {
|
||||
this._value = message
|
||||
}
|
||||
public get next(): MessageNode | null {
|
||||
return this._next
|
||||
}
|
||||
public set next(node: MessageNode | null) {
|
||||
this._next = node
|
||||
}
|
||||
|
||||
constructor(message: string) {
|
||||
this._value = message
|
||||
this._next = null
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageQueue {
|
||||
private _first: MessageNode | null
|
||||
private _last: MessageNode | null
|
||||
|
||||
public get first(): MessageNode | null {
|
||||
return this._first
|
||||
}
|
||||
public set first(messageNode: MessageNode | null) {
|
||||
this._first = messageNode
|
||||
}
|
||||
public get last(): MessageNode | null {
|
||||
return this._last
|
||||
}
|
||||
public set last(messageNode: MessageNode | null) {
|
||||
this._last = messageNode
|
||||
}
|
||||
private _size: number
|
||||
public get size(): number {
|
||||
return this._size
|
||||
}
|
||||
public set size(v: number) {
|
||||
this._size = v
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this._first = null
|
||||
this._last = null
|
||||
this._size = 0
|
||||
}
|
||||
enqueue(message: string): boolean {
|
||||
const newNode = new MessageNode(message)
|
||||
if (this._size === 0 || !this._last) {
|
||||
this._first = newNode
|
||||
this._last = newNode
|
||||
} else {
|
||||
this._last.next = newNode
|
||||
this._last = newNode
|
||||
}
|
||||
this._size++
|
||||
return true
|
||||
}
|
||||
dequeue(): string | null {
|
||||
if (this._size === 0 || !this._first) return null
|
||||
|
||||
let prev = this._first
|
||||
this._first = prev.next
|
||||
prev.next = null
|
||||
|
||||
this._size--
|
||||
return prev.value
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,9 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
},
|
||||
UserHealth: async () => { },
|
||||
GetUserInfo: ({ ctx }) => mainHandler.appUserManager.GetUserInfo(ctx),
|
||||
UpdateCallbackUrl: async ({ ctx, req }) => {
|
||||
return mainHandler.appUserManager.UpdateCallbackUrl(ctx, req)
|
||||
},
|
||||
GetUserOperations: async ({ ctx, req }) => {
|
||||
return mainHandler.paymentManager.GetUserOperations(ctx.user_id, req)
|
||||
},
|
||||
|
|
@ -209,6 +212,7 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
SetMockAppBalance: async ({ ctx, req }) => {
|
||||
await mainHandler.applicationManager.SetMockAppBalance(ctx.app_id, req)
|
||||
},
|
||||
GetLiveDebitRequests: async ({ ctx }) => { },
|
||||
GetLiveUserOperations: async ({ ctx, cb }) => {
|
||||
},
|
||||
GetMigrationUpdate: async ({ ctx, cb }) => {
|
||||
|
|
@ -261,7 +265,32 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.adminManager.GetInviteTokenState(ctx, req);
|
||||
}
|
||||
|
||||
},
|
||||
AuthorizeDebit: async ({ ctx, req }) => {
|
||||
return mainHandler.debitManager.AuthorizeDebit(ctx, req)
|
||||
},
|
||||
GetDebitAuthorizations: async ({ ctx }) => {
|
||||
return mainHandler.debitManager.GetDebitAuthorizations(ctx)
|
||||
},
|
||||
BanDebit: async ({ ctx, req }) => {
|
||||
const err = Types.DebitOperationValidate(req, {
|
||||
npub_CustomCheck: pub => pub !== '',
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.debitManager.BanDebit(ctx, req)
|
||||
},
|
||||
ResetDebit: async ({ ctx, req }) => {
|
||||
const err = Types.DebitOperationValidate(req, {
|
||||
npub_CustomCheck: pub => pub !== '',
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.debitManager.ResetDebit(ctx, req)
|
||||
},
|
||||
EditDebit: async ({ ctx, req }) => {
|
||||
return mainHandler.debitManager.EditDebit(ctx, req);
|
||||
},
|
||||
RespondToDebit: async ({ ctx, req }) => {
|
||||
return mainHandler.debitManager.RespondToDebit(ctx, req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import crypto from 'crypto';
|
||||
import { Between, DataSource, EntityManager, FindOperator, IsNull, LessThanOrEqual, MoreThanOrEqual } from "typeorm"
|
||||
import { generatePrivateKey, getPublicKey } from 'nostr-tools';
|
||||
import { generateSecretKey, getPublicKey } from 'nostr-tools';
|
||||
import { Application } from "./entity/Application.js"
|
||||
import UserStorage from './userStorage.js';
|
||||
import { ApplicationUser } from './entity/ApplicationUser.js';
|
||||
|
|
@ -67,10 +67,11 @@ export default class {
|
|||
}
|
||||
|
||||
async GenerateApplicationKeys(app: Application) {
|
||||
const priv = generatePrivateKey()
|
||||
const priv = generateSecretKey()
|
||||
const pub = getPublicKey(priv)
|
||||
await this.UpdateApplication(app, { nostr_private_key: priv, nostr_public_key: pub })
|
||||
return { privateKey: priv, publicKey: pub, appId: app.app_id, name: app.name }
|
||||
const privString = Buffer.from(priv).toString('hex')
|
||||
await this.UpdateApplication(app, { nostr_private_key: privString, nostr_public_key: pub })
|
||||
return { privateKey: privString, publicKey: pub, appId: app.app_id, name: app.name }
|
||||
}
|
||||
|
||||
async AddApplicationUser(application: Application, userIdentifier: string, balance: number, nostrPub?: string) {
|
||||
|
|
@ -161,9 +162,11 @@ export default class {
|
|||
|
||||
async AddNPubToApplicationUser(serialId: number, nPub: string, entityManager = this.DB) {
|
||||
return entityManager.getRepository(ApplicationUser).update(serialId, { nostr_public_key: nPub })
|
||||
|
||||
}
|
||||
|
||||
async UpdateUserCallbackUrl(application: Application, userIdentifier: string, callbackUrl: string, entityManager = this.DB) {
|
||||
return entityManager.getRepository(ApplicationUser).update({ application: { app_id: application.app_id }, identifier: userIdentifier }, { callback_url: callbackUrl })
|
||||
}
|
||||
|
||||
async RemoveApplicationUserAndBaseUser(appUser: ApplicationUser, entityManager = this.DB) {
|
||||
const baseUser = appUser.user;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { Product } from "./entity/Product.js"
|
|||
import { LndNodeInfo } from "./entity/LndNodeInfo.js"
|
||||
import { TrackedProvider } from "./entity/TrackedProvider.js"
|
||||
import { InviteToken } from "./entity/InviteToken.js"
|
||||
import { DebitAccess } from "./entity/DebitAccess.js"
|
||||
|
||||
|
||||
export type DbSettings = {
|
||||
|
|
@ -61,7 +62,7 @@ export default async (settings: DbSettings, migrations: Function[]): Promise<{ s
|
|||
// logging: true,
|
||||
entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment,
|
||||
UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment, LspOrder, LndNodeInfo, TrackedProvider,
|
||||
InviteToken
|
||||
InviteToken, DebitAccess
|
||||
],
|
||||
//synchronize: true,
|
||||
migrations
|
||||
|
|
|
|||
58
src/services/storage/debitStorage.ts
Normal file
58
src/services/storage/debitStorage.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { DataSource, EntityManager } from "typeorm"
|
||||
import UserStorage from './userStorage.js';
|
||||
import TransactionsQueue from "./transactionsQueue.js";
|
||||
import { DebitAccess, DebitAccessRules } from "./entity/DebitAccess.js";
|
||||
type AccessToAdd = {
|
||||
npub: string
|
||||
rules?: DebitAccessRules
|
||||
authorize: boolean
|
||||
}
|
||||
export default class {
|
||||
DB: DataSource | EntityManager
|
||||
txQueue: TransactionsQueue
|
||||
constructor(DB: DataSource | EntityManager, txQueue: TransactionsQueue) {
|
||||
this.DB = DB
|
||||
this.txQueue = txQueue
|
||||
}
|
||||
|
||||
async AddDebitAccess(appUserId: string, access: AccessToAdd, entityManager = this.DB) {
|
||||
const entry = entityManager.getRepository(DebitAccess).create({
|
||||
app_user_id: appUserId,
|
||||
npub: access.npub,
|
||||
authorized: access.authorize,
|
||||
rules: access.rules,
|
||||
})
|
||||
return this.txQueue.PushToQueue<DebitAccess>({ exec: async db => db.getRepository(DebitAccess).save(entry), dbTx: false })
|
||||
}
|
||||
|
||||
async GetAllUserDebitAccess(appUserId: string) {
|
||||
return this.DB.getRepository(DebitAccess).find({ where: { app_user_id: appUserId } })
|
||||
}
|
||||
|
||||
async GetDebitAccess(appUserId: string, authorizedPub: string) {
|
||||
return this.DB.getRepository(DebitAccess).findOne({ where: { app_user_id: appUserId, npub: authorizedPub } })
|
||||
}
|
||||
|
||||
async IncrementDebitAccess(appUserId: string, authorizedPub: string, amount: number) {
|
||||
return this.DB.getRepository(DebitAccess).increment({ app_user_id: appUserId, npub: authorizedPub }, 'total_debits', amount)
|
||||
}
|
||||
|
||||
async UpdateDebitAccess(appUserId: string, authorizedPub: string, authorized: boolean) {
|
||||
return this.DB.getRepository(DebitAccess).update({ app_user_id: appUserId, npub: authorizedPub }, { authorized })
|
||||
}
|
||||
async UpdateDebitAccessRules(appUserId: string, authorizedPub: string, rules?: DebitAccessRules) {
|
||||
return this.DB.getRepository(DebitAccess).update({ app_user_id: appUserId, npub: authorizedPub }, { rules: rules || null })
|
||||
}
|
||||
|
||||
async DenyDebitAccess(appUserId: string, pub: string) {
|
||||
const access = await this.GetDebitAccess(appUserId, pub)
|
||||
if (!access) {
|
||||
await this.AddDebitAccess(appUserId, { npub: pub, authorize: false })
|
||||
}
|
||||
await this.UpdateDebitAccess(appUserId, pub, false)
|
||||
}
|
||||
|
||||
async RemoveDebitAccess(appUserId: string, authorizedPub: string) {
|
||||
return this.DB.getRepository(DebitAccess).delete({ app_user_id: appUserId, npub: authorizedPub })
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,9 @@ export class ApplicationUser {
|
|||
@Column({ nullable: true, unique: true })
|
||||
nostr_public_key?: string
|
||||
|
||||
@Column({ default: "" })
|
||||
callback_url: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
|
|
|
|||
30
src/services/storage/entity/DebitAccess.ts
Normal file
30
src/services/storage/entity/DebitAccess.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, Index, Check, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
export type DebitAccessRules = Record<string/* rule name */, string[]/* rule values */>
|
||||
@Entity()
|
||||
@Index("unique_debit_access", ["app_user_id", "npub"], { unique: true })
|
||||
export class DebitAccess {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
serial_id: number
|
||||
|
||||
@Column()
|
||||
app_user_id: string
|
||||
|
||||
@Column()
|
||||
npub: string
|
||||
|
||||
@Column()
|
||||
authorized: boolean
|
||||
|
||||
@Column({ type: 'simple-json', default: null, nullable: true })
|
||||
rules: DebitAccessRules | null
|
||||
|
||||
@Column({ default: 0 })
|
||||
total_debits: number
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
@ -44,6 +44,9 @@ export class UserInvoicePayment {
|
|||
})
|
||||
paymentIndex: number
|
||||
|
||||
@Column({ nullable: true })
|
||||
debit_to_pub: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import TransactionsQueue, { TX } from "./transactionsQueue.js";
|
|||
import EventsLogManager from "./eventsLog.js";
|
||||
import { LiquidityStorage } from "./liquidityStorage.js";
|
||||
import { StateBundler } from "./stateBundler.js";
|
||||
import DebitStorage from "./debitStorage.js"
|
||||
export type StorageSettings = {
|
||||
dbSettings: DbSettings
|
||||
eventLogPath: string
|
||||
|
|
@ -28,6 +29,7 @@ export default class {
|
|||
paymentStorage: PaymentStorage
|
||||
metricsStorage: MetricsStorage
|
||||
liquidityStorage: LiquidityStorage
|
||||
debitStorage: DebitStorage
|
||||
eventsLog: EventsLogManager
|
||||
stateBundler: StateBundler
|
||||
constructor(settings: StorageSettings) {
|
||||
|
|
@ -44,6 +46,7 @@ export default class {
|
|||
this.paymentStorage = new PaymentStorage(this.DB, this.userStorage, this.txQueue)
|
||||
this.metricsStorage = new MetricsStorage(this.settings)
|
||||
this.liquidityStorage = new LiquidityStorage(this.DB, this.txQueue)
|
||||
this.debitStorage = new DebitStorage(this.DB, this.txQueue)
|
||||
try { if (this.settings.dataDir) fs.mkdirSync(this.settings.dataDir) } catch (e) { }
|
||||
const executedMetricsMigrations = await this.metricsStorage.Connect(metricsMigrations)
|
||||
return { executedMigrations, executedMetricsMigrations };
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class DebitAccess1726496225078 implements MigrationInterface {
|
||||
name = 'DebitAccess1726496225078'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE "debit_access" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "app_user_id" varchar NOT NULL, "key" varchar NOT NULL, "key_type" varchar NOT NULL, "total_debits" integer NOT NULL DEFAULT (0), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')))`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "unique_debit_access" ON "debit_access" ("app_user_id", "key", "key_type") `);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "unique_debit_access"`);
|
||||
await queryRunner.query(`DROP TABLE "debit_access"`);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class DebitAccessFixes1726685229264 implements MigrationInterface {
|
||||
name = 'DebitAccessFixes1726685229264'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "unique_debit_access"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_debit_access" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "app_user_id" varchar NOT NULL, "total_debits" integer NOT NULL DEFAULT (0), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')))`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_debit_access"("serial_id", "app_user_id", "total_debits", "created_at", "updated_at") SELECT "serial_id", "app_user_id", "total_debits", "created_at", "updated_at" FROM "debit_access"`);
|
||||
await queryRunner.query(`DROP TABLE "debit_access"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_debit_access" RENAME TO "debit_access"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_debit_access" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "app_user_id" varchar NOT NULL, "total_debits" integer NOT NULL DEFAULT (0), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')), "npub" varchar NOT NULL, "authorized" boolean NOT NULL, "rules" text)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_debit_access"("serial_id", "app_user_id", "total_debits", "created_at", "updated_at") SELECT "serial_id", "app_user_id", "total_debits", "created_at", "updated_at" FROM "debit_access"`);
|
||||
await queryRunner.query(`DROP TABLE "debit_access"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_debit_access" RENAME TO "debit_access"`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "unique_debit_access" ON "debit_access" ("app_user_id", "npub") `);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "unique_debit_access"`);
|
||||
await queryRunner.query(`ALTER TABLE "debit_access" RENAME TO "temporary_debit_access"`);
|
||||
await queryRunner.query(`CREATE TABLE "debit_access" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "app_user_id" varchar NOT NULL, "total_debits" integer NOT NULL DEFAULT (0), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')))`);
|
||||
await queryRunner.query(`INSERT INTO "debit_access"("serial_id", "app_user_id", "total_debits", "created_at", "updated_at") SELECT "serial_id", "app_user_id", "total_debits", "created_at", "updated_at" FROM "temporary_debit_access"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_debit_access"`);
|
||||
await queryRunner.query(`ALTER TABLE "debit_access" RENAME TO "temporary_debit_access"`);
|
||||
await queryRunner.query(`CREATE TABLE "debit_access" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "app_user_id" varchar NOT NULL, "key" varchar NOT NULL, "key_type" varchar NOT NULL, "total_debits" integer NOT NULL DEFAULT (0), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')))`);
|
||||
await queryRunner.query(`INSERT INTO "debit_access"("serial_id", "app_user_id", "total_debits", "created_at", "updated_at") SELECT "serial_id", "app_user_id", "total_debits", "created_at", "updated_at" FROM "temporary_debit_access"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_debit_access"`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "unique_debit_access" ON "debit_access" ("app_user_id", "key", "key_type") `);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class DebitToPub1727105758354 implements MigrationInterface {
|
||||
name = 'DebitToPub1727105758354'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_a609a4d3d8d9b07b90692a3c45"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_user_invoice_payment" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "invoice" varchar NOT NULL, "paid_amount" integer NOT NULL, "routing_fees" integer NOT NULL, "service_fees" integer NOT NULL, "paid_at_unix" integer NOT NULL, "internal" boolean NOT NULL DEFAULT (0), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')), "userSerialId" integer, "linkedApplicationSerialId" integer, "liquidityProvider" varchar, "paymentIndex" integer NOT NULL DEFAULT (-1), "debit_to_pub" varchar, CONSTRAINT "FK_6bcac90887eea1dc61d37db2994" FOREIGN KEY ("linkedApplicationSerialId") REFERENCES "application" ("serial_id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_ef2aa6761ab681bbbd5f94e0fcb" FOREIGN KEY ("userSerialId") REFERENCES "user" ("serial_id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_user_invoice_payment"("serial_id", "invoice", "paid_amount", "routing_fees", "service_fees", "paid_at_unix", "internal", "created_at", "updated_at", "userSerialId", "linkedApplicationSerialId", "liquidityProvider", "paymentIndex") SELECT "serial_id", "invoice", "paid_amount", "routing_fees", "service_fees", "paid_at_unix", "internal", "created_at", "updated_at", "userSerialId", "linkedApplicationSerialId", "liquidityProvider", "paymentIndex" FROM "user_invoice_payment"`);
|
||||
await queryRunner.query(`DROP TABLE "user_invoice_payment"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_user_invoice_payment" RENAME TO "user_invoice_payment"`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a609a4d3d8d9b07b90692a3c45" ON "user_invoice_payment" ("invoice") `);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_a609a4d3d8d9b07b90692a3c45"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_invoice_payment" RENAME TO "temporary_user_invoice_payment"`);
|
||||
await queryRunner.query(`CREATE TABLE "user_invoice_payment" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "invoice" varchar NOT NULL, "paid_amount" integer NOT NULL, "routing_fees" integer NOT NULL, "service_fees" integer NOT NULL, "paid_at_unix" integer NOT NULL, "internal" boolean NOT NULL DEFAULT (0), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')), "userSerialId" integer, "linkedApplicationSerialId" integer, "liquidityProvider" varchar, "paymentIndex" integer NOT NULL DEFAULT (-1), CONSTRAINT "FK_6bcac90887eea1dc61d37db2994" FOREIGN KEY ("linkedApplicationSerialId") REFERENCES "application" ("serial_id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_ef2aa6761ab681bbbd5f94e0fcb" FOREIGN KEY ("userSerialId") REFERENCES "user" ("serial_id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "user_invoice_payment"("serial_id", "invoice", "paid_amount", "routing_fees", "service_fees", "paid_at_unix", "internal", "created_at", "updated_at", "userSerialId", "linkedApplicationSerialId", "liquidityProvider", "paymentIndex") SELECT "serial_id", "invoice", "paid_amount", "routing_fees", "service_fees", "paid_at_unix", "internal", "created_at", "updated_at", "userSerialId", "linkedApplicationSerialId", "liquidityProvider", "paymentIndex" FROM "temporary_user_invoice_payment"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_user_invoice_payment"`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a609a4d3d8d9b07b90692a3c45" ON "user_invoice_payment" ("invoice") `);
|
||||
}
|
||||
}
|
||||
24
src/services/storage/migrations/1727112281043-user_cb_url.ts
Normal file
24
src/services/storage/migrations/1727112281043-user_cb_url.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class UserCbUrl1727112281043 implements MigrationInterface {
|
||||
name = 'UserCbUrl1727112281043'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_0a0dbb25a73306b037dec82251"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_application_user" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "identifier" varchar NOT NULL, "nostr_public_key" varchar, "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')), "userSerialId" integer, "applicationSerialId" integer, "callback_url" varchar NOT NULL DEFAULT (''), CONSTRAINT "REL_0796a381bcc624f52e9a155712" UNIQUE ("userSerialId"), CONSTRAINT "UQ_3175dc397c8285d1e532554dea5" UNIQUE ("nostr_public_key"), CONSTRAINT "FK_1b3bdb6f660cd99533a1e673ef1" FOREIGN KEY ("applicationSerialId") REFERENCES "application" ("serial_id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_0796a381bcc624f52e9a155712b" FOREIGN KEY ("userSerialId") REFERENCES "user" ("serial_id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_application_user"("serial_id", "identifier", "nostr_public_key", "created_at", "updated_at", "userSerialId", "applicationSerialId") SELECT "serial_id", "identifier", "nostr_public_key", "created_at", "updated_at", "userSerialId", "applicationSerialId" FROM "application_user"`);
|
||||
await queryRunner.query(`DROP TABLE "application_user"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_application_user" RENAME TO "application_user"`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0a0dbb25a73306b037dec82251" ON "application_user" ("identifier") `);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_0a0dbb25a73306b037dec82251"`);
|
||||
await queryRunner.query(`ALTER TABLE "application_user" RENAME TO "temporary_application_user"`);
|
||||
await queryRunner.query(`CREATE TABLE "application_user" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "identifier" varchar NOT NULL, "nostr_public_key" varchar, "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')), "userSerialId" integer, "applicationSerialId" integer, CONSTRAINT "REL_0796a381bcc624f52e9a155712" UNIQUE ("userSerialId"), CONSTRAINT "UQ_3175dc397c8285d1e532554dea5" UNIQUE ("nostr_public_key"), CONSTRAINT "FK_1b3bdb6f660cd99533a1e673ef1" FOREIGN KEY ("applicationSerialId") REFERENCES "application" ("serial_id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_0796a381bcc624f52e9a155712b" FOREIGN KEY ("userSerialId") REFERENCES "user" ("serial_id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "application_user"("serial_id", "identifier", "nostr_public_key", "created_at", "updated_at", "userSerialId", "applicationSerialId") SELECT "serial_id", "identifier", "nostr_public_key", "created_at", "updated_at", "userSerialId", "applicationSerialId" FROM "temporary_application_user"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_application_user"`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0a0dbb25a73306b037dec82251" ON "application_user" ("identifier") `);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,7 +12,11 @@ import { CreateInviteTokenTable1721751414878 } from "./1721751414878-create_invi
|
|||
import { PaymentIndex1721760297610 } from './1721760297610-payment_index.js'
|
||||
import { HtlcCount1724266887195 } from './1724266887195-htlc_count.js'
|
||||
import { BalanceEvents1724860966825 } from './1724860966825-balance_events.js'
|
||||
const allMigrations = [Initial1703170309875, LspOrder1718387847693, LiquidityProvider1719335699480, LndNodeInfo1720187506189, TrackedProvider1720814323679, CreateInviteTokenTable1721751414878, PaymentIndex1721760297610]
|
||||
import { DebitAccess1726496225078 } from './1726496225078-debit_access.js'
|
||||
import { DebitAccessFixes1726685229264 } from './1726685229264-debit_access_fixes.js'
|
||||
import { DebitToPub1727105758354 } from './1727105758354-debit_to_pub.js'
|
||||
import { UserCbUrl1727112281043 } from './1727112281043-user_cb_url.js'
|
||||
const allMigrations = [Initial1703170309875, LspOrder1718387847693, LiquidityProvider1719335699480, LndNodeInfo1720187506189, TrackedProvider1720814323679, CreateInviteTokenTable1721751414878, PaymentIndex1721760297610, DebitAccess1726496225078, DebitAccessFixes1726685229264, DebitToPub1727105758354, UserCbUrl1727112281043]
|
||||
const allMetricsMigrations = [LndMetrics1703170330183, ChannelRouting1709316653538, HtlcCount1724266887195, BalanceEvents1724860966825]
|
||||
export const TypeOrmMigrationRunner = async (log: PubLogger, storageManager: Storage, settings: DbSettings, arg: string | undefined): Promise<boolean> => {
|
||||
if (arg === 'fake_initial_migration') {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import crypto from 'crypto';
|
||||
import { Between, DataSource, EntityManager, FindOperator, IsNull, LessThanOrEqual, MoreThan, MoreThanOrEqual } from "typeorm"
|
||||
import { Between, DataSource, EntityManager, FindOperator, IsNull, LessThanOrEqual, MoreThan, MoreThanOrEqual, Not } from "typeorm"
|
||||
import { User } from './entity/User.js';
|
||||
import { UserTransactionPayment } from './entity/UserTransactionPayment.js';
|
||||
import { EphemeralKeyType, UserEphemeralKey } from './entity/UserEphemeralKey.js';
|
||||
|
|
@ -154,7 +154,7 @@ export default class {
|
|||
})
|
||||
}
|
||||
|
||||
async AddPendingExternalPayment(userId: string, invoice: string, amounts: { payAmount: number, serviceFee: number, networkFee: number }, linkedApplication: Application, liquidityProvider: string | undefined,dbTx:DataSource|EntityManager): Promise<UserInvoicePayment> {
|
||||
async AddPendingExternalPayment(userId: string, invoice: string, amounts: { payAmount: number, serviceFee: number, networkFee: number }, linkedApplication: Application, liquidityProvider: string | undefined, dbTx: DataSource | EntityManager, debitNpub?: string): Promise<UserInvoicePayment> {
|
||||
const newPayment = dbTx.getRepository(UserInvoicePayment).create({
|
||||
user: await this.userStorage.GetUser(userId, dbTx),
|
||||
paid_amount: amounts.payAmount,
|
||||
|
|
@ -164,7 +164,8 @@ export default class {
|
|||
paid_at_unix: 0,
|
||||
internal: false,
|
||||
linkedApplication,
|
||||
liquidityProvider
|
||||
liquidityProvider,
|
||||
debit_to_pub: debitNpub
|
||||
})
|
||||
return dbTx.getRepository(UserInvoicePayment).save(newPayment)
|
||||
}
|
||||
|
|
@ -185,7 +186,7 @@ export default class {
|
|||
})
|
||||
}
|
||||
|
||||
async AddInternalPayment(userId: string, invoice: string, amount: number, serviceFees: number, linkedApplication: Application): Promise<UserInvoicePayment> {
|
||||
async AddInternalPayment(userId: string, invoice: string, amount: number, serviceFees: number, linkedApplication: Application, debitNpub?: string): Promise<UserInvoicePayment> {
|
||||
const newPayment = this.DB.getRepository(UserInvoicePayment).create({
|
||||
user: await this.userStorage.GetUser(userId),
|
||||
paid_amount: amount,
|
||||
|
|
@ -194,7 +195,8 @@ export default class {
|
|||
service_fees: serviceFees,
|
||||
paid_at_unix: Math.floor(Date.now() / 1000),
|
||||
internal: true,
|
||||
linkedApplication
|
||||
linkedApplication,
|
||||
debit_to_pub: debitNpub
|
||||
})
|
||||
return this.txQueue.PushToQueue<UserInvoicePayment>({ exec: async db => db.getRepository(UserInvoicePayment).save(newPayment), dbTx: false, description: `add internal invoice payment for ${userId} linked to ${linkedApplication.app_id}: ${invoice}, amt: ${amount} ` })
|
||||
}
|
||||
|
|
@ -215,6 +217,25 @@ export default class {
|
|||
})
|
||||
}
|
||||
|
||||
GetUserDebitPayments(userId: string, sinceUnix: number, debitToNpub: string, entityManager = this.DB): Promise<UserInvoicePayment[]> {
|
||||
const pending = {
|
||||
user: { user_id: userId },
|
||||
debit_to_pub: debitToNpub,
|
||||
paid_at_unix: 0,
|
||||
}
|
||||
const paid = {
|
||||
user: { user_id: userId },
|
||||
debit_to_pub: debitToNpub,
|
||||
paid_at_unix: MoreThan(sinceUnix),
|
||||
}
|
||||
return entityManager.getRepository(UserInvoicePayment).find({
|
||||
where: [pending, paid],
|
||||
order: {
|
||||
paid_at_unix: 'DESC'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async AddUserTransactionPayment(userId: string, address: string, txHash: string, txOutput: number, amount: number, chainFees: number, serviceFees: number, internal: boolean, height: number, linkedApplication: Application): Promise<UserTransactionPayment> {
|
||||
const newTx = this.DB.getRepository(UserTransactionPayment).create({
|
||||
user: await this.userStorage.GetUser(userId),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue