From 5474e5217d134b144676afd302ffbd460b7bfa9f Mon Sep 17 00:00:00 2001 From: boufni95 Date: Fri, 20 Sep 2024 17:26:28 +0000 Subject: [PATCH] ban and reset --- package.json | 3 +- proto/autogenerated/client.md | 36 ++++++---- proto/autogenerated/go/http_client.go | 75 ++++++++++++------- proto/autogenerated/go/types.go | 6 +- proto/autogenerated/ts/express_server.ts | 88 ++++++++++++++++------- proto/autogenerated/ts/http_client.ts | 33 ++++++--- proto/autogenerated/ts/nostr_client.ts | 16 ++++- proto/autogenerated/ts/nostr_transport.ts | 46 +++++++++--- proto/autogenerated/ts/types.ts | 52 +++++++------- proto/service/methods.proto | 18 +++-- proto/service/structs.proto | 8 +-- src/services/main/debitManager.ts | 7 +- src/services/serverMethods/index.ts | 13 +++- src/services/storage/debitStorage.ts | 17 ++++- 14 files changed, 285 insertions(+), 133 deletions(-) diff --git a/package.json b/package.json index d2123f6f..eb2a8e80 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "test": "npm run clean && tsc && node build/src/tests/testRunner.js", "start": "npm run clean && tsc && node build/src/index.js", "start:ci": "git reset --hard && git pull && npm run start", + "gen": "cd proto && rimraf autogenerated && export PATH=$PATH:~/Lightning.Pub/proto && protoc -I ./service --pub_out=. service/*", "build_autogenerated": "cd proto && rimraf autogenerated && protoc -I ./service --pub_out=. service/*", "build_lnd_client_1": "cd proto && protoc -I ./others --plugin=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_out=./lnd --ts_proto_opt=esModuleInterop=true others/* ", "build_lnd_client": "cd proto && rimraf lnd/* && npx protoc --ts_out ./lnd --ts_opt long_type_string --proto_path others others/* ", @@ -79,4 +80,4 @@ "ts-node": "10.7.0", "typescript": "5.5.4" } -} +} \ No newline at end of file diff --git a/proto/autogenerated/client.md b/proto/autogenerated/client.md index f039792a..8c592393 100644 --- a/proto/autogenerated/client.md +++ b/proto/autogenerated/client.md @@ -33,6 +33,11 @@ The nostr server will send back a message response, and inside the body there wi - input: [DebitAuthorizationRequest](#DebitAuthorizationRequest) - output: [DebitAuthorization](#DebitAuthorization) +- BanDebit + - auth type: __User__ + - input: [DebitOperation](#DebitOperation) + - This methods has an __empty__ __response__ body + - BanUser - auth type: __Admin__ - input: [BanUserRequest](#BanUserRequest) @@ -185,9 +190,9 @@ The nostr server will send back a message response, and inside the body there wi - input: [PayInvoiceRequest](#PayInvoiceRequest) - output: [PayInvoiceResponse](#PayInvoiceResponse) -- RemoveAuthorizedDebit +- ResetDebit - auth type: __User__ - - input: [RemoveAuthorizedDebitRequest](#RemoveAuthorizedDebitRequest) + - input: [DebitOperation](#DebitOperation) - This methods has an __empty__ __response__ body - UseInviteLink @@ -283,6 +288,13 @@ The nostr server will send back a message response, and inside the body there wi - input: [DebitAuthorizationRequest](#DebitAuthorizationRequest) - output: [DebitAuthorization](#DebitAuthorization) +- BanDebit + - auth type: __User__ + - http method: __post__ + - http route: __/api/user/debit/ban__ + - input: [DebitOperation](#DebitOperation) + - This methods has an __empty__ __response__ body + - BanUser - auth type: __Admin__ - http method: __post__ @@ -586,13 +598,6 @@ The nostr server will send back a message response, and inside the body there wi - input: [PayInvoiceRequest](#PayInvoiceRequest) - output: [PayInvoiceResponse](#PayInvoiceResponse) -- RemoveAuthorizedDebit - - auth type: __User__ - - http method: __post__ - - http route: __/api/user/debit/remove__ - - input: [RemoveAuthorizedDebitRequest](#RemoveAuthorizedDebitRequest) - - This methods has an __empty__ __response__ body - - RequestNPubLinkingToken - auth type: __App__ - http method: __post__ @@ -600,6 +605,13 @@ The nostr server will send back a message response, and inside the body there wi - input: [RequestNPubLinkingTokenRequest](#RequestNPubLinkingTokenRequest) - output: [RequestNPubLinkingTokenResponse](#RequestNPubLinkingTokenResponse) +- ResetDebit + - auth type: __User__ + - http method: __post__ + - http route: __/api/user/debit/reset__ + - input: [DebitOperation](#DebitOperation) + - This methods has an __empty__ __response__ body + - ResetNPubLinkingToken - auth type: __App__ - http method: __post__ @@ -766,6 +778,9 @@ The nostr server will send back a message response, and inside the body there wi ### DebitExpirationRule - __expires_at_unix__: _number_ +### DebitOperation + - __npub__: _string_ + ### DebitRule - __rule__: _[DebitRule_rule](#DebitRule_rule)_ @@ -979,9 +994,6 @@ The nostr server will send back a message response, and inside the body there wi ### RelaysMigration - __relays__: ARRAY of: _string_ -### RemoveAuthorizedDebitRequest - - __npub__: _string_ - ### RequestNPubLinkingTokenRequest - __user_identifier__: _string_ diff --git a/proto/autogenerated/go/http_client.go b/proto/autogenerated/go/http_client.go index 99e511af..e10f774d 100644 --- a/proto/autogenerated/go/http_client.go +++ b/proto/autogenerated/go/http_client.go @@ -61,6 +61,7 @@ type Client struct { AddProduct func(req AddProductRequest) (*Product, error) AuthApp func(req AuthAppRequest) (*AuthApp, error) AuthorizeDebit func(req DebitAuthorizationRequest) (*DebitAuthorization, error) + BanDebit func(req DebitOperation) error BanUser func(req BanUserRequest) (*BanUserResponse, error) // batching method: BatchUser not implemented CreateOneTimeInviteLink func(req CreateOneTimeInviteLinkRequest) (*CreateOneTimeInviteLinkResponse, error) @@ -102,8 +103,8 @@ type Client struct { PayAddress func(req PayAddressRequest) (*PayAddressResponse, error) PayAppUserInvoice func(req PayAppUserInvoiceRequest) (*PayInvoiceResponse, error) PayInvoice func(req PayInvoiceRequest) (*PayInvoiceResponse, error) - RemoveAuthorizedDebit func(req RemoveAuthorizedDebitRequest) error RequestNPubLinkingToken func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error) + ResetDebit func(req DebitOperation) error ResetNPubLinkingToken func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error) SendAppUserToAppPayment func(req SendAppUserToAppPaymentRequest) error SendAppUserToAppUserPayment func(req SendAppUserToAppUserPaymentRequest) error @@ -319,6 +320,30 @@ func NewClient(params ClientParams) *Client { } return &res, nil }, + BanDebit: func(req DebitOperation) error { + auth, err := params.RetrieveUserAuth() + if err != nil { + return err + } + finalRoute := "/api/user/debit/ban" + 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 + }, BanUser: func(req BanUserRequest) (*BanUserResponse, error) { auth, err := params.RetrieveAdminAuth() if err != nil { @@ -1300,30 +1325,6 @@ func NewClient(params ClientParams) *Client { } return &res, nil }, - RemoveAuthorizedDebit: func(req RemoveAuthorizedDebitRequest) error { - auth, err := params.RetrieveUserAuth() - if err != nil { - return err - } - finalRoute := "/api/user/debit/remove" - 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 - }, RequestNPubLinkingToken: func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error) { auth, err := params.RetrieveAppAuth() if err != nil { @@ -1353,6 +1354,30 @@ func NewClient(params ClientParams) *Client { } return &res, nil }, + ResetDebit: func(req DebitOperation) error { + auth, err := params.RetrieveUserAuth() + if err != nil { + return err + } + finalRoute := "/api/user/debit/reset" + 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 + }, ResetNPubLinkingToken: func(req RequestNPubLinkingTokenRequest) (*RequestNPubLinkingTokenResponse, error) { auth, err := params.RetrieveAppAuth() if err != nil { diff --git a/proto/autogenerated/go/types.go b/proto/autogenerated/go/types.go index 9a4b6765..39825bf5 100644 --- a/proto/autogenerated/go/types.go +++ b/proto/autogenerated/go/types.go @@ -180,6 +180,9 @@ type DebitAuthorizations struct { type DebitExpirationRule struct { Expires_at_unix int64 `json:"expires_at_unix"` } +type DebitOperation struct { + Npub string `json:"npub"` +} type DebitRule struct { Rule *DebitRule_rule `json:"rule"` } @@ -393,9 +396,6 @@ type Product struct { type RelaysMigration struct { Relays []string `json:"relays"` } -type RemoveAuthorizedDebitRequest struct { - Npub string `json:"npub"` -} type RequestNPubLinkingTokenRequest struct { User_identifier string `json:"user_identifier"` } diff --git a/proto/autogenerated/ts/express_server.ts b/proto/autogenerated/ts/express_server.ts index 2993271d..d4186e7c 100644 --- a/proto/autogenerated/ts/express_server.ts +++ b/proto/autogenerated/ts/express_server.ts @@ -188,6 +188,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.BanDebit) throw new Error('method: BanDebit is not implemented') + app.post('/api/user/debit/ban', async (req, res) => { + const info: Types.RequestInfo = { rpcName: 'BanDebit', batch: false, nostr: false, batchSize: 0} + const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n } + let authCtx: Types.AuthContext = {} + try { + if (!methods.BanDebit) throw new Error('method: BanDebit is not implemented') + const authContext = await opts.UserAuthGuard(req.headers['authorization']) + authCtx = authContext + stats.guard = process.hrtime.bigint() + const request = req.body + const error = Types.DebitOperationValidate(request) + stats.validate = process.hrtime.bigint() + if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback) + const query = req.query + const params = req.params + await methods.BanDebit({rpcName:'BanDebit', ctx:authContext , req: request}) + stats.handle = process.hrtime.bigint() + res.json({status: 'OK'}) + opts.metricsCallback([{ ...info, ...stats, ...authContext }]) + } catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } + }) if (!opts.allowNotImplementedMethods && !methods.BanUser) throw new Error('method: BanUser is not implemented') app.post('/api/admin/user/ban', async (req, res) => { const info: Types.RequestInfo = { rpcName: 'BanUser', batch: false, nostr: false, batchSize: 0} @@ -255,6 +277,18 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => { callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) } break + case 'BanDebit': + if (!methods.BanDebit) { + throw new Error('method BanDebit not found' ) + } else { + const error = Types.DebitOperationValidate(operation.req) + opStats.validate = process.hrtime.bigint() + if (error !== null) throw error + await methods.BanDebit({...operation, ctx}); responses.push({ status: 'OK' }) + opStats.handle = process.hrtime.bigint() + callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) + } + break case 'DecodeInvoice': if (!methods.DecodeInvoice) { throw new Error('method DecodeInvoice not found' ) @@ -423,14 +457,14 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => { callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) } break - case 'RemoveAuthorizedDebit': - if (!methods.RemoveAuthorizedDebit) { - throw new Error('method RemoveAuthorizedDebit not found' ) + case 'ResetDebit': + if (!methods.ResetDebit) { + throw new Error('method ResetDebit not found' ) } else { - const error = Types.RemoveAuthorizedDebitRequestValidate(operation.req) + const error = Types.DebitOperationValidate(operation.req) opStats.validate = process.hrtime.bigint() if (error !== null) throw error - await methods.RemoveAuthorizedDebit({...operation, ctx}); responses.push({ status: 'OK' }) + await methods.ResetDebit({...operation, ctx}); responses.push({ status: 'OK' }) opStats.handle = process.hrtime.bigint() callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) } @@ -1177,28 +1211,6 @@ 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.RemoveAuthorizedDebit) throw new Error('method: RemoveAuthorizedDebit is not implemented') - app.post('/api/user/debit/remove', async (req, res) => { - const info: Types.RequestInfo = { rpcName: 'RemoveAuthorizedDebit', 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.RemoveAuthorizedDebit) throw new Error('method: RemoveAuthorizedDebit 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.RemoveAuthorizedDebitRequestValidate(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.RemoveAuthorizedDebit({rpcName:'RemoveAuthorizedDebit', 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.RequestNPubLinkingToken) throw new Error('method: RequestNPubLinkingToken is not implemented') app.post('/api/app/user/npub/token', async (req, res) => { const info: Types.RequestInfo = { rpcName: 'RequestNPubLinkingToken', batch: false, nostr: false, batchSize: 0} @@ -1221,6 +1233,28 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => { opts.metricsCallback([{ ...info, ...stats, ...authContext }]) } catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } }) + if (!opts.allowNotImplementedMethods && !methods.ResetDebit) throw new Error('method: ResetDebit is not implemented') + app.post('/api/user/debit/reset', async (req, res) => { + const info: Types.RequestInfo = { rpcName: 'ResetDebit', batch: false, nostr: false, batchSize: 0} + const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n } + let authCtx: Types.AuthContext = {} + try { + if (!methods.ResetDebit) throw new Error('method: ResetDebit is not implemented') + const authContext = await opts.UserAuthGuard(req.headers['authorization']) + authCtx = authContext + stats.guard = process.hrtime.bigint() + const request = req.body + const error = Types.DebitOperationValidate(request) + stats.validate = process.hrtime.bigint() + if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback) + const query = req.query + const params = req.params + await methods.ResetDebit({rpcName:'ResetDebit', ctx:authContext , req: request}) + stats.handle = process.hrtime.bigint() + res.json({status: 'OK'}) + opts.metricsCallback([{ ...info, ...stats, ...authContext }]) + } catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } + }) if (!opts.allowNotImplementedMethods && !methods.ResetNPubLinkingToken) throw new Error('method: ResetNPubLinkingToken is not implemented') app.post('/api/app/user/npub/token/reset', async (req, res) => { const info: Types.RequestInfo = { rpcName: 'ResetNPubLinkingToken', batch: false, nostr: false, batchSize: 0} diff --git a/proto/autogenerated/ts/http_client.ts b/proto/autogenerated/ts/http_client.ts index 96f52149..982aa76d 100644 --- a/proto/autogenerated/ts/http_client.ts +++ b/proto/autogenerated/ts/http_client.ts @@ -115,6 +115,17 @@ export default (params: ClientParams) => ({ } return { status: 'ERROR', reason: 'invalid response' } }, + BanDebit: async (request: Types.DebitOperation): Promise => { + const auth = await params.retrieveUserAuth() + if (auth === null) throw new Error('retrieveUserAuth() returned null') + let finalRoute = '/api/user/debit/ban' + const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } }) + if (data.status === 'ERROR' && typeof data.reason === 'string') return data + if (data.status === 'OK') { + return data + } + return { status: 'ERROR', reason: 'invalid response' } + }, BanUser: async (request: Types.BanUserRequest): Promise => { const auth = await params.retrieveAdminAuth() if (auth === null) throw new Error('retrieveAdminAuth() returned null') @@ -630,17 +641,6 @@ export default (params: ClientParams) => ({ } return { status: 'ERROR', reason: 'invalid response' } }, - RemoveAuthorizedDebit: async (request: Types.RemoveAuthorizedDebitRequest): Promise => { - const auth = await params.retrieveUserAuth() - if (auth === null) throw new Error('retrieveUserAuth() returned null') - let finalRoute = '/api/user/debit/remove' - 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' } - }, RequestNPubLinkingToken: async (request: Types.RequestNPubLinkingTokenRequest): Promise => { const auth = await params.retrieveAppAuth() if (auth === null) throw new Error('retrieveAppAuth() returned null') @@ -655,6 +655,17 @@ export default (params: ClientParams) => ({ } return { status: 'ERROR', reason: 'invalid response' } }, + ResetDebit: async (request: Types.DebitOperation): Promise => { + const auth = await params.retrieveUserAuth() + if (auth === null) throw new Error('retrieveUserAuth() returned null') + let finalRoute = '/api/user/debit/reset' + const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } }) + if (data.status === 'ERROR' && typeof data.reason === 'string') return data + if (data.status === 'OK') { + return data + } + return { status: 'ERROR', reason: 'invalid response' } + }, ResetNPubLinkingToken: async (request: Types.RequestNPubLinkingTokenRequest): Promise => { const auth = await params.retrieveAppAuth() if (auth === null) throw new Error('retrieveAppAuth() returned null') diff --git a/proto/autogenerated/ts/nostr_client.ts b/proto/autogenerated/ts/nostr_client.ts index 1f2878b8..a160d576 100644 --- a/proto/autogenerated/ts/nostr_client.ts +++ b/proto/autogenerated/ts/nostr_client.ts @@ -72,6 +72,18 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ } return { status: 'ERROR', reason: 'invalid response' } }, + BanDebit: async (request: Types.DebitOperation): Promise => { + const auth = await params.retrieveNostrUserAuth() + if (auth === null) throw new Error('retrieveNostrUserAuth() returned null') + const nostrRequest: NostrRequest = {} + nostrRequest.body = request + const data = await send(params.pubDestination, {rpcName:'BanDebit',authIdentifier:auth, ...nostrRequest }) + if (data.status === 'ERROR' && typeof data.reason === 'string') return data + if (data.status === 'OK') { + return data + } + return { status: 'ERROR', reason: 'invalid response' } + }, BanUser: async (request: Types.BanUserRequest): Promise => { const auth = await params.retrieveNostrAdminAuth() if (auth === null) throw new Error('retrieveNostrAdminAuth() returned null') @@ -504,12 +516,12 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ } return { status: 'ERROR', reason: 'invalid response' } }, - RemoveAuthorizedDebit: async (request: Types.RemoveAuthorizedDebitRequest): Promise => { + ResetDebit: async (request: Types.DebitOperation): Promise => { 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:'RemoveAuthorizedDebit',authIdentifier:auth, ...nostrRequest }) + const data = await send(params.pubDestination, {rpcName:'ResetDebit',authIdentifier:auth, ...nostrRequest }) if (data.status === 'ERROR' && typeof data.reason === 'string') return data if (data.status === 'OK') { return data diff --git a/proto/autogenerated/ts/nostr_transport.ts b/proto/autogenerated/ts/nostr_transport.ts index 816b8ad8..1ff5846e 100644 --- a/proto/autogenerated/ts/nostr_transport.ts +++ b/proto/autogenerated/ts/nostr_transport.ts @@ -96,6 +96,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 'BanDebit': + try { + if (!methods.BanDebit) throw new Error('method: BanDebit is not implemented') + const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier) + stats.guard = process.hrtime.bigint() + authCtx = authContext + const request = req.body + const error = Types.DebitOperationValidate(request) + stats.validate = process.hrtime.bigint() + if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback) + await methods.BanDebit({rpcName:'BanDebit', ctx:authContext , req: request}) + stats.handle = process.hrtime.bigint() + res({status: 'OK'}) + opts.metricsCallback([{ ...info, ...stats, ...authContext }]) + }catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } + break case 'BanUser': try { if (!methods.BanUser) throw new Error('method: BanUser is not implemented') @@ -155,6 +171,18 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => { callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) } break + case 'BanDebit': + if (!methods.BanDebit) { + throw new Error('method not defined: BanDebit') + } else { + const error = Types.DebitOperationValidate(operation.req) + opStats.validate = process.hrtime.bigint() + if (error !== null) throw error + await methods.BanDebit({...operation, ctx}); responses.push({ status: 'OK' }) + opStats.handle = process.hrtime.bigint() + callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) + } + break case 'DecodeInvoice': if (!methods.DecodeInvoice) { throw new Error('method not defined: DecodeInvoice') @@ -323,14 +351,14 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => { callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) } break - case 'RemoveAuthorizedDebit': - if (!methods.RemoveAuthorizedDebit) { - throw new Error('method not defined: RemoveAuthorizedDebit') + case 'ResetDebit': + if (!methods.ResetDebit) { + throw new Error('method not defined: ResetDebit') } else { - const error = Types.RemoveAuthorizedDebitRequestValidate(operation.req) + const error = Types.DebitOperationValidate(operation.req) opStats.validate = process.hrtime.bigint() if (error !== null) throw error - await methods.RemoveAuthorizedDebit({...operation, ctx}); responses.push({ status: 'OK' }) + await methods.ResetDebit({...operation, ctx}); responses.push({ status: 'OK' }) opStats.handle = process.hrtime.bigint() callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) } @@ -764,17 +792,17 @@ 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 'RemoveAuthorizedDebit': + case 'ResetDebit': try { - if (!methods.RemoveAuthorizedDebit) throw new Error('method: RemoveAuthorizedDebit is not implemented') + if (!methods.ResetDebit) throw new Error('method: ResetDebit is not implemented') const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier) stats.guard = process.hrtime.bigint() authCtx = authContext const request = req.body - const error = Types.RemoveAuthorizedDebitRequestValidate(request) + const error = Types.DebitOperationValidate(request) stats.validate = process.hrtime.bigint() if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback) - await methods.RemoveAuthorizedDebit({rpcName:'RemoveAuthorizedDebit', ctx:authContext , req: request}) + await methods.ResetDebit({rpcName:'ResetDebit', ctx:authContext , req: request}) stats.handle = process.hrtime.bigint() res({status: 'OK'}) opts.metricsCallback([{ ...info, ...stats, ...authContext }]) diff --git a/proto/autogenerated/ts/types.ts b/proto/autogenerated/ts/types.ts index 625f93bb..e4bed0b5 100644 --- a/proto/autogenerated/ts/types.ts +++ b/proto/autogenerated/ts/types.ts @@ -34,8 +34,8 @@ export type UserContext = { app_user_id: string user_id: string } -export type UserMethodInputs = AddProduct_Input | AuthorizeDebit_Input | DecodeInvoice_Input | EnrollAdminToken_Input | GetDebitAuthorizations_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetPaymentState_Input | GetUserInfo_Input | GetUserOperations_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | OpenChannel_Input | PayAddress_Input | PayInvoice_Input | RemoveAuthorizedDebit_Input | UserHealth_Input -export type UserMethodOutputs = AddProduct_Output | AuthorizeDebit_Output | DecodeInvoice_Output | EnrollAdminToken_Output | GetDebitAuthorizations_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetPaymentState_Output | GetUserInfo_Output | GetUserOperations_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | OpenChannel_Output | PayAddress_Output | PayInvoice_Output | RemoveAuthorizedDebit_Output | UserHealth_Output +export type UserMethodInputs = AddProduct_Input | AuthorizeDebit_Input | BanDebit_Input | DecodeInvoice_Input | EnrollAdminToken_Input | GetDebitAuthorizations_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetPaymentState_Input | GetUserInfo_Input | GetUserOperations_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | OpenChannel_Input | PayAddress_Input | PayInvoice_Input | ResetDebit_Input | UserHealth_Input +export type UserMethodOutputs = AddProduct_Output | AuthorizeDebit_Output | BanDebit_Output | DecodeInvoice_Output | EnrollAdminToken_Output | GetDebitAuthorizations_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetPaymentState_Output | GetUserInfo_Output | GetUserOperations_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | OpenChannel_Output | PayAddress_Output | PayInvoice_Output | ResetDebit_Output | UserHealth_Output export type AuthContext = AdminContext | AppContext | GuestContext | GuestWithPubContext | MetricsContext | UserContext export type AddApp_Input = {rpcName:'AddApp', req: AddAppRequest} @@ -59,6 +59,9 @@ export type AuthApp_Output = ResultError | ({ status: 'OK' } & AuthApp) export type AuthorizeDebit_Input = {rpcName:'AuthorizeDebit', req: DebitAuthorizationRequest} export type AuthorizeDebit_Output = ResultError | ({ status: 'OK' } & DebitAuthorization) +export type BanDebit_Input = {rpcName:'BanDebit', req: DebitOperation} +export type BanDebit_Output = ResultError | { status: 'OK' } + export type BanUser_Input = {rpcName:'BanUser', req: BanUserRequest} export type BanUser_Output = ResultError | ({ status: 'OK' } & BanUserResponse) @@ -204,12 +207,12 @@ export type PayAppUserInvoice_Output = ResultError | ({ status: 'OK' } & PayInvo export type PayInvoice_Input = {rpcName:'PayInvoice', req: PayInvoiceRequest} export type PayInvoice_Output = ResultError | ({ status: 'OK' } & PayInvoiceResponse) -export type RemoveAuthorizedDebit_Input = {rpcName:'RemoveAuthorizedDebit', req: RemoveAuthorizedDebitRequest} -export type RemoveAuthorizedDebit_Output = ResultError | { status: 'OK' } - export type RequestNPubLinkingToken_Input = {rpcName:'RequestNPubLinkingToken', req: RequestNPubLinkingTokenRequest} export type RequestNPubLinkingToken_Output = ResultError | ({ status: 'OK' } & RequestNPubLinkingTokenResponse) +export type ResetDebit_Input = {rpcName:'ResetDebit', req: DebitOperation} +export type ResetDebit_Output = ResultError | { status: 'OK' } + export type ResetNPubLinkingToken_Input = {rpcName:'ResetNPubLinkingToken', req: RequestNPubLinkingTokenRequest} export type ResetNPubLinkingToken_Output = ResultError | ({ status: 'OK' } & RequestNPubLinkingTokenResponse) @@ -242,6 +245,7 @@ export type ServerMethods = { AddProduct?: (req: AddProduct_Input & {ctx: UserContext }) => Promise AuthApp?: (req: AuthApp_Input & {ctx: AdminContext }) => Promise AuthorizeDebit?: (req: AuthorizeDebit_Input & {ctx: UserContext }) => Promise + BanDebit?: (req: BanDebit_Input & {ctx: UserContext }) => Promise BanUser?: (req: BanUser_Input & {ctx: AdminContext }) => Promise CreateOneTimeInviteLink?: (req: CreateOneTimeInviteLink_Input & {ctx: AdminContext }) => Promise DecodeInvoice?: (req: DecodeInvoice_Input & {ctx: UserContext }) => Promise @@ -282,8 +286,8 @@ export type ServerMethods = { PayAddress?: (req: PayAddress_Input & {ctx: UserContext }) => Promise PayAppUserInvoice?: (req: PayAppUserInvoice_Input & {ctx: AppContext }) => Promise PayInvoice?: (req: PayInvoice_Input & {ctx: UserContext }) => Promise - RemoveAuthorizedDebit?: (req: RemoveAuthorizedDebit_Input & {ctx: UserContext }) => Promise RequestNPubLinkingToken?: (req: RequestNPubLinkingToken_Input & {ctx: AppContext }) => Promise + ResetDebit?: (req: ResetDebit_Input & {ctx: UserContext }) => Promise ResetNPubLinkingToken?: (req: ResetNPubLinkingToken_Input & {ctx: AppContext }) => Promise SendAppUserToAppPayment?: (req: SendAppUserToAppPayment_Input & {ctx: AppContext }) => Promise SendAppUserToAppUserPayment?: (req: SendAppUserToAppUserPayment_Input & {ctx: AppContext }) => Promise @@ -962,6 +966,24 @@ export const DebitExpirationRuleValidate = (o?: DebitExpirationRule, opts: Debit return null } +export type DebitOperation = { + npub: string +} +export const DebitOperationOptionalFields: [] = [] +export type DebitOperationOptions = OptionsBaseMessage & { + checkOptionalsAreSet?: [] + npub_CustomCheck?: (v: string) => boolean +} +export const DebitOperationValidate = (o?: DebitOperation, opts: DebitOperationOptions = {}, path: string = 'DebitOperation::root.'): Error | null => { + if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message') + if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null') + + if (typeof o.npub !== 'string') return new Error(`${path}.npub: is not a string`) + if (opts.npub_CustomCheck && !opts.npub_CustomCheck(o.npub)) return new Error(`${path}.npub: custom check failed`) + + return null +} + export type DebitRule = { rule: DebitRule_rule } @@ -2231,24 +2253,6 @@ export const RelaysMigrationValidate = (o?: RelaysMigration, opts: RelaysMigrati return null } -export type RemoveAuthorizedDebitRequest = { - npub: string -} -export const RemoveAuthorizedDebitRequestOptionalFields: [] = [] -export type RemoveAuthorizedDebitRequestOptions = OptionsBaseMessage & { - checkOptionalsAreSet?: [] - npub_CustomCheck?: (v: string) => boolean -} -export const RemoveAuthorizedDebitRequestValidate = (o?: RemoveAuthorizedDebitRequest, opts: RemoveAuthorizedDebitRequestOptions = {}, path: string = 'RemoveAuthorizedDebitRequest::root.'): Error | null => { - if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message') - if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null') - - if (typeof o.npub !== 'string') return new Error(`${path}.npub: is not a string`) - if (opts.npub_CustomCheck && !opts.npub_CustomCheck(o.npub)) return new Error(`${path}.npub: custom check failed`) - - return null -} - export type RequestNPubLinkingTokenRequest = { user_identifier: string } diff --git a/proto/service/methods.proto b/proto/service/methods.proto index fde9edeb..7918b97d 100644 --- a/proto/service/methods.proto +++ b/proto/service/methods.proto @@ -441,18 +441,24 @@ service LightningPub { option (http_route) = "/api/user/debit/get"; option (nostr) = true; } - rpc RemoveAuthorizedDebit(structs.RemoveAuthorizedDebitRequest) returns (structs.Empty){ - option (auth_type) = "User"; - option (http_method) = "post"; - option (http_route) = "/api/user/debit/remove"; - option (nostr) = true; - } rpc AuthorizeDebit(structs.DebitAuthorizationRequest) returns (structs.DebitAuthorization){ option (auth_type) = "User"; option (http_method) = "post"; option (http_route) = "/api/user/debit/authorize"; option (nostr) = true; } + rpc BanDebit(structs.DebitOperation) returns (structs.Empty){ + option (auth_type) = "User"; + option (http_method) = "post"; + option (http_route) = "/api/user/debit/ban"; + option (nostr) = true; + } + rpc ResetDebit(structs.DebitOperation) returns (structs.Empty){ + option (auth_type) = "User"; + option (http_method) = "post"; + option (http_route) = "/api/user/debit/reset"; + option (nostr) = true; + } rpc GetLiveDebitRequests(structs.Empty) returns (stream structs.LiveDebitRequest){ option (auth_type) = "User"; option (http_method) = "post"; diff --git a/proto/service/structs.proto b/proto/service/structs.proto index f560c86f..84bc746c 100644 --- a/proto/service/structs.proto +++ b/proto/service/structs.proto @@ -479,7 +479,9 @@ message GetInviteTokenStateResponse { bool used = 1; } - +message DebitOperation { + string npub = 1; +} message DebitAuthorizationRequest { string authorize_npub = 1; @@ -498,10 +500,6 @@ message DebitAuthorizations { repeated DebitAuthorization debits = 1; } -message RemoveAuthorizedDebitRequest { - string npub = 1; -} - message DebitExpirationRule { int64 expires_at_unix = 1; } diff --git a/src/services/main/debitManager.ts b/src/services/main/debitManager.ts index e36bdbbd..4377d44f 100644 --- a/src/services/main/debitManager.ts +++ b/src/services/main/debitManager.ts @@ -69,7 +69,10 @@ export class DebitManager { return { debits } } - RemoveAuthorizedDebit = async (ctx: Types.UserContext, req: Types.RemoveAuthorizedDebitRequest): Promise => { + BanDebit = async (ctx: Types.UserContext, req: Types.DebitOperation): Promise => { + await this.storage.debitStorage.DenyDebitAccess(ctx.app_user_id, req.npub) + } + ResetDebit = async (ctx: Types.UserContext, req: Types.DebitOperation): Promise => { await this.storage.debitStorage.RemoveDebitAccess(ctx.app_user_id, req.npub) } @@ -86,7 +89,7 @@ export class DebitManager { const { amount_sats, pointer } = pointerdata if (!pointer) { // TODO: debit from app owner balance - return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[2], code: 2 } } + return { status: 'fail', debitRes: { res: 'GFY', error: nip68errs[1], code: 1 } } } const appUserId = pointer const pointerFreq = pointerdata as RecurringDebit diff --git a/src/services/serverMethods/index.ts b/src/services/serverMethods/index.ts index 7ae9e7ca..96cb035b 100644 --- a/src/services/serverMethods/index.ts +++ b/src/services/serverMethods/index.ts @@ -269,13 +269,20 @@ export default (mainHandler: Main): Types.ServerMethods => { GetDebitAuthorizations: async ({ ctx }) => { return mainHandler.debitManager.GetDebitAuthorizations(ctx) }, - RemoveAuthorizedDebit: async ({ ctx, req }) => { - const err = Types.RemoveAuthorizedDebitRequestValidate(req, { + BanDebit: async ({ ctx, req }) => { + const err = Types.DebitOperationValidate(req, { npub_CustomCheck: pub => pub !== '', }) if (err != null) throw new Error(err.message) - return mainHandler.debitManager.RemoveAuthorizedDebit(ctx, req) + return mainHandler.debitManager.BanDebit(ctx, req) }, + ResetDebit: async ({ ctx, req }) => { + const err = Types.DebitOperationValidate(req, { + npub_CustomCheck: pub => pub !== '', + }) + if (err != null) throw new Error(err.message) + return mainHandler.debitManager.ResetDebit(ctx, req) + } } } \ No newline at end of file diff --git a/src/services/storage/debitStorage.ts b/src/services/storage/debitStorage.ts index af5457b4..74554783 100644 --- a/src/services/storage/debitStorage.ts +++ b/src/services/storage/debitStorage.ts @@ -10,11 +10,11 @@ export default class { this.txQueue = txQueue } - async AddDebitAccess(appUserId: string, pubToAuthorize: string, entityManager = this.DB) { + async AddDebitAccess(appUserId: string, pubToAuthorize: string, authorize = true, entityManager = this.DB) { const entry = entityManager.getRepository(DebitAccess).create({ app_user_id: appUserId, npub: pubToAuthorize, - authorized: true, + authorized: authorize, }) return this.txQueue.PushToQueue({ exec: async db => db.getRepository(DebitAccess).save(entry), dbTx: false }) } @@ -31,11 +31,22 @@ export default class { return this.DB.getRepository(DebitAccess).increment({ app_user_id: appUserId, npub: authorizedPub }, 'total_debits', amount) } + async UpdateDebitAccess(appUserId: string, authorizedPub: string, authorized: boolean) { + return this.DB.getRepository(DebitAccess).update({ app_user_id: appUserId, npub: authorizedPub }, { authorized }) + } async UpdateDebitAccessRules(appUserId: string, authorizedPub: string, rules: DebitAccessRules) { return this.DB.getRepository(DebitAccess).update({ app_user_id: appUserId, npub: authorizedPub }, { rules }) } + async DenyDebitAccess(appUserId: string, pub: string) { + const access = await this.GetDebitAccess(appUserId, pub) + if (!access) { + await this.AddDebitAccess(appUserId, pub, false) + } + await this.UpdateDebitAccess(appUserId, pub, false) + } + async RemoveDebitAccess(appUserId: string, authorizedPub: string) { - return this.DB.getRepository(DebitAccess).update({ app_user_id: appUserId, npub: authorizedPub }, { authorized: false }) + return this.DB.getRepository(DebitAccess).delete({ app_user_id: appUserId, npub: authorizedPub }) } } \ No newline at end of file