UX improvements.

This commit is contained in:
2026-07-12 00:40:19 +00:00
parent 994c9fbfdf
commit 43d407bf2e
9 changed files with 552 additions and 267 deletions

View File

@@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"time"
@@ -329,24 +330,87 @@ func (api *API) StatusHandler(w http.ResponseWriter, r *http.Request, startTime
})
}
// getDaemonStatus checks whether each managed daemon is active.
func getDaemonStatus() map[string]map[string]bool {
daemons := map[string]string{
"dnsmasq": "dnsmasq.service",
"haproxy": "haproxy.service",
"wireguard": "wg-quick@wg0.service",
"crowdsec": "crowdsec.service",
// getDaemonStatus checks whether each managed daemon is active and gets its version.
func getDaemonStatus() map[string]map[string]any {
type daemonInfo struct {
unit string
verCmd []string // command to get version
verParse func(string) string // extract version from output
}
result := make(map[string]map[string]bool, len(daemons)+1)
for name, unit := range daemons {
err := exec.Command("systemctl", "is-active", "--quiet", unit).Run()
result[name] = map[string]bool{"active": err == nil}
firstWord := func(s string) string {
if i := strings.IndexByte(s, ' '); i > 0 {
return s[:i]
}
return strings.TrimSpace(s)
}
daemons := map[string]daemonInfo{
"dnsmasq": {
unit: "dnsmasq.service",
verCmd: []string{"dnsmasq", "--version"},
verParse: func(out string) string {
// "Dnsmasq version 2.90 ..."
if i := strings.Index(out, "version "); i >= 0 {
return firstWord(out[i+8:])
}
return ""
},
},
"haproxy": {
unit: "haproxy.service",
verCmd: []string{"haproxy", "-v"},
verParse: func(out string) string {
// "HAProxy version 2.8.5-1 ..."
if i := strings.Index(out, "version "); i >= 0 {
return firstWord(out[i+8:])
}
return ""
},
},
"wireguard": {
unit: "wg-quick@wg0.service",
verCmd: []string{"wg", "--version"},
verParse: func(out string) string {
// "wireguard-tools v1.0.20210914 - ..."
if i := strings.Index(out, " v"); i >= 0 {
return firstWord(out[i+1:])
}
return ""
},
},
"crowdsec": {
unit: "crowdsec.service",
verCmd: []string{"cscli", "version", "--raw"},
verParse: func(out string) string {
return firstWord(out)
},
},
}
result := make(map[string]map[string]any, len(daemons)+1)
for name, info := range daemons {
err := exec.Command("systemctl", "is-active", "--quiet", info.unit).Run()
entry := map[string]any{"active": err == nil}
if out, verErr := exec.Command(info.verCmd[0], info.verCmd[1:]...).Output(); verErr == nil {
if v := info.verParse(string(out)); v != "" {
entry["version"] = v
}
}
result[name] = entry
}
// nftables has no persistent service — check if the wild-cloud table exists
err := exec.Command("sudo", "nft", "list", "table", "inet", "wild-cloud").Run()
result["nftables"] = map[string]bool{"active": err == nil}
nftErr := exec.Command("sudo", "nft", "list", "table", "inet", "wild-cloud").Run()
nftEntry := map[string]any{"active": nftErr == nil}
if out, verErr := exec.Command("nft", "--version").Output(); verErr == nil {
// "nftables v1.0.9 (Old Doc Yak #3)"
s := string(out)
if i := strings.Index(s, " v"); i >= 0 {
nftEntry["version"] = firstWord(s[i+1:])
}
}
result["nftables"] = nftEntry
return result
}