feat(ux): Hostname in Sidebar + CPU-Anzahl neben Load Average

- Sidebar: Hostname unter der Versionsnummer (aus /system/health);
  10px, gedimmt — beim Verwalten mehrerer Boxen sofort erkennbar
- /system/resources: neues Feld num_cpus (via runtime.NumCPU)
- Dashboard Resources-Card: Load-Average-Label zeigt "(N CPU)" —
  1.80 auf 1 CPU ist saturiert, auf 4 CPUs ist es harmlos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-25 09:49:15 +02:00
parent c99b0cef4c
commit 757b58f607
7 changed files with 25 additions and 9 deletions

View File

@@ -1 +1 @@
1.1.98
1.1.99

View File

@@ -60,7 +60,7 @@ import (
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
)
var version = "1.1.98"
var version = "1.1.99"
func main() {
addr := os.Getenv("EDGEGUARD_API_ADDR")

View File

@@ -11,7 +11,7 @@ import (
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
)
var version = "1.1.98"
var version = "1.1.99"
const usage = `edgeguard-ctl — EdgeGuard CLI

View File

@@ -35,7 +35,7 @@ import (
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
)
var version = "1.1.98"
var version = "1.1.99"
const (
// renewTickInterval — how often we re-evaluate expiring certs.

View File

@@ -8,6 +8,7 @@ import (
"net/http"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"syscall"
@@ -211,6 +212,7 @@ type resources struct {
LoadAvg1 float64 `json:"load_avg_1"`
LoadAvg5 float64 `json:"load_avg_5"`
LoadAvg15 float64 `json:"load_avg_15"`
NumCPUs int `json:"num_cpus"`
MemTotalKB int64 `json:"mem_total_kb"`
MemAvailKB int64 `json:"mem_avail_kb"`
MemUsedPct float64 `json:"mem_used_pct"`
@@ -234,7 +236,7 @@ func (h *SystemHandler) Resources(c *gin.Context) {
// gin.Context erstellen kann. Best-effort: fehlende Quellen lassen
// die jeweiligen Felder einfach auf 0.
func computeLocalSystemResources() resources {
r := resources{}
r := resources{NumCPUs: runtime.NumCPU()}
if data, err := os.ReadFile("/proc/loadavg"); err == nil {
f := strings.Fields(string(data))
if len(f) >= 3 {

View File

@@ -112,13 +112,16 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
queryKey: ['system', 'health'],
queryFn: async () => {
const r = await apiClient.get('/system/health')
return isEnvelope(r.data) ? (r.data.data as { version?: string }) : { version: '' }
return isEnvelope(r.data)
? (r.data.data as { version?: string; hostname?: string })
: { version: '' }
},
refetchInterval: 60_000,
refetchOnWindowFocus: true,
staleTime: 30_000,
})
const version = health?.version || '…'
const version = health?.version || '…'
const hostname = health?.hostname || null
return (
<nav className={`sidebar${isOpen ? ' open' : ''}`}>
@@ -162,7 +165,14 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
</div>
))}
<div className="sidebar-version">v{version}</div>
<div className="sidebar-version">
{hostname && (
<div style={{ fontSize: 10, opacity: 0.6, marginBottom: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{hostname}
</div>
)}
v{version}
</div>
</nav>
)
}

View File

@@ -89,6 +89,7 @@ interface ServiceStatus {
}
interface Resources {
load_avg_1: number; load_avg_5: number; load_avg_15: number
num_cpus?: number
mem_total_kb: number; mem_avail_kb: number; mem_used_pct: number
disk_total_gb: number; disk_free_gb: number; disk_used_pct: number
conntrack_count: number; conntrack_max: number
@@ -664,7 +665,10 @@ function ResourcesCard({ r }: { r?: Resources | null }) {
<Card size="small">
<Row gutter={[16, 8]}>
<Col xs={12} sm={6} md={4}>
<Text type="secondary" style={{ fontSize: 11, textTransform: 'uppercase' }}>{t('dashboard.resCard.load')}</Text>
<Text type="secondary" style={{ fontSize: 11, textTransform: 'uppercase' }}>
{t('dashboard.resCard.load')}
{r.num_cpus != null && <span style={{ fontWeight: 400, opacity: 0.7 }}> ({r.num_cpus} CPU)</span>}
</Text>
<div style={{ fontSize: 18, fontWeight: 600 }}>
{r.load_avg_1.toFixed(2)} / {r.load_avg_5.toFixed(2)} / {r.load_avg_15.toFixed(2)}
</div>