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

132 lines
3.0 KiB
Go

package rest
import (
"sort"
"strconv"
"strings"
"github.com/zap-proto/zip"
"github.com/hanzoai/commerce/config"
"github.com/hanzoai/commerce/datastore"
"github.com/hanzoai/commerce/log"
"github.com/hanzoai/commerce/middleware"
"github.com/hanzoai/commerce/models/mixin"
"github.com/hanzoai/commerce/models/organization"
"github.com/hanzoai/commerce/util/json/http"
"github.com/hanzoai/commerce/util/template"
)
// Wrapped model, with a few display helpers
type endpoint struct {
mixin.BaseModel
rest *Rest
id string
count string
prefix string
kind string
}
func newEndpoint(db *datastore.Datastore, r *Rest) *endpoint {
endpoint := new(endpoint)
endpoint.prefix = strings.TrimLeft(r.Prefix, "/")
endpoint.rest = r
endpoint.kind = r.Kind
endpoint.BaseModel.Init(db, r.newKind())
return endpoint
}
func (e *endpoint) FirstId() string {
if e.id == "" {
if ok, _ := e.BaseModel.Query().Get(); ok {
e.id = e.BaseModel.Id()
} else {
e.id = "<id>"
}
}
return e.id
}
func (e *endpoint) EntityCount() string {
if e.count == "" {
count, _ := e.Query().All().Count()
e.count = strconv.Itoa(count)
}
return e.count
}
func (e *endpoint) Url() string {
return config.UrlFor("api", "/"+e.prefix+e.kind)
}
func (e *endpoint) UrlWithId() string {
return e.Url() + "/" + e.FirstId()
}
type byKind []*Rest
func (e byKind) Len() int { return len(e) }
func (e byKind) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
func (e byKind) Less(i, j int) bool { return e[i].Kind < e[j].Kind }
func ListRoutes() zip.Handler {
sort.Sort(byKind(restApis))
return func(c *zip.Ctx) error {
// Only show routes in development mode
if !config.IsDevelopment {
return c.Next()
}
ctx := c.Context()
// Get default org
db := datastore.New(ctx)
org := organization.New(db)
err := org.GetOrCreate("Name=", "suchtees")
if err != nil {
return http.Fail(c, 500, "Unable to fetch organization", err)
}
// Get namespaced datastore context
orgDb := datastore.New(org.Namespaced(ctx))
// We special case order endpoint because of a few useful API calls we want to work.
var orderEndpoint *endpoint
// Wrap models for display
endpoints := make([]*endpoint, len(restApis))
for i, r := range restApis {
// Create fancy endpoint documentation for this API. If it has a
// prefix of /c/, all calls should be made against the default
// namespace, otherwise our fixture organization's namespace.
if r.Prefix == "/c/" {
endpoints[i] = newEndpoint(db, r)
} else {
endpoints[i] = newEndpoint(orgDb, r)
}
// Check if this is the order endpoint, if so we'll save a reference for later.
if r.Kind == "order" {
orderEndpoint = endpoints[i]
}
}
token := middleware.GetAccessToken(c)
log.Debug("fixture organization id: %v", org.Id())
// Generate kind map
return template.Render(c, "index.html",
"email", "[email protected]",
"endpoints", endpoints,
"orderEndpoint", orderEndpoint,
"organization", org,
"password", "suchtees",
"token", token,
)
}
}