package main import ( "log" "gitea.dooplex.hu/admin/felhom-controller/internal/notify" "gitea.dooplex.hu/admin/felhom-controller/internal/settings" ) // R-1199 (rehearsal 2026-10-20) and R-1199 REOPENED (2026-10-22). A fresh box shows the household its DEFAULT // notification list, but the hub mails only by its OWN stored list. v0.338.0 pushed that default at every start when // nothing was saved on the box — and on a REBUILT box that overwrote the list the household had saved on its old box // (demo-felhom, 07:54:55 UTC 2026-10-22: its own 13 events became the 17 defaults), again at every start, because the // push created no local save. `08` „the household decides". // // Now, at start: // - a list saved on this box: as before (R-922 — re-pushed when it carries an address or a clear), as the household's; // - nothing saved here: READ the hub's list first. One the household saved (on any box) is ADOPTED as this box's // list — nothing is pushed; otherwise the box's default is pushed once, as a default, and saved here, so the next // start sends nothing. A hub that cannot be read (an older hub, no network) gets nothing; the next start asks again. // // The hub refuses a default over a household's list on its own side too (felhom.eu hub household_prefs.go), which also // stops a box still on v0.338.0. Pinned by TestR1199Reopened_* (prefs_sync_wiring_test.go). type prefsHub interface { FetchPreferences() (*notify.HubPrefs, error) SyncPreferencesAs(source, email string, enabledEvents []string, cooldownHours int, emailCleared bool) (string, error) } type prefsStore interface { GetNotificationPrefs() *settings.NotificationPrefs NotificationPrefsSaved() bool SetNotificationPrefs(*settings.NotificationPrefs) error } // startupPrefsSync runs the start-up step above and returns what it did (for the log and the tests): "pushed", // "adopted", "default-pushed", "default-kept", "nothing", "no-hub" or "error". func startupPrefsSync(hub prefsHub, st prefsStore, logger *log.Logger) string { logf := func(f string, a ...any) { if logger != nil { logger.Printf(f, a...) } } prefs := st.GetNotificationPrefs() if st.NotificationPrefsSaved() { if !prefs.SyncOnStartup() { return "nothing" } if _, err := hub.SyncPreferencesAs(notify.PrefsSourceHousehold, prefs.Email, prefs.EnabledEvents, prefs.CooldownHours, prefs.EmailCleared); err != nil { logf("[WARN] Failed to sync notification preferences on startup: %v", err) return "error" } return "pushed" } hp, err := hub.FetchPreferences() if err != nil { logf("[WARN] notification prefs: no list saved on this box and the hub's could not be read (%v) — nothing pushed; asked again at the next start (R-1199)", err) return "no-hub" } if hp.Exists && hp.HouseholdSaved { adopted := &settings.NotificationPrefs{Email: hp.Email, EnabledEvents: hp.EnabledEvents, CooldownHours: hp.CooldownHours} if adopted.EnabledEvents == nil { adopted.EnabledEvents = []string{} } if err := st.SetNotificationPrefs(adopted); err != nil { logf("[ERROR] notification prefs: adopting the household's list from the hub failed: %v", err) return "error" } logf("[INFO] notification prefs: adopted the household's saved list from the hub (%d events, address set=%t) — nothing pushed (R-1199)", len(adopted.EnabledEvents), adopted.Email != "") return "adopted" } status, err := hub.SyncPreferencesAs(notify.PrefsSourceDefault, "", prefs.EnabledEvents, prefs.CooldownHours, false) if err != nil { logf("[WARN] notification prefs: pushing the default list failed: %v", err) return "error" } if status == "kept" { logf("[INFO] notification prefs: the hub kept the household's own list over this box's default — adopted at the next start (R-1199)") return "default-kept" } local := &settings.NotificationPrefs{EnabledEvents: prefs.EnabledEvents, CooldownHours: prefs.CooldownHours} if err := st.SetNotificationPrefs(local); err != nil { logf("[ERROR] notification prefs: saving the pushed default on this box failed: %v", err) return "error" } logf("[INFO] notification prefs: no household list anywhere — pushed this box's default once and saved it here (R-1199)") return "default-pushed" }