Enterprise-Backup-, Recovery-, Verification-, Security- und Monitoring-Plattform fuer Proxmox VE, Windows, Linux und Dateisysteme. Der Leitsatz, der fast jede Entscheidung erklaert: Ein Backup gilt erst als vertrauenswuerdig, wenn Integritaet geprueft und Wiederherstellbarkeit nachgewiesen wurde. Deshalb steigt ein Wiederherstellungspunkt erst nach einem tatsaechlich durchgefuehrten Restore-Test auf "recoverable", und Unbekanntes geht in keine Bewertung als "gut" ein. Umfang (Phasen 0-23): - Repository Engine: inhaltsadressierte Bloecke, atomares Commit-Protokoll, Katalogaufbau allein aus den Manifesten — ohne Datenbank - Backup Engine: inhaltsabhaengiges Chunking, Deduplizierung trotz Verschluesselung, zstd, AES-256-GCM, Streaming mit Gegendruck - Agenten fuer Windows und Linux mit Auftragsabholung (Pull-Modell) - Proxmox-Provider mit beiden Zugriffswegen auf die Sicherungsarchive - Scheduler, Recovery Engine mit Pruefpunkt, Verification, Unveraenderlichkeit - Weboberflaeche, Kennzahlen, Meldungen, Berichte, Security Center, Ransomware-Heuristik (meldet, handelt nie) - Disaster Recovery, Haertung, Leistungsmessung, Chaos Testing - Eingefrorene Vertraege fuer API, Migrationen, Backup-Format und Repository - Auslieferungspaket fuer linux/amd64, linux/arm64 und windows/amd64 Nicht enthalten und als solches gekennzeichnet: Kapazitaetsprognose, Backup Copy, Changed Block Tracking bei Proxmox, erweiterte Attribute und ACLs. Gebaut, aber nie auf echter Hardware gefahren: der Windows-Dienst, die systemd-Einheit und der verpflichtende Proxmox-Meilenstein — ob eine wiederhergestellte VM startet, ist ungeprueft. Einzelheiten in CHANGELOG.md und docs/release-candidate.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
/**
|
|
* Hook zum Laden des Systemzustands.
|
|
*
|
|
* Der Hook zeigt niemals Platzhalter- oder Demo-Werte: solange kein echtes
|
|
* Ergebnis vorliegt, bleibt der Zustand ausdruecklich "laedt" oder "Fehler"
|
|
* (PROMPT.md §138/§139).
|
|
*/
|
|
|
|
import { useCallback, useEffect, useState } from 'react';
|
|
import { ApiError, requestApi } from '../../api/client';
|
|
import type { SystemHealth } from '../../types/api';
|
|
|
|
/** Ladezustand der Systemzustandsabfrage. */
|
|
export type HealthLoadState = 'loading' | 'loaded' | 'failed';
|
|
|
|
/** Rueckgabewert des Hooks. */
|
|
export interface UseSystemHealthResult {
|
|
/** Aktueller Ladezustand. */
|
|
loadState: HealthLoadState;
|
|
/** Geladener Systemzustand; null, solange keiner vorliegt. */
|
|
systemHealth: SystemHealth | null;
|
|
/** Aufgetretener Fehler; null, wenn kein Fehler vorliegt. */
|
|
loadError: ApiError | null;
|
|
/** Laedt den Systemzustand erneut. */
|
|
reload: () => void;
|
|
}
|
|
|
|
/**
|
|
* Laedt den Systemzustand von GET /api/v1/health.
|
|
*
|
|
* @param refreshIntervalMs Abstand automatischer Aktualisierungen in Millisekunden.
|
|
* Ein Wert von 0 deaktiviert die automatische Aktualisierung.
|
|
*/
|
|
export function useSystemHealth(refreshIntervalMs = 0): UseSystemHealthResult {
|
|
const [loadState, setLoadState] = useState<HealthLoadState>('loading');
|
|
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
|
|
const [loadError, setLoadError] = useState<ApiError | null>(null);
|
|
|
|
// reloadCounter erzwingt einen erneuten Lauf des Effekts bei manuellem Neuladen.
|
|
const [reloadCounter, setReloadCounter] = useState(0);
|
|
|
|
const reload = useCallback(() => {
|
|
setReloadCounter((previousCounter) => previousCounter + 1);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
// Der Controller bricht laufende Anfragen ab, wenn die Komponente verschwindet.
|
|
const abortController = new AbortController();
|
|
|
|
async function loadSystemHealth(): Promise<void> {
|
|
try {
|
|
const loadedHealth = await requestApi<SystemHealth>('/health', {
|
|
signal: abortController.signal,
|
|
});
|
|
|
|
setSystemHealth(loadedHealth);
|
|
setLoadError(null);
|
|
setLoadState('loaded');
|
|
} catch (caughtError) {
|
|
// Ein Abbruch ist kein Fehler, sondern Folge des Aufraeumens.
|
|
if (caughtError instanceof DOMException && caughtError.name === 'AbortError') {
|
|
return;
|
|
}
|
|
|
|
// Der zuletzt bekannte Zustand wird verworfen: eine veraltete Anzeige
|
|
// als aktuellen Zustand auszugeben waere irrefuehrend.
|
|
setSystemHealth(null);
|
|
setLoadError(
|
|
caughtError instanceof ApiError
|
|
? caughtError
|
|
: new ApiError({
|
|
code: 'UNEXPECTED_ERROR',
|
|
message: 'Der Systemzustand konnte nicht ermittelt werden.',
|
|
statusCode: 0,
|
|
requestId: '',
|
|
}),
|
|
);
|
|
setLoadState('failed');
|
|
}
|
|
}
|
|
|
|
void loadSystemHealth();
|
|
|
|
if (refreshIntervalMs <= 0) {
|
|
return () => abortController.abort();
|
|
}
|
|
|
|
const refreshTimer = window.setInterval(() => {
|
|
void loadSystemHealth();
|
|
}, refreshIntervalMs);
|
|
|
|
return () => {
|
|
window.clearInterval(refreshTimer);
|
|
abortController.abort();
|
|
};
|
|
}, [refreshIntervalMs, reloadCounter]);
|
|
|
|
return { loadState, systemHealth, loadError, reload };
|
|
}
|