Files
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

58 lines
1.5 KiB
Go

package hooks
import (
"log/slog"
"github.com/hanzoai/base/apis"
"github.com/hanzoai/base/core"
"github.com/luxfi/bank/collections"
)
// RegisterDocumentHooks attaches hooks for KYC document lifecycle.
func RegisterDocumentHooks(app core.App) {
// Set default status on create.
app.OnRecordCreate(collections.DocumentCollectionName).BindFunc(func(e *core.RecordEvent) error {
if e.Record.GetString("status") == "" {
e.Record.Set("status", "pending")
}
// Validate document type.
validTypes := map[string]bool{
"passport": true, "drivers_license": true, "utility_bill": true,
"bank_statement": true, "selfie": true, "proof_of_address": true,
}
if !validTypes[e.Record.GetString("type")] {
return apis.NewBadRequestError("invalid document type", nil)
}
return e.Next()
})
// Audit trail on status change (approved/rejected).
app.OnRecordUpdateExecute(collections.DocumentCollectionName).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 {
writeAudit(app, e.Record.GetString("account"), "system", "document_"+newStatus, map[string]any{
"documentId": e.Record.Id,
"type": e.Record.GetString("type"),
"from": oldStatus,
"to": newStatus,
})
app.Logger().Info("document status changed",
slog.String("documentId", e.Record.Id),
slog.String("from", oldStatus),
slog.String("to", newStatus),
)
}
return nil
})
}