From ed4f8be019a1f24f709a07a436784cd493dcfbe8 Mon Sep 17 00:00:00 2001 From: Michael Chus Date: Fri, 27 Mar 2026 22:47:59 +0300 Subject: [PATCH] =?UTF-8?q?fix(webui):=20services=20table=20=E2=80=94=20sh?= =?UTF-8?q?ow=20state=20badge,=20full=20status=20on=20click?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace raw systemctl output in table cell with: - state badge (active/failed/inactive) — click to expand - full systemctl status in collapsible pre block (max 200px scroll) Fixes layout explosion from multi-line status text in table. Co-Authored-By: Claude Sonnet 4.6 --- audit/internal/app/app.go | 5 +++++ audit/internal/app/app_test.go | 4 ++++ audit/internal/webui/api.go | 10 ++++++---- audit/internal/webui/pages.go | 17 ++++++++++++++--- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/audit/internal/app/app.go b/audit/internal/app/app.go index 6a14e7f..95c1580 100644 --- a/audit/internal/app/app.go +++ b/audit/internal/app/app.go @@ -57,6 +57,7 @@ type networkManager interface { type serviceManager interface { ListBeeServices() ([]string, error) + ServiceState(name string) string ServiceStatus(name string) (string, error) ServiceDo(name string, action platform.ServiceAction) (string, error) } @@ -356,6 +357,10 @@ func (a *App) ListBeeServices() ([]string, error) { return a.services.ListBeeServices() } +func (a *App) ServiceState(name string) string { + return a.services.ServiceState(name) +} + func (a *App) ServiceStatus(name string) (string, error) { return a.services.ServiceStatus(name) } diff --git a/audit/internal/app/app_test.go b/audit/internal/app/app_test.go index c15ac45..bd1d79e 100644 --- a/audit/internal/app/app_test.go +++ b/audit/internal/app/app_test.go @@ -52,6 +52,10 @@ func (f fakeServices) ListBeeServices() ([]string, error) { return nil, nil } +func (f fakeServices) ServiceState(name string) string { + return "active" +} + func (f fakeServices) ServiceStatus(name string) (string, error) { return f.serviceStatusFn(name) } diff --git a/audit/internal/webui/api.go b/audit/internal/webui/api.go index de8bc4d..392e335 100644 --- a/audit/internal/webui/api.go +++ b/audit/internal/webui/api.go @@ -236,13 +236,15 @@ func (h *handler) handleAPIServicesList(w http.ResponseWriter, r *http.Request) return } type serviceInfo struct { - Name string `json:"name"` - Status string `json:"status"` + Name string `json:"name"` + State string `json:"state"` + Body string `json:"body"` } result := make([]serviceInfo, 0, len(names)) for _, name := range names { - status, _ := h.opts.App.ServiceStatus(name) - result = append(result, serviceInfo{Name: name, Status: status}) + state := h.opts.App.ServiceState(name) + body, _ := h.opts.App.ServiceStatus(name) + result = append(result, serviceInfo{Name: name, State: state, Body: body}) } writeJSON(w, result) } diff --git a/audit/internal/webui/pages.go b/audit/internal/webui/pages.go index 92f3e24..85c08b6 100644 --- a/audit/internal/webui/pages.go +++ b/audit/internal/webui/pages.go @@ -515,9 +515,16 @@ func renderServices() string { function loadServices() { fetch('/api/services').then(r=>r.json()).then(svcs => { const rows = svcs.map(s => { - const st = s.status||'unknown'; - const badge = st.includes('active') ? 'badge-ok' : st.includes('failed') ? 'badge-err' : 'badge-warn'; - return ''+s.name+''+st+'' + + const st = s.state||'unknown'; + const badge = st==='active' ? 'badge-ok' : st==='failed' ? 'badge-err' : 'badge-warn'; + const id = 'svc-body-'+s.name.replace(/[^a-z0-9]/g,'-'); + const body = (s.body||'').replace(//g,'>'); + return '' + + ''+s.name+'' + + ''+st+' ▾' + + '' + + '' + + '' + ' ' + ' ' + '' + @@ -527,6 +534,10 @@ function loadServices() { ''+rows+'
ServiceStatusActions
'; }); } +function toggleBody(id) { + const el = document.getElementById(id); + if (el) el.style.display = el.style.display==='none' ? 'block' : 'none'; +} function svcAction(name, action) { fetch('/api/services/action',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name,action})}) .then(r=>r.json()).then(d => {