Files
zeekay 1943f1c093 Hanzo Commerce
Multi-tenant commerce and billing: orders, subscriptions, invoices, plans,
usage metering and a credit ledger. One Go binary. MIT OR Apache-2.0.

Claude-Session: https://claude.ai/code/session_01SAtaDFm8VDiWrhYHNNtxMw
2026-09-23 21:52:16 -07:00

45 lines
1.8 KiB
Go

package catalog
import (
"os"
"github.com/zap-proto/zip"
"github.com/hanzoai/commerce/models/catalogentry"
"github.com/hanzoai/commerce/util/json/http"
)
// RefreshModels PULLS the upstream catalog and lands it. POST /catalog/models is
// the PUSH of already-decided rows; this is the same landing, for an upstream
// that has to be read rather than told. Both funnel through
// catalogentry.UpsertModels, so the rule that a sync owns cost and admin owns
// price is enforced in one place regardless of which endpoint a row came through.
//
// Scheduling is a cron entry poking this with the service token, the same shape
// billing-autorecharge uses, so there is one way to run a periodic job. That is
// why the gate is requirePlatform and not requireSuperAdmin: the service token
// is a platform principal but carries no SuperAdmin claim, so under the narrower
// gate every scheduled run 403'd and the model catalog was never populated at
// all.
//
// It is deliberately NOT run at boot: a boot-time call to a third party is a
// boot hazard, and the catalog that is already stored is the one to serve until
// a scheduled run says otherwise.
func RefreshModels(c *zip.Ctx) error {
if !requirePlatform(c) {
return nil
}
rows, err := catalogentry.FetchOpenRouter(c.Context(), os.Getenv("OPENROUTER_API_KEY"))
if err != nil {
// Nothing has been written, so the last good catalog stands. A sync that
// cannot see its upstream must never conclude the upstream is empty —
// that conclusion would withdraw every model we resell.
return http.Fail(c, 502, "failed to read the upstream model catalog", err)
}
res, err := catalogentry.Refresh(catalogDB(c), catalogentry.ServesOpenRouter, rows)
if err != nil {
return http.Fail(c, 500, "failed to refresh the model catalog", err)
}
return http.Render(c, 200, res)
}