Files
felhom.eu/hub/internal/api/household_prefs.go
T

72 lines
3.1 KiB
Go

package api
import (
"encoding/json"
"net/http"
)
// R-1199 reopened (2026-10-22). See store/household_prefs.go for the measured case (demo-felhom's list overwritten by
// controller 0.338.0's start-up default push). Two halves live here:
//
// - prefsPushIsHousehold: which pushes are the household's own save. A controller ≥ 0.339.0 says so (`source`). An
// older one does not: its pushes with an address or a clear are the household's (only a saved list carries
// them, R-922); a push with neither is either a page save without an address or 0.338.0's default push, and the
// two cannot be told apart — it is treated as NOT the household's, so it never overwrites a household's list
// (the cost: on a box still on ≤ 0.338.0, a household that saves without an address does not change a list it
// saved earlier, until the box takes the release that sends `source`).
// - handleGetPreferences: a box with no local list reads the household's list and adopts it, instead of pushing
// its default over it. Self-scoped like the save: a per-customer key reads only its own customer.
//
// Pinned by TestR1199Reopened_* (household_prefs_test.go).
func prefsPushIsHousehold(source, email string, emailCleared bool) bool {
switch source {
case "household":
return true
case "default":
return false
}
return email != "" || emailCleared
}
// handleGetPreferences answers GET /api/v1/preferences?customer_id=<id> with the stored list and whether the
// household saved it. `exists` false = no row. The address is returned only to the household's own box key (or the
// global key) — the box shows it on its own page.
func (h *Handler) handleGetPreferences(w http.ResponseWriter, r *http.Request) {
authCustomerID, isGlobal, ok := h.checkAuthCustomer(r)
if !ok {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
customerID := r.URL.Query().Get("customer_id")
if customerID == "" {
http.Error(w, "customer_id required", http.StatusBadRequest)
return
}
if !isGlobal && authCustomerID != customerID {
http.Error(w, "Forbidden: customer_id does not match the key", http.StatusForbidden)
return
}
prefs, err := h.store.GetNotificationPrefs(customerID)
if err != nil {
h.logger.Printf("[ERROR] Reading notification prefs for %s: %v", customerID, err)
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
out := struct {
Exists bool `json:"exists"`
HouseholdSaved bool `json:"household_saved"`
Email string `json:"email"`
EnabledEvents []string `json:"enabled_events"`
CooldownHours int `json:"cooldown_hours"`
}{EnabledEvents: []string{}}
if prefs != nil {
out.Exists, out.HouseholdSaved, out.Email, out.CooldownHours = true, prefs.HouseholdSaved, prefs.Email, prefs.CooldownHours
if prefs.EnabledEvents != nil {
out.EnabledEvents = prefs.EnabledEvents
}
}
h.logger.Printf("[DEBUG] Notification prefs read for %s: exists=%t household_saved=%t", customerID, out.Exists, out.HouseholdSaved)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(out)
}