hub v0.71.0: paired recovery mails (F11), prefs seeding at claim + empty-email no-clobber (F12), priority headers + operator test leg (F14-light)

This commit is contained in:
2026-07-22 20:57:29 +02:00
parent 5b35023574
commit c766c8af82
15 changed files with 832 additions and 30 deletions
+14 -2
View File
@@ -1904,13 +1904,25 @@ func (h *Handler) handleSavePreferences(w http.ResponseWriter, r *http.Request)
return
}
if err := h.store.SaveNotificationPrefs(payload.CustomerID, payload.Email, payload.EnabledEvents, payload.CooldownHours); err != nil {
// Empty-email no-clobber guard (v0.71.0, audit F12): a controller push with an empty email
// (e.g. an unconfigured box) must never wipe a stored non-empty address — the seeded/edited
// email is the customer's alert lifeline. Events + cooldown from the push still apply; a push
// with a non-empty email updates everything (customer edits keep working).
saveEmail := payload.Email
if saveEmail == "" {
if existing, err := h.store.GetNotificationPrefs(payload.CustomerID); err == nil && existing != nil && existing.Email != "" {
saveEmail = existing.Email
h.logger.Printf("[INFO] Notification prefs push for %s had empty email — preserving stored address", payload.CustomerID)
}
}
if err := h.store.SaveNotificationPrefs(payload.CustomerID, saveEmail, payload.EnabledEvents, payload.CooldownHours); err != nil {
h.logger.Printf("[ERROR] Failed to save notification prefs for %s: %v", payload.CustomerID, err)
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
h.logger.Printf("[INFO] Notification preferences updated for %s: email=%s, events=%v", payload.CustomerID, payload.Email, payload.EnabledEvents)
h.logger.Printf("[INFO] Notification preferences updated for %s: email=%s, events=%v", payload.CustomerID, saveEmail, payload.EnabledEvents)
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"ok"}`))
}