Files
archivmail/internal/safego/safego.go
T
sysopsandClaude Sonnet 5 798cb2817c fix: Crash-Robustheit + Performance in Backend und Frontend härten
Backend: recover() in allen langlebigen Goroutinen (neues internal/safego-
Paket), MIME-Multipart-Tiefenlimit gegen Stack-Overflow, IMAP-Zeilenlängen-
und FETCH-Result-Limits gegen OOM, MBOX-Buffer-Aliasing-Bug (Datenkorruption
beim Import), ungeprüfte Type Assertions abgesichert, Data Race im
API-Key-Rate-Limiter behoben, SMTP-Session-Panic führt jetzt zu 451-Retry
statt Prozessabsturz. Performance: O(n²)-String-Concat in Mailparser und
IMAP-Parser durch strings.Builder ersetzt.

Frontend: Error Boundaries für Root und Mail-Detailansicht ergänzt (gab es
vorher nicht), zahlreiche Guards gegen nil-Slices aus dem Backend-JSON die
sonst .map()/.length-Crashes/White-Screens auslösten, defekte JSON-Antworten
in api/core.ts abgefangen, zwei React-Key-Bugs bei löschbaren Listen
korrigiert.

Verifiziert auf 192.168.1.132: Build und Tests der geänderten Pakete
fehlerfrei, keine Regressionen gegenüber vorbestehendem Stand.

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

33 lines
1.1 KiB
Go

// Package safego starts background goroutines that cannot take the process
// down. A panic inside a detached goroutine is fatal for the whole binary in
// Go — there is no per-goroutine boundary like net/http provides for request
// handlers. Every "fire and forget" goroutine in archivmail (mail sending,
// importer runs, boot-resume jobs) therefore goes through Go/Run.
package safego
import (
"fmt"
"log/slog"
)
// Run executes fn synchronously and recovers from a panic, logging it with the
// given name. It returns true when fn completed without panicking.
func Run(logger *slog.Logger, name string, fn func()) (ok bool) {
defer func() {
if r := recover(); r != nil {
ok = false
if logger != nil {
logger.Error("recovered from panic in background task",
"task", name, "panic", fmt.Sprintf("%v", r))
}
}
}()
fn()
return true
}
// Go runs fn in a new goroutine, recovering and logging any panic.
func Go(logger *slog.Logger, name string, fn func()) {
go Run(logger, name, fn) //nolint:errcheck // result is irrelevant for detached tasks
}