fix(PROJ-34): Mandanten-Retention ist Opt-in — kein globaler Lock für Mandanten

storage.Save(): retention_days eines Mandanten muss explizit > 0 sein.
Globale config greift nie automatisch auf Mandanten-Mails.
Mails ohne Mandant: globale config als Fallback (unveraendert).
Frontend: Hinweis und Labels klargestellt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-03-31 10:50:54 +02:00
parent 4aadf7a4d2
commit ebc9e278ea
2 changed files with 18 additions and 22 deletions
+9 -6
View File
@@ -324,16 +324,19 @@ func (s *Store) Save(ctx context.Context, raw []byte, _ time.Time, tenantID *int
} else {
s.insertMetaMinimal(ctx, id, len(raw), tenantID)
}
// PROJ-34: Set retention lock — prefer per-tenant retention, fall back to global.
effectiveRetention := s.retentionDays
// PROJ-34: Set retention lock.
// Mandanten-Mails: nur wenn der Mandant explizit retention_days > 0 gesetzt hat.
// Globale config greift NICHT automatisch — jeder Mandant muss selbst opt-in.
// Mails ohne Mandant (tenantID == nil): globale config als Fallback.
if tenantID != nil {
var tenantDays int
if err := s.db.QueryRow(ctx, `SELECT retention_days FROM tenants WHERE id=$1`, *tenantID).Scan(&tenantDays); err == nil && tenantDays > 0 {
effectiveRetention = tenantDays
until := time.Now().AddDate(0, 0, tenantDays)
_, _ = s.db.Exec(ctx, `UPDATE emails SET retain_until=$1 WHERE id=$2 AND retain_until IS NULL`, until, id)
}
}
if effectiveRetention > 0 {
until := time.Now().AddDate(0, 0, effectiveRetention)
// else: tenant hat retention_days=0 → kein Lock gesetzt → keine automatische Löschung
} else if s.retentionDays > 0 {
until := time.Now().AddDate(0, 0, s.retentionDays)
_, _ = s.db.Exec(ctx, `UPDATE emails SET retain_until=$1 WHERE id=$2 AND retain_until IS NULL`, until, id)
}
}