Files
bank/hooks/wallets.go
zeekay e63bbf3c56 feat: complete NestJS→Base port — documents, wallets, conversions, email
Collections:
- documents: KYC file uploads (PDF/JPG/PNG, 10MB max, owner-scoped)
- wallets: multi-currency wallets with external IDs
- conversions: FX conversion records with quote tracking

Hooks:
- documents: type validation, status change audit trail
- wallets: auto-create balance on wallet create, status audit
- email: SMTP notifications on tx completion + KYC status change

Routes:
- GET /v1/accounts/:id/wallets
- GET /v1/accounts/:id/transactions (paginated history)

Full NestJS bank replacement: 10 collections, 13 hooks, 9 routes.
2026-03-31 16:26:23 -07:00

70 lines
1.9 KiB
Go

package hooks
import (
"log/slog"
"github.com/hanzoai/base/core"
"github.com/luxfi/bank/collections"
)
// RegisterWalletHooks attaches hooks for wallet lifecycle.
func RegisterWalletHooks(app core.App) {
// Default status on create + ensure balance record exists.
app.OnRecordCreateExecute(collections.WalletCollectionName).BindFunc(func(e *core.RecordEvent) error {
if err := e.Next(); err != nil {
return err
}
if e.Record.GetString("status") == "" {
e.Record.Set("status", "active")
}
// Create balance record if it doesn't exist for this account+currency.
accountId := e.Record.GetString("account")
currency := e.Record.GetString("currency")
existing, _ := app.FindFirstRecordByFilter(
collections.BalanceCollectionName,
`account = {:accountId} && currency = {:currency}`,
map[string]any{"accountId": accountId, "currency": currency},
)
if existing == nil {
balCol, err := app.FindCollectionByNameOrId(collections.BalanceCollectionName)
if err == nil {
bal := core.NewRecord(balCol)
bal.Set("account", accountId)
bal.Set("currency", currency)
bal.Set("available", 0)
bal.Set("held", 0)
_ = app.Save(bal)
}
}
return nil
})
// Audit on status change.
app.OnRecordUpdateExecute(collections.WalletCollectionName).BindFunc(func(e *core.RecordEvent) error {
if err := e.Next(); err != nil {
return err
}
oldStatus := e.Record.Original().GetString("status")
newStatus := e.Record.GetString("status")
if oldStatus != newStatus && (newStatus == "suspended" || newStatus == "closed") {
writeAudit(app, e.Record.GetString("account"), "system", "wallet_"+newStatus, map[string]any{
"walletId": e.Record.Id,
"currency": e.Record.GetString("currency"),
})
app.Logger().Info("wallet status changed",
slog.String("walletId", e.Record.Id),
slog.String("from", oldStatus),
slog.String("to", newStatus),
)
}
return nil
})
}