Email Configuration Successful!
This is a test email to verify your SMTP settings are working correctly.
package admin import ( "log" "time" "github.com/Wei-Shaw/sub2api/internal/handler/dto" "github.com/Wei-Shaw/sub2api/internal/pkg/response" "github.com/Wei-Shaw/sub2api/internal/server/middleware" "github.com/Wei-Shaw/sub2api/internal/service" "github.com/gin-gonic/gin" ) // SettingHandler 系统设置处理器 type SettingHandler struct { settingService *service.SettingService emailService *service.EmailService turnstileService *service.TurnstileService } // NewSettingHandler 创建系统设置处理器 func NewSettingHandler(settingService *service.SettingService, emailService *service.EmailService, turnstileService *service.TurnstileService) *SettingHandler { return &SettingHandler{ settingService: settingService, emailService: emailService, turnstileService: turnstileService, } } // GetSettings 获取所有系统设置 // GET /api/v1/admin/settings func (h *SettingHandler) GetSettings(c *gin.Context) { settings, err := h.settingService.GetAllSettings(c.Request.Context()) if err != nil { response.ErrorFrom(c, err) return } response.Success(c, dto.SystemSettings{ RegistrationEnabled: settings.RegistrationEnabled, EmailVerifyEnabled: settings.EmailVerifyEnabled, SmtpHost: settings.SmtpHost, SmtpPort: settings.SmtpPort, SmtpUsername: settings.SmtpUsername, SmtpPasswordConfigured: settings.SmtpPasswordConfigured, SmtpFrom: settings.SmtpFrom, SmtpFromName: settings.SmtpFromName, SmtpUseTLS: settings.SmtpUseTLS, TurnstileEnabled: settings.TurnstileEnabled, TurnstileSiteKey: settings.TurnstileSiteKey, TurnstileSecretKeyConfigured: settings.TurnstileSecretKeyConfigured, SiteName: settings.SiteName, SiteLogo: settings.SiteLogo, SiteSubtitle: settings.SiteSubtitle, ApiBaseUrl: settings.ApiBaseUrl, ContactInfo: settings.ContactInfo, DocUrl: settings.DocUrl, DefaultConcurrency: settings.DefaultConcurrency, DefaultBalance: settings.DefaultBalance, }) } // UpdateSettingsRequest 更新设置请求 type UpdateSettingsRequest struct { // 注册设置 RegistrationEnabled bool `json:"registration_enabled"` EmailVerifyEnabled bool `json:"email_verify_enabled"` // 邮件服务设置 SmtpHost string `json:"smtp_host"` SmtpPort int `json:"smtp_port"` SmtpUsername string `json:"smtp_username"` SmtpPassword string `json:"smtp_password"` SmtpFrom string `json:"smtp_from_email"` SmtpFromName string `json:"smtp_from_name"` SmtpUseTLS bool `json:"smtp_use_tls"` // Cloudflare Turnstile 设置 TurnstileEnabled bool `json:"turnstile_enabled"` TurnstileSiteKey string `json:"turnstile_site_key"` TurnstileSecretKey string `json:"turnstile_secret_key"` // OEM设置 SiteName string `json:"site_name"` SiteLogo string `json:"site_logo"` SiteSubtitle string `json:"site_subtitle"` ApiBaseUrl string `json:"api_base_url"` ContactInfo string `json:"contact_info"` DocUrl string `json:"doc_url"` // 默认配置 DefaultConcurrency int `json:"default_concurrency"` DefaultBalance float64 `json:"default_balance"` } // UpdateSettings 更新系统设置 // PUT /api/v1/admin/settings func (h *SettingHandler) UpdateSettings(c *gin.Context) { var req UpdateSettingsRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "Invalid request: "+err.Error()) return } previousSettings, err := h.settingService.GetAllSettings(c.Request.Context()) if err != nil { response.ErrorFrom(c, err) return } // 验证参数 if req.DefaultConcurrency < 1 { req.DefaultConcurrency = 1 } if req.DefaultBalance < 0 { req.DefaultBalance = 0 } if req.SmtpPort <= 0 { req.SmtpPort = 587 } // Turnstile 参数验证 if req.TurnstileEnabled { // 检查必填字段 if req.TurnstileSiteKey == "" { response.BadRequest(c, "Turnstile Site Key is required when enabled") return } if req.TurnstileSecretKey == "" { response.BadRequest(c, "Turnstile Secret Key is required when enabled") return } // 获取当前设置,检查参数是否有变化 currentSettings, err := h.settingService.GetAllSettings(c.Request.Context()) if err != nil { response.ErrorFrom(c, err) return } // 当 site_key 或 secret_key 任一变化时验证(避免配置错误导致无法登录) siteKeyChanged := currentSettings.TurnstileSiteKey != req.TurnstileSiteKey secretKeyChanged := currentSettings.TurnstileSecretKey != req.TurnstileSecretKey if siteKeyChanged || secretKeyChanged { if err := h.turnstileService.ValidateSecretKey(c.Request.Context(), req.TurnstileSecretKey); err != nil { response.ErrorFrom(c, err) return } } } settings := &service.SystemSettings{ RegistrationEnabled: req.RegistrationEnabled, EmailVerifyEnabled: req.EmailVerifyEnabled, SmtpHost: req.SmtpHost, SmtpPort: req.SmtpPort, SmtpUsername: req.SmtpUsername, SmtpPassword: req.SmtpPassword, SmtpFrom: req.SmtpFrom, SmtpFromName: req.SmtpFromName, SmtpUseTLS: req.SmtpUseTLS, TurnstileEnabled: req.TurnstileEnabled, TurnstileSiteKey: req.TurnstileSiteKey, TurnstileSecretKey: req.TurnstileSecretKey, SiteName: req.SiteName, SiteLogo: req.SiteLogo, SiteSubtitle: req.SiteSubtitle, ApiBaseUrl: req.ApiBaseUrl, ContactInfo: req.ContactInfo, DocUrl: req.DocUrl, DefaultConcurrency: req.DefaultConcurrency, DefaultBalance: req.DefaultBalance, } if err := h.settingService.UpdateSettings(c.Request.Context(), settings); err != nil { response.ErrorFrom(c, err) return } h.auditSettingsUpdate(c, previousSettings, settings, req) // 重新获取设置返回 updatedSettings, err := h.settingService.GetAllSettings(c.Request.Context()) if err != nil { response.ErrorFrom(c, err) return } response.Success(c, dto.SystemSettings{ RegistrationEnabled: updatedSettings.RegistrationEnabled, EmailVerifyEnabled: updatedSettings.EmailVerifyEnabled, SmtpHost: updatedSettings.SmtpHost, SmtpPort: updatedSettings.SmtpPort, SmtpUsername: updatedSettings.SmtpUsername, SmtpPasswordConfigured: updatedSettings.SmtpPasswordConfigured, SmtpFrom: updatedSettings.SmtpFrom, SmtpFromName: updatedSettings.SmtpFromName, SmtpUseTLS: updatedSettings.SmtpUseTLS, TurnstileEnabled: updatedSettings.TurnstileEnabled, TurnstileSiteKey: updatedSettings.TurnstileSiteKey, TurnstileSecretKeyConfigured: updatedSettings.TurnstileSecretKeyConfigured, SiteName: updatedSettings.SiteName, SiteLogo: updatedSettings.SiteLogo, SiteSubtitle: updatedSettings.SiteSubtitle, ApiBaseUrl: updatedSettings.ApiBaseUrl, ContactInfo: updatedSettings.ContactInfo, DocUrl: updatedSettings.DocUrl, DefaultConcurrency: updatedSettings.DefaultConcurrency, DefaultBalance: updatedSettings.DefaultBalance, }) } func (h *SettingHandler) auditSettingsUpdate(c *gin.Context, before *service.SystemSettings, after *service.SystemSettings, req UpdateSettingsRequest) { if before == nil || after == nil { return } changed := diffSettings(before, after, req) if len(changed) == 0 { return } subject, _ := middleware.GetAuthSubjectFromContext(c) role, _ := middleware.GetUserRoleFromContext(c) log.Printf("AUDIT: settings updated at=%s user_id=%d role=%s changed=%v", time.Now().UTC().Format(time.RFC3339), subject.UserID, role, changed, ) } func diffSettings(before *service.SystemSettings, after *service.SystemSettings, req UpdateSettingsRequest) []string { changed := make([]string, 0, 16) if before.RegistrationEnabled != after.RegistrationEnabled { changed = append(changed, "registration_enabled") } if before.EmailVerifyEnabled != after.EmailVerifyEnabled { changed = append(changed, "email_verify_enabled") } if before.SmtpHost != after.SmtpHost { changed = append(changed, "smtp_host") } if before.SmtpPort != after.SmtpPort { changed = append(changed, "smtp_port") } if before.SmtpUsername != after.SmtpUsername { changed = append(changed, "smtp_username") } if req.SmtpPassword != "" { changed = append(changed, "smtp_password") } if before.SmtpFrom != after.SmtpFrom { changed = append(changed, "smtp_from_email") } if before.SmtpFromName != after.SmtpFromName { changed = append(changed, "smtp_from_name") } if before.SmtpUseTLS != after.SmtpUseTLS { changed = append(changed, "smtp_use_tls") } if before.TurnstileEnabled != after.TurnstileEnabled { changed = append(changed, "turnstile_enabled") } if before.TurnstileSiteKey != after.TurnstileSiteKey { changed = append(changed, "turnstile_site_key") } if req.TurnstileSecretKey != "" { changed = append(changed, "turnstile_secret_key") } if before.SiteName != after.SiteName { changed = append(changed, "site_name") } if before.SiteLogo != after.SiteLogo { changed = append(changed, "site_logo") } if before.SiteSubtitle != after.SiteSubtitle { changed = append(changed, "site_subtitle") } if before.ApiBaseUrl != after.ApiBaseUrl { changed = append(changed, "api_base_url") } if before.ContactInfo != after.ContactInfo { changed = append(changed, "contact_info") } if before.DocUrl != after.DocUrl { changed = append(changed, "doc_url") } if before.DefaultConcurrency != after.DefaultConcurrency { changed = append(changed, "default_concurrency") } if before.DefaultBalance != after.DefaultBalance { changed = append(changed, "default_balance") } return changed } // TestSmtpRequest 测试SMTP连接请求 type TestSmtpRequest struct { SmtpHost string `json:"smtp_host" binding:"required"` SmtpPort int `json:"smtp_port"` SmtpUsername string `json:"smtp_username"` SmtpPassword string `json:"smtp_password"` SmtpUseTLS bool `json:"smtp_use_tls"` } // TestSmtpConnection 测试SMTP连接 // POST /api/v1/admin/settings/test-smtp func (h *SettingHandler) TestSmtpConnection(c *gin.Context) { var req TestSmtpRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "Invalid request: "+err.Error()) return } if req.SmtpPort <= 0 { req.SmtpPort = 587 } // 如果未提供密码,从数据库获取已保存的密码 password := req.SmtpPassword if password == "" { savedConfig, err := h.emailService.GetSmtpConfig(c.Request.Context()) if err == nil && savedConfig != nil { password = savedConfig.Password } } config := &service.SmtpConfig{ Host: req.SmtpHost, Port: req.SmtpPort, Username: req.SmtpUsername, Password: password, UseTLS: req.SmtpUseTLS, } err := h.emailService.TestSmtpConnectionWithConfig(config) if err != nil { response.ErrorFrom(c, err) return } response.Success(c, gin.H{"message": "SMTP connection successful"}) } // SendTestEmailRequest 发送测试邮件请求 type SendTestEmailRequest struct { Email string `json:"email" binding:"required,email"` SmtpHost string `json:"smtp_host" binding:"required"` SmtpPort int `json:"smtp_port"` SmtpUsername string `json:"smtp_username"` SmtpPassword string `json:"smtp_password"` SmtpFrom string `json:"smtp_from_email"` SmtpFromName string `json:"smtp_from_name"` SmtpUseTLS bool `json:"smtp_use_tls"` } // SendTestEmail 发送测试邮件 // POST /api/v1/admin/settings/send-test-email func (h *SettingHandler) SendTestEmail(c *gin.Context) { var req SendTestEmailRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "Invalid request: "+err.Error()) return } if req.SmtpPort <= 0 { req.SmtpPort = 587 } // 如果未提供密码,从数据库获取已保存的密码 password := req.SmtpPassword if password == "" { savedConfig, err := h.emailService.GetSmtpConfig(c.Request.Context()) if err == nil && savedConfig != nil { password = savedConfig.Password } } config := &service.SmtpConfig{ Host: req.SmtpHost, Port: req.SmtpPort, Username: req.SmtpUsername, Password: password, From: req.SmtpFrom, FromName: req.SmtpFromName, UseTLS: req.SmtpUseTLS, } siteName := h.settingService.GetSiteName(c.Request.Context()) subject := "[" + siteName + "] Test Email" body := `
This is a test email to verify your SMTP settings are working correctly.