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>
134 lines
5.4 KiB
Go
134 lines
5.4 KiB
Go
// Package httpapi implementiert die öffentliche REST-Schnittstelle laut SYNCOVA_API.md.
|
|
package httpapi
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
)
|
|
|
|
// ErrorCode ist ein stabiler, maschinenlesbarer Fehlercode der API.
|
|
//
|
|
// Codes sind Teil des API-Vertrags: Clients dürfen auf sie reagieren, während
|
|
// die Fehlermeldung sich ändern darf. Sie beschreiben die Ursache verständlich,
|
|
// ohne interne Details preiszugeben (SYNCOVA_API.md §26).
|
|
type ErrorCode string
|
|
|
|
const (
|
|
// ErrorCodeBadRequest meldet einen syntaktisch fehlerhaften Request.
|
|
ErrorCodeBadRequest ErrorCode = "BAD_REQUEST"
|
|
// ErrorCodeValidationFailed meldet fachlich ungültige Eingabewerte.
|
|
ErrorCodeValidationFailed ErrorCode = "VALIDATION_FAILED"
|
|
// ErrorCodeUnauthenticated meldet einen fehlenden oder ungültigen Nachweis.
|
|
ErrorCodeUnauthenticated ErrorCode = "UNAUTHENTICATED"
|
|
// ErrorCodePermissionDenied meldet eine fehlende Berechtigung.
|
|
ErrorCodePermissionDenied ErrorCode = "PERMISSION_DENIED"
|
|
// ErrorCodeNotFound meldet eine nicht vorhandene Ressource.
|
|
ErrorCodeNotFound ErrorCode = "NOT_FOUND"
|
|
// ErrorCodeMethodNotAllowed meldet eine für diese Route unzulässige HTTP-Methode.
|
|
ErrorCodeMethodNotAllowed ErrorCode = "METHOD_NOT_ALLOWED"
|
|
// ErrorCodeConflict meldet einen Konflikt mit dem aktuellen Zustand.
|
|
ErrorCodeConflict ErrorCode = "CONFLICT"
|
|
// ErrorCodePayloadTooLarge meldet eine Überschreitung des Body-Limits.
|
|
ErrorCodePayloadTooLarge ErrorCode = "PAYLOAD_TOO_LARGE"
|
|
// ErrorCodeRateLimited meldet zu viele Anfragen.
|
|
ErrorCodeRateLimited ErrorCode = "RATE_LIMITED"
|
|
// ErrorCodeInternal meldet einen unerwarteten Serverfehler.
|
|
ErrorCodeInternal ErrorCode = "INTERNAL_ERROR"
|
|
// ErrorCodeServiceUnavailable meldet einen vorübergehend nicht verfügbaren Dienst.
|
|
ErrorCodeServiceUnavailable ErrorCode = "SERVICE_UNAVAILABLE"
|
|
// ErrorCodeNotImplemented meldet eine bewusst noch nicht implementierte Funktion.
|
|
//
|
|
// PROMPT.md §138 verlangt diese ehrliche Antwort statt einer vorgetäuschten
|
|
// Funktion oder erfundener Daten.
|
|
ErrorCodeNotImplemented ErrorCode = "NOT_IMPLEMENTED"
|
|
)
|
|
|
|
// APIError ist ein Fehler, der sich unmittelbar in eine API-Antwort übersetzen lässt.
|
|
//
|
|
// Er trennt die für den Aufrufer bestimmte Darstellung von der internen Ursache:
|
|
// Letztere wird geloggt, aber niemals ausgeliefert (SYNCOVA_API.md §26).
|
|
type APIError struct {
|
|
// StatusCode ist der auszuliefernde HTTP-Statuscode.
|
|
StatusCode int
|
|
// Code ist der stabile maschinenlesbare Fehlercode.
|
|
Code ErrorCode
|
|
// Message ist die für Menschen bestimmte Erklärung ohne interne Details.
|
|
Message string
|
|
// Details trägt optionale, unbedenkliche Zusatzinformationen (z. B. Feldnamen).
|
|
Details map[string]any
|
|
// cause ist die interne Ursache. Sie wird ausschließlich geloggt.
|
|
cause error
|
|
}
|
|
|
|
// Error erfüllt das error-Interface.
|
|
func (apiError *APIError) Error() string {
|
|
if apiError.cause != nil {
|
|
return fmt.Sprintf("%s: %s: %v", apiError.Code, apiError.Message, apiError.cause)
|
|
}
|
|
|
|
return fmt.Sprintf("%s: %s", apiError.Code, apiError.Message)
|
|
}
|
|
|
|
// Unwrap gibt die interne Ursache für errors.Is/errors.As frei.
|
|
func (apiError *APIError) Unwrap() error {
|
|
return apiError.cause
|
|
}
|
|
|
|
// WithCause hinterlegt die interne Ursache eines Fehlers.
|
|
// Die Ursache erscheint im Log, niemals in der Antwort an den Aufrufer.
|
|
func (apiError *APIError) WithCause(causeError error) *APIError {
|
|
apiError.cause = causeError
|
|
return apiError
|
|
}
|
|
|
|
// WithDetails ergänzt unbedenkliche Zusatzinformationen für den Aufrufer.
|
|
func (apiError *APIError) WithDetails(errorDetails map[string]any) *APIError {
|
|
apiError.Details = errorDetails
|
|
return apiError
|
|
}
|
|
|
|
// NewBadRequestError meldet einen syntaktisch fehlerhaften Request.
|
|
func NewBadRequestError(errorMessage string) *APIError {
|
|
return &APIError{StatusCode: http.StatusBadRequest, Code: ErrorCodeBadRequest, Message: errorMessage}
|
|
}
|
|
|
|
// NewValidationError meldet fachlich ungültige Eingabewerte.
|
|
func NewValidationError(errorMessage string) *APIError {
|
|
return &APIError{StatusCode: http.StatusUnprocessableEntity, Code: ErrorCodeValidationFailed, Message: errorMessage}
|
|
}
|
|
|
|
// NewNotFoundError meldet eine nicht vorhandene Ressource.
|
|
func NewNotFoundError(errorMessage string) *APIError {
|
|
return &APIError{StatusCode: http.StatusNotFound, Code: ErrorCodeNotFound, Message: errorMessage}
|
|
}
|
|
|
|
// NewInternalError meldet einen unerwarteten Serverfehler.
|
|
//
|
|
// Die Nachricht ist bewusst generisch: interne Details könnten Angreifern die
|
|
// Struktur des Systems verraten.
|
|
func NewInternalError(causeError error) *APIError {
|
|
return &APIError{
|
|
StatusCode: http.StatusInternalServerError,
|
|
Code: ErrorCodeInternal,
|
|
Message: "Bei der Verarbeitung der Anfrage ist ein interner Fehler aufgetreten.",
|
|
cause: causeError,
|
|
}
|
|
}
|
|
|
|
// NewServiceUnavailableError meldet einen vorübergehend nicht verfügbaren Dienst.
|
|
func NewServiceUnavailableError(errorMessage string) *APIError {
|
|
return &APIError{StatusCode: http.StatusServiceUnavailable, Code: ErrorCodeServiceUnavailable, Message: errorMessage}
|
|
}
|
|
|
|
// NewNotImplementedError meldet eine noch nicht implementierte Funktion.
|
|
//
|
|
// Sie ist der vorgeschriebene Weg für geplante, aber unfertige Endpunkte
|
|
// (PROMPT.md §138).
|
|
func NewNotImplementedError(featureName string) *APIError {
|
|
return &APIError{
|
|
StatusCode: http.StatusNotImplemented,
|
|
Code: ErrorCodeNotImplemented,
|
|
Message: fmt.Sprintf("%s ist in dieser Version noch nicht implementiert.", featureName),
|
|
}
|
|
}
|