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 ./...
63 lines
1.8 KiB
Go
63 lines
1.8 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
|
|
"banger/internal/api"
|
|
"banger/internal/model"
|
|
"banger/internal/system"
|
|
)
|
|
|
|
func (d *Daemon) DashboardSummary(ctx context.Context) (api.DashboardSummary, error) {
|
|
summary := api.DashboardSummary{
|
|
GeneratedAt: model.Now(),
|
|
Sudo: api.SudoStatus{
|
|
Command: "sudo -v",
|
|
},
|
|
}
|
|
if err := system.CheckSudo(ctx); err != nil {
|
|
summary.Sudo.Error = err.Error()
|
|
} else {
|
|
summary.Sudo.Available = true
|
|
}
|
|
|
|
if host, err := system.ReadHostResources(); err == nil {
|
|
summary.Host.CPUCount = host.CPUCount
|
|
summary.Host.TotalMemoryBytes = host.TotalMemoryBytes
|
|
}
|
|
if usage, err := system.ReadFilesystemUsage(d.layout.StateDir); err == nil {
|
|
summary.Host.StateFilesystemTotalBytes = usage.TotalBytes
|
|
summary.Host.StateFilesystemFreeBytes = usage.FreeBytes
|
|
}
|
|
|
|
images, err := d.store.ListImages(ctx)
|
|
if err != nil {
|
|
return api.DashboardSummary{}, err
|
|
}
|
|
for _, image := range images {
|
|
summary.Banger.ImageCount++
|
|
if image.Managed {
|
|
summary.Banger.ManagedImageCount++
|
|
}
|
|
}
|
|
|
|
vms, err := d.store.ListVMs(ctx)
|
|
if err != nil {
|
|
return api.DashboardSummary{}, err
|
|
}
|
|
for _, vm := range vms {
|
|
summary.Banger.VMCount++
|
|
summary.Banger.ConfiguredVCPUCount += vm.Spec.VCPUCount
|
|
summary.Banger.ConfiguredMemoryBytes += int64(vm.Spec.MemoryMiB) * 1024 * 1024
|
|
summary.Banger.ConfiguredDiskBytes += vm.Spec.WorkDiskSizeBytes
|
|
summary.Banger.UsedSystemOverlayBytes += vm.Stats.SystemOverlayBytes
|
|
summary.Banger.UsedWorkDiskBytes += vm.Stats.WorkDiskBytes
|
|
if vm.State == model.VMStateRunning && system.ProcessRunning(vm.Runtime.PID, vm.Runtime.APISockPath) {
|
|
summary.Banger.RunningVMCount++
|
|
summary.Banger.RunningCPUPercent += vm.Stats.CPUPercent
|
|
summary.Banger.RunningRSSBytes += vm.Stats.RSSBytes
|
|
summary.Banger.RunningVSZBytes += vm.Stats.VSZBytes
|
|
}
|
|
}
|
|
return summary, nil
|
|
}
|