custom ofers
This commit is contained in:
parent
a945038b16
commit
1f5c3041bd
22 changed files with 1254 additions and 78 deletions
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -32,7 +32,7 @@
|
|||
"grpc-tools": "^1.12.4",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"nostr-tools": "github:shocknet/nostr-tools#da188cd4bd195f44cc690074a3898f354ae85100",
|
||||
"nostr-tools": "github:shocknet/nostr-tools#27575ffb69d615691242df433a0ccc063f6b8346",
|
||||
"pg": "^8.4.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
@ -3796,9 +3796,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/nostr-tools": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "git+ssh://git@github.com/shocknet/nostr-tools.git#da188cd4bd195f44cc690074a3898f354ae85100",
|
||||
"integrity": "sha512-kc41K75rXEnLhqIwlQmjaGsZ9yYTbyP8VW7B2Q+0U/pqaMyt25Nt0QCWiIYS04m0sanvD77OhmddvI1s2ntKog==",
|
||||
"version": "2.10.4",
|
||||
"resolved": "git+ssh://git@github.com/shocknet/nostr-tools.git#27575ffb69d615691242df433a0ccc063f6b8346",
|
||||
"integrity": "sha512-ZQxr1yalFLi5coqG5pHWmjHGehLgCZbQusE/59mre/CgqrFMbGJY77AGTyhDnaGgqRc7B/UJnIvqGVVKhTVRmQ==",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@noble/ciphers": "^0.5.1",
|
||||
|
|
@ -3809,7 +3809,7 @@
|
|||
"@scure/bip39": "1.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"nostr-wasm": "v0.1.0"
|
||||
"nostr-wasm": "0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
"grpc-tools": "^1.12.4",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"nostr-tools": "github:shocknet/nostr-tools#da188cd4bd195f44cc690074a3898f354ae85100",
|
||||
"nostr-tools": "github:shocknet/nostr-tools#27575ffb69d615691242df433a0ccc063f6b8346",
|
||||
"pg": "^8.4.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [AddProductRequest](#AddProductRequest)
|
||||
- output: [Product](#Product)
|
||||
|
||||
- AddUserOffer
|
||||
- auth type: __User__
|
||||
- input: [OfferConfig](#OfferConfig)
|
||||
- output: [OfferId](#OfferId)
|
||||
|
||||
- AuthApp
|
||||
- auth type: __Admin__
|
||||
- input: [AuthAppRequest](#AuthAppRequest)
|
||||
|
|
@ -68,6 +73,11 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [DecodeInvoiceRequest](#DecodeInvoiceRequest)
|
||||
- output: [DecodeInvoiceResponse](#DecodeInvoiceResponse)
|
||||
|
||||
- DeleteUserOffer
|
||||
- auth type: __User__
|
||||
- input: [OfferId](#OfferId)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- EditDebit
|
||||
- auth type: __User__
|
||||
- input: [DebitAuthorizationRequest](#DebitAuthorizationRequest)
|
||||
|
|
@ -153,6 +163,16 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- This methods has an __empty__ __request__ body
|
||||
- output: [UserInfo](#UserInfo)
|
||||
|
||||
- GetUserOffer
|
||||
- auth type: __User__
|
||||
- input: [OfferId](#OfferId)
|
||||
- output: [OfferConfig](#OfferConfig)
|
||||
|
||||
- GetUserOffers
|
||||
- auth type: __User__
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [UserOffers](#UserOffers)
|
||||
|
||||
- GetUserOperations
|
||||
- auth type: __User__
|
||||
- input: [GetUserOperationsRequest](#GetUserOperationsRequest)
|
||||
|
|
@ -225,6 +245,11 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [UpdateChannelPolicyRequest](#UpdateChannelPolicyRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- UpdateUserOffer
|
||||
- auth type: __User__
|
||||
- input: [OfferConfig](#OfferConfig)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- UseInviteLink
|
||||
- auth type: __GuestWithPub__
|
||||
- input: [UseInviteLinkRequest](#UseInviteLinkRequest)
|
||||
|
|
@ -311,6 +336,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [AddProductRequest](#AddProductRequest)
|
||||
- output: [Product](#Product)
|
||||
|
||||
- AddUserOffer
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/offer/add__
|
||||
- input: [OfferConfig](#OfferConfig)
|
||||
- output: [OfferId](#OfferId)
|
||||
|
||||
- AuthApp
|
||||
- auth type: __Admin__
|
||||
- http method: __post__
|
||||
|
|
@ -367,6 +399,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [DecodeInvoiceRequest](#DecodeInvoiceRequest)
|
||||
- output: [DecodeInvoiceResponse](#DecodeInvoiceResponse)
|
||||
|
||||
- DeleteUserOffer
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/offer/delete__
|
||||
- input: [OfferId](#OfferId)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- EditDebit
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
|
|
@ -539,6 +578,20 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- This methods has an __empty__ __request__ body
|
||||
- output: [UserInfo](#UserInfo)
|
||||
|
||||
- GetUserOffer
|
||||
- auth type: __User__
|
||||
- http method: __get__
|
||||
- http route: __/api/user/offer/get__
|
||||
- input: [OfferId](#OfferId)
|
||||
- output: [OfferConfig](#OfferConfig)
|
||||
|
||||
- GetUserOffers
|
||||
- auth type: __User__
|
||||
- http method: __get__
|
||||
- http route: __/api/user/offers/get__
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [UserOffers](#UserOffers)
|
||||
|
||||
- GetUserOperations
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
|
|
@ -733,6 +786,13 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- input: [UpdateChannelPolicyRequest](#UpdateChannelPolicyRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- UpdateUserOffer
|
||||
- auth type: __User__
|
||||
- http method: __post__
|
||||
- http route: __/api/user/offer/update__
|
||||
- input: [OfferConfig](#OfferConfig)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- UseInviteLink
|
||||
- auth type: __GuestWithPub__
|
||||
- http method: __post__
|
||||
|
|
@ -764,6 +824,8 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
### AddAppUserInvoiceRequest
|
||||
- __http_callback_url__: _string_
|
||||
- __invoice_req__: _[NewInvoiceRequest](#NewInvoiceRequest)_
|
||||
- __offer_string__: _string_ *this field is optional
|
||||
- __payer_data__: _[PayerData](#PayerData)_ *this field is optional
|
||||
- __payer_identifier__: _string_
|
||||
- __receiver_identifier__: _string_
|
||||
|
||||
|
|
@ -1056,6 +1118,17 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
### NewInvoiceResponse
|
||||
- __invoice__: _string_
|
||||
|
||||
### OfferConfig
|
||||
- __callback_url__: _string_
|
||||
- __expected_data__: MAP with key: _string_ and value: _[OfferDataType](#OfferDataType)_
|
||||
- __label__: _string_
|
||||
- __noffer__: _string_
|
||||
- __offer_id__: _string_
|
||||
- __price_sats__: _number_
|
||||
|
||||
### OfferId
|
||||
- __offer_id__: _string_
|
||||
|
||||
### OpenChannel
|
||||
- __active__: _boolean_
|
||||
- __capacity__: _number_
|
||||
|
|
@ -1106,6 +1179,9 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __preimage__: _string_
|
||||
- __service_fee__: _number_
|
||||
|
||||
### PayerData
|
||||
- __data__: MAP with key: _string_ and value: _string_
|
||||
|
||||
### PaymentState
|
||||
- __amount__: _number_
|
||||
- __network_fee__: _number_
|
||||
|
|
@ -1201,6 +1277,9 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __userId__: _string_
|
||||
- __user_identifier__: _string_
|
||||
|
||||
### UserOffers
|
||||
- __offers__: ARRAY of: _[OfferConfig](#OfferConfig)_
|
||||
|
||||
### UserOperation
|
||||
- __amount__: _number_
|
||||
- __confirmed__: _boolean_
|
||||
|
|
@ -1239,6 +1318,9 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- __MONTH__
|
||||
- __WEEK__
|
||||
|
||||
### OfferDataType
|
||||
- __DATA_STRING__
|
||||
|
||||
### OperationType
|
||||
- __CHAIN_OP__
|
||||
- __INVOICE_OP__
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ type Client struct {
|
|||
AddAppUserInvoice func(req AddAppUserInvoiceRequest) (*NewInvoiceResponse, error)
|
||||
AddPeer func(req AddPeerRequest) error
|
||||
AddProduct func(req AddProductRequest) (*Product, error)
|
||||
AddUserOffer func(req OfferConfig) (*OfferId, error)
|
||||
AuthApp func(req AuthAppRequest) (*AuthApp, error)
|
||||
AuthorizeDebit func(req DebitAuthorizationRequest) (*DebitAuthorization, error)
|
||||
BanDebit func(req DebitOperation) error
|
||||
|
|
@ -68,6 +69,7 @@ type Client struct {
|
|||
CloseChannel func(req CloseChannelRequest) (*CloseChannelResponse, error)
|
||||
CreateOneTimeInviteLink func(req CreateOneTimeInviteLinkRequest) (*CreateOneTimeInviteLinkResponse, error)
|
||||
DecodeInvoice func(req DecodeInvoiceRequest) (*DecodeInvoiceResponse, error)
|
||||
DeleteUserOffer func(req OfferId) error
|
||||
EditDebit func(req DebitAuthorizationRequest) error
|
||||
EncryptionExchange func(req EncryptionExchangeRequest) error
|
||||
EnrollAdminToken func(req EnrollAdminTokenRequest) error
|
||||
|
|
@ -92,6 +94,8 @@ type Client struct {
|
|||
GetSeed func() (*LndSeed, error)
|
||||
GetUsageMetrics func() (*UsageMetrics, error)
|
||||
GetUserInfo func() (*UserInfo, error)
|
||||
GetUserOffer func(req OfferId) (*OfferConfig, error)
|
||||
GetUserOffers func() (*UserOffers, error)
|
||||
GetUserOperations func(req GetUserOperationsRequest) (*GetUserOperationsResponse, error)
|
||||
HandleLnurlAddress func(routeParams HandleLnurlAddress_RouteParams) (*LnurlPayInfoResponse, error)
|
||||
HandleLnurlPay func(query HandleLnurlPay_Query) (*HandleLnurlPayResponse, error)
|
||||
|
|
@ -118,6 +122,7 @@ type Client struct {
|
|||
SetMockInvoiceAsPaid func(req SetMockInvoiceAsPaidRequest) error
|
||||
UpdateCallbackUrl func(req CallbackUrl) (*CallbackUrl, error)
|
||||
UpdateChannelPolicy func(req UpdateChannelPolicyRequest) error
|
||||
UpdateUserOffer func(req OfferConfig) error
|
||||
UseInviteLink func(req UseInviteLinkRequest) error
|
||||
UserHealth func() error
|
||||
}
|
||||
|
|
@ -293,6 +298,35 @@ func NewClient(params ClientParams) *Client {
|
|||
}
|
||||
return &res, nil
|
||||
},
|
||||
AddUserOffer: func(req OfferConfig) (*OfferId, error) {
|
||||
auth, err := params.RetrieveUserAuth()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalRoute := "/api/user/offer/add"
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resBody, err := doPostRequest(params.BaseURL+finalRoute, body, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := ResultError{}
|
||||
err = json.Unmarshal(resBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Status == "ERROR" {
|
||||
return nil, fmt.Errorf(result.Reason)
|
||||
}
|
||||
res := OfferId{}
|
||||
err = json.Unmarshal(resBody, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
},
|
||||
AuthApp: func(req AuthAppRequest) (*AuthApp, error) {
|
||||
auth, err := params.RetrieveAdminAuth()
|
||||
if err != nil {
|
||||
|
|
@ -492,6 +526,30 @@ func NewClient(params ClientParams) *Client {
|
|||
}
|
||||
return &res, nil
|
||||
},
|
||||
DeleteUserOffer: func(req OfferId) error {
|
||||
auth, err := params.RetrieveUserAuth()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
finalRoute := "/api/user/offer/delete"
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resBody, err := doPostRequest(params.BaseURL+finalRoute, body, auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result := ResultError{}
|
||||
err = json.Unmarshal(resBody, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.Status == "ERROR" {
|
||||
return fmt.Errorf(result.Reason)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
EditDebit: func(req DebitAuthorizationRequest) error {
|
||||
auth, err := params.RetrieveUserAuth()
|
||||
if err != nil {
|
||||
|
|
@ -1023,6 +1081,50 @@ func NewClient(params ClientParams) *Client {
|
|||
}
|
||||
return &res, nil
|
||||
},
|
||||
GetUserOffer: func(req OfferId) (*OfferConfig, error) {
|
||||
auth, err := params.RetrieveUserAuth()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalRoute := "/api/user/offer/get"
|
||||
resBody, err := doGetRequest(params.BaseURL+finalRoute, auth)
|
||||
result := ResultError{}
|
||||
err = json.Unmarshal(resBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Status == "ERROR" {
|
||||
return nil, fmt.Errorf(result.Reason)
|
||||
}
|
||||
res := OfferConfig{}
|
||||
err = json.Unmarshal(resBody, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
},
|
||||
GetUserOffers: func() (*UserOffers, error) {
|
||||
auth, err := params.RetrieveUserAuth()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalRoute := "/api/user/offers/get"
|
||||
resBody, err := doGetRequest(params.BaseURL+finalRoute, auth)
|
||||
result := ResultError{}
|
||||
err = json.Unmarshal(resBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Status == "ERROR" {
|
||||
return nil, fmt.Errorf(result.Reason)
|
||||
}
|
||||
res := UserOffers{}
|
||||
err = json.Unmarshal(resBody, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
},
|
||||
GetUserOperations: func(req GetUserOperationsRequest) (*GetUserOperationsResponse, error) {
|
||||
auth, err := params.RetrieveUserAuth()
|
||||
if err != nil {
|
||||
|
|
@ -1717,6 +1819,30 @@ func NewClient(params ClientParams) *Client {
|
|||
}
|
||||
return nil
|
||||
},
|
||||
UpdateUserOffer: func(req OfferConfig) error {
|
||||
auth, err := params.RetrieveUserAuth()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
finalRoute := "/api/user/offer/update"
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resBody, err := doPostRequest(params.BaseURL+finalRoute, body, auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result := ResultError{}
|
||||
err = json.Unmarshal(resBody, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.Status == "ERROR" {
|
||||
return fmt.Errorf(result.Reason)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
UseInviteLink: func(req UseInviteLinkRequest) error {
|
||||
auth, err := params.RetrieveGuestWithPubAuth()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@ const (
|
|||
WEEK IntervalType = "WEEK"
|
||||
)
|
||||
|
||||
type OfferDataType string
|
||||
|
||||
const (
|
||||
DATA_STRING OfferDataType = "DATA_STRING"
|
||||
)
|
||||
|
||||
type OperationType string
|
||||
|
||||
const (
|
||||
|
|
@ -94,6 +100,8 @@ type AddAppRequest struct {
|
|||
type AddAppUserInvoiceRequest struct {
|
||||
Http_callback_url string `json:"http_callback_url"`
|
||||
Invoice_req *NewInvoiceRequest `json:"invoice_req"`
|
||||
Offer_string string `json:"offer_string"`
|
||||
Payer_data *PayerData `json:"payer_data"`
|
||||
Payer_identifier string `json:"payer_identifier"`
|
||||
Receiver_identifier string `json:"receiver_identifier"`
|
||||
}
|
||||
|
|
@ -386,6 +394,17 @@ type NewInvoiceRequest struct {
|
|||
type NewInvoiceResponse struct {
|
||||
Invoice string `json:"invoice"`
|
||||
}
|
||||
type OfferConfig struct {
|
||||
Callback_url string `json:"callback_url"`
|
||||
Expected_data map[string]OfferDataType `json:"expected_data"`
|
||||
Label string `json:"label"`
|
||||
Noffer string `json:"noffer"`
|
||||
Offer_id string `json:"offer_id"`
|
||||
Price_sats int64 `json:"price_sats"`
|
||||
}
|
||||
type OfferId struct {
|
||||
Offer_id string `json:"offer_id"`
|
||||
}
|
||||
type OpenChannel struct {
|
||||
Active bool `json:"active"`
|
||||
Capacity int64 `json:"capacity"`
|
||||
|
|
@ -436,6 +455,9 @@ type PayInvoiceResponse struct {
|
|||
Preimage string `json:"preimage"`
|
||||
Service_fee int64 `json:"service_fee"`
|
||||
}
|
||||
type PayerData struct {
|
||||
Data map[string]string `json:"data"`
|
||||
}
|
||||
type PaymentState struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Network_fee int64 `json:"network_fee"`
|
||||
|
|
@ -531,6 +553,9 @@ type UserInfo struct {
|
|||
Userid string `json:"userId"`
|
||||
User_identifier string `json:"user_identifier"`
|
||||
}
|
||||
type UserOffers struct {
|
||||
Offers []OfferConfig `json:"offers"`
|
||||
}
|
||||
type UserOperation struct {
|
||||
Amount int64 `json:"amount"`
|
||||
Confirmed bool `json:"confirmed"`
|
||||
|
|
|
|||
|
|
@ -166,6 +166,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.AddUserOffer) throw new Error('method: AddUserOffer is not implemented')
|
||||
app.post('/api/user/offer/add', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'AddUserOffer', 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.AddUserOffer) throw new Error('method: AddUserOffer 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.OfferConfigValidate(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.AddUserOffer({rpcName:'AddUserOffer', 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.AuthApp) throw new Error('method: AuthApp is not implemented')
|
||||
app.post('/api/admin/app/auth', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'AuthApp', batch: false, nostr: false, batchSize: 0}
|
||||
|
|
@ -287,6 +309,18 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'AddUserOffer':
|
||||
if (!methods.AddUserOffer) {
|
||||
throw new Error('method AddUserOffer not found' )
|
||||
} else {
|
||||
const error = Types.OfferConfigValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
const res = await methods.AddUserOffer({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'AuthorizeDebit':
|
||||
if (!methods.AuthorizeDebit) {
|
||||
throw new Error('method AuthorizeDebit not found' )
|
||||
|
|
@ -323,6 +357,18 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'DeleteUserOffer':
|
||||
if (!methods.DeleteUserOffer) {
|
||||
throw new Error('method DeleteUserOffer not found' )
|
||||
} else {
|
||||
const error = Types.OfferIdValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.DeleteUserOffer({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'EditDebit':
|
||||
if (!methods.EditDebit) {
|
||||
throw new Error('method EditDebit not found' )
|
||||
|
|
@ -409,6 +455,28 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetUserOffer':
|
||||
if (!methods.GetUserOffer) {
|
||||
throw new Error('method GetUserOffer not found' )
|
||||
} else {
|
||||
const error = Types.OfferIdValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
const res = await methods.GetUserOffer({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetUserOffers':
|
||||
if (!methods.GetUserOffers) {
|
||||
throw new Error('method GetUserOffers not found' )
|
||||
} else {
|
||||
opStats.validate = opStats.guard
|
||||
const res = await methods.GetUserOffers({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetUserOperations':
|
||||
if (!methods.GetUserOperations) {
|
||||
throw new Error('method GetUserOperations not found' )
|
||||
|
|
@ -515,6 +583,18 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'UpdateUserOffer':
|
||||
if (!methods.UpdateUserOffer) {
|
||||
throw new Error('method UpdateUserOffer not found' )
|
||||
} else {
|
||||
const error = Types.OfferConfigValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.UpdateUserOffer({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'UserHealth':
|
||||
if (!methods.UserHealth) {
|
||||
throw new Error('method UserHealth not found' )
|
||||
|
|
@ -601,6 +681,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.DeleteUserOffer) throw new Error('method: DeleteUserOffer is not implemented')
|
||||
app.post('/api/user/offer/delete', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'DeleteUserOffer', 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.DeleteUserOffer) throw new Error('method: DeleteUserOffer 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.OfferIdValidate(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.DeleteUserOffer({rpcName:'DeleteUserOffer', 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.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}
|
||||
|
|
@ -1011,6 +1113,47 @@ 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.GetUserOffer) throw new Error('method: GetUserOffer is not implemented')
|
||||
app.get('/api/user/offer/get', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'GetUserOffer', 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.GetUserOffer) throw new Error('method: GetUserOffer 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.OfferIdValidate(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.GetUserOffer({rpcName:'GetUserOffer', 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.GetUserOffers) throw new Error('method: GetUserOffers is not implemented')
|
||||
app.get('/api/user/offers/get', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'GetUserOffers', 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.GetUserOffers) throw new Error('method: GetUserOffers 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.GetUserOffers({rpcName:'GetUserOffers', 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.GetUserOperations) throw new Error('method: GetUserOperations is not implemented')
|
||||
app.post('/api/user/operations', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'GetUserOperations', batch: false, nostr: false, batchSize: 0}
|
||||
|
|
@ -1565,6 +1708,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.UpdateUserOffer) throw new Error('method: UpdateUserOffer is not implemented')
|
||||
app.post('/api/user/offer/update', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'UpdateUserOffer', 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.UpdateUserOffer) throw new Error('method: UpdateUserOffer 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.OfferConfigValidate(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.UpdateUserOffer({rpcName:'UpdateUserOffer', 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.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}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,20 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddUserOffer: async (request: Types.OfferConfig): Promise<ResultError | ({ status: 'OK' }& Types.OfferId)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/offer/add'
|
||||
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.OfferIdValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AuthApp: async (request: Types.AuthAppRequest): Promise<ResultError | ({ status: 'OK' }& Types.AuthApp)> => {
|
||||
const auth = await params.retrieveAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveAdminAuth() returned null')
|
||||
|
|
@ -204,6 +218,17 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
DeleteUserOffer: async (request: Types.OfferId): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/offer/delete'
|
||||
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' }
|
||||
},
|
||||
EditDebit: async (request: Types.DebitAuthorizationRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
|
|
@ -483,6 +508,34 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetUserOffer: async (request: Types.OfferId): Promise<ResultError | ({ status: 'OK' }& Types.OfferConfig)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/offer/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.OfferConfigValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetUserOffers: async (): Promise<ResultError | ({ status: 'OK' }& Types.UserOffers)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/offers/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.UserOffersValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetUserOperations: async (request: Types.GetUserOperationsRequest): Promise<ResultError | ({ status: 'OK' }& Types.GetUserOperationsResponse)> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
|
|
@ -821,6 +874,17 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
UpdateUserOffer: async (request: Types.OfferConfig): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveUserAuth()
|
||||
if (auth === null) throw new Error('retrieveUserAuth() returned null')
|
||||
let finalRoute = '/api/user/offer/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') {
|
||||
return data
|
||||
}
|
||||
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')
|
||||
|
|
|
|||
|
|
@ -54,6 +54,21 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AddUserOffer: async (request: Types.OfferConfig): Promise<ResultError | ({ status: 'OK' }& Types.OfferId)> => {
|
||||
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:'AddUserOffer',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.OfferIdValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
AuthApp: async (request: Types.AuthAppRequest): Promise<ResultError | ({ status: 'OK' }& Types.AuthApp)> => {
|
||||
const auth = await params.retrieveNostrAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrAdminAuth() returned null')
|
||||
|
|
@ -167,6 +182,18 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
DeleteUserOffer: async (request: Types.OfferId): 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:'DeleteUserOffer',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' }
|
||||
},
|
||||
EditDebit: async (request: Types.DebitAuthorizationRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
|
|
@ -409,6 +436,34 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetUserOffer: async (request: Types.OfferId): Promise<ResultError | ({ status: 'OK' }& Types.OfferConfig)> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
const data = await send(params.pubDestination, {rpcName:'GetUserOffer',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.OfferConfigValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetUserOffers: async (): Promise<ResultError | ({ status: 'OK' }& Types.UserOffers)> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
const nostrRequest: NostrRequest = {}
|
||||
const data = await send(params.pubDestination, {rpcName:'GetUserOffers',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.UserOffersValidate(result)
|
||||
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetUserOperations: async (request: Types.GetUserOperationsRequest): Promise<ResultError | ({ status: 'OK' }& Types.GetUserOperationsResponse)> => {
|
||||
const auth = await params.retrieveNostrUserAuth()
|
||||
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
|
||||
|
|
@ -606,6 +661,18 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
UpdateUserOffer: async (request: Types.OfferConfig): 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:'UpdateUserOffer',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' }
|
||||
},
|
||||
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,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 'AddUserOffer':
|
||||
try {
|
||||
if (!methods.AddUserOffer) throw new Error('method: AddUserOffer 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.OfferConfigValidate(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.AddUserOffer({rpcName:'AddUserOffer', 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 'AuthApp':
|
||||
try {
|
||||
if (!methods.AuthApp) throw new Error('method: AuthApp is not implemented')
|
||||
|
|
@ -175,6 +191,18 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'AddUserOffer':
|
||||
if (!methods.AddUserOffer) {
|
||||
throw new Error('method not defined: AddUserOffer')
|
||||
} else {
|
||||
const error = Types.OfferConfigValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
const res = await methods.AddUserOffer({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'AuthorizeDebit':
|
||||
if (!methods.AuthorizeDebit) {
|
||||
throw new Error('method not defined: AuthorizeDebit')
|
||||
|
|
@ -211,6 +239,18 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'DeleteUserOffer':
|
||||
if (!methods.DeleteUserOffer) {
|
||||
throw new Error('method not defined: DeleteUserOffer')
|
||||
} else {
|
||||
const error = Types.OfferIdValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.DeleteUserOffer({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'EditDebit':
|
||||
if (!methods.EditDebit) {
|
||||
throw new Error('method not defined: EditDebit')
|
||||
|
|
@ -297,6 +337,28 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetUserOffer':
|
||||
if (!methods.GetUserOffer) {
|
||||
throw new Error('method not defined: GetUserOffer')
|
||||
} else {
|
||||
const error = Types.OfferIdValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
const res = await methods.GetUserOffer({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetUserOffers':
|
||||
if (!methods.GetUserOffers) {
|
||||
throw new Error('method not defined: GetUserOffers')
|
||||
} else {
|
||||
opStats.validate = opStats.guard
|
||||
const res = await methods.GetUserOffers({...operation, ctx}); responses.push({ status: 'OK', ...res })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'GetUserOperations':
|
||||
if (!methods.GetUserOperations) {
|
||||
throw new Error('method not defined: GetUserOperations')
|
||||
|
|
@ -403,6 +465,18 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
|||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'UpdateUserOffer':
|
||||
if (!methods.UpdateUserOffer) {
|
||||
throw new Error('method not defined: UpdateUserOffer')
|
||||
} else {
|
||||
const error = Types.OfferConfigValidate(operation.req)
|
||||
opStats.validate = process.hrtime.bigint()
|
||||
if (error !== null) throw error
|
||||
await methods.UpdateUserOffer({...operation, ctx}); responses.push({ status: 'OK' })
|
||||
opStats.handle = process.hrtime.bigint()
|
||||
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
|
||||
}
|
||||
break
|
||||
case 'UserHealth':
|
||||
if (!methods.UserHealth) {
|
||||
throw new Error('method not defined: UserHealth')
|
||||
|
|
@ -471,6 +545,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 'DeleteUserOffer':
|
||||
try {
|
||||
if (!methods.DeleteUserOffer) throw new Error('method: DeleteUserOffer 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.OfferIdValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
|
||||
await methods.DeleteUserOffer({rpcName:'DeleteUserOffer', 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 'EditDebit':
|
||||
try {
|
||||
if (!methods.EditDebit) throw new Error('method: EditDebit is not implemented')
|
||||
|
|
@ -710,6 +800,35 @@ 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 'GetUserOffer':
|
||||
try {
|
||||
if (!methods.GetUserOffer) throw new Error('method: GetUserOffer 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.OfferIdValidate(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.GetUserOffer({rpcName:'GetUserOffer', 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 'GetUserOffers':
|
||||
try {
|
||||
if (!methods.GetUserOffers) throw new Error('method: GetUserOffers 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.GetUserOffers({rpcName:'GetUserOffers', 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 'GetUserOperations':
|
||||
try {
|
||||
if (!methods.GetUserOperations) throw new Error('method: GetUserOperations is not implemented')
|
||||
|
|
@ -928,6 +1047,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 'UpdateUserOffer':
|
||||
try {
|
||||
if (!methods.UpdateUserOffer) throw new Error('method: UpdateUserOffer 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.OfferConfigValidate(request)
|
||||
stats.validate = process.hrtime.bigint()
|
||||
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
|
||||
await methods.UpdateUserOffer({rpcName:'UpdateUserOffer', 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 '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 | 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 | 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 | PayAddress_Output | PayInvoice_Output | ResetDebit_Output | RespondToDebit_Output | UpdateCallbackUrl_Output | UserHealth_Output
|
||||
export type UserMethodInputs = AddProduct_Input | AddUserOffer_Input | AuthorizeDebit_Input | BanDebit_Input | DecodeInvoice_Input | DeleteUserOffer_Input | EditDebit_Input | EnrollAdminToken_Input | GetDebitAuthorizations_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetPaymentState_Input | GetUserInfo_Input | GetUserOffer_Input | GetUserOffers_Input | GetUserOperations_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | PayAddress_Input | PayInvoice_Input | ResetDebit_Input | RespondToDebit_Input | UpdateCallbackUrl_Input | UpdateUserOffer_Input | UserHealth_Input
|
||||
export type UserMethodOutputs = AddProduct_Output | AddUserOffer_Output | AuthorizeDebit_Output | BanDebit_Output | DecodeInvoice_Output | DeleteUserOffer_Output | EditDebit_Output | EnrollAdminToken_Output | GetDebitAuthorizations_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetPaymentState_Output | GetUserInfo_Output | GetUserOffer_Output | GetUserOffers_Output | GetUserOperations_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | PayAddress_Output | PayInvoice_Output | ResetDebit_Output | RespondToDebit_Output | UpdateCallbackUrl_Output | UpdateUserOffer_Output | UserHealth_Output
|
||||
export type AuthContext = AdminContext | AppContext | GuestContext | GuestWithPubContext | MetricsContext | UserContext
|
||||
|
||||
export type AddApp_Input = {rpcName:'AddApp', req: AddAppRequest}
|
||||
|
|
@ -56,6 +56,9 @@ export type AddPeer_Output = ResultError | { status: 'OK' }
|
|||
export type AddProduct_Input = {rpcName:'AddProduct', req: AddProductRequest}
|
||||
export type AddProduct_Output = ResultError | ({ status: 'OK' } & Product)
|
||||
|
||||
export type AddUserOffer_Input = {rpcName:'AddUserOffer', req: OfferConfig}
|
||||
export type AddUserOffer_Output = ResultError | ({ status: 'OK' } & OfferId)
|
||||
|
||||
export type AuthApp_Input = {rpcName:'AuthApp', req: AuthAppRequest}
|
||||
export type AuthApp_Output = ResultError | ({ status: 'OK' } & AuthApp)
|
||||
|
||||
|
|
@ -80,6 +83,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 DeleteUserOffer_Input = {rpcName:'DeleteUserOffer', req: OfferId}
|
||||
export type DeleteUserOffer_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type EditDebit_Input = {rpcName:'EditDebit', req: DebitAuthorizationRequest}
|
||||
export type EditDebit_Output = ResultError | { status: 'OK' }
|
||||
|
||||
|
|
@ -158,6 +164,12 @@ export type GetUsageMetrics_Output = ResultError | ({ status: 'OK' } & UsageMetr
|
|||
export type GetUserInfo_Input = {rpcName:'GetUserInfo'}
|
||||
export type GetUserInfo_Output = ResultError | ({ status: 'OK' } & UserInfo)
|
||||
|
||||
export type GetUserOffer_Input = {rpcName:'GetUserOffer', req: OfferId}
|
||||
export type GetUserOffer_Output = ResultError | ({ status: 'OK' } & OfferConfig)
|
||||
|
||||
export type GetUserOffers_Input = {rpcName:'GetUserOffers'}
|
||||
export type GetUserOffers_Output = ResultError | ({ status: 'OK' } & UserOffers)
|
||||
|
||||
export type GetUserOperations_Input = {rpcName:'GetUserOperations', req: GetUserOperationsRequest}
|
||||
export type GetUserOperations_Output = ResultError | ({ status: 'OK' } & GetUserOperationsResponse)
|
||||
|
||||
|
|
@ -252,6 +264,9 @@ export type UpdateCallbackUrl_Output = ResultError | ({ status: 'OK' } & Callbac
|
|||
export type UpdateChannelPolicy_Input = {rpcName:'UpdateChannelPolicy', req: UpdateChannelPolicyRequest}
|
||||
export type UpdateChannelPolicy_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type UpdateUserOffer_Input = {rpcName:'UpdateUserOffer', req: OfferConfig}
|
||||
export type UpdateUserOffer_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type UseInviteLink_Input = {rpcName:'UseInviteLink', req: UseInviteLinkRequest}
|
||||
export type UseInviteLink_Output = ResultError | { status: 'OK' }
|
||||
|
||||
|
|
@ -265,6 +280,7 @@ export type ServerMethods = {
|
|||
AddAppUserInvoice?: (req: AddAppUserInvoice_Input & {ctx: AppContext }) => Promise<NewInvoiceResponse>
|
||||
AddPeer?: (req: AddPeer_Input & {ctx: AdminContext }) => Promise<void>
|
||||
AddProduct?: (req: AddProduct_Input & {ctx: UserContext }) => Promise<Product>
|
||||
AddUserOffer?: (req: AddUserOffer_Input & {ctx: UserContext }) => Promise<OfferId>
|
||||
AuthApp?: (req: AuthApp_Input & {ctx: AdminContext }) => Promise<AuthApp>
|
||||
AuthorizeDebit?: (req: AuthorizeDebit_Input & {ctx: UserContext }) => Promise<DebitAuthorization>
|
||||
BanDebit?: (req: BanDebit_Input & {ctx: UserContext }) => Promise<void>
|
||||
|
|
@ -272,6 +288,7 @@ export type ServerMethods = {
|
|||
CloseChannel?: (req: CloseChannel_Input & {ctx: AdminContext }) => Promise<CloseChannelResponse>
|
||||
CreateOneTimeInviteLink?: (req: CreateOneTimeInviteLink_Input & {ctx: AdminContext }) => Promise<CreateOneTimeInviteLinkResponse>
|
||||
DecodeInvoice?: (req: DecodeInvoice_Input & {ctx: UserContext }) => Promise<DecodeInvoiceResponse>
|
||||
DeleteUserOffer?: (req: DeleteUserOffer_Input & {ctx: UserContext }) => Promise<void>
|
||||
EditDebit?: (req: EditDebit_Input & {ctx: UserContext }) => Promise<void>
|
||||
EncryptionExchange?: (req: EncryptionExchange_Input & {ctx: GuestContext }) => Promise<void>
|
||||
EnrollAdminToken?: (req: EnrollAdminToken_Input & {ctx: UserContext }) => Promise<void>
|
||||
|
|
@ -296,6 +313,8 @@ export type ServerMethods = {
|
|||
GetSeed?: (req: GetSeed_Input & {ctx: AdminContext }) => Promise<LndSeed>
|
||||
GetUsageMetrics?: (req: GetUsageMetrics_Input & {ctx: MetricsContext }) => Promise<UsageMetrics>
|
||||
GetUserInfo?: (req: GetUserInfo_Input & {ctx: UserContext }) => Promise<UserInfo>
|
||||
GetUserOffer?: (req: GetUserOffer_Input & {ctx: UserContext }) => Promise<OfferConfig>
|
||||
GetUserOffers?: (req: GetUserOffers_Input & {ctx: UserContext }) => Promise<UserOffers>
|
||||
GetUserOperations?: (req: GetUserOperations_Input & {ctx: UserContext }) => Promise<GetUserOperationsResponse>
|
||||
HandleLnurlAddress?: (req: HandleLnurlAddress_Input & {ctx: GuestContext }) => Promise<LnurlPayInfoResponse>
|
||||
HandleLnurlPay?: (req: HandleLnurlPay_Input & {ctx: GuestContext }) => Promise<HandleLnurlPayResponse>
|
||||
|
|
@ -322,6 +341,7 @@ export type ServerMethods = {
|
|||
SetMockInvoiceAsPaid?: (req: SetMockInvoiceAsPaid_Input & {ctx: GuestContext }) => Promise<void>
|
||||
UpdateCallbackUrl?: (req: UpdateCallbackUrl_Input & {ctx: UserContext }) => Promise<CallbackUrl>
|
||||
UpdateChannelPolicy?: (req: UpdateChannelPolicy_Input & {ctx: AdminContext }) => Promise<void>
|
||||
UpdateUserOffer?: (req: UpdateUserOffer_Input & {ctx: UserContext }) => Promise<void>
|
||||
UseInviteLink?: (req: UseInviteLink_Input & {ctx: GuestWithPubContext }) => Promise<void>
|
||||
UserHealth?: (req: UserHealth_Input & {ctx: UserContext }) => Promise<void>
|
||||
}
|
||||
|
|
@ -344,6 +364,13 @@ export const enumCheckIntervalType = (e?: IntervalType): boolean => {
|
|||
for (const v in IntervalType) if (e === v) return true
|
||||
return false
|
||||
}
|
||||
export enum OfferDataType {
|
||||
DATA_STRING = 'DATA_STRING',
|
||||
}
|
||||
export const enumCheckOfferDataType = (e?: OfferDataType): boolean => {
|
||||
for (const v in OfferDataType) if (e === v) return true
|
||||
return false
|
||||
}
|
||||
export enum OperationType {
|
||||
CHAIN_OP = 'CHAIN_OP',
|
||||
INVOICE_OP = 'INVOICE_OP',
|
||||
|
|
@ -424,14 +451,19 @@ export const AddAppRequestValidate = (o?: AddAppRequest, opts: AddAppRequestOpti
|
|||
export type AddAppUserInvoiceRequest = {
|
||||
http_callback_url: string
|
||||
invoice_req: NewInvoiceRequest
|
||||
offer_string?: string
|
||||
payer_data?: PayerData
|
||||
payer_identifier: string
|
||||
receiver_identifier: string
|
||||
}
|
||||
export const AddAppUserInvoiceRequestOptionalFields: [] = []
|
||||
export type AddAppUserInvoiceRequestOptionalField = 'offer_string' | 'payer_data'
|
||||
export const AddAppUserInvoiceRequestOptionalFields: AddAppUserInvoiceRequestOptionalField[] = ['offer_string', 'payer_data']
|
||||
export type AddAppUserInvoiceRequestOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
checkOptionalsAreSet?: AddAppUserInvoiceRequestOptionalField[]
|
||||
http_callback_url_CustomCheck?: (v: string) => boolean
|
||||
invoice_req_Options?: NewInvoiceRequestOptions
|
||||
offer_string_CustomCheck?: (v?: string) => boolean
|
||||
payer_data_Options?: PayerDataOptions
|
||||
payer_identifier_CustomCheck?: (v: string) => boolean
|
||||
receiver_identifier_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
|
|
@ -446,6 +478,15 @@ export const AddAppUserInvoiceRequestValidate = (o?: AddAppUserInvoiceRequest, o
|
|||
if (invoice_reqErr !== null) return invoice_reqErr
|
||||
|
||||
|
||||
if ((o.offer_string || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('offer_string')) && typeof o.offer_string !== 'string') return new Error(`${path}.offer_string: is not a string`)
|
||||
if (opts.offer_string_CustomCheck && !opts.offer_string_CustomCheck(o.offer_string)) return new Error(`${path}.offer_string: custom check failed`)
|
||||
|
||||
if (typeof o.payer_data === 'object' || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('payer_data')) {
|
||||
const payer_dataErr = PayerDataValidate(o.payer_data, opts.payer_data_Options, `${path}.payer_data`)
|
||||
if (payer_dataErr !== null) return payer_dataErr
|
||||
}
|
||||
|
||||
|
||||
if (typeof o.payer_identifier !== 'string') return new Error(`${path}.payer_identifier: is not a string`)
|
||||
if (opts.payer_identifier_CustomCheck && !opts.payer_identifier_CustomCheck(o.payer_identifier)) return new Error(`${path}.payer_identifier: custom check failed`)
|
||||
|
||||
|
|
@ -2201,6 +2242,69 @@ export const NewInvoiceResponseValidate = (o?: NewInvoiceResponse, opts: NewInvo
|
|||
return null
|
||||
}
|
||||
|
||||
export type OfferConfig = {
|
||||
callback_url: string
|
||||
expected_data: Record<string, OfferDataType>
|
||||
label: string
|
||||
noffer: string
|
||||
offer_id: string
|
||||
price_sats: number
|
||||
}
|
||||
export const OfferConfigOptionalFields: [] = []
|
||||
export type OfferConfigOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
callback_url_CustomCheck?: (v: string) => boolean
|
||||
expected_data_CustomCheck?: (v: Record<string, OfferDataType>) => boolean
|
||||
label_CustomCheck?: (v: string) => boolean
|
||||
noffer_CustomCheck?: (v: string) => boolean
|
||||
offer_id_CustomCheck?: (v: string) => boolean
|
||||
price_sats_CustomCheck?: (v: number) => boolean
|
||||
}
|
||||
export const OfferConfigValidate = (o?: OfferConfig, opts: OfferConfigOptions = {}, path: string = 'OfferConfig::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.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.expected_data !== 'object' || o.expected_data === null) return new Error(`${path}.expected_data: is not an object or is null`)
|
||||
for (const key in o.expected_data) {
|
||||
if (!enumCheckOfferDataType(o.expected_data[key])) return new Error(`${path}.expected_data['${key}']: is not a OfferDataType`)
|
||||
}
|
||||
|
||||
if (typeof o.label !== 'string') return new Error(`${path}.label: is not a string`)
|
||||
if (opts.label_CustomCheck && !opts.label_CustomCheck(o.label)) return new Error(`${path}.label: custom check failed`)
|
||||
|
||||
if (typeof o.noffer !== 'string') return new Error(`${path}.noffer: is not a string`)
|
||||
if (opts.noffer_CustomCheck && !opts.noffer_CustomCheck(o.noffer)) return new Error(`${path}.noffer: custom check failed`)
|
||||
|
||||
if (typeof o.offer_id !== 'string') return new Error(`${path}.offer_id: is not a string`)
|
||||
if (opts.offer_id_CustomCheck && !opts.offer_id_CustomCheck(o.offer_id)) return new Error(`${path}.offer_id: custom check failed`)
|
||||
|
||||
if (typeof o.price_sats !== 'number') return new Error(`${path}.price_sats: is not a number`)
|
||||
if (opts.price_sats_CustomCheck && !opts.price_sats_CustomCheck(o.price_sats)) return new Error(`${path}.price_sats: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type OfferId = {
|
||||
offer_id: string
|
||||
}
|
||||
export const OfferIdOptionalFields: [] = []
|
||||
export type OfferIdOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
offer_id_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
export const OfferIdValidate = (o?: OfferId, opts: OfferIdOptions = {}, path: string = 'OfferId::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.offer_id !== 'string') return new Error(`${path}.offer_id: is not a string`)
|
||||
if (opts.offer_id_CustomCheck && !opts.offer_id_CustomCheck(o.offer_id)) return new Error(`${path}.offer_id: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type OpenChannel = {
|
||||
active: boolean
|
||||
capacity: number
|
||||
|
|
@ -2482,6 +2586,26 @@ export const PayInvoiceResponseValidate = (o?: PayInvoiceResponse, opts: PayInvo
|
|||
return null
|
||||
}
|
||||
|
||||
export type PayerData = {
|
||||
data: Record<string, string>
|
||||
}
|
||||
export const PayerDataOptionalFields: [] = []
|
||||
export type PayerDataOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
data_CustomCheck?: (v: Record<string, string>) => boolean
|
||||
}
|
||||
export const PayerDataValidate = (o?: PayerData, opts: PayerDataOptions = {}, path: string = 'PayerData::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.data !== 'object' || o.data === null) return new Error(`${path}.data: is not an object or is null`)
|
||||
for (const key in o.data) {
|
||||
if (typeof o.data[key] !== 'string') return new Error(`${path}.data['${key}']: is not a string`)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type PaymentState = {
|
||||
amount: number
|
||||
network_fee: number
|
||||
|
|
@ -3018,6 +3142,29 @@ export const UserInfoValidate = (o?: UserInfo, opts: UserInfoOptions = {}, path:
|
|||
return null
|
||||
}
|
||||
|
||||
export type UserOffers = {
|
||||
offers: OfferConfig[]
|
||||
}
|
||||
export const UserOffersOptionalFields: [] = []
|
||||
export type UserOffersOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
offers_ItemOptions?: OfferConfigOptions
|
||||
offers_CustomCheck?: (v: OfferConfig[]) => boolean
|
||||
}
|
||||
export const UserOffersValidate = (o?: UserOffers, opts: UserOffersOptions = {}, path: string = 'UserOffers::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.offers)) return new Error(`${path}.offers: is not an array`)
|
||||
for (let index = 0; index < o.offers.length; index++) {
|
||||
const offersErr = OfferConfigValidate(o.offers[index], opts.offers_ItemOptions, `${path}.offers[${index}]`)
|
||||
if (offersErr !== null) return offersErr
|
||||
}
|
||||
if (opts.offers_CustomCheck && !opts.offers_CustomCheck(o.offers)) return new Error(`${path}.offers: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type UserOperation = {
|
||||
amount: number
|
||||
confirmed: boolean
|
||||
|
|
|
|||
|
|
@ -467,6 +467,42 @@ service LightningPub {
|
|||
option (http_route) = "/api/user/lnurl_channel/url";
|
||||
option (nostr) = true;
|
||||
}
|
||||
|
||||
rpc GetUserOffers(structs.Empty) returns (structs.UserOffers){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "get";
|
||||
option (http_route) = "/api/user/offers/get";
|
||||
option (nostr) = true;
|
||||
}
|
||||
|
||||
rpc GetUserOffer(structs.OfferId) returns (structs.OfferConfig){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "get";
|
||||
option (http_route) = "/api/user/offer/get";
|
||||
option (nostr) = true;
|
||||
}
|
||||
|
||||
rpc UpdateUserOffer(structs.OfferConfig) returns (structs.Empty){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/offer/update";
|
||||
option (nostr) = true;
|
||||
}
|
||||
|
||||
rpc DeleteUserOffer(structs.OfferId) returns (structs.Empty){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/offer/delete";
|
||||
option (nostr) = true;
|
||||
}
|
||||
|
||||
rpc AddUserOffer(structs.OfferConfig) returns (structs.OfferId){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "post";
|
||||
option (http_route) = "/api/user/offer/add";
|
||||
option (nostr) = true;
|
||||
}
|
||||
|
||||
rpc GetDebitAuthorizations(structs.Empty) returns (structs.DebitAuthorizations){
|
||||
option (auth_type) = "User";
|
||||
option (http_method) = "get";
|
||||
|
|
|
|||
|
|
@ -269,6 +269,8 @@ message AddAppUserInvoiceRequest {
|
|||
string payer_identifier = 2;
|
||||
string http_callback_url = 3;
|
||||
NewInvoiceRequest invoice_req = 4;
|
||||
optional PayerData payer_data = 5;
|
||||
optional string offer_string = 6;
|
||||
}
|
||||
|
||||
message GetAppUserRequest {
|
||||
|
|
@ -331,6 +333,10 @@ message PayAddressResponse{
|
|||
int64 network_fee = 4;
|
||||
}
|
||||
|
||||
message PayerData {
|
||||
map<string,string> data = 1;
|
||||
}
|
||||
|
||||
message NewInvoiceRequest{
|
||||
int64 amountSats = 1;
|
||||
string memo = 2;
|
||||
|
|
@ -614,3 +620,24 @@ message DebitResponse {
|
|||
string invoice = 4;
|
||||
}
|
||||
}
|
||||
|
||||
enum OfferDataType {
|
||||
DATA_STRING = 0;
|
||||
}
|
||||
|
||||
message OfferId {
|
||||
string offer_id = 1;
|
||||
}
|
||||
|
||||
message OfferConfig {
|
||||
string offer_id = 1;
|
||||
string label = 2;
|
||||
int64 price_sats = 3;
|
||||
string callback_url = 4;
|
||||
map<string, OfferDataType> expected_data = 5;
|
||||
string noffer = 6;
|
||||
}
|
||||
|
||||
message UserOffers {
|
||||
repeated OfferConfig offers = 1;
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
|||
}
|
||||
if (event.kind === 21001) {
|
||||
const offerReq = j as NofferData
|
||||
mainHandler.handleNip69Noffer(offerReq, event)
|
||||
mainHandler.offerManager.handleNip69Noffer(offerReq, event)
|
||||
return
|
||||
} else if (event.kind === 21002) {
|
||||
const debitReq = j as NdebitData
|
||||
|
|
|
|||
|
|
@ -193,7 +193,10 @@ export default class {
|
|||
if (req.invoice_req.zap) {
|
||||
zapInfo = this.paymentManager.validateZapEvent(req.invoice_req.zap, req.invoice_req.amountSats)
|
||||
}
|
||||
const opts: InboundOptionals = { callbackUrl: cbUrl, expiry: defaultInvoiceExpiry, expectedPayer: payer.user, linkedApplication: app, zapInfo }
|
||||
const opts: InboundOptionals = {
|
||||
callbackUrl: cbUrl, expiry: defaultInvoiceExpiry, expectedPayer: payer.user, linkedApplication: app, zapInfo,
|
||||
offerId: req.offer_string, payerData: req.payer_data?.data
|
||||
}
|
||||
const appUserInvoice = await this.paymentManager.NewInvoice(receiver.user.user_id, req.invoice_req, opts)
|
||||
return {
|
||||
invoice: appUserInvoice.invoice
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { Unlocker } from "./unlocker.js"
|
|||
import { defaultInvoiceExpiry } from "../storage/paymentStorage.js"
|
||||
import { DebitManager } from "./debitManager.js"
|
||||
import { NofferData } from "nostr-tools/lib/types/nip69.js"
|
||||
import { OfferManager } from "./offerManager.js"
|
||||
|
||||
type UserOperationsSub = {
|
||||
id: string
|
||||
|
|
@ -49,6 +50,7 @@ export default class {
|
|||
liquidityManager: LiquidityManager
|
||||
liquidityProvider: LiquidityProvider
|
||||
debitManager: DebitManager
|
||||
offerManager: OfferManager
|
||||
utils: Utils
|
||||
rugPullTracker: RugPullTracker
|
||||
unlocker: Unlocker
|
||||
|
|
@ -71,6 +73,8 @@ export default class {
|
|||
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)
|
||||
this.offerManager = new OfferManager(this.storage, this.lnd, this.applicationManager, this.productManager)
|
||||
|
||||
}
|
||||
|
||||
Stop() {
|
||||
|
|
@ -89,6 +93,7 @@ export default class {
|
|||
this.nostrSend = f
|
||||
this.liquidityProvider.attachNostrSend(f)
|
||||
this.debitManager.attachNostrSend(f)
|
||||
this.offerManager.attachNostrSend(f)
|
||||
}
|
||||
|
||||
htlcCb: HtlcCb = (e) => {
|
||||
|
|
@ -286,70 +291,6 @@ export default class {
|
|||
log({ unsigned: event })
|
||||
this.nostrSend({ type: 'app', appId: invoice.linkedApplication.app_id }, { type: 'event', event }, zapInfo.relays || undefined)
|
||||
}
|
||||
|
||||
async getNofferInvoice(offerReq: NofferData, appId: string): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> {
|
||||
try {
|
||||
|
||||
const { remote } = await this.lnd.ChannelBalance()
|
||||
const { offer, amount } = offerReq
|
||||
const split = offer.split(':')
|
||||
if (split.length === 1) {
|
||||
if (!amount || isNaN(amount) || amount < 10 || amount > remote) {
|
||||
return { success: false, code: 5, max: remote }
|
||||
}
|
||||
const res = await this.applicationManager.AddAppUserInvoice(appId, {
|
||||
http_callback_url: "", payer_identifier: split[0], receiver_identifier: split[0],
|
||||
invoice_req: { amountSats: amount, memo: "Default NIP-69 Offer", zap: offerReq.zap }
|
||||
})
|
||||
return { success: true, invoice: res.invoice }
|
||||
} else if (split[0] === 'p') {
|
||||
const product = await this.productManager.NewProductInvoice(split[1])
|
||||
return { success: true, invoice: product.invoice }
|
||||
} else {
|
||||
return { success: false, code: 1, max: remote }
|
||||
}
|
||||
} catch (e: any) {
|
||||
getLogger({ component: "noffer" })(ERROR, e.message || e)
|
||||
return { success: false, code: 1, max: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
async handleNip69Noffer(offerReq: NofferData, event: NostrEvent) {
|
||||
const offerInvoice = await this.getNofferInvoice(offerReq, event.appId)
|
||||
if (!offerInvoice.success) {
|
||||
const code = offerInvoice.code
|
||||
const e = newNofferResponse(JSON.stringify({ code, error: codeToMessage(code), range: { min: 10, max: offerInvoice.max } }), event)
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||
return
|
||||
}
|
||||
const e = newNofferResponse(JSON.stringify({ bolt11: offerInvoice.invoice }), event)
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const codeToMessage = (code: number) => {
|
||||
switch (code) {
|
||||
case 1: return 'Invalid Offer'
|
||||
case 2: return 'Temporary Failure'
|
||||
case 3: return 'Expired Offer'
|
||||
case 4: return 'Unsupported Feature'
|
||||
case 5: return 'Invalid Amount'
|
||||
default: throw new Error("unknown error code" + code)
|
||||
}
|
||||
}
|
||||
|
||||
const newNofferResponse = (content: string, event: NostrEvent): UnsignedEvent => {
|
||||
return {
|
||||
content,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 21001,
|
||||
pubkey: "",
|
||||
tags: [
|
||||
['p', event.pub],
|
||||
['e', event.id],
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
239
src/services/main/offerManager.ts
Normal file
239
src/services/main/offerManager.ts
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
import crypto from 'crypto';
|
||||
import * as Types from "../../../proto/autogenerated/ts/types.js";
|
||||
import ApplicationManager from "./applicationManager.js";
|
||||
import ProductManager from "./productManager.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';
|
||||
import { NofferData } from "nostr-tools/lib/types/nip69.js"
|
||||
import { UserOffer } from '../storage/entity/UserOffer.js';
|
||||
import { DeepPartial } from 'typeorm';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { LoadNosrtSettingsFromEnv } from '../nostr/index.js';
|
||||
|
||||
const mapToOfferConfig = (offer: UserOffer, { pubkey, relay }: { pubkey: string, relay: string }): Types.OfferConfig => {
|
||||
if (offer.expected_data) {
|
||||
const keys = Object.keys(offer.expected_data)
|
||||
for (const key of keys) {
|
||||
const v = offer.expected_data[key] as Types.OfferDataType
|
||||
if (!Types.OfferDataType[v]) {
|
||||
offer.expected_data[key] = Types.OfferDataType.DATA_STRING
|
||||
}
|
||||
}
|
||||
}
|
||||
const offerStr = offer.offer_id
|
||||
const priceType: nip19.OfferPriceType = offer.price_sats === 0 ? nip19.OfferPriceType.Spontaneous : nip19.OfferPriceType.Fixed
|
||||
const noffer = nip19.nofferEncode({ pubkey, offer: offerStr, priceType, relay })
|
||||
return {
|
||||
label: offer.label,
|
||||
price_sats: offer.price_sats,
|
||||
callback_url: offer.callback_url,
|
||||
expected_data: (offer.expected_data || {}) as Record<string, Types.OfferDataType>,
|
||||
offer_id: offer.offer_id,
|
||||
noffer: noffer
|
||||
}
|
||||
}
|
||||
export class OfferManager {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_nostrSend: NostrSend | null = null
|
||||
|
||||
applicationManager: ApplicationManager
|
||||
productManager: ProductManager
|
||||
storage: Storage
|
||||
lnd: LND
|
||||
logger = getLogger({ component: 'DebitManager' })
|
||||
constructor(storage: Storage, lnd: LND, applicationManager: ApplicationManager, productManager: ProductManager) {
|
||||
this.storage = storage
|
||||
this.lnd = lnd
|
||||
this.applicationManager = applicationManager
|
||||
this.productManager = productManager
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
async AddUserOffer(ctx: Types.UserContext, req: Types.OfferConfig): Promise<Types.OfferId> {
|
||||
const newOffer = await this.storage.offerStorage.AddUserOffer(ctx.app_user_id, {
|
||||
expected_data: req.expected_data,
|
||||
label: req.label,
|
||||
price_sats: req.price_sats,
|
||||
callback_url: req.callback_url,
|
||||
})
|
||||
return {
|
||||
offer_id: newOffer.offer_id
|
||||
}
|
||||
}
|
||||
|
||||
async DeleteUserOffer(ctx: Types.UserContext, req: Types.OfferId) {
|
||||
await this.storage.offerStorage.DeleteUserOffer(ctx.app_user_id, req.offer_id)
|
||||
}
|
||||
|
||||
async UpdateUserOffer(ctx: Types.UserContext, req: Types.OfferConfig) {
|
||||
await this.storage.offerStorage.UpdateUserOffer(ctx.app_user_id, {
|
||||
expected_data: req.expected_data,
|
||||
label: req.label,
|
||||
price_sats: req.price_sats,
|
||||
callback_url: req.callback_url,
|
||||
})
|
||||
}
|
||||
|
||||
async GetUserOffer(ctx: Types.UserContext, req: Types.OfferId): Promise<Types.OfferConfig> {
|
||||
const app = await this.applicationManager.GetApp(ctx.app_id)
|
||||
if (!app) {
|
||||
throw new Error("App not found")
|
||||
}
|
||||
const offer = await this.storage.offerStorage.GetUserOffer(ctx.app_user_id, req.offer_id)
|
||||
if (!offer) {
|
||||
throw new Error("Offer not found")
|
||||
}
|
||||
const nostrSettings = LoadNosrtSettingsFromEnv()
|
||||
return mapToOfferConfig(offer, { pubkey: app.npub, relay: nostrSettings.relays[0] })
|
||||
}
|
||||
|
||||
async GetUserOffers(ctx: Types.UserContext): Promise<Types.UserOffers> {
|
||||
const app = await this.applicationManager.GetApp(ctx.app_id)
|
||||
if (!app) {
|
||||
throw new Error("App not found")
|
||||
}
|
||||
const offers = await this.storage.offerStorage.GetUserOffers(ctx.app_user_id)
|
||||
const nostrSettings = LoadNosrtSettingsFromEnv()
|
||||
return {
|
||||
offers: offers.map(o => mapToOfferConfig(o, { pubkey: app.npub, relay: nostrSettings.relays[0] }))
|
||||
}
|
||||
}
|
||||
|
||||
ValidateExpectedData(userOffer: UserOffer, payerData: any): { passed: false, validated: undefined } | { passed: true, validated: Record<string, string> } {
|
||||
const expected = userOffer.expected_data
|
||||
if (!expected) {
|
||||
return { passed: true, validated: {} }
|
||||
}
|
||||
const expectedKeys = Object.keys(expected)
|
||||
if (expectedKeys.length === 0) {
|
||||
return { passed: true, validated: {} }
|
||||
}
|
||||
if (typeof payerData !== 'object' || payerData === null) {
|
||||
return { passed: false, validated: undefined }
|
||||
}
|
||||
const validated: Record<string, string> = {}
|
||||
for (const key of expectedKeys) {
|
||||
if (typeof payerData[key] !== 'string') {
|
||||
return { passed: false, validated: undefined }
|
||||
}
|
||||
validated[key] = payerData[key]
|
||||
}
|
||||
return { passed: true, validated }
|
||||
}
|
||||
|
||||
async handleNip69Noffer(offerReq: NofferData, event: NostrEvent) {
|
||||
const offerInvoice = await this.getNofferInvoice(offerReq, event.appId)
|
||||
if (!offerInvoice.success) {
|
||||
const code = offerInvoice.code
|
||||
const e = newNofferResponse(JSON.stringify({ code, error: codeToMessage(code), range: { min: 10, max: offerInvoice.max } }), event)
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||
return
|
||||
}
|
||||
const e = newNofferResponse(JSON.stringify({ bolt11: offerInvoice.invoice }), event)
|
||||
this.nostrSend({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||
return
|
||||
}
|
||||
|
||||
async HandleDefaultUserOffer(offerReq: NofferData, appId: string, remote: number): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> {
|
||||
const { amount, offer } = offerReq
|
||||
if (!amount || isNaN(amount) || amount < 10 || amount > remote) {
|
||||
return { success: false, code: 5, max: remote }
|
||||
}
|
||||
const res = await this.applicationManager.AddAppUserInvoice(appId, {
|
||||
http_callback_url: "", payer_identifier: offer, receiver_identifier: offer,
|
||||
invoice_req: { amountSats: amount, memo: "Default NIP-69 Offer", zap: offerReq.zap },
|
||||
offer_string: 'offer'
|
||||
})
|
||||
return { success: true, invoice: res.invoice }
|
||||
}
|
||||
|
||||
async HandleUserOffer(offerReq: NofferData, appId: string, remote: number): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> {
|
||||
const { amount, offer } = offerReq
|
||||
const userOffer = await this.storage.offerStorage.GetOffer(offer)
|
||||
if (!userOffer) {
|
||||
return this.HandleDefaultUserOffer(offerReq, appId, remote)
|
||||
}
|
||||
let amt = userOffer.price_sats
|
||||
if (userOffer.price_sats === 0) {
|
||||
if (!amount || isNaN(amount) || amount < 10 || amount > remote) {
|
||||
return { success: false, code: 5, max: remote }
|
||||
}
|
||||
amt = amount
|
||||
}
|
||||
const { passed, validated } = this.ValidateExpectedData(userOffer, offerReq.payer_data)
|
||||
if (!passed) {
|
||||
return { success: false, code: 1, max: remote }
|
||||
}
|
||||
const res = await this.applicationManager.AddAppUserInvoice(appId, {
|
||||
http_callback_url: userOffer.callback_url, payer_identifier: offer, receiver_identifier: offer,
|
||||
invoice_req: { amountSats: amt, memo: userOffer.label, zap: offerReq.zap },
|
||||
payer_data: validated ? { data: validated } : undefined,
|
||||
offer_string: offer
|
||||
})
|
||||
return { success: true, invoice: res.invoice }
|
||||
}
|
||||
|
||||
async getNofferInvoice(offerReq: NofferData, appId: string): Promise<{ success: true, invoice: string } | { success: false, code: number, max: number }> {
|
||||
try {
|
||||
const { remote } = await this.lnd.ChannelBalance()
|
||||
const split = offerReq.offer.split(':')
|
||||
if (split.length === 1) {
|
||||
return this.HandleUserOffer(offerReq, appId, remote)
|
||||
} else if (split[0] === 'p') {
|
||||
const product = await this.productManager.NewProductInvoice(split[1])
|
||||
return { success: true, invoice: product.invoice }
|
||||
} else {
|
||||
return { success: false, code: 1, max: remote }
|
||||
}
|
||||
} catch (e: any) {
|
||||
getLogger({ component: "noffer" })(ERROR, e.message || e)
|
||||
return { success: false, code: 1, max: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
const newNofferResponse = (content: string, event: NostrEvent): UnsignedEvent => {
|
||||
return {
|
||||
content,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
kind: 21001,
|
||||
pubkey: "",
|
||||
tags: [
|
||||
['p', event.pub],
|
||||
['e', event.id],
|
||||
],
|
||||
}
|
||||
}
|
||||
const codeToMessage = (code: number) => {
|
||||
switch (code) {
|
||||
case 1: return 'Invalid Offer'
|
||||
case 2: return 'Temporary Failure'
|
||||
case 3: return 'Expired Offer'
|
||||
case 4: return 'Unsupported Feature'
|
||||
case 5: return 'Invalid Amount'
|
||||
default: throw new Error("unknown error code" + code)
|
||||
}
|
||||
}
|
||||
|
|
@ -323,6 +323,33 @@ export default (mainHandler: Main): Types.ServerMethods => {
|
|||
},
|
||||
RespondToDebit: async ({ ctx, req }) => {
|
||||
return mainHandler.debitManager.RespondToDebit(ctx, req);
|
||||
},
|
||||
AddUserOffer: async ({ ctx, req }) => {
|
||||
const err = Types.OfferConfigValidate(req, {
|
||||
label_CustomCheck: label => label !== '',
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.offerManager.AddUserOffer(ctx, req)
|
||||
},
|
||||
DeleteUserOffer: async ({ ctx, req }) => {
|
||||
const err = Types.OfferIdValidate(req, {
|
||||
offer_id_CustomCheck: id => id !== ''
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.offerManager.DeleteUserOffer(ctx, req)
|
||||
},
|
||||
UpdateUserOffer: async ({ ctx, req }) => {
|
||||
return mainHandler.offerManager.UpdateUserOffer(ctx, req)
|
||||
},
|
||||
GetUserOffers: async ({ ctx }) => {
|
||||
return mainHandler.offerManager.GetUserOffers(ctx)
|
||||
},
|
||||
GetUserOffer: async ({ ctx, req }) => {
|
||||
const err = Types.OfferIdValidate(req, {
|
||||
offer_id_CustomCheck: id => id !== ''
|
||||
})
|
||||
if (err != null) throw new Error(err.message)
|
||||
return mainHandler.offerManager.GetUserOffer(ctx, req)
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/services/storage/entity/UserOffer.ts
Normal file
37
src/services/storage/entity/UserOffer.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, CreateDateColumn, UpdateDateColumn } from "typeorm"
|
||||
import { User } from "./User.js"
|
||||
|
||||
@Entity()
|
||||
export class UserOffer {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
serial_id: number
|
||||
|
||||
@Column()
|
||||
app_user_id: string
|
||||
|
||||
@Column({ unique: true, nullable: false })
|
||||
offer_id: string
|
||||
|
||||
@Column()
|
||||
label: string
|
||||
|
||||
@Column({ default: 0 })
|
||||
price_sats: number
|
||||
|
||||
@Column({ default: "" })
|
||||
callback_url: string
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: 'simple-json',
|
||||
default: null
|
||||
})
|
||||
expected_data: Record<string, string> | null
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date
|
||||
}
|
||||
|
|
@ -58,6 +58,15 @@ export class UserReceivingInvoice {
|
|||
})
|
||||
zap_info?: ZapInfo
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
type: 'simple-json'
|
||||
})
|
||||
payer_data?: Record<string, string>
|
||||
|
||||
@Column({ default: "" })
|
||||
offer_id?: string
|
||||
|
||||
@Column({
|
||||
nullable: true,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import EventsLogManager from "./eventsLog.js";
|
|||
import { LiquidityStorage } from "./liquidityStorage.js";
|
||||
import { StateBundler } from "./stateBundler.js";
|
||||
import DebitStorage from "./debitStorage.js"
|
||||
import OfferStorage from "./offerStorage.js"
|
||||
export type StorageSettings = {
|
||||
dbSettings: DbSettings
|
||||
eventLogPath: string
|
||||
|
|
@ -30,6 +31,7 @@ export default class {
|
|||
metricsStorage: MetricsStorage
|
||||
liquidityStorage: LiquidityStorage
|
||||
debitStorage: DebitStorage
|
||||
offerStorage: OfferStorage
|
||||
eventsLog: EventsLogManager
|
||||
stateBundler: StateBundler
|
||||
constructor(settings: StorageSettings) {
|
||||
|
|
@ -47,6 +49,7 @@ export default class {
|
|||
this.metricsStorage = new MetricsStorage(this.settings)
|
||||
this.liquidityStorage = new LiquidityStorage(this.DB, this.txQueue)
|
||||
this.debitStorage = new DebitStorage(this.DB, this.txQueue)
|
||||
this.offerStorage = new OfferStorage(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 };
|
||||
|
|
|
|||
41
src/services/storage/offerStorage.ts
Normal file
41
src/services/storage/offerStorage.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { DataSource, EntityManager } from "typeorm"
|
||||
import crypto from 'crypto';
|
||||
import UserStorage from './userStorage.js';
|
||||
import TransactionsQueue from "./transactionsQueue.js";
|
||||
import { DebitAccess, DebitAccessRules } from "./entity/DebitAccess.js";
|
||||
import { UserOffer } from "./entity/UserOffer.js";
|
||||
export default class {
|
||||
|
||||
|
||||
DB: DataSource | EntityManager
|
||||
txQueue: TransactionsQueue
|
||||
constructor(DB: DataSource | EntityManager, txQueue: TransactionsQueue) {
|
||||
this.DB = DB
|
||||
this.txQueue = txQueue
|
||||
}
|
||||
async AddUserOffer(appUserId: string, req: Partial<UserOffer>): Promise<UserOffer> {
|
||||
const newUserOffer = this.DB.getRepository(UserOffer).create({
|
||||
...req,
|
||||
app_user_id: appUserId,
|
||||
offer_id: crypto.randomBytes(34).toString('hex')
|
||||
})
|
||||
return this.txQueue.PushToQueue<UserOffer>({ exec: async db => db.getRepository(UserOffer).save(newUserOffer), dbTx: false, description: `add offer for ${appUserId}: ${req.label} ` })
|
||||
}
|
||||
|
||||
async DeleteUserOffer(appUserId: string, offerId: string, entityManager = this.DB) {
|
||||
await entityManager.getRepository(UserOffer).delete({ app_user_id: appUserId, offer_id: offerId })
|
||||
}
|
||||
async UpdateUserOffer(app_user_id: string, req: Partial<UserOffer>) {
|
||||
return this.DB.getRepository(UserOffer).update({ app_user_id, offer_id: req.offer_id }, req)
|
||||
}
|
||||
|
||||
async GetUserOffers(app_user_id: string): Promise<UserOffer[]> {
|
||||
return this.DB.getRepository(UserOffer).find({ where: { app_user_id } })
|
||||
}
|
||||
async GetUserOffer(app_user_id: string, offer_id: string): Promise<UserOffer | null> {
|
||||
return this.DB.getRepository(UserOffer).findOne({ where: { app_user_id, offer_id } })
|
||||
}
|
||||
async GetOffer(offer_id: string): Promise<UserOffer | null> {
|
||||
return this.DB.getRepository(UserOffer).findOne({ where: { offer_id } })
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import { UserToUserPayment } from './entity/UserToUserPayment.js';
|
|||
import { Application } from './entity/Application.js';
|
||||
import TransactionsQueue from "./transactionsQueue.js";
|
||||
import { LoggedEvent } from './eventsLog.js';
|
||||
export type InboundOptionals = { product?: Product, callbackUrl?: string, expiry: number, expectedPayer?: User, linkedApplication?: Application, zapInfo?: ZapInfo }
|
||||
export type InboundOptionals = { product?: Product, callbackUrl?: string, expiry: number, expectedPayer?: User, linkedApplication?: Application, zapInfo?: ZapInfo, offerId?: string, payerData?: Record<string, string> }
|
||||
export const defaultInvoiceExpiry = 60 * 60
|
||||
export default class {
|
||||
DB: DataSource | EntityManager
|
||||
|
|
@ -102,7 +102,9 @@ export default class {
|
|||
payer: options.expectedPayer,
|
||||
linkedApplication: options.linkedApplication,
|
||||
zap_info: options.zapInfo,
|
||||
liquidityProvider: providerDestination
|
||||
liquidityProvider: providerDestination,
|
||||
offer_id: options.offerId,
|
||||
payer_data: options.payerData,
|
||||
})
|
||||
return this.txQueue.PushToQueue<UserReceivingInvoice>({ exec: async db => db.getRepository(UserReceivingInvoice).save(newUserInvoice), dbTx: false, description: `add invoice for ${user.user_id} linked to ${options.linkedApplication?.app_id}: ${invoice} ` })
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue