diff --git a/agent-linux/collector/pbs.go b/agent-linux/collector/pbs.go index 5387169..0268f36 100644 --- a/agent-linux/collector/pbs.go +++ b/agent-linux/collector/pbs.go @@ -13,6 +13,7 @@ type PBSInfo struct { Datastores []PBSDatastore `json:"datastores,omitempty"` Tasks []PBSTask `json:"tasks,omitempty"` GC []PBSGCStatus `json:"gc,omitempty"` + SyncJobs int `json:"sync_jobs"` } type PBSDatastore struct { @@ -161,6 +162,14 @@ func CollectPBS() *PBSInfo { pbs.GC = append(pbs.GC, gc) } - log.Printf("PBS: %d Datastores, %d Tasks gesammelt", len(pbs.Datastores), len(pbs.Tasks)) + // Sync Jobs zählen + if out, err := exec.Command("/usr/sbin/proxmox-backup-manager", "sync-job", "list", "--output-format", "json").Output(); err == nil { + var jobs []interface{} + if json.Unmarshal(out, &jobs) == nil { + pbs.SyncJobs = len(jobs) + } + } + + log.Printf("PBS: %d Datastores, %d Tasks, %d SyncJobs gesammelt", len(pbs.Datastores), len(pbs.Tasks), pbs.SyncJobs) return pbs } diff --git a/frontend/src/components/ProxmoxPanel.jsx b/frontend/src/components/ProxmoxPanel.jsx index 7ba4da8..941f382 100644 --- a/frontend/src/components/ProxmoxPanel.jsx +++ b/frontend/src/components/ProxmoxPanel.jsx @@ -127,7 +127,7 @@ export default function ProxmoxPanel({ agentId, customers, onClose, onReload }) ) : tab === 'updates' ? ( ) : tab === 'pbs' ? ( - + ) : tab === 'backups' ? ( ) : tab === 'agent' ? ( @@ -953,7 +953,7 @@ function formatUptime(seconds) { return `${h}h ${m}m` } -function PBSTab({ pbs, agentId }) { +function PBSTab({ pbs, agentId, sys }) { const [loading, setLoading] = React.useState({}) const [feedback, setFeedback] = React.useState({}) @@ -974,7 +974,6 @@ function PBSTab({ pbs, agentId }) { if (s < 3600) return `${Math.floor(s/60)}m ${s%60}s` return `${Math.floor(s/3600)}h ${Math.floor((s%3600)/60)}m` } - const taskStatusColor = (s) => { if (!s) return 'text-gray-500' if (s === 'OK' || s.startsWith('OK')) return 'text-green-400' @@ -1003,18 +1002,57 @@ function PBSTab({ pbs, agentId }) { } } + const totalBackups = pbs.datastores?.reduce((s, d) => s + (d.backup_count || 0), 0) || 0 + const kernel = sys?.opnsense_version || '—' + + const StatCard = ({ icon, value, label, iconColor }) => ( +
+
{icon}
+
{value}
+
{label}
+
+ ) + return (
- {/* Header */} + {/* Titel */}
-
Proxmox Backup Server
- {pbs.version &&
Version {pbs.version}
} +
Backup Server
+ {pbs.version &&
PBS {pbs.version}
} +
+ + {/* Stat-Karten */} +
+ } + value={pbs.datastores?.length || 0} + label="Datastores" + iconColor="text-orange-400" + /> + } + value={totalBackups} + label="Backups" + iconColor="text-blue-400" + /> + } + value={pbs.sync_jobs ?? 0} + label="Sync Jobs" + iconColor="text-green-400" + /> + } + value={kernel !== '—' ? kernel.split('-')[0] : '—'} + label="Kernel" + iconColor="text-orange-400" + />
{/* Datastores */} {pbs.datastores?.length > 0 && (
-

Datastores

+

Datastores

{pbs.datastores.map((ds) => { const usedPct = ds.total > 0 ? (ds.used / ds.total * 100) : 0 const barColor = usedPct > 90 ? 'bg-red-500' : usedPct > 70 ? 'bg-yellow-500' : 'bg-green-500' @@ -1035,46 +1073,35 @@ function PBSTab({ pbs, agentId }) { } return (
- {/* Titel + Backup-Count */}
-
- - - -
+ + + {ds.name}
- {ds.backup_count > 0 && ( - {ds.backup_count} Backups - )} + {ds.backup_count > 0 && {ds.backup_count} Backups}
- - {/* Speicher-Balken */}
{formatBytes(ds.used)} / {formatBytes(ds.total)} {usedPct.toFixed(1)}%
-
+
- - {/* GC-Status */} - {gc && gc.status && ( + {gc?.status && (
GC: {gc.status} {gc.last_run > 0 && Letzter Lauf: {formatTs(gc.last_run)}} {gc.duration > 0 && ({formatDuration(gc.duration)})}
)} - - {/* Aktions-Buttons */}
- + - +
) @@ -1085,7 +1112,7 @@ function PBSTab({ pbs, agentId }) { {/* Tasks */} {pbs.tasks?.length > 0 && (
-

Letzte Tasks

+

Letzte Tasks