From 1fb29d59b70921533749e40e425ed7174223dfb0 Mon Sep 17 00:00:00 2001 From: Eilen6316 Date: Sat, 21 Mar 2026 23:36:30 +0800 Subject: [PATCH] fix(settings): prevent SMTP config overwrite and stabilize test after refresh --- .../internal/handler/admin/setting_handler.go | 94 +++++++++++++++---- backend/internal/service/email_service.go | 11 ++- frontend/src/views/admin/SettingsView.vue | 23 ++++- 3 files changed, 100 insertions(+), 28 deletions(-) diff --git a/backend/internal/handler/admin/setting_handler.go b/backend/internal/handler/admin/setting_handler.go index c91566c8..1c89393f 100644 --- a/backend/internal/handler/admin/setting_handler.go +++ b/backend/internal/handler/admin/setting_handler.go @@ -231,11 +231,27 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) { if req.DefaultBalance < 0 { req.DefaultBalance = 0 } + req.SMTPHost = strings.TrimSpace(req.SMTPHost) + req.SMTPUsername = strings.TrimSpace(req.SMTPUsername) + req.SMTPPassword = strings.TrimSpace(req.SMTPPassword) + req.SMTPFrom = strings.TrimSpace(req.SMTPFrom) + req.SMTPFromName = strings.TrimSpace(req.SMTPFromName) if req.SMTPPort <= 0 { req.SMTPPort = 587 } req.DefaultSubscriptions = normalizeDefaultSubscriptions(req.DefaultSubscriptions) + // SMTP 配置保护:如果请求中 smtp_host 为空但数据库中已有配置,则保留已有 SMTP 配置 + // 防止前端加载设置失败时空表单覆盖已保存的 SMTP 配置 + if req.SMTPHost == "" && previousSettings.SMTPHost != "" { + req.SMTPHost = previousSettings.SMTPHost + req.SMTPPort = previousSettings.SMTPPort + req.SMTPUsername = previousSettings.SMTPUsername + req.SMTPFrom = previousSettings.SMTPFrom + req.SMTPFromName = previousSettings.SMTPFromName + req.SMTPUseTLS = previousSettings.SMTPUseTLS + } + // Turnstile 参数验证 if req.TurnstileEnabled { // 检查必填字段 @@ -828,7 +844,7 @@ func equalDefaultSubscriptions(a, b []service.DefaultSubscriptionSetting) bool { // TestSMTPRequest 测试SMTP连接请求 type TestSMTPRequest struct { - SMTPHost string `json:"smtp_host" binding:"required"` + SMTPHost string `json:"smtp_host"` SMTPPort int `json:"smtp_port"` SMTPUsername string `json:"smtp_username"` SMTPPassword string `json:"smtp_password"` @@ -844,18 +860,35 @@ func (h *SettingHandler) TestSMTPConnection(c *gin.Context) { return } - if req.SMTPPort <= 0 { - req.SMTPPort = 587 + req.SMTPHost = strings.TrimSpace(req.SMTPHost) + req.SMTPUsername = strings.TrimSpace(req.SMTPUsername) + + var savedConfig *service.SMTPConfig + if cfg, err := h.emailService.GetSMTPConfig(c.Request.Context()); err == nil && cfg != nil { + savedConfig = cfg } - // 如果未提供密码,从数据库获取已保存的密码 - password := req.SMTPPassword - if password == "" { - savedConfig, err := h.emailService.GetSMTPConfig(c.Request.Context()) - if err == nil && savedConfig != nil { - password = savedConfig.Password + if req.SMTPHost == "" && savedConfig != nil { + req.SMTPHost = savedConfig.Host + } + if req.SMTPPort <= 0 { + if savedConfig != nil && savedConfig.Port > 0 { + req.SMTPPort = savedConfig.Port + } else { + req.SMTPPort = 587 } } + if req.SMTPUsername == "" && savedConfig != nil { + req.SMTPUsername = savedConfig.Username + } + password := strings.TrimSpace(req.SMTPPassword) + if password == "" && savedConfig != nil { + password = savedConfig.Password + } + if req.SMTPHost == "" { + response.BadRequest(c, "SMTP host is required") + return + } config := &service.SMTPConfig{ Host: req.SMTPHost, @@ -877,7 +910,7 @@ func (h *SettingHandler) TestSMTPConnection(c *gin.Context) { // SendTestEmailRequest 发送测试邮件请求 type SendTestEmailRequest struct { Email string `json:"email" binding:"required,email"` - SMTPHost string `json:"smtp_host" binding:"required"` + SMTPHost string `json:"smtp_host"` SMTPPort int `json:"smtp_port"` SMTPUsername string `json:"smtp_username"` SMTPPassword string `json:"smtp_password"` @@ -895,18 +928,43 @@ func (h *SettingHandler) SendTestEmail(c *gin.Context) { return } - if req.SMTPPort <= 0 { - req.SMTPPort = 587 + req.SMTPHost = strings.TrimSpace(req.SMTPHost) + req.SMTPUsername = strings.TrimSpace(req.SMTPUsername) + req.SMTPFrom = strings.TrimSpace(req.SMTPFrom) + req.SMTPFromName = strings.TrimSpace(req.SMTPFromName) + + var savedConfig *service.SMTPConfig + if cfg, err := h.emailService.GetSMTPConfig(c.Request.Context()); err == nil && cfg != nil { + savedConfig = cfg } - // 如果未提供密码,从数据库获取已保存的密码 - password := req.SMTPPassword - if password == "" { - savedConfig, err := h.emailService.GetSMTPConfig(c.Request.Context()) - if err == nil && savedConfig != nil { - password = savedConfig.Password + if req.SMTPHost == "" && savedConfig != nil { + req.SMTPHost = savedConfig.Host + } + if req.SMTPPort <= 0 { + if savedConfig != nil && savedConfig.Port > 0 { + req.SMTPPort = savedConfig.Port + } else { + req.SMTPPort = 587 } } + if req.SMTPUsername == "" && savedConfig != nil { + req.SMTPUsername = savedConfig.Username + } + password := strings.TrimSpace(req.SMTPPassword) + if password == "" && savedConfig != nil { + password = savedConfig.Password + } + if req.SMTPFrom == "" && savedConfig != nil { + req.SMTPFrom = savedConfig.From + } + if req.SMTPFromName == "" && savedConfig != nil { + req.SMTPFromName = savedConfig.FromName + } + if req.SMTPHost == "" { + response.BadRequest(c, "SMTP host is required") + return + } config := &service.SMTPConfig{ Host: req.SMTPHost, diff --git a/backend/internal/service/email_service.go b/backend/internal/service/email_service.go index 44edf7f7..00691233 100644 --- a/backend/internal/service/email_service.go +++ b/backend/internal/service/email_service.go @@ -12,6 +12,7 @@ import ( "net/smtp" "net/url" "strconv" + "strings" "time" infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors" @@ -111,7 +112,7 @@ func (s *EmailService) GetSMTPConfig(ctx context.Context) (*SMTPConfig, error) { return nil, fmt.Errorf("get smtp settings: %w", err) } - host := settings[SettingKeySMTPHost] + host := strings.TrimSpace(settings[SettingKeySMTPHost]) if host == "" { return nil, ErrEmailNotConfigured } @@ -128,10 +129,10 @@ func (s *EmailService) GetSMTPConfig(ctx context.Context) (*SMTPConfig, error) { return &SMTPConfig{ Host: host, Port: port, - Username: settings[SettingKeySMTPUsername], - Password: settings[SettingKeySMTPPassword], - From: settings[SettingKeySMTPFrom], - FromName: settings[SettingKeySMTPFromName], + Username: strings.TrimSpace(settings[SettingKeySMTPUsername]), + Password: strings.TrimSpace(settings[SettingKeySMTPPassword]), + From: strings.TrimSpace(settings[SettingKeySMTPFrom]), + FromName: strings.TrimSpace(settings[SettingKeySMTPFromName]), UseTLS: useTLS, }, nil } diff --git a/frontend/src/views/admin/SettingsView.vue b/frontend/src/views/admin/SettingsView.vue index 99cd247e..dafa75f5 100644 --- a/frontend/src/views/admin/SettingsView.vue +++ b/frontend/src/views/admin/SettingsView.vue @@ -1580,7 +1580,7 @@