diff --git a/proto/autogenerated/client.md b/proto/autogenerated/client.md index 320f86b3..ecd52722 100644 --- a/proto/autogenerated/client.md +++ b/proto/autogenerated/client.md @@ -58,6 +58,11 @@ The nostr server will send back a message response, and inside the body there wi - input: [DecodeInvoiceRequest](#DecodeInvoiceRequest) - output: [DecodeInvoiceResponse](#DecodeInvoiceResponse) +- EditDebit + - auth type: __User__ + - input: [DebitAuthorizationRequest](#DebitAuthorizationRequest) + - This methods has an __empty__ __response__ body + - EnrollAdminToken - auth type: __User__ - input: [EnrollAdminTokenRequest](#EnrollAdminTokenRequest) @@ -328,6 +333,13 @@ The nostr server will send back a message response, and inside the body there wi - input: [DecodeInvoiceRequest](#DecodeInvoiceRequest) - output: [DecodeInvoiceResponse](#DecodeInvoiceResponse) +- EditDebit + - auth type: __User__ + - http method: __post__ + - http route: __/api/user/debit/edit__ + - input: [DebitAuthorizationRequest](#DebitAuthorizationRequest) + - This methods has an __empty__ __response__ body + - EncryptionExchange - auth type: __Guest__ - http method: __post__ diff --git a/proto/autogenerated/go/http_client.go b/proto/autogenerated/go/http_client.go index 8500be04..1177a478 100644 --- a/proto/autogenerated/go/http_client.go +++ b/proto/autogenerated/go/http_client.go @@ -66,6 +66,7 @@ type Client struct { // batching method: BatchUser not implemented CreateOneTimeInviteLink func(req CreateOneTimeInviteLinkRequest) (*CreateOneTimeInviteLinkResponse, error) DecodeInvoice func(req DecodeInvoiceRequest) (*DecodeInvoiceResponse, error) + EditDebit func(req DebitAuthorizationRequest) error EncryptionExchange func(req EncryptionExchangeRequest) error EnrollAdminToken func(req EnrollAdminTokenRequest) error GetApp func() (*Application, error) @@ -433,6 +434,30 @@ func NewClient(params ClientParams) *Client { } return &res, nil }, + EditDebit: func(req DebitAuthorizationRequest) error { + auth, err := params.RetrieveUserAuth() + if err != nil { + return err + } + finalRoute := "/api/user/debit/edit" + 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 + }, EncryptionExchange: func(req EncryptionExchangeRequest) error { auth, err := params.RetrieveGuestAuth() if err != nil { diff --git a/proto/autogenerated/ts/express_server.ts b/proto/autogenerated/ts/express_server.ts index b8e9f2dd..050396bc 100644 --- a/proto/autogenerated/ts/express_server.ts +++ b/proto/autogenerated/ts/express_server.ts @@ -301,6 +301,18 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => { callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) } break + case 'EditDebit': + if (!methods.EditDebit) { + throw new Error('method EditDebit not found' ) + } else { + const error = Types.DebitAuthorizationRequestValidate(operation.req) + opStats.validate = process.hrtime.bigint() + if (error !== null) throw error + await methods.EditDebit({...operation, ctx}); responses.push({ status: 'OK' }) + opStats.handle = process.hrtime.bigint() + callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) + } + break case 'EnrollAdminToken': if (!methods.EnrollAdminToken) { throw new Error('method EnrollAdminToken not found' ) @@ -545,6 +557,28 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => { opts.metricsCallback([{ ...info, ...stats, ...authContext }]) } catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } }) + if (!opts.allowNotImplementedMethods && !methods.EditDebit) throw new Error('method: EditDebit is not implemented') + app.post('/api/user/debit/edit', async (req, res) => { + const info: Types.RequestInfo = { rpcName: 'EditDebit', batch: false, nostr: false, batchSize: 0} + const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n } + let authCtx: Types.AuthContext = {} + try { + if (!methods.EditDebit) throw new Error('method: EditDebit is not implemented') + const authContext = await opts.UserAuthGuard(req.headers['authorization']) + authCtx = authContext + stats.guard = process.hrtime.bigint() + const request = req.body + const error = Types.DebitAuthorizationRequestValidate(request) + stats.validate = process.hrtime.bigint() + if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback) + const query = req.query + const params = req.params + await methods.EditDebit({rpcName:'EditDebit', ctx:authContext , req: request}) + stats.handle = process.hrtime.bigint() + res.json({status: 'OK'}) + opts.metricsCallback([{ ...info, ...stats, ...authContext }]) + } catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } + }) if (!opts.allowNotImplementedMethods && !methods.EncryptionExchange) throw new Error('method: EncryptionExchange is not implemented') app.post('/api/encryption/exchange', async (req, res) => { const info: Types.RequestInfo = { rpcName: 'EncryptionExchange', batch: false, nostr: false, batchSize: 0} diff --git a/proto/autogenerated/ts/http_client.ts b/proto/autogenerated/ts/http_client.ts index 84f54d1a..2b2fbe6a 100644 --- a/proto/autogenerated/ts/http_client.ts +++ b/proto/autogenerated/ts/http_client.ts @@ -179,6 +179,17 @@ export default (params: ClientParams) => ({ } return { status: 'ERROR', reason: 'invalid response' } }, + EditDebit: async (request: Types.DebitAuthorizationRequest): Promise => { + const auth = await params.retrieveUserAuth() + if (auth === null) throw new Error('retrieveUserAuth() returned null') + let finalRoute = '/api/user/debit/edit' + const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } }) + if (data.status === 'ERROR' && typeof data.reason === 'string') return data + if (data.status === 'OK') { + return data + } + return { status: 'ERROR', reason: 'invalid response' } + }, EncryptionExchange: async (request: Types.EncryptionExchangeRequest): Promise => { const auth = await params.retrieveGuestAuth() if (auth === null) throw new Error('retrieveGuestAuth() returned null') diff --git a/proto/autogenerated/ts/nostr_client.ts b/proto/autogenerated/ts/nostr_client.ts index 3ac48d64..20085352 100644 --- a/proto/autogenerated/ts/nostr_client.ts +++ b/proto/autogenerated/ts/nostr_client.ts @@ -140,6 +140,18 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ } return { status: 'ERROR', reason: 'invalid response' } }, + EditDebit: async (request: Types.DebitAuthorizationRequest): 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:'EditDebit',authIdentifier:auth, ...nostrRequest }) + if (data.status === 'ERROR' && typeof data.reason === 'string') return data + if (data.status === 'OK') { + return data + } + return { status: 'ERROR', reason: 'invalid response' } + }, EnrollAdminToken: async (request: Types.EnrollAdminTokenRequest): Promise => { const auth = await params.retrieveNostrUserAuth() if (auth === null) throw new Error('retrieveNostrUserAuth() returned null') diff --git a/proto/autogenerated/ts/nostr_transport.ts b/proto/autogenerated/ts/nostr_transport.ts index 479aee05..e7a00622 100644 --- a/proto/autogenerated/ts/nostr_transport.ts +++ b/proto/autogenerated/ts/nostr_transport.ts @@ -195,6 +195,18 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => { callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) } break + case 'EditDebit': + if (!methods.EditDebit) { + throw new Error('method not defined: EditDebit') + } else { + const error = Types.DebitAuthorizationRequestValidate(operation.req) + opStats.validate = process.hrtime.bigint() + if (error !== null) throw error + await methods.EditDebit({...operation, ctx}); responses.push({ status: 'OK' }) + opStats.handle = process.hrtime.bigint() + callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) + } + break case 'EnrollAdminToken': if (!methods.EnrollAdminToken) { throw new Error('method not defined: EnrollAdminToken') @@ -427,6 +439,22 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => { opts.metricsCallback([{ ...info, ...stats, ...authContext }]) }catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } break + case 'EditDebit': + try { + if (!methods.EditDebit) throw new Error('method: EditDebit is not implemented') + const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier) + stats.guard = process.hrtime.bigint() + authCtx = authContext + const request = req.body + const error = Types.DebitAuthorizationRequestValidate(request) + stats.validate = process.hrtime.bigint() + if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback) + await methods.EditDebit({rpcName:'EditDebit', ctx:authContext , req: request}) + stats.handle = process.hrtime.bigint() + res({status: 'OK'}) + opts.metricsCallback([{ ...info, ...stats, ...authContext }]) + }catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } + break case 'EnrollAdminToken': try { if (!methods.EnrollAdminToken) throw new Error('method: EnrollAdminToken is not implemented') diff --git a/proto/autogenerated/ts/types.ts b/proto/autogenerated/ts/types.ts index ce4982b5..164f0c19 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 | 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 | UpdateCallbackUrl_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 | UpdateCallbackUrl_Output | UserHealth_Output +export type UserMethodInputs = AddProduct_Input | AuthorizeDebit_Input | BanDebit_Input | DecodeInvoice_Input | EditDebit_Input | EnrollAdminToken_Input | GetDebitAuthorizations_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetPaymentState_Input | GetUserInfo_Input | GetUserOperations_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | OpenChannel_Input | PayAddress_Input | PayInvoice_Input | ResetDebit_Input | UpdateCallbackUrl_Input | UserHealth_Input +export type UserMethodOutputs = AddProduct_Output | AuthorizeDebit_Output | BanDebit_Output | DecodeInvoice_Output | EditDebit_Output | EnrollAdminToken_Output | GetDebitAuthorizations_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetPaymentState_Output | GetUserInfo_Output | GetUserOperations_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | OpenChannel_Output | PayAddress_Output | PayInvoice_Output | ResetDebit_Output | UpdateCallbackUrl_Output | UserHealth_Output export type AuthContext = AdminContext | AppContext | GuestContext | GuestWithPubContext | MetricsContext | UserContext export type AddApp_Input = {rpcName:'AddApp', req: AddAppRequest} @@ -74,6 +74,9 @@ export type CreateOneTimeInviteLink_Output = ResultError | ({ status: 'OK' } & C export type DecodeInvoice_Input = {rpcName:'DecodeInvoice', req: DecodeInvoiceRequest} export type DecodeInvoice_Output = ResultError | ({ status: 'OK' } & DecodeInvoiceResponse) +export type EditDebit_Input = {rpcName:'EditDebit', req: DebitAuthorizationRequest} +export type EditDebit_Output = ResultError | { status: 'OK' } + export type EncryptionExchange_Input = {rpcName:'EncryptionExchange', req: EncryptionExchangeRequest} export type EncryptionExchange_Output = ResultError | { status: 'OK' } @@ -252,6 +255,7 @@ export type ServerMethods = { BanUser?: (req: BanUser_Input & {ctx: AdminContext }) => Promise CreateOneTimeInviteLink?: (req: CreateOneTimeInviteLink_Input & {ctx: AdminContext }) => Promise DecodeInvoice?: (req: DecodeInvoice_Input & {ctx: UserContext }) => Promise + EditDebit?: (req: EditDebit_Input & {ctx: UserContext }) => Promise EncryptionExchange?: (req: EncryptionExchange_Input & {ctx: GuestContext }) => Promise EnrollAdminToken?: (req: EnrollAdminToken_Input & {ctx: UserContext }) => Promise GetApp?: (req: GetApp_Input & {ctx: AppContext }) => Promise diff --git a/proto/service/methods.proto b/proto/service/methods.proto index 7b273f01..031ee20a 100644 --- a/proto/service/methods.proto +++ b/proto/service/methods.proto @@ -453,6 +453,12 @@ service LightningPub { option (http_route) = "/api/user/debit/authorize"; option (nostr) = true; } + rpc EditDebit(structs.DebitAuthorizationRequest) returns (structs.Empty){ + option (auth_type) = "User"; + option (http_method) = "post"; + option (http_route) = "/api/user/debit/edit"; + option (nostr) = true; + } rpc BanDebit(structs.DebitOperation) returns (structs.Empty){ option (auth_type) = "User"; option (http_method) = "post"; diff --git a/src/services/main/debitManager.ts b/src/services/main/debitManager.ts index a763f41e..d1e86b83 100644 --- a/src/services/main/debitManager.ts +++ b/src/services/main/debitManager.ts @@ -51,7 +51,8 @@ const debitRulesToDebitAccessRules = (rule: Types.DebitRule[]): DebitAccessRules case Types.DebitRule_rule_type.FREQUENCY_RULE: const intervals = rule.frequency_rule.number_of_intervals.toString() const unit = intervalTypeToUnit(rule.frequency_rule.interval) - return { key: frequencyRuleName, val: [intervals, unit, rule.frequency_rule.amount.toString()] } + rules[frequencyRuleName] = [intervals, unit, rule.frequency_rule.amount.toString()]; + break default: throw new Error("invalid rule") } @@ -147,6 +148,14 @@ export class DebitManager { return { debits } } + EditDebit = async (ctx: Types.UserContext, req: Types.DebitAuthorizationRequest): Promise => { + const access = await this.storage.debitStorage.GetDebitAccess(ctx.app_user_id, req.authorize_npub); + if (!access) { + throw new Error("Debit does not exist") + } + await this.storage.debitStorage.UpdateDebitAccessRules(ctx.app_user_id, req.authorize_npub, debitRulesToDebitAccessRules(req.rules)); + } + BanDebit = async (ctx: Types.UserContext, req: Types.DebitOperation): Promise => { await this.storage.debitStorage.DenyDebitAccess(ctx.app_user_id, req.npub) } diff --git a/src/services/serverMethods/index.ts b/src/services/serverMethods/index.ts index 88fe05f8..ebde0728 100644 --- a/src/services/serverMethods/index.ts +++ b/src/services/serverMethods/index.ts @@ -285,6 +285,9 @@ export default (mainHandler: Main): Types.ServerMethods => { }) if (err != null) throw new Error(err.message) return mainHandler.debitManager.ResetDebit(ctx, req) + }, + EditDebit: async ({ ctx, req }) => { + return mainHandler.debitManager.EditDebit(ctx, req); } } diff --git a/src/services/storage/debitStorage.ts b/src/services/storage/debitStorage.ts index 2e8a5700..c06450a9 100644 --- a/src/services/storage/debitStorage.ts +++ b/src/services/storage/debitStorage.ts @@ -40,7 +40,7 @@ export default class { 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) { + async UpdateDebitAccessRules(appUserId: string, authorizedPub: string, rules?: DebitAccessRules) { return this.DB.getRepository(DebitAccess).update({ app_user_id: appUserId, npub: authorizedPub }, { rules }) }