Documentation
¶
Overview ¶
Package templates mounts /v1/templates — the Hanzo starter-kit gallery, in TWO layers that never mix:
- the PUBLIC catalog: deployable app/site scaffolds (source of truth: hanzoai/gallery), vendored so the unified `cloud` binary ships it with no external dependency. Reference content — embedded, immutable, and with NO write route, so nothing a customer does can add to it.
- a customer's OWN templates: rows in {DataDir}/templates.db keyed by the gateway-minted org (principal.Org — never a request field), PRIVATE to that org. Only that org lists, reads, edits, deletes, or forks them.
Two layers rather than one visibility flag is the whole safety argument: a private template cannot surface in the public hanzo.app catalog by CONSTRUCTION — it lives in a different container, reached only by a query that binds org — not by a filter every future reader has to remember. An anonymous GET never touches the store at all.
A slug is single-valued across both layers: publishing over a public slug is 409, so a slug still names exactly one template and no org can shadow the gallery.
ONE template is ONE entry. The shapes it ships in — format, page, theme — are Variants inside that entry, chosen at fork time.
Surface:
GET /v1/templates public catalog + (validated caller) that org's own -> {data:[Template]}
GET /v1/templates/:slug one template: the caller org's own, else public -> Template
POST /v1/templates publish a template PRIVATE to the caller's org -> 201 Template
PUT /v1/templates/:slug replace the caller org's own template -> Template
DELETE /v1/templates/:slug delete the caller org's own template -> 204
Index ¶
- func Mount(app cloud.Router, deps cloud.Deps) error
- func Shutdown(_ context.Context) error
- type Store
- func (s *Store) Close() error
- func (s *Store) Delete(ctx context.Context, org, slug string) (bool, error)
- func (s *Store) Get(ctx context.Context, org, slug string) (Template, bool, error)
- func (s *Store) List(ctx context.Context, org string) ([]Template, error)
- func (s *Store) Put(ctx context.Context, t Template, create bool, now int64) error
- type Template
- type Variant
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store holds every org's PRIVATE templates — ONE SQLite file ({DataDir}/templates.db) whose isolation key is the org column, the same discipline clients/projects and clients/marketplace keep.
The PUBLIC catalog is deliberately NOT in this table: it stays the embedded catalog.json, which has no write route at all. So a private row has no path into the public gallery by CONSTRUCTION, not by a filter someone has to remember to write — the two live in different containers, and every read of this one binds org.
The whole Template is one JSON `doc`: it is already the exact shape the API serves, so a row can never drift from it as fields are added, and (org, slug) — the only two things ever queried on — stay real columns.
func (*Store) Get ¶
Get returns org's own template at slug. found=false (nil error) when absent — including when the row belongs to ANOTHER org, because the WHERE binds org.
func (*Store) List ¶
List returns org's own templates (tenant-scoped read; there is no all-orgs counterpart, because nothing in this subsystem legitimately wants one).
type Template ¶
type Template struct {
Slug string `json:"slug"`
Title string `json:"title"`
Category string `json:"category"`
Description string `json:"description"`
Framework string `json:"framework"`
Features []string `json:"features"`
UseCase string `json:"useCase"`
Tier *int `json:"tier,omitempty"`
Rating *float64 `json:"rating,omitempty"`
Source string `json:"source"`
Preview string `json:"preview"`
Demo string `json:"demo,omitempty"` // live demo (<slug>.hanzo.app), when deployed
Variants []Variant `json:"variants,omitempty"` // the shapes this template ships in
Org string `json:"org,omitempty"` // owner of a PRIVATE template; empty in the public catalog
}
Template is one starter kit as the console gallery browser consumes it. The `Source`/`Preview` URLs point at the live gallery (gallery.hanzo.ai) for a public entry and at whatever the customer supplies for one of their own; `Demo` is the deployed site itself.
Org is the OWNER of a private template, stamped by the SERVER from the row's key. It is empty on every public catalog entry — that emptiness is what the console badges "yours" on, and it is never read from a request body.
func List ¶
List returns the validated PUBLIC starter-kit catalog (the SAME slice the anonymous HTTP GET serves). Read-only reference content; callers must not mutate the returned slice. Private org templates are deliberately NOT here — they are reachable only through Lookup, which takes the org it isolates on.
func Lookup ¶
Lookup resolves ONE template for a caller org: that org's OWN private template first, then the public catalog. It is the single door other subsystems (the projects fork flow) read templates through, so "which templates may this org use" is answered in exactly one place. org "" (anonymous/unvalidated) resolves against the public catalog only.
func (Template) Variant ¶
Variant resolves a variant id against the template and is the ONE place the resolution rule lives. The empty id means "no preference" and yields the template's first (default) shape; a template that ships in a single shape answers with itself, so callers never branch on len(Variants).
type Variant ¶
type Variant struct {
ID string `json:"id"` // selector, unique within the template ("react", "grid-3-fluid")
Label string `json:"label"` // human label for the picker
Kind string `json:"kind"` // the axis it varies: format | page | theme
Framework string `json:"framework,omitempty"` // only when it differs from the template's
Source string `json:"source"`
}
Variant is one SHAPE of a template: the same design in another format (html/react/bootstrap), on another page (folio's about/contact/grid-3), or in another theme. A variant is an option resolved at fork time from what the user asks for — never a catalog row of its own, which is what made one portfolio template read as 26 templates and one dashboard as 2.