feat(dashboard): WG server tunnels show peer summary instead of per-peer rows

Server tunnels with many peers previously flooded the WG card. Now
server mode shows "X/Y peers online · ▼rx ▲tx" (3-min handshake
threshold) while client tunnels retain full per-peer detail.
Adds i18n keys dashboard.wgCard.peersOnline (EN/DE). v1.1.111.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-26 20:01:49 +02:00
parent 4406f9ac5b
commit 326f79cf00
7 changed files with 28 additions and 13 deletions

View File

@@ -1 +1 @@
1.1.110
1.1.111

View File

@@ -60,7 +60,7 @@ import (
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
)
var version = "1.1.110"
var version = "1.1.111"
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.110"
var version = "1.1.111"
const usage = `edgeguard-ctl — EdgeGuard CLI

View File

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

View File

@@ -734,7 +734,8 @@
},
"wgCard": {
"title": "WireGuard",
"empty": "Noch kein WG-Tunnel angelegt."
"empty": "Noch kein WG-Tunnel angelegt.",
"peersOnline": "{{online}} / {{total}} Peers verbunden"
},
"firewallCard": {
"title": "Firewall",

View File

@@ -734,7 +734,8 @@
},
"wgCard": {
"title": "WireGuard",
"empty": "No WG tunnel configured yet."
"empty": "No WG tunnel configured yet.",
"peersOnline": "{{online}} / {{total}} peers online"
},
"firewallCard": {
"title": "Firewall",

View File

@@ -518,6 +518,10 @@ export default function DashboardPage() {
<Space direction="vertical" style={{ width: '100%' }} size={4}>
{(wgIfaces.data ?? []).map(ifc => {
const status = (wgStatus.data ?? []).filter(s => s.interface === ifc.name)
const nowSec = Date.now() / 1000
const onlineCount = status.filter(s => s.last_handshake_unix > 0 && nowSec - s.last_handshake_unix < 180).length
const totalRx = status.reduce((acc, s) => acc + s.transfer_rx, 0)
const totalTx = status.reduce((acc, s) => acc + s.transfer_tx, 0)
return (
<div key={ifc.id} style={{ borderBottom: '1px solid #F1F5F9', padding: '4px 0' }}>
<Space>
@@ -525,13 +529,22 @@ export default function DashboardPage() {
<Tag color={ifc.mode === 'server' ? 'blue' : 'purple'}>{ifc.mode}</Tag>
<StatusDot active={ifc.active} />
</Space>
{status.map(s => (
<div key={s.peer_public_key} style={{ fontSize: 12, color: '#64748B', marginTop: 2 }}>
{s.endpoint && <span>{s.endpoint} · </span>}
<span>{relativeTime(s.last_handshake_unix, t)}</span>
{' · ▼'}{formatBytes(s.transfer_rx)}{' · ▲'}{formatBytes(s.transfer_tx)}
</div>
))}
{ifc.mode === 'server' ? (
status.length > 0 && (
<div style={{ fontSize: 12, color: '#64748B', marginTop: 2 }}>
{t('dashboard.wgCard.peersOnline', { online: onlineCount, total: status.length })}
{' · ▼'}{formatBytes(totalRx)}{' ▲'}{formatBytes(totalTx)}
</div>
)
) : (
status.map(s => (
<div key={s.peer_public_key} style={{ fontSize: 12, color: '#64748B', marginTop: 2 }}>
{s.endpoint && <span>{s.endpoint} · </span>}
<span>{relativeTime(s.last_handshake_unix, t)}</span>
{' · ▼'}{formatBytes(s.transfer_rx)}{' ▲'}{formatBytes(s.transfer_tx)}
</div>
))
)}
</div>
)
})}