mirror of
https://github.com/getAlby/hub.git
synced 2026-08-13 12:33:39 +02:00
fix: add err handling to db queries (#2064)
* fix: add err handling to db queries * chore: add error handling to GetApp * chore: wrap the original error * fix: retrieve balance after err != nil check * chore: minor code cleanup * chore: add error handling to app permission listing in GetApp * chore: convert GetBudgetUsageSat to GetBudgetUsage * chore: add tests for GetBudgetUsage * chore: separate budget window tests --------- Co-authored-by: Roland Bewick <roland.bewick@gmail.com>
This commit is contained in:
parent
4a924c3d11
commit
3aa167a7c4
13 changed files with 387 additions and 45 deletions
49
api/api.go
49
api/api.go
|
|
@ -406,12 +406,17 @@ func (api *api) DeleteLightningAddress(ctx context.Context, appId uint) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (api *api) GetApp(dbApp *db.App) *App {
|
||||
func (api *api) GetApp(dbApp *db.App) (*App, error) {
|
||||
|
||||
paySpecificPermission := db.AppPermission{}
|
||||
appPermissions := []db.AppPermission{}
|
||||
var expiresAt *time.Time
|
||||
api.db.Where("app_id = ?", dbApp.ID).Find(&appPermissions)
|
||||
if err := api.db.Where("app_id = ?", dbApp.ID).Find(&appPermissions).Error; err != nil {
|
||||
logger.Logger.WithError(err).WithFields(logrus.Fields{
|
||||
"app_id": dbApp.ID,
|
||||
}).Error("Failed to list app permissions")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
requestMethods := []string{}
|
||||
for _, appPerm := range appPermissions {
|
||||
|
|
@ -424,9 +429,14 @@ func (api *api) GetApp(dbApp *db.App) *App {
|
|||
}
|
||||
|
||||
// renewsIn := ""
|
||||
budgetUsage := uint64(0)
|
||||
maxAmount := uint64(paySpecificPermission.MaxAmountSat)
|
||||
budgetUsage = queries.GetBudgetUsageSat(api.db, &paySpecificPermission)
|
||||
budgetUsage, err := queries.GetBudgetUsage(api.db, &paySpecificPermission)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).WithFields(logrus.Fields{
|
||||
"app_id": dbApp.ID,
|
||||
}).Error("Failed to get budget usage for app")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var metadata Metadata
|
||||
if dbApp.Metadata != nil {
|
||||
|
|
@ -455,7 +465,7 @@ func (api *api) GetApp(dbApp *db.App) *App {
|
|||
ExpiresAt: expiresAt,
|
||||
MaxAmountSat: maxAmount,
|
||||
Scopes: requestMethods,
|
||||
BudgetUsage: budgetUsage,
|
||||
BudgetUsage: budgetUsage / 1000,
|
||||
BudgetRenewal: paySpecificPermission.BudgetRenewal,
|
||||
Isolated: dbApp.Isolated,
|
||||
Metadata: metadata,
|
||||
|
|
@ -465,10 +475,17 @@ func (api *api) GetApp(dbApp *db.App) *App {
|
|||
}
|
||||
|
||||
if dbApp.Isolated {
|
||||
response.Balance = queries.GetIsolatedBalance(api.db, dbApp.ID)
|
||||
balance, err := queries.GetIsolatedBalance(api.db, dbApp.ID)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).WithFields(logrus.Fields{
|
||||
"app_id": dbApp.ID,
|
||||
}).Error("Failed to get isolated app balance")
|
||||
return nil, err
|
||||
}
|
||||
response.Balance = balance
|
||||
}
|
||||
|
||||
return &response
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, orderBy string) (*ListAppsResponse, error) {
|
||||
|
|
@ -586,7 +603,14 @@ func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, o
|
|||
}
|
||||
|
||||
if dbApp.Isolated {
|
||||
apiApp.Balance = queries.GetIsolatedBalance(api.db, dbApp.ID)
|
||||
balance, err := queries.GetIsolatedBalance(api.db, dbApp.ID)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).WithFields(logrus.Fields{
|
||||
"app_id": dbApp.ID,
|
||||
}).Error("Failed to get isolated app balance")
|
||||
return nil, err
|
||||
}
|
||||
apiApp.Balance = balance
|
||||
}
|
||||
|
||||
for _, appPermission := range permissionsMap[dbApp.ID] {
|
||||
|
|
@ -595,7 +619,14 @@ func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, o
|
|||
if appPermission.Scope == constants.PAY_INVOICE_SCOPE {
|
||||
apiApp.BudgetRenewal = appPermission.BudgetRenewal
|
||||
apiApp.MaxAmountSat = uint64(appPermission.MaxAmountSat)
|
||||
apiApp.BudgetUsage = queries.GetBudgetUsageSat(api.db, &appPermission)
|
||||
budgetUsage, err := queries.GetBudgetUsage(api.db, &appPermission)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).WithFields(logrus.Fields{
|
||||
"app_id": dbApp.ID,
|
||||
}).Error("Failed to get budget usage for app")
|
||||
return nil, err
|
||||
}
|
||||
apiApp.BudgetUsage = budgetUsage / 1000
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ type API interface {
|
|||
UpdateApp(app *db.App, updateAppRequest *UpdateAppRequest) error
|
||||
Transfer(ctx context.Context, fromAppId *uint, toAppId *uint, amountMsat uint64) error
|
||||
DeleteApp(app *db.App) error
|
||||
GetApp(app *db.App) *App
|
||||
GetApp(app *db.App) (*App, error)
|
||||
ListApps(limit uint64, offset uint64, filters ListAppsFilters, orderBy string) (*ListAppsResponse, error)
|
||||
CreateLightningAddress(ctx context.Context, createLightningAddressRequest *CreateLightningAddressRequest) error
|
||||
DeleteLightningAddress(ctx context.Context, appId uint) error
|
||||
|
|
|
|||
|
|
@ -8,15 +8,18 @@ import (
|
|||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func GetBudgetUsageSat(tx *gorm.DB, appPermission *db.AppPermission) uint64 {
|
||||
func GetBudgetUsage(tx *gorm.DB, appPermission *db.AppPermission) (uint64, error) {
|
||||
var result struct {
|
||||
Sum uint64
|
||||
}
|
||||
tx.
|
||||
err := tx.
|
||||
Table("transactions").
|
||||
Select("SUM(amount_msat + fee_msat + fee_reserve_msat) as sum").
|
||||
Where("app_id = ? AND type = ? AND (state = ? OR state = ?) AND created_at > ?", appPermission.AppId, constants.TRANSACTION_TYPE_OUTGOING, constants.TRANSACTION_STATE_SETTLED, constants.TRANSACTION_STATE_PENDING, getStartOfBudget(appPermission.BudgetRenewal)).Scan(&result)
|
||||
return result.Sum / 1000
|
||||
Where("app_id = ? AND type = ? AND (state = ? OR state = ?) AND created_at > ?", appPermission.AppId, constants.TRANSACTION_TYPE_OUTGOING, constants.TRANSACTION_STATE_SETTLED, constants.TRANSACTION_STATE_PENDING, getStartOfBudget(appPermission.BudgetRenewal)).Scan(&result).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.Sum, nil
|
||||
}
|
||||
|
||||
func getStartOfBudget(budget_type string) time.Time {
|
||||
|
|
|
|||
234
db/queries/get_budget_usage_test.go
Normal file
234
db/queries/get_budget_usage_test.go
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
package queries
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/getAlby/hub/constants"
|
||||
"github.com/getAlby/hub/db"
|
||||
"github.com/getAlby/hub/tests"
|
||||
)
|
||||
|
||||
func TestGetBudgetUsage_IncludesPendingAndSettledOutgoing(t *testing.T) {
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
app, _, err := tests.CreateApp(svc)
|
||||
require.NoError(t, err)
|
||||
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
Scope: constants.PAY_INVOICE_SCOPE,
|
||||
BudgetRenewal: constants.BUDGET_RENEWAL_NEVER,
|
||||
}
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_PENDING,
|
||||
AmountMsat: 50000,
|
||||
FeeMsat: 1000,
|
||||
FeeReserveMsat: 2000,
|
||||
}).Error)
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_SETTLED,
|
||||
AmountMsat: 25000,
|
||||
FeeMsat: 500,
|
||||
FeeReserveMsat: 500,
|
||||
}).Error)
|
||||
|
||||
budgetUsage, err := GetBudgetUsage(svc.DB, appPermission)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(79000), budgetUsage)
|
||||
}
|
||||
|
||||
func TestGetBudgetUsage_ExcludesWrongStateTypeAndApp(t *testing.T) {
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
app, _, err := tests.CreateApp(svc)
|
||||
require.NoError(t, err)
|
||||
|
||||
otherApp, _, err := tests.CreateApp(svc)
|
||||
require.NoError(t, err)
|
||||
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
Scope: constants.PAY_INVOICE_SCOPE,
|
||||
BudgetRenewal: constants.BUDGET_RENEWAL_NEVER,
|
||||
}
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_SETTLED,
|
||||
AmountMsat: 20000,
|
||||
FeeMsat: 1000,
|
||||
FeeReserveMsat: 0,
|
||||
}).Error)
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_INCOMING,
|
||||
State: constants.TRANSACTION_STATE_SETTLED,
|
||||
AmountMsat: 90000,
|
||||
FeeMsat: 0,
|
||||
FeeReserveMsat: 0,
|
||||
}).Error)
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_FAILED,
|
||||
AmountMsat: 90000,
|
||||
FeeMsat: 0,
|
||||
FeeReserveMsat: 0,
|
||||
}).Error)
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &otherApp.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_SETTLED,
|
||||
AmountMsat: 90000,
|
||||
FeeMsat: 0,
|
||||
FeeReserveMsat: 0,
|
||||
}).Error)
|
||||
|
||||
budgetUsage, err := GetBudgetUsage(svc.DB, appPermission)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(21000), budgetUsage)
|
||||
}
|
||||
|
||||
func TestGetBudgetUsage_BudgetWindowDaily(t *testing.T) {
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
app, _, err := tests.CreateApp(svc)
|
||||
require.NoError(t, err)
|
||||
|
||||
appPermissionDaily := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
Scope: constants.PAY_INVOICE_SCOPE,
|
||||
BudgetRenewal: constants.BUDGET_RENEWAL_DAILY,
|
||||
}
|
||||
|
||||
dailyStart := getStartOfBudget(constants.BUDGET_RENEWAL_DAILY)
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_SETTLED,
|
||||
AmountMsat: 20000,
|
||||
FeeMsat: 1000,
|
||||
FeeReserveMsat: 0,
|
||||
CreatedAt: dailyStart.Add(1 * time.Minute),
|
||||
}).Error)
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_PENDING,
|
||||
AmountMsat: 20000,
|
||||
FeeMsat: 0,
|
||||
FeeReserveMsat: 1000,
|
||||
CreatedAt: dailyStart.Add(2 * time.Hour),
|
||||
}).Error)
|
||||
|
||||
budgetUsage, err := GetBudgetUsage(svc.DB, appPermissionDaily)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(42000), budgetUsage)
|
||||
}
|
||||
|
||||
func TestGetBudgetUsage_BudgetWindowWeekly(t *testing.T) {
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
app, _, err := tests.CreateApp(svc)
|
||||
require.NoError(t, err)
|
||||
|
||||
appPermissionWeekly := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
Scope: constants.PAY_INVOICE_SCOPE,
|
||||
BudgetRenewal: constants.BUDGET_RENEWAL_WEEKLY,
|
||||
}
|
||||
|
||||
weeklyStart := getStartOfBudget(constants.BUDGET_RENEWAL_WEEKLY)
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_SETTLED,
|
||||
AmountMsat: 10000,
|
||||
FeeMsat: 1000,
|
||||
FeeReserveMsat: 0,
|
||||
CreatedAt: weeklyStart.Add(30 * time.Minute),
|
||||
}).Error)
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_PENDING,
|
||||
AmountMsat: 7000,
|
||||
FeeMsat: 0,
|
||||
FeeReserveMsat: 0,
|
||||
CreatedAt: weeklyStart.Add(-1 * time.Hour),
|
||||
}).Error)
|
||||
|
||||
budgetUsage, err := GetBudgetUsage(svc.DB, appPermissionWeekly)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(11000), budgetUsage)
|
||||
}
|
||||
|
||||
func TestGetBudgetUsage_BudgetWindowNever(t *testing.T) {
|
||||
svc, err := tests.CreateTestService(t)
|
||||
require.NoError(t, err)
|
||||
defer svc.Remove()
|
||||
|
||||
app, _, err := tests.CreateApp(svc)
|
||||
require.NoError(t, err)
|
||||
|
||||
appPermission := &db.AppPermission{
|
||||
AppId: app.ID,
|
||||
App: *app,
|
||||
Scope: constants.PAY_INVOICE_SCOPE,
|
||||
BudgetRenewal: constants.BUDGET_RENEWAL_NEVER,
|
||||
}
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_SETTLED,
|
||||
AmountMsat: 5000,
|
||||
FeeMsat: 1000,
|
||||
FeeReserveMsat: 0,
|
||||
CreatedAt: time.Now().AddDate(-2, 0, 0),
|
||||
}).Error)
|
||||
|
||||
require.NoError(t, svc.DB.Create(&db.Transaction{
|
||||
AppId: &app.ID,
|
||||
Type: constants.TRANSACTION_TYPE_OUTGOING,
|
||||
State: constants.TRANSACTION_STATE_PENDING,
|
||||
AmountMsat: 4000,
|
||||
FeeMsat: 0,
|
||||
FeeReserveMsat: 1000,
|
||||
CreatedAt: time.Now().AddDate(-1, 0, 0),
|
||||
}).Error)
|
||||
|
||||
budgetUsage, err := GetBudgetUsage(svc.DB, appPermission)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint64(11000), budgetUsage)
|
||||
}
|
||||
|
|
@ -5,23 +5,29 @@ import (
|
|||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func GetIsolatedBalance(tx *gorm.DB, appId uint) int64 {
|
||||
func GetIsolatedBalance(tx *gorm.DB, appId uint) (int64, error) {
|
||||
var received struct {
|
||||
Sum int64
|
||||
}
|
||||
tx.
|
||||
err := tx.
|
||||
Table("transactions").
|
||||
Select("SUM(amount_msat) as sum").
|
||||
Where("app_id = ? AND type = ? AND state = ?", appId, constants.TRANSACTION_TYPE_INCOMING, constants.TRANSACTION_STATE_SETTLED).Scan(&received)
|
||||
Where("app_id = ? AND type = ? AND state = ?", appId, constants.TRANSACTION_TYPE_INCOMING, constants.TRANSACTION_STATE_SETTLED).Scan(&received).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var spent struct {
|
||||
Sum int64
|
||||
}
|
||||
|
||||
tx.
|
||||
err = tx.
|
||||
Table("transactions").
|
||||
Select("SUM(amount_msat + fee_msat + fee_reserve_msat) as sum").
|
||||
Where("app_id = ? AND type = ? AND (state = ? OR state = ?)", appId, constants.TRANSACTION_TYPE_OUTGOING, constants.TRANSACTION_STATE_SETTLED, constants.TRANSACTION_STATE_PENDING).Scan(&spent)
|
||||
Where("app_id = ? AND type = ? AND (state = ? OR state = ?)", appId, constants.TRANSACTION_TYPE_OUTGOING, constants.TRANSACTION_STATE_SETTLED, constants.TRANSACTION_STATE_PENDING).Scan(&spent).Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return received.Sum - spent.Sum
|
||||
return received.Sum - spent.Sum, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ func TestGetIsolatedBalance_PendingNoOverflow(t *testing.T) {
|
|||
}
|
||||
svc.DB.Save(&tx)
|
||||
|
||||
balance := GetIsolatedBalance(svc.DB, app.ID)
|
||||
balance, err := GetIsolatedBalance(svc.DB, app.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(-11000), balance)
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ func TestGetIsolatedBalance_SettledNoOverflow(t *testing.T) {
|
|||
}
|
||||
svc.DB.Save(&tx)
|
||||
|
||||
balance := GetIsolatedBalance(svc.DB, app.ID)
|
||||
balance, err := GetIsolatedBalance(svc.DB, app.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(-1000), balance)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1051,7 +1051,12 @@ func (httpSvc *HttpService) appsShowByPubkeyHandler(c echo.Context) error {
|
|||
})
|
||||
}
|
||||
|
||||
response := httpSvc.api.GetApp(dbApp)
|
||||
response, err := httpSvc.api.GetApp(dbApp)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
|
@ -1079,7 +1084,12 @@ func (httpSvc *HttpService) appsShowHandler(c echo.Context) error {
|
|||
})
|
||||
}
|
||||
|
||||
response := httpSvc.api.GetApp(dbApp)
|
||||
response, err := httpSvc.api.GetApp(dbApp)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ type getBalanceResponse struct {
|
|||
// BudgetRenewal string `json:"budget_renewal"`
|
||||
}
|
||||
|
||||
// TODO: remove checkPermission - can it be a middleware?
|
||||
func (controller *nip47Controller) HandleGetBalanceEvent(ctx context.Context, nip47Request *models.Request, requestEventId uint, app *db.App, publishResponse publishFunc) {
|
||||
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
|
|
@ -30,10 +29,20 @@ func (controller *nip47Controller) HandleGetBalanceEvent(ctx context.Context, ni
|
|||
|
||||
balance := int64(0)
|
||||
if app.Isolated {
|
||||
balance = queries.GetIsolatedBalance(controller.db, app.ID)
|
||||
var err error
|
||||
balance, err = queries.GetIsolatedBalance(controller.db, app.ID)
|
||||
if err != nil {
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
"request_event_id": requestEventId,
|
||||
}).WithError(err).Error("Failed to fetch isolated balance")
|
||||
publishResponse(&models.Response{
|
||||
ResultType: nip47Request.Method,
|
||||
Error: mapNip47Error(err),
|
||||
}, nostr.Tags{})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
balances, err := controller.lnClient.GetBalances(ctx, true)
|
||||
balance = balances.Lightning.TotalSpendable
|
||||
if err != nil {
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
"request_event_id": requestEventId,
|
||||
|
|
@ -44,6 +53,7 @@ func (controller *nip47Controller) HandleGetBalanceEvent(ctx context.Context, ni
|
|||
}, nostr.Tags{})
|
||||
return
|
||||
}
|
||||
balance = balances.Lightning.TotalSpendable
|
||||
}
|
||||
|
||||
responsePayload := &getBalanceResponse{
|
||||
|
|
|
|||
|
|
@ -37,10 +37,21 @@ func (controller *nip47Controller) HandleGetBudgetEvent(ctx context.Context, nip
|
|||
return
|
||||
}
|
||||
|
||||
usedBudget := queries.GetBudgetUsageSat(controller.db, &appPermission)
|
||||
usedBudget, err := queries.GetBudgetUsage(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(maxAmount * 1000),
|
||||
UsedBudget: usedBudget * 1000,
|
||||
UsedBudget: usedBudget,
|
||||
RenewalPeriod: appPermission.BudgetRenewal,
|
||||
RenewsAt: queries.GetBudgetRenewsAt(appPermission.BudgetRenewal),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -399,7 +399,9 @@ func TestSendKeysend_IsolatedAppToNoApp(t *testing.T) {
|
|||
result := svc.DB.Find(&transactions)
|
||||
assert.Equal(t, int64(3), result.RowsAffected)
|
||||
// expect balance to be decreased
|
||||
assert.Equal(t, int64(10000), queries.GetIsolatedBalance(svc.DB, app.ID))
|
||||
balance, err := queries.GetIsolatedBalance(svc.DB, app.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(10000), balance)
|
||||
}
|
||||
|
||||
func TestSendKeysend_IsolatedAppToIsolatedApp(t *testing.T) {
|
||||
|
|
@ -489,10 +491,14 @@ func TestSendKeysend_IsolatedAppToIsolatedApp(t *testing.T) {
|
|||
result := svc.DB.Find(&transactions)
|
||||
assert.Equal(t, int64(3), result.RowsAffected)
|
||||
// expect balance to be decreased
|
||||
assert.Equal(t, int64(10000), queries.GetIsolatedBalance(svc.DB, app.ID))
|
||||
balance, err := queries.GetIsolatedBalance(svc.DB, app.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(10000), balance)
|
||||
|
||||
// expect app2 to receive the payment
|
||||
assert.Equal(t, int64(123000), queries.GetIsolatedBalance(svc.DB, app2.ID))
|
||||
balance, err = queries.GetIsolatedBalance(svc.DB, app2.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(123000), balance)
|
||||
|
||||
// check notifications
|
||||
assert.Equal(t, 2, len(mockEventConsumer.GetConsumedEvents()))
|
||||
|
|
|
|||
|
|
@ -107,7 +107,9 @@ func TestSendPaymentSync_SelfPayment_NoAppToIsolatedApp(t *testing.T) {
|
|||
result := svc.DB.Find(&transactions)
|
||||
assert.Equal(t, int64(2), result.RowsAffected)
|
||||
// expect balance to be increased
|
||||
assert.Equal(t, int64(123000), queries.GetIsolatedBalance(svc.DB, app.ID))
|
||||
balance, err := queries.GetIsolatedBalance(svc.DB, app.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(123000), balance)
|
||||
}
|
||||
|
||||
func TestSendPaymentSync_SelfPayment_NoAppToApp(t *testing.T) {
|
||||
|
|
@ -230,7 +232,9 @@ func TestSendPaymentSync_SelfPayment_IsolatedAppToNoApp(t *testing.T) {
|
|||
result := svc.DB.Find(&transactions)
|
||||
assert.Equal(t, int64(3), result.RowsAffected)
|
||||
// expect balance to be decreased
|
||||
assert.Equal(t, int64(0), queries.GetIsolatedBalance(svc.DB, app.ID))
|
||||
balance, err := queries.GetIsolatedBalance(svc.DB, app.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(0), balance)
|
||||
}
|
||||
|
||||
func TestSendPaymentSync_SelfPayment_IsolatedAppToApp(t *testing.T) {
|
||||
|
|
@ -306,7 +310,9 @@ func TestSendPaymentSync_SelfPayment_IsolatedAppToApp(t *testing.T) {
|
|||
result := svc.DB.Find(&transactions)
|
||||
assert.Equal(t, int64(3), result.RowsAffected)
|
||||
// expect balance to be decreased
|
||||
assert.Equal(t, int64(0), queries.GetIsolatedBalance(svc.DB, app.ID))
|
||||
balance, err := queries.GetIsolatedBalance(svc.DB, app.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(0), balance)
|
||||
}
|
||||
|
||||
func TestSendPaymentSync_SelfPayment_IsolatedAppToIsolatedApp(t *testing.T) {
|
||||
|
|
@ -388,7 +394,9 @@ func TestSendPaymentSync_SelfPayment_IsolatedAppToIsolatedApp(t *testing.T) {
|
|||
result := svc.DB.Find(&transactions)
|
||||
assert.Equal(t, int64(3), result.RowsAffected)
|
||||
// expect balance to be decreased
|
||||
assert.Equal(t, int64(0), queries.GetIsolatedBalance(svc.DB, app.ID))
|
||||
balance, err := queries.GetIsolatedBalance(svc.DB, app.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(0), balance)
|
||||
|
||||
// check notifications
|
||||
assert.Equal(t, 2, len(mockEventConsumer.GetConsumedEvents()))
|
||||
|
|
@ -481,7 +489,9 @@ func TestSendPaymentSync_SelfPayment_IsolatedAppToSelf(t *testing.T) {
|
|||
assert.Equal(t, int64(3), result.RowsAffected)
|
||||
|
||||
// expect balance to be unchanged
|
||||
assert.Equal(t, int64(123000), queries.GetIsolatedBalance(svc.DB, app.ID))
|
||||
balance, err := queries.GetIsolatedBalance(svc.DB, app.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(123000), balance)
|
||||
}
|
||||
|
||||
func TestSendPaymentSync_SelfPayment_IsolatedAppToApp_AmountProvidedIgnoredOnNonZeroAmountInvoice(t *testing.T) {
|
||||
|
|
@ -560,5 +570,7 @@ func TestSendPaymentSync_SelfPayment_IsolatedAppToApp_AmountProvidedIgnoredOnNon
|
|||
result := svc.DB.Find(&transactions)
|
||||
assert.Equal(t, int64(3), result.RowsAffected)
|
||||
// expect balance to be decreased
|
||||
assert.Equal(t, int64(0), queries.GetIsolatedBalance(svc.DB, app.ID))
|
||||
balance, err := queries.GetIsolatedBalance(svc.DB, app.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(0), balance)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1060,7 +1060,10 @@ func (svc *transactionsService) validateCanPay(tx *gorm.DB, appId *uint, amount
|
|||
}
|
||||
|
||||
if app.Isolated {
|
||||
balance := queries.GetIsolatedBalance(tx, appPermission.AppId)
|
||||
balance, err := queries.GetIsolatedBalance(tx, appPermission.AppId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to calculate isolated balance for app: %w", err)
|
||||
}
|
||||
|
||||
if int64(amountWithFeeReserve) > balance {
|
||||
logger.Logger.WithFields(logrus.Fields{
|
||||
|
|
@ -1087,8 +1090,11 @@ func (svc *transactionsService) validateCanPay(tx *gorm.DB, appId *uint, amount
|
|||
}
|
||||
|
||||
if appPermission.MaxAmountSat > 0 {
|
||||
budgetUsageSat := queries.GetBudgetUsageSat(tx, &appPermission)
|
||||
if int(amountWithFeeReserve/1000) > appPermission.MaxAmountSat-int(budgetUsageSat) {
|
||||
budgetUsage, err := queries.GetBudgetUsage(tx, &appPermission)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to calculate budget usage for app: %w", err)
|
||||
}
|
||||
if int(amountWithFeeReserve/1000) > appPermission.MaxAmountSat-int(budgetUsage/1000) {
|
||||
message := NewQuotaExceededError().Error()
|
||||
if description != "" {
|
||||
message += " " + description
|
||||
|
|
@ -1429,9 +1435,14 @@ func (svc *transactionsService) checkBudgetUsage(dbTransaction *db.Transaction,
|
|||
return
|
||||
}
|
||||
|
||||
budgetUsage := queries.GetBudgetUsageSat(gormTransaction, &appPermission)
|
||||
budgetUsage, err := queries.GetBudgetUsage(gormTransaction, &appPermission)
|
||||
if err != nil {
|
||||
logger.Logger.WithField("app_id", dbTransaction.AppId).WithError(err).Error("failed to get budget usage")
|
||||
return
|
||||
}
|
||||
budgetUsageSat := budgetUsage / 1000
|
||||
warningUsage := uint64(math.Floor(float64(appPermission.MaxAmountSat) * 0.8))
|
||||
if budgetUsage >= warningUsage && budgetUsage-dbTransaction.AmountMsat/1000 < warningUsage {
|
||||
if budgetUsageSat >= warningUsage && budgetUsageSat-dbTransaction.AmountMsat/1000 < warningUsage {
|
||||
svc.eventPublisher.Publish(&events.Event{
|
||||
Event: "nwc_budget_warning",
|
||||
Properties: map[string]interface{}{
|
||||
|
|
|
|||
|
|
@ -72,7 +72,10 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
|
||||
switch method {
|
||||
case "GET":
|
||||
app := app.api.GetApp(dbApp)
|
||||
app, err := app.api.GetApp(dbApp)
|
||||
if err != nil {
|
||||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
return WailsRequestRouterResponse{Body: app, Error: ""}
|
||||
}
|
||||
}
|
||||
|
|
@ -93,7 +96,10 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
|
|||
|
||||
switch method {
|
||||
case "GET":
|
||||
app := app.api.GetApp(dbApp)
|
||||
app, err := app.api.GetApp(dbApp)
|
||||
if err != nil {
|
||||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
return WailsRequestRouterResponse{Body: app, Error: ""}
|
||||
case "PATCH":
|
||||
updateAppRequest := &api.UpdateAppRequest{}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue