feat: track last settled transaction time for apps (#2214)

* feat: track last settled transaction time for apps

* fix: use default subwallet ordering and app_id logging in event handler

* chore: rename to last_settled_transaction_at and remove last tx migration

* chore: split app settlement update and budget check

* chore: extract app display name helper into utils function
This commit is contained in:
Adithya Vardhan 2026-04-09 16:26:45 +05:30 committed by GitHub
parent 18efdcdf11
commit b1bd7c2587
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 163 additions and 120 deletions

View file

@ -456,22 +456,23 @@ func (api *api) GetApp(dbApp *db.App) (*App, error) {
}
response := App{
ID: dbApp.ID,
Name: dbApp.Name,
Description: dbApp.Description,
CreatedAt: dbApp.CreatedAt,
UpdatedAt: dbApp.UpdatedAt,
AppPubkey: dbApp.AppPubkey,
ExpiresAt: expiresAt,
MaxAmountSat: maxAmount,
Scopes: requestMethods,
BudgetUsage: budgetUsage / 1000,
BudgetRenewal: paySpecificPermission.BudgetRenewal,
Isolated: dbApp.Isolated,
Metadata: metadata,
WalletPubkey: walletPubkey,
UniqueWalletPubkey: uniqueWalletPubkey,
LastUsedAt: dbApp.LastUsedAt,
ID: dbApp.ID,
Name: dbApp.Name,
Description: dbApp.Description,
CreatedAt: dbApp.CreatedAt,
UpdatedAt: dbApp.UpdatedAt,
AppPubkey: dbApp.AppPubkey,
ExpiresAt: expiresAt,
MaxAmountSat: maxAmount,
Scopes: requestMethods,
BudgetUsage: budgetUsage / 1000,
BudgetRenewal: paySpecificPermission.BudgetRenewal,
Isolated: dbApp.Isolated,
Metadata: metadata,
WalletPubkey: walletPubkey,
UniqueWalletPubkey: uniqueWalletPubkey,
LastUsedAt: dbApp.LastUsedAt,
LastSettledTransactionAt: dbApp.LastSettledTransactionAt,
}
if dbApp.Isolated {
@ -525,15 +526,7 @@ func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, o
}
}
if orderBy == "" {
orderBy = "last_used_at"
}
if orderBy == "last_used_at" {
// when ordering by last used at, apps with last_used_at is NULL should be ordered last
orderBy = "last_used_at IS NULL, " + orderBy
}
query = query.Order(orderBy + " DESC")
query = query.Order(resolveAppOrderBy(orderBy))
if limit == 0 {
limit = 100
@ -590,16 +583,17 @@ func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, o
uniqueWalletPubkey = true
}
apiApp := App{
ID: dbApp.ID,
Name: dbApp.Name,
Description: dbApp.Description,
CreatedAt: dbApp.CreatedAt,
UpdatedAt: dbApp.UpdatedAt,
AppPubkey: dbApp.AppPubkey,
Isolated: dbApp.Isolated,
WalletPubkey: walletPubkey,
UniqueWalletPubkey: uniqueWalletPubkey,
LastUsedAt: dbApp.LastUsedAt,
ID: dbApp.ID,
Name: dbApp.Name,
Description: dbApp.Description,
CreatedAt: dbApp.CreatedAt,
UpdatedAt: dbApp.UpdatedAt,
AppPubkey: dbApp.AppPubkey,
Isolated: dbApp.Isolated,
WalletPubkey: walletPubkey,
UniqueWalletPubkey: uniqueWalletPubkey,
LastUsedAt: dbApp.LastUsedAt,
LastSettledTransactionAt: dbApp.LastSettledTransactionAt,
}
if dbApp.Isolated {
@ -650,6 +644,17 @@ func (api *api) ListApps(limit uint64, offset uint64, filters ListAppsFilters, o
}, nil
}
func resolveAppOrderBy(orderBy string) string {
switch orderBy {
case "created_at":
return "created_at DESC"
case "last_settled_transaction":
return "last_settled_transaction_at IS NULL, last_settled_transaction_at DESC"
default:
return "last_used_at IS NULL, last_used_at DESC"
}
}
func (api *api) ListChannels(ctx context.Context) ([]Channel, error) {
lnClient := api.svc.GetLNClient()
if lnClient == nil {

View file

@ -89,23 +89,24 @@ type API interface {
var ErrLNClientNotStarted = errors.New("LNClient not started")
type App struct {
ID uint `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
AppPubkey string `json:"appPubkey"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastUsedAt *time.Time `json:"lastUsedAt"`
ExpiresAt *time.Time `json:"expiresAt"`
Scopes []string `json:"scopes"`
MaxAmountSat uint64 `json:"maxAmount"`
BudgetUsage uint64 `json:"budgetUsage"`
BudgetRenewal string `json:"budgetRenewal"`
Isolated bool `json:"isolated"`
WalletPubkey string `json:"walletPubkey"`
UniqueWalletPubkey bool `json:"uniqueWalletPubkey"`
Balance int64 `json:"balance"`
Metadata Metadata `json:"metadata,omitempty"`
ID uint `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
AppPubkey string `json:"appPubkey"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastUsedAt *time.Time `json:"lastUsedAt"`
LastSettledTransactionAt *time.Time `json:"lastSettledTransactionAt"`
ExpiresAt *time.Time `json:"expiresAt"`
Scopes []string `json:"scopes"`
MaxAmountSat uint64 `json:"maxAmount"`
BudgetUsage uint64 `json:"budgetUsage"`
BudgetRenewal string `json:"budgetRenewal"`
Isolated bool `json:"isolated"`
WalletPubkey string `json:"walletPubkey"`
UniqueWalletPubkey bool `json:"uniqueWalletPubkey"`
Balance int64 `json:"balance"`
Metadata Metadata `json:"metadata,omitempty"`
}
type ListAppsFilters struct {