mirror of
https://github.com/hanzoai/platform-services.git
synced 2026-10-30 18:23:56 +00:00
78 lines
3.3 KiB
Go
78 lines
3.3 KiB
Go
package server
|
|
|
|
import (
|
|
"github.com/hanzoai/base/core"
|
|
"github.com/hanzoai/base/tools/hook"
|
|
)
|
|
|
|
// This service is overwhelmingly a typed front for OTHER services: 10 of its 11
|
|
// categories are pass-throughs (config.go holds the upstream wiring). It owns
|
|
// exactly ONE Base collection — cloud_spend_alert — the native home of the
|
|
// console spendAlertRouter's `cloudSpendAlert` table. Documented per category:
|
|
//
|
|
// cloudStatus → PROXY (incident.io) — no local state
|
|
// platform → PROXY (PaaS) — no local state
|
|
// infrastructure→ PROXY (PaaS) — no local state
|
|
// explorer → PROXY (Lux Explorer) — no local state
|
|
// search → PROXY (Hanzo Search API) — no local state
|
|
// vector → PROXY (Hanzo Vector API) — no local state
|
|
// nlFilter → PROXY (Bedrock + AI Features) — no local state
|
|
// bots → PROXY (Bot Gateway/Commerce/KMS) — no local state
|
|
// kms → PROXY (Hanzo KMS) — no local state (KMS is SoT)
|
|
// billing → PROXY (Stripe) — no local state
|
|
// spendAlert → BASE (cloud_spend_alert) — STORED here ← the one row
|
|
//
|
|
// There is no cache layer: proxied reads go to the upstream every call (the
|
|
// routers did the same; cloudStatus's 60s cache lived in the console process and
|
|
// is intentionally not reintroduced — add it only if the upstream rate-limits).
|
|
|
|
// SpendAlertCollection is the single Base collection backing the spendAlert
|
|
// category. One row per alert, every row scoped by `org`.
|
|
const SpendAlertCollection = "cloud_spend_alert"
|
|
|
|
// Field names mirror the Prisma cloudSpendAlert model the router read/wrote.
|
|
const (
|
|
faOrg = "org"
|
|
faTitle = "title"
|
|
faThreshold = "threshold"
|
|
faTriggeredAt = "triggeredAt"
|
|
faCreated = "created"
|
|
faUpdated = "updated"
|
|
)
|
|
|
|
// RegisterCollections ensures the cloud_spend_alert collection exists at
|
|
// startup. Idempotent: re-running finds the existing collection and no-ops.
|
|
// Wired via OnBootstrap so it runs once before the ZAP listener accepts calls.
|
|
func RegisterCollections(app core.App) {
|
|
app.OnBootstrap().Bind(&hook.Handler[*core.BootstrapEvent]{
|
|
Id: "platformServicesCollections",
|
|
Func: func(e *core.BootstrapEvent) error {
|
|
if err := e.Next(); err != nil {
|
|
return err
|
|
}
|
|
return EnsureCollection(app)
|
|
},
|
|
})
|
|
}
|
|
|
|
// EnsureCollection creates the cloud_spend_alert collection if absent. Exported
|
|
// so tests / one-shot migrations can provision it directly. Idempotent.
|
|
func EnsureCollection(app core.App) error {
|
|
if _, err := app.FindCollectionByNameOrId(SpendAlertCollection); err == nil {
|
|
return nil // already exists
|
|
}
|
|
|
|
col := core.NewBaseCollection(SpendAlertCollection)
|
|
col.Fields.Add(&core.TextField{Name: faOrg, Required: true, Max: 255})
|
|
col.Fields.Add(&core.TextField{Name: faTitle, Required: true, Max: 100})
|
|
col.Fields.Add(&core.NumberField{Name: faThreshold, Required: true})
|
|
col.Fields.Add(&core.DateField{Name: faTriggeredAt}) // null until the alert fires
|
|
col.Fields.Add(&core.AutodateField{Name: faCreated, OnCreate: true})
|
|
col.Fields.Add(&core.AutodateField{Name: faUpdated, OnCreate: true, OnUpdate: true})
|
|
|
|
// Index on org: alerts are always queried by org.
|
|
col.AddIndex("idx_cloud_spend_alert_org", false, faOrg, "")
|
|
|
|
return app.Save(col)
|
|
}
|