Files
archivmail/features/PROJ-73-restliche-crash-haertung.md
T
sysopsandClaude Sonnet 5 075afa005a fix(PROJ-74): Test-/Vet-Signatur-Drift beheben, go build/vet/test wieder grün
archivmail-export nutzte noch die alte storage.New(path string)-Signatur
statt storage.Config (fehlender Keyfile/Compress hätte Rohbytes statt
Klartext-EML exportiert). Toten Self-Assignment-Code in storage.go entfernt.
Testdateien (storage, audit, api, userstore, auth) an aktuelle Signaturen
angeglichen; auth-Tests liefen bisher gegen einen SQLite-Pfad statt Postgres-
DSN und wurden auf das TEST_DATABASE_URL-Schema-Isolationsmuster der übrigen
Pakete umgestellt. api_test.go las den Login-Token noch aus dem JSON-Body
statt aus dem httpOnly-Cookie (Auth-Contract-Drift).

TestParseMissingDate an tatsächliches Verhalten angepasst: der Parser lässt
das Datum bewusst als Zero-Value, der time.Now()-Fallback sitzt in der
Storage-Schicht — damit bleibt nachvollziehbar ob ein Datum aus der Mail
stammt oder vom Archiv gesetzt wurde (GoBD).

Verifiziert auf 192.168.1.132: go build/vet/test ./... komplett grün,
kein Skip (Postgres + Manticore erreichbar).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
2026-08-05 14:07:46 +02:00

3.7 KiB

id, title, status, created
id title status created
PROJ-73 Restliche Crash-Härtung (Upload-Job-Status bei Panic, fehlende nil-Checks) In Review 2026-08-05

Problem

Nach der großen Crash-Robustheit-Härtung (Commit 798cb28) sind zwei kleinere Lücken bewusst offen geblieben:

  1. runUploadJob (internal/api/upload.go) läuft jetzt zwar dank internal/safego nicht mehr prozessfatal bei einem Panic, der Job-Status bleibt aber dauerhaft auf "running" hängen — der Nutzer bekommt nie eine Fehlermeldung im Upload-UI, der Job wirkt wie ein hängender Prozess.
  2. internal/api/ocr_handlers.go:66 (s.users.GetByUsername(...)) und analoge Stellen dereferenzieren das Ergebnis ohne expliziten u == nil- Check. Aktuell liefert kein Store (nil, nil), daher kein akuter Bug — aber ungeschützt gegen künftige Store-Implementierungen oder Refactorings.

Lösung (Vorschlag)

  1. In runUploadJob: defer einbauen, der bei recover() != nil den Job-Status auf "error" setzt (inkl. Fehlermeldung), statt nur zu loggen.
  2. Alle GetByUsername-Aufrufstellen (grep GetByUsername über internal/api/) auf if u == nil { ... }-Guard nach dem err == nil- Check prüfen und ergänzen wo fehlend.

Implementation Notes

1. Upload-Job-Status bei Panicinternal/api/upload.go:148-162

runUploadJob bekommt direkt nach ctx := context.Background() ein defer func(){ if rec := recover(); rec != nil { ... } }():

  • setzt unter job.mu Status = "error" und ErrMsg,
  • gibt den Panic anschließend per panic(rec) weiter, damit safego.Run ihn wie bisher mit Task-Namen loggt (Prozess bleibt stabil).

Bewusste Abweichung vom Spec-Vorschlag: die ErrMsg enthält nicht den Panic-Wert, sondern den generischen Text „Import wegen eines internen Fehlers abgebrochen". Ein Panic-Wert kann Fragmente von Mail-Inhalten transportieren und ErrMsg wird über /upload/progress/{jobID} ans Frontend ausgeliefert (DSGVO). Die Details stehen im Server-Log.

Feldnamen sind konsistent zum bestehenden Muster (Status "running"/"done"/ "error", ErrMsg mit JSON-Tag error_msg), snapshot() liefert sie bereits unverändert ans Frontend — keine API-Änderung nötig.

2. nil-Guards nach GetByUsername — alle 13 Aufrufstellen in internal/api/, jeweils nur die vorhandene if err != nil-Bedingung erweitert (kein neuer Fehlerpfad, damit Statuscodes/Logging unverändert bleiben):

Datei:Zeile neue Bedingung
restore_handlers.go:43, :140 err != nil || user == nil (500)
search_handlers.go:429, :497 err != nil || u == nil || !mailBelongsToUser(...) (403)
export.go:373 err != nil || u == nil || !mailBelongsToUser(...) (403)
export.go:472 err != nil || u == nil (500)
smtpout_handlers.go:103 err != nil || u == nil || u.Email == "" (400)
ocr_handlers.go:66 err != nil || u == nil || !mailBelongsToUser(...) (403)
auth_handlers.go:106 err != nil || user == nil (500)
profile_handlers.go:36, :113, :183 err != nil || user == nil (500)
ediscovery.go:134 err != nil || u == nil (500)

Alle Pfade, die eine Zugriffsprüfung machen (Search/Export/OCR), fallen bei u == nil auf „access denied" zurück, nicht auf „erlaubt" — fail-closed.

Lokal kein go build möglich (kein Go-Toolchain), nur statische Prüfung.

Deployed auf 132 am 2026-08-05.

Acceptance Criteria

  • Upload-Job zeigt nach einem simulierten Panic im Verarbeitungspfad Status "error" mit Fehlermeldung statt dauerhaft "running".
  • Alle GetByUsername-Stellen in internal/api/ haben einen expliziten nil-Guard.
  • go build ./... && go vet ./... auf 132 fehlerfrei für geänderte Dateien.