npm run lint rief next lint auf, das es in Next.js 16 nicht mehr gibt — seit dem Next-16-Upgrade lief effektiv gar kein Lint mehr. Umgestellt auf eslint . mit Flat-Config (eslint.config.mjs statt .eslintrc.json). Der dadurch wieder sichtbare Lint-Lauf zeigte 30 Findings (25 Fehler, 5 Warnungen), alle gefixt: - 19x react-hooks/set-state-in-effect: Loading-States wo möglich als echte Ableitung statt eigenem Effect-State (use-mobile.tsx komplett auf useSyncExternalStore umgebaut), sonst async-Wrapper mit Cancel-Guard um bestehende Loader — Timing/Ladeanzeige unverändert. - react-hooks/refs (useSearch.ts): Ref-Schreibzugriff aus dem Render in einen Effect verschoben. - 4x no-html-link-for-pages: <a href> durch next/link ersetzt in admin/login, forgot-password, signup. - Rest (exhaustive-deps, no-img-element, unused disable) einzeln gefixt. - 4 bewusst belassene disable-Kommentare mit Begründung (shadcn/ui-Datei, QR-Code-data-URL, Full-Reload nach Auth laut Projektregel). eslint-Major-Upgrade auf 10 selbst bleibt blockiert: eslint-plugin-react/ jsx-a11y/import unterstützen ESLint 10 in ihrer aktuellen Latest-Version noch nicht (Crash beim Laden), siehe Feature-Spec PROJ-79. Verifiziert auf 132 (Build-Sandbox, kein Live-Deploy): npm ci/tsc/lint/ build grün, 8 Kern-Routen per Standalone-Server auf HTTP 200 geprüft. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
3.8 KiB
id, title, status, created
| id | title | status | created |
|---|---|---|---|
| PROJ-73 | Restliche Crash-Härtung (Upload-Job-Status bei Panic, fehlende nil-Checks) | Deployed | 2026-08-05 |
Problem
Nach der großen Crash-Robustheit-Härtung (Commit 798cb28) sind zwei kleinere
Lücken bewusst offen geblieben:
runUploadJob(internal/api/upload.go) läuft jetzt zwar dankinternal/safegonicht 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.internal/api/ocr_handlers.go:66(s.users.GetByUsername(...)) und analoge Stellen dereferenzieren das Ergebnis ohne explizitenu == 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)
- In
runUploadJob:defereinbauen, der beirecover() != nilden Job-Status auf"error"setzt (inkl. Fehlermeldung), statt nur zu loggen. - Alle
GetByUsername-Aufrufstellen (grepGetByUsernameüberinternal/api/) aufif u == nil { ... }-Guard nach demerr == nil- Check prüfen und ergänzen wo fehlend.
Implementation Notes
1. Upload-Job-Status bei Panic — internal/api/upload.go:148-162
runUploadJob bekommt direkt nach ctx := context.Background() ein
defer func(){ if rec := recover(); rec != nil { ... } }():
- setzt unter
job.muStatus = "error"undErrMsg, - gibt den Panic anschließend per
panic(rec)weiter, damitsafego.Runihn 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.
Deployed auf 131 (Produktiv) 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 ininternal/api/haben einen expliziten nil-Guard. go build ./... && go vet ./...auf 132 fehlerfrei für geänderte Dateien.