v0.88.0: app-email SMTP relay (in-process shim + per-app injection)
In-process go-smtp shim (Shape 1): apps → shim → hub → Resend, Resend key stays hub-side. From-header allowlist (reject 5xx pre-hub), single-shot raw-MIME forward, status→SMTP mapping. Global + per-app toggles gate compose-time env injection from .felhom.yml smtp_mapping. Hungarian UI on settings + app config pages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -398,6 +398,23 @@ func (s *Server) deployHandler(w http.ResponseWriter, r *http.Request, name stri
|
||||
}
|
||||
data["CurrentValues"] = optValues
|
||||
}
|
||||
|
||||
// App-email per-app toggle — only for apps that declare an smtp_mapping. Shown with
|
||||
// honest context whether or not the global toggle is on.
|
||||
if supported, enabled := s.stackMgr.AppEmailStatus(name); supported {
|
||||
data["AppEmailSupported"] = true
|
||||
data["AppEmailAppOn"] = enabled
|
||||
data["AppEmailGlobalOn"] = s.settings.AppEmailEnabled()
|
||||
local := meta.SMTPMapping.FromLocal
|
||||
if local == "" {
|
||||
local = meta.Slug
|
||||
}
|
||||
domain := "felhom.eu"
|
||||
if len(s.cfg.MailRelay.FromDomains) > 0 && s.cfg.MailRelay.FromDomains[0] != "" {
|
||||
domain = s.cfg.MailRelay.FromDomains[0]
|
||||
}
|
||||
data["AppEmailFromAddress"] = local + "@" + domain
|
||||
}
|
||||
}
|
||||
|
||||
// Memory info for deploy page (only for non-deployed apps)
|
||||
@@ -881,6 +898,13 @@ func (s *Server) settingsData() map[string]interface{} {
|
||||
|
||||
data["NotificationPrefs"] = s.settings.GetNotificationPrefs()
|
||||
|
||||
// App-email (SMTP relay) — global toggle. Only meaningful when a hub is configured (the relay
|
||||
// path runs through the hub); the template hides the control otherwise.
|
||||
appEmail := s.settings.GetAppEmail()
|
||||
data["AppEmailEnabled"] = appEmail.Enabled
|
||||
data["AppEmailFromName"] = appEmail.FromName
|
||||
data["AppEmailAvailable"] = s.cfg.Hub.URL != "" && s.cfg.MailRelay.HardEnabled()
|
||||
|
||||
// Storage paths with display data
|
||||
storagePaths := s.settings.GetStoragePaths()
|
||||
connectedCount := 0
|
||||
@@ -1101,6 +1125,40 @@ func (s *Server) settingsNotificationsHandler(w http.ResponseWriter, r *http.Req
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
}
|
||||
|
||||
// settingsAppEmailHandler saves the global app-email toggle and starts/stops the on-box
|
||||
// SMTP shim to match (no controller restart needed).
|
||||
func (s *Server) settingsAppEmailHandler(w http.ResponseWriter, r *http.Request) {
|
||||
_ = r.ParseForm()
|
||||
enabled := r.FormValue("app_email_enabled") == "on" || r.FormValue("app_email_enabled") == "true"
|
||||
fromName := strings.TrimSpace(r.FormValue("app_email_from_name"))
|
||||
|
||||
data := s.settingsData()
|
||||
if err := s.settings.SetAppEmail(enabled, fromName); err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Failed to save app-email toggle: %v", err)
|
||||
data["AppEmailError"] = "Hiba az alkalmazás-email beállítás mentésekor"
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
return
|
||||
}
|
||||
// Reconcile the shim's running state with the new toggle.
|
||||
if s.mailShim != nil {
|
||||
if err := s.mailShim.Apply(enabled); err != nil {
|
||||
s.logger.Printf("[ERROR] [web] app-email shim could not be %s: %v", map[bool]string{true: "started", false: "stopped"}[enabled], err)
|
||||
data = s.settingsData()
|
||||
data["AppEmailError"] = "A beállítás elmentve, de az email-szolgáltatás indítása nem sikerült."
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
return
|
||||
}
|
||||
}
|
||||
s.logger.Printf("[INFO] [web] App-email globally %s (from_name=%q)", map[bool]string{true: "enabled", false: "disabled"}[enabled], fromName)
|
||||
data = s.settingsData()
|
||||
if enabled {
|
||||
data["AppEmailSuccess"] = "Alkalmazás-email bekapcsolva. Kapcsold be az egyes alkalmazásoknál is, ahol email-küldést szeretnél."
|
||||
} else {
|
||||
data["AppEmailSuccess"] = "Alkalmazás-email kikapcsolva."
|
||||
}
|
||||
s.executeTemplate(w, r, "settings", data)
|
||||
}
|
||||
|
||||
func (s *Server) settingsNotificationsTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := s.settingsData()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user