package email import ( "fmt" "html/template" "net/http" "strings" "github.com/go-chi/chi/v5" ) // DevHandler serves live HTML previews of email templates. // It is ONLY safe to mount in development environments — never in production. type DevHandler struct { renderer *Renderer } // NewDevHandler creates a new DevHandler backed by the given renderer. func NewDevHandler(r *Renderer) *DevHandler { return &DevHandler{renderer: r} } // List serves an HTML page listing all available email template previews. // Mount at GET /dev/emails. func (h *DevHandler) List(w http.ResponseWriter, r *http.Request) { purposes := h.renderer.Purposes() var sb strings.Builder sb.WriteString(` Email Templates — Dev Preview

Email Templates

Development preview — not visible in production.

`) w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusOK) _, _ = fmt.Fprint(w, sb.String()) } // Preview renders an email template with placeholder data and serves the HTML. // Mount at GET /dev/emails/{purpose}. func (h *DevHandler) Preview(w http.ResponseWriter, r *http.Request) { purpose := chi.URLParam(r, "purpose") ctx := placeholderContext(purpose) rendered, err := h.renderer.Render(purpose, ctx) if err != nil { http.Error(w, fmt.Sprintf("render %q: %v", purpose, err), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusOK) _, _ = fmt.Fprint(w, rendered.HTML) } // placeholderContext returns realistic placeholder data for each email type. // These values are used by the dev preview only — never sent to real users. func placeholderContext(purpose string) EmailContext { switch purpose { case "login_otp": return EmailContext{ Code: "482916", ExpiresIn: 10, Purpose: "sign in", } case "magic_link": return EmailContext{ ActionURL: "https://example.com/auth/verify?token=preview-token", ButtonText: "Sign In \u2192", ExpiresIn: 15, } case "password_reset": return EmailContext{ ActionURL: "https://example.com/auth/reset?token=preview-token", ButtonText: "Reset Password \u2192", ExpiresIn: 60, } case "verify_email": return EmailContext{ Code: "738201", ExpiresIn: 30, Purpose: "verify your email", } case "welcome": return EmailContext{ ActionURL: "https://example.com/dashboard", ButtonText: "Get Started \u2192", Name: "Jordan", } default: return EmailContext{} } }