package api import ( "log/slog" "net/http" "github.com/alchemistkay/guestguard/internal/notification" ) type unsubscribeHandler struct { logger *slog.Logger signer *notification.UnsubscribeSigner suppress *notification.SuppressionRepo } // GET /unsubscribe/{token} — surface the email address that the token // belongs to so the frontend can show a confirmation page. Honoured even // before the user clicks "Confirm" so they see what's being unsubscribed. func (h *unsubscribeHandler) preview(w http.ResponseWriter, r *http.Request) { if h.signer == nil { writeError(w, http.StatusServiceUnavailable, "unsubscribe not configured") return } email, err := h.signer.Verify(r.PathValue("token")) if err != nil { writeError(w, http.StatusBadRequest, "invalid unsubscribe link") return } writeJSON(w, http.StatusOK, map[string]string{"email": email}) } // POST /unsubscribe/{token} — add the email to the suppression list. // Idempotent: clicking the link twice keeps the existing entry. func (h *unsubscribeHandler) confirm(w http.ResponseWriter, r *http.Request) { if h.signer == nil || h.suppress == nil { writeError(w, http.StatusServiceUnavailable, "unsubscribe not configured") return } email, err := h.signer.Verify(r.PathValue("token")) if err != nil { writeError(w, http.StatusBadRequest, "invalid unsubscribe link") return } if err := h.suppress.Add(r.Context(), email, "user clicked unsubscribe", notification.SuppressionUser); err != nil { h.logger.Error("add suppression", "err", err, "email", email) writeError(w, http.StatusInternalServerError, "failed to unsubscribe") return } writeJSON(w, http.StatusOK, map[string]string{"status": "unsubscribed", "email": email}) }