alby-hub/nip47/controllers/get_budget_controller.go
Roland bdce8fe8d2
Some checks are pending
Multiplatform Docker build & push / build (push) Waiting to run
Code quality - linting and typechecking / linting (push) Waiting to run
Backend testing with Postgres / test-postgres (push) Waiting to run
fix: use scope constant in get_budget permission query (#2510)
* fix: use scope constant in get_budget permission query

The get_budget controller filtered the app_permissions scope column with
models.PAY_INVOICE_METHOD, which only matched because the method and
scope constants share the same string value. Use
constants.PAY_INVOICE_SCOPE like every other scope lookup, and document
why the unchecked First result is safe.

Fixes #2503

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: return error from get_budget on unexpected permission query failure

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 12:39:07 +07:00

79 lines
2.4 KiB
Go

package controllers
import (
"context"
"errors"
"github.com/getAlby/go-nostr"
"github.com/getAlby/hub/db/queries"
"gorm.io/gorm"
"github.com/getAlby/hub/constants"
"github.com/getAlby/hub/db"
"github.com/getAlby/hub/logger"
"github.com/getAlby/hub/nip47/models"
"github.com/sirupsen/logrus"
)
type getBudgetResponse struct {
UsedBudget uint64 `json:"used_budget"`
TotalBudget uint64 `json:"total_budget"`
RenewsAt *uint64 `json:"renews_at,omitempty"`
RenewalPeriod string `json:"renewal_period"`
}
func (controller *nip47Controller) HandleGetBudgetEvent(ctx context.Context, nip47Request *models.Request, requestEventId uint, app *db.App, publishResponse publishFunc) {
logger.Logger.WithFields(logrus.Fields{
"request_event_id": requestEventId,
}).Debug("Getting budget")
appPermission := db.AppPermission{}
result := controller.db.Where("app_id = ? AND scope = ?", app.ID, constants.PAY_INVOICE_SCOPE).First(&appPermission)
if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
logger.Logger.WithFields(logrus.Fields{
"request_event_id": requestEventId,
}).WithError(result.Error).Error("Failed to fetch pay_invoice permission")
publishResponse(&models.Response{
ResultType: nip47Request.Method,
Error: mapNip47Error(result.Error),
}, nostr.Tags{})
return
}
// On ErrRecordNotFound appPermission stays zero-valued and maxAmountSat == 0,
// which returns the same empty "no budget" response as a permission with no
// budget set.
maxAmountSat := appPermission.MaxAmountSat
if maxAmountSat == 0 {
publishResponse(&models.Response{
ResultType: nip47Request.Method,
Result: struct{}{},
}, nostr.Tags{})
return
}
usedBudgetMsat, err := queries.GetBudgetUsageMsat(controller.db, &appPermission)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"request_event_id": requestEventId,
}).WithError(err).Error("Failed to fetch budget usage")
publishResponse(&models.Response{
ResultType: nip47Request.Method,
Error: mapNip47Error(err),
}, nostr.Tags{})
return
}
responsePayload := &getBudgetResponse{
TotalBudget: uint64(maxAmountSat * 1000),
UsedBudget: usedBudgetMsat,
RenewalPeriod: appPermission.BudgetRenewal,
RenewsAt: queries.GetBudgetRenewsAt(appPermission.BudgetRenewal),
}
publishResponse(&models.Response{
ResultType: nip47Request.Method,
Result: responsePayload,
}, nostr.Tags{})
}