templates

package
v1.801.360 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package templates is a gallery of starter kits you can deploy as they come.

The Hanzo starter-kit gallery at /v1/templates, 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 — every route is a TYPED op (zip.Get[In, Out] and friends), so each is ONE registry entry the document, the MCP tool, the CLI command and the generated SDK method are all projected from:

GET    /v1/templates          public catalog + (validated caller) that org's own -> {data:[StarterKit]}
GET    /v1/templates/:slug    one kit: the caller org's own, else public         -> StarterKit
POST   /v1/templates          publish a kit PRIVATE to the caller's org          -> 201 StarterKit
PUT    /v1/templates/:slug    replace the caller org's own kit                   -> StarterKit
DELETE /v1/templates/:slug    delete the caller org's own kit                    -> 204

The value is StarterKit, not Template: the OpenAPI schema namespace is FLAT across the whole fleet and apps/guide already publishes a `Template` (a Guide playbook prompt/snippet, {id,title,body,enabled}). openapi.Weave refuses one name with two shapes — every generated SDK would bind whichever it read last — so the name that was not yet published is the one that yields, qualified by the value's own vocabulary ("Template is one starter kit", below) rather than by a place.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount registers the templates surface. templates is a "complex" mount now (a package-global `mounted` so Lookup is ONE door for the projects fork flow, and a shutdown that closes the store), so it builds the Service value directly.

func Shutdown

func Shutdown(_ context.Context) error

Shutdown closes the per-org store. Idempotent.

Types

type StarterKit added in v1.801.350

type StarterKit struct {
	Slug        string   `json:"slug"`        // the kit's identity — lowercase alphanumeric with dashes, max 40
	Title       string   `json:"title"`       // display name
	Category    string   `json:"category"`    // groups the kit in the gallery browser ("Portfolio", "SaaS")
	Description string   `json:"description"` // the browse-card blurb
	Framework   string   `json:"framework"`   // the stack the kit is built on ("Next.js 14.2 + TS")
	Features    []string `json:"features"`    // the highlights the card lists, at most 32
	UseCase     string   `json:"useCase"`     // what the kit is for, in a phrase
	// Tier is public-gallery curation, carried verbatim from the embedded catalog.
	// No request can set it — neither write body has the field and neither builds a
	// kit carrying one — so it is absent on every customer-published kit.
	Tier *int `json:"tier,omitempty"`
	// Rating is public-gallery curation, on the same terms as Tier: catalog-only,
	// never accepted from a request, absent on a customer's own kit.
	Rating   *float64  `json:"rating,omitempty"`
	Source   string    `json:"source"`             // the repository the kit is forked from
	Preview  string    `json:"preview"`            // the still image the browse card renders
	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
}

StarterKit 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

func List() ([]StarterKit, error)

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

func Lookup(ctx context.Context, org, slug string) (StarterKit, bool)

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 (StarterKit) Variant added in v1.801.350

func (t StarterKit) Variant(id string) (Variant, bool)

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 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 StarterKit 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) Close

func (s *Store) Close() error

Close closes the underlying database.

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, org, slug string) (bool, error)

Delete removes org's own template at slug, reporting whether a row went.

func (*Store) Get

func (s *Store) Get(ctx context.Context, org, slug string) (StarterKit, bool, error)

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

func (s *Store) List(ctx context.Context, org string) ([]StarterKit, error)

List returns org's own templates (tenant-scoped read; there is no all-orgs counterpart, because nothing in this subsystem legitimately wants one).

func (*Store) Put

func (s *Store) Put(ctx context.Context, t StarterKit, create bool, now int64) error

Put writes t for its own org. create=true inserts and reports errConflict if the org already holds that slug; create=false replaces an existing row and reports sql.ErrNoRows if there is none — so "publish" can never silently clobber and "edit" can never silently create.

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"`              // the repository this shape is forked from; the synthesized default shape carries the template's own
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL