initial commit
Some checks failed
ci / go (push) Has been cancelled
ci / admin-ui (push) Has been cancelled
ci / runtime-sdk (push) Has been cancelled
ci / helm (push) Has been cancelled
ci / openapi (push) Has been cancelled
ci / container (push) Has been cancelled
ci / release-config (push) Has been cancelled
ci / shared styling (push) Has been cancelled
ci / security (push) Has been cancelled
ci / all green (push) Has been cancelled

This commit is contained in:
Daniel Noulet 2026-10-07 15:11:35 +02:00
commit 951dd8d26b
175 changed files with 50041 additions and 0 deletions

View file

@ -0,0 +1,75 @@
package httpapi
import (
"net/http"
"github.com/urpi/go-bananas/internal/domain"
"github.com/urpi/go-bananas/internal/store"
)
// PreferenceHandlers serves the caller's own interface preferences.
//
// A preference belongs to the account rather than to a browser, so it is stored
// against the identity subject and travels with the person to another device. It
// is deliberately not part of the session token: the token is issued by the
// identity provider and this service does not control its claims, so a choice made
// here has to be read back from this API.
type PreferenceHandlers struct {
store *store.Store
log Logger
}
// NewPreferenceHandlers builds the preference handler set.
func NewPreferenceHandlers(st *store.Store, log Logger) *PreferenceHandlers {
return &PreferenceHandlers{store: st, log: log}
}
// Get returns the caller's preferences, or the empty preference when they have
// chosen nothing yet.
func (h *PreferenceHandlers) Get(w http.ResponseWriter, r *http.Request) {
identity := mustIdentity(r.Context())
prefs, err := h.store.Preferences(r.Context(), identity.Subject)
if err != nil {
h.log.Error("read preferences", "subject", identity.Subject, "error", err)
writeProblem(w, r, err)
return
}
writeJSON(w, r, http.StatusOK, prefs)
}
// Update records the caller's preferences and echoes back what was stored, so the
// client renders the server's decision rather than its own input.
//
// An empty selectedTheme clears the choice. Clearing through the same endpoint
// rather than a second route keeps the write surface to one path, which matters
// because this endpoint is the only thing that writes the row.
func (h *PreferenceHandlers) Update(w http.ResponseWriter, r *http.Request) {
identity := mustIdentity(r.Context())
var request domain.UserPreferences
if err := decodeJSON(r, &request); err != nil {
writeProblem(w, r, err)
return
}
stored, err := h.store.SetPreferences(r.Context(), identity.Subject, request)
if err != nil {
h.log.Warn("update preferences", "subject", identity.Subject, "error", err)
writeProblem(w, r, err)
return
}
writeJSON(w, r, http.StatusOK, stored)
}
// Reset forgets the caller's preferences and returns them to their defaults.
func (h *PreferenceHandlers) Reset(w http.ResponseWriter, r *http.Request) {
identity := mustIdentity(r.Context())
if _, err := h.store.ClearPreferences(r.Context(), identity.Subject); err != nil {
h.log.Error("clear preferences", "subject", identity.Subject, "error", err)
writeProblem(w, r, err)
return
}
writeJSON(w, r, http.StatusOK, domain.UserPreferences{})
}