Files
zeekay b0a85aec05 feat(flow): flowweb — native Go front door, embeds UI + landing, proxies API
One binary fronts flow.hanzo.ai instead of the split flow-site landing + a
separate app host + a disabled-ingress backend. flowweb go:embeds the built flow
UI (Vite frontend) plus a self-contained Hanzo Flow landing and serves it with
SPA fallback, while reverse-proxying /api, /health, /openapi.json, /docs, /logs
etc. to the flow backend (FLOW_BACKEND_URL, default 127.0.0.1:7860). One image,
one origin — hanzoai/world + hanzoai/cloud's embedded-SPA pattern.

At plain `go build` it ships the committed landing placeholder; the image build
overwrites core/cmd/flowweb/frontend/ with the real Vite bundle before `go build`.
Verified: go build clean, GET / serves the landing (200), /api/* proxies.
2026-07-17 22:41:34 -07:00

41 lines
1.1 KiB
Go

// Command flowweb is the ONE native binary that fronts flow.hanzo.ai.
//
// It go:embeds the built flow UI (the Vite frontend + the landing) and serves it
// directly, while reverse-proxying the API surface to the flow backend. One image,
// one origin — no separate static landing site, no second app host, no
// disabled-ingress split. This mirrors hanzoai/world and hanzoai/cloud's
// embedded-SPA pattern: the Go binary is the front door; the Python backend (or,
// over time, the Go flow-core engine next door in this module) answers the API.
package main
import (
"log"
"net/http"
"os"
"time"
)
func main() {
addr := ":" + env("FLOW_WEB_PORT", "8080")
backend := env("FLOW_BACKEND_URL", "http://127.0.0.1:7860")
h, err := newHandler(backend)
if err != nil {
log.Fatalf("flowweb: %v", err)
}
srv := &http.Server{
Addr: addr,
Handler: h,
ReadHeaderTimeout: 15 * time.Second,
}
log.Printf("flowweb: embedded UI on %s, API → %s", addr, backend)
log.Fatal(srv.ListenAndServe())
}
func env(k, def string) string {
if v := os.Getenv(k); v != "" {
return v
}
return def
}