From 48a99d58eec70352f9a1a4ae21ee478f20467ae9 Mon Sep 17 00:00:00 2001 From: hobokenchicken Date: Tue, 30 Jun 2026 15:59:55 -0400 Subject: [PATCH] fix: use APP_URL for email links instead of host:port DUMPSTER_PORT=8080 (internal) was baked into email reset links, producing http://dumpster.dustin.coffee:8080/reset-password which doesn't resolve through Caddy. Added Config.AppURL() that reads APP_URL env var (set to https://dumpster.dustin.coffee on server), falls back to http://host:port for dev. --- internal/auth/email_handlers.go | 10 ++-------- internal/config/config.go | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/internal/auth/email_handlers.go b/internal/auth/email_handlers.go index a88592c..142ec09 100644 --- a/internal/auth/email_handlers.go +++ b/internal/auth/email_handlers.go @@ -39,10 +39,7 @@ func (h *Handler) RequestVerification(w http.ResponseWriter, r *http.Request) { return } - appURL := "http://" + h.cfg.Host - if h.cfg.Port != "80" && h.cfg.Port != "443" { - appURL += ":" + h.cfg.Port - } + appURL := h.cfg.AppURL() if err := h.mailer.SendVerificationEmail(user.Email, user.Username, token, appURL); err != nil { http.Error(w, `{"error":"failed to send email"}`, http.StatusInternalServerError) @@ -112,10 +109,7 @@ func (h *Handler) RequestPasswordReset(w http.ResponseWriter, r *http.Request) { return } - appURL := "http://" + h.cfg.Host - if h.cfg.Port != "80" && h.cfg.Port != "443" { - appURL += ":" + h.cfg.Port - } + appURL := h.cfg.AppURL() h.mailer.SendPasswordResetEmail(req.Email, username, token, appURL) w.WriteHeader(http.StatusNoContent) diff --git a/internal/config/config.go b/internal/config/config.go index 476217e..daeca56 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,6 +2,7 @@ package config import ( "bufio" + "fmt" "os" "strconv" "strings" @@ -151,6 +152,22 @@ func Load() *Config { } } +func (c *Config) AppURL() string { + if v := getEnv("APP_URL", ""); v != "" { + return v + } + scheme := "http" + port := c.Port + if c.Database.SSLMode == "require" { + // ponytail: using SSLMode as a rough https hint; add explicit APP_URL for real setups + scheme = "https" + } + if port == "80" || port == "443" { + return fmt.Sprintf("%s://%s", scheme, c.Host) + } + return fmt.Sprintf("%s://%s:%s", scheme, c.Host, port) +} + func (c *Config) DatabaseDSN() string { return "postgres://" + c.Database.User + ":" + c.Database.Password + "@" + c.Database.Host + ":" + c.Database.Port + "/" + c.Database.Database + "?sslmode=" + c.Database.SSLMode }