syncova-backup/apps/api/internal/httpapi/errors.go
Jerrit Fritzsche 610719c316
Some checks failed
CI / Backend (Go) (push) Failing after 3m7s
CI / Frontend (React/TypeScript) (push) Successful in 37s
CI / Sicherheitsprüfungen (push) Successful in 44s
Syncova Backups V1
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>
2026-08-17 09:10:54 +02:00

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),
}
}