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:
@@ -60,7 +60,7 @@ import (
|
|||||||
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.98"
|
var version = "1.1.99"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
"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
|
const usage = `edgeguard-ctl — EdgeGuard CLI
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.98"
|
var version = "1.1.99"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// renewTickInterval — how often we re-evaluate expiring certs.
|
// renewTickInterval — how often we re-evaluate expiring certs.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -211,6 +212,7 @@ type resources struct {
|
|||||||
LoadAvg1 float64 `json:"load_avg_1"`
|
LoadAvg1 float64 `json:"load_avg_1"`
|
||||||
LoadAvg5 float64 `json:"load_avg_5"`
|
LoadAvg5 float64 `json:"load_avg_5"`
|
||||||
LoadAvg15 float64 `json:"load_avg_15"`
|
LoadAvg15 float64 `json:"load_avg_15"`
|
||||||
|
NumCPUs int `json:"num_cpus"`
|
||||||
MemTotalKB int64 `json:"mem_total_kb"`
|
MemTotalKB int64 `json:"mem_total_kb"`
|
||||||
MemAvailKB int64 `json:"mem_avail_kb"`
|
MemAvailKB int64 `json:"mem_avail_kb"`
|
||||||
MemUsedPct float64 `json:"mem_used_pct"`
|
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
|
// gin.Context erstellen kann. Best-effort: fehlende Quellen lassen
|
||||||
// die jeweiligen Felder einfach auf 0.
|
// die jeweiligen Felder einfach auf 0.
|
||||||
func computeLocalSystemResources() resources {
|
func computeLocalSystemResources() resources {
|
||||||
r := resources{}
|
r := resources{NumCPUs: runtime.NumCPU()}
|
||||||
if data, err := os.ReadFile("/proc/loadavg"); err == nil {
|
if data, err := os.ReadFile("/proc/loadavg"); err == nil {
|
||||||
f := strings.Fields(string(data))
|
f := strings.Fields(string(data))
|
||||||
if len(f) >= 3 {
|
if len(f) >= 3 {
|
||||||
|
|||||||
@@ -112,13 +112,16 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
|
|||||||
queryKey: ['system', 'health'],
|
queryKey: ['system', 'health'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const r = await apiClient.get('/system/health')
|
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,
|
refetchInterval: 60_000,
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
staleTime: 30_000,
|
staleTime: 30_000,
|
||||||
})
|
})
|
||||||
const version = health?.version || '…'
|
const version = health?.version || '…'
|
||||||
|
const hostname = health?.hostname || null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className={`sidebar${isOpen ? ' open' : ''}`}>
|
<nav className={`sidebar${isOpen ? ' open' : ''}`}>
|
||||||
@@ -162,7 +165,14 @@ export default function Sidebar({ isOpen, onClose }: SidebarProps) {
|
|||||||
</div>
|
</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>
|
</nav>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ interface ServiceStatus {
|
|||||||
}
|
}
|
||||||
interface Resources {
|
interface Resources {
|
||||||
load_avg_1: number; load_avg_5: number; load_avg_15: number
|
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
|
mem_total_kb: number; mem_avail_kb: number; mem_used_pct: number
|
||||||
disk_total_gb: number; disk_free_gb: number; disk_used_pct: number
|
disk_total_gb: number; disk_free_gb: number; disk_used_pct: number
|
||||||
conntrack_count: number; conntrack_max: number
|
conntrack_count: number; conntrack_max: number
|
||||||
@@ -664,7 +665,10 @@ function ResourcesCard({ r }: { r?: Resources | null }) {
|
|||||||
<Card size="small">
|
<Card size="small">
|
||||||
<Row gutter={[16, 8]}>
|
<Row gutter={[16, 8]}>
|
||||||
<Col xs={12} sm={6} md={4}>
|
<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 }}>
|
<div style={{ fontSize: 18, fontWeight: 600 }}>
|
||||||
{r.load_avg_1.toFixed(2)} / {r.load_avg_5.toFixed(2)} / {r.load_avg_15.toFixed(2)}
|
{r.load_avg_1.toFixed(2)} / {r.load_avg_5.toFixed(2)} / {r.load_avg_15.toFixed(2)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user