1
0
forked from wrenn/wrenn

Add template build system with admin panel, async workers, and FlattenRootfs RPC

Introduces an end-to-end template building pipeline: admins submit a recipe
(list of shell commands) via the dashboard, a Redis-backed worker pool spins
up a sandbox, executes each command, and produces either a full snapshot
(with healthcheck) or an image-only template (rootfs flattened via a new
FlattenRootfs host-agent RPC). Build progress and per-step logs are persisted
to a new template_builds table and polled by the frontend.

Backend:
- New FlattenRootfs RPC (proto + host agent + sandbox manager)
- BuildService with Redis queue (BLPOP) and configurable worker pool (default 2)
- Admin-only REST endpoints: POST/GET /v1/admin/builds, GET /v1/admin/builds/{id}
- Migration for template_builds table with JSONB logs and recipe columns
- sqlc queries for build CRUD and progress updates

Frontend:
- /admin/templates page with Templates + Builds tabs
- Create Template dialog with recipe textarea, healthcheck, specs
- Build history with expandable per-step logs, status badges, progress bars
- Auto-polling every 3s for active builds
- AdminSidebar updated with Templates nav item
This commit is contained in:
2026-03-26 15:27:21 +06:00
parent 6898528096
commit 1ce62934b3
17 changed files with 2031 additions and 26 deletions

View File

@ -22,7 +22,8 @@ var openapiYAML []byte
// Server is the control plane HTTP server.
type Server struct {
router chi.Router
router chi.Router
BuildSvc *service.BuildService
}
// New constructs the chi router and registers all routes.
@ -47,6 +48,7 @@ func New(
teamSvc := &service.TeamService{DB: queries, Pool: pgPool, HostPool: pool}
auditSvc := &service.AuditService{DB: queries}
statsSvc := &service.StatsService{DB: queries, Pool: pgPool}
buildSvc := &service.BuildService{DB: queries, Redis: rdb, Pool: pool, Scheduler: sched}
al := audit.New(queries)
@ -65,6 +67,7 @@ func New(
auditH := newAuditHandler(auditSvc)
statsH := newStatsHandler(statsSvc)
metricsH := newSandboxMetricsHandler(queries, pool)
buildH := newBuildHandler(buildSvc)
// OpenAPI spec and docs.
r.Get("/openapi.yaml", serveOpenAPI)
@ -174,9 +177,12 @@ func New(
r.Use(requireJWT(jwtSecret))
r.Use(requireAdmin(queries))
r.Put("/teams/{id}/byoc", teamH.SetBYOC)
r.Post("/builds", buildH.Create)
r.Get("/builds", buildH.List)
r.Get("/builds/{id}", buildH.Get)
})
return &Server{router: r}
return &Server{router: r, BuildSvc: buildSvc}
}
// Handler returns the HTTP handler.