Serve a local web UI from bangerd
Add a localhost-only web console so VM and image management no longer depends on the CLI for every inspection and lifecycle action. Wire bangerd up to a configurable web listener, expose dashboard and async image-build state through the daemon, and serve CSRF-protected HTML pages with host-path picking, VM/image detail views, logs, ports, and progress polling for long-running operations. Keep the browser path aligned with the existing sudo and host-owned artifact model: surface sudo readiness, print the web URL in daemon status, and document the new workflow. Polish the UI with resource usage cards, clearer clickable affordances, cancel paths, confirmation prompts, image-name links, and HTTP port links. Validation: GOCACHE=/tmp/banger-gocache go test ./...
This commit is contained in:
parent
30f0c0b54a
commit
2362d0ae39
24 changed files with 3308 additions and 52 deletions
65
internal/daemon/web.go
Normal file
65
internal/daemon/web.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"banger/internal/model"
|
||||
"banger/internal/paths"
|
||||
"banger/internal/webui"
|
||||
)
|
||||
|
||||
func (d *Daemon) startWebServer() error {
|
||||
listenAddr := strings.TrimSpace(d.config.WebListenAddr)
|
||||
if listenAddr == "" {
|
||||
d.webURL = ""
|
||||
return nil
|
||||
}
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
if d.logger != nil {
|
||||
d.logger.Error("web ui listen failed", "addr", listenAddr, "error", err.Error())
|
||||
}
|
||||
return fmt.Errorf("web ui listen on %s: %w", listenAddr, err)
|
||||
}
|
||||
d.webListener = listener
|
||||
d.webURL = "http://" + listener.Addr().String()
|
||||
d.webServer = &http.Server{
|
||||
Handler: webui.NewHandler(d),
|
||||
ReadHeaderTimeout: 5 * time.Second,
|
||||
}
|
||||
if d.logger != nil {
|
||||
d.logger.Info("web ui serving", "addr", listener.Addr().String(), "url", d.webURL)
|
||||
}
|
||||
go func() {
|
||||
err := d.webServer.Serve(listener)
|
||||
if err == nil || errors.Is(err, http.ErrServerClosed) {
|
||||
return
|
||||
}
|
||||
if d.logger != nil {
|
||||
d.logger.Error("web ui serve failed", "addr", listener.Addr().String(), "error", err.Error())
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Daemon) Layout() paths.Layout {
|
||||
return d.layout
|
||||
}
|
||||
|
||||
func (d *Daemon) Config() model.DaemonConfig {
|
||||
return d.config
|
||||
}
|
||||
|
||||
func (d *Daemon) ListVMs(ctx context.Context) ([]model.VMRecord, error) {
|
||||
return d.store.ListVMs(ctx)
|
||||
}
|
||||
|
||||
func (d *Daemon) ListImages(ctx context.Context) ([]model.Image, error) {
|
||||
return d.store.ListImages(ctx)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue