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.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