From 2eb4a576395c11f810dde36f8044d04edfef9085 Mon Sep 17 00:00:00 2001 From: kyx236 Date: Sun, 8 Feb 2026 02:57:19 +0800 Subject: [PATCH] feat(mail): Add support for multiple domains per mail service - Add `Domains` field to MailServiceConfig for managing additional domains under single API - Implement `matchDomain` helper function for precise and subdomain matching logic - Update `GetServiceByDomain` to check both primary domain and additional domains list - Enhance EmailConfig UI to display domain count and allow comma-separated domain input - Add domains field to mail service request/response structures in API handlers - Update frontend types to include domains array in MailService interface - Improve documentation with clarification on primary vs additional domains usage - Allows single mail service API to manage multiple email domains for verification and operations --- backend/cmd/main.go | 17 ++++++++++------- backend/internal/config/config.go | 13 +++++++------ backend/internal/mail/service.go | 23 ++++++++++++++++++++--- frontend/src/pages/EmailConfig.tsx | 20 +++++++++++++++++--- frontend/src/types/index.ts | 3 ++- 5 files changed, 56 insertions(+), 20 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index 80b6179..e3f7121 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -140,7 +140,7 @@ func startServer(cfg *config.Config) { mux.HandleFunc("/api/db/owners/clear-used", api.CORS(handleClearUsedOwners)) // 清理已使用 mux.HandleFunc("/api/db/owners/delete/", api.CORS(handleDeleteOwner)) // DELETE /api/db/owners/delete/{id} mux.HandleFunc("/api/db/owners/batch-delete", api.CORS(handleBatchDeleteOwners)) // POST 批量删除 - mux.HandleFunc("/api/db/owners/ids", api.CORS(handleGetOwnerIDs)) // GET 获取所有ID(全选用) + mux.HandleFunc("/api/db/owners/ids", api.CORS(handleGetOwnerIDs)) // GET 获取所有ID(全选用) mux.HandleFunc("/api/db/owners/refetch-account-ids", api.CORS(api.HandleRefetchAccountIDs)) mux.HandleFunc("/api/upload/validate", api.CORS(api.HandleUploadValidate)) @@ -739,6 +739,7 @@ func handleMailServices(w http.ResponseWriter, r *http.Request) { "apiBase": s.APIBase, "apiToken": s.APIToken, "domain": s.Domain, + "domains": s.Domains, "emailPath": s.EmailPath, "addUserApi": s.AddUserAPI, } @@ -747,12 +748,13 @@ func handleMailServices(w http.ResponseWriter, r *http.Request) { case "POST": var req struct { Services []struct { - Name string `json:"name"` - APIBase string `json:"apiBase"` - APIToken string `json:"apiToken"` - Domain string `json:"domain"` - EmailPath string `json:"emailPath"` - AddUserAPI string `json:"addUserApi"` + Name string `json:"name"` + APIBase string `json:"apiBase"` + APIToken string `json:"apiToken"` + Domain string `json:"domain"` + Domains []string `json:"domains"` + EmailPath string `json:"emailPath"` + AddUserAPI string `json:"addUserApi"` } `json:"services"` } @@ -778,6 +780,7 @@ func handleMailServices(w http.ResponseWriter, r *http.Request) { APIBase: s.APIBase, APIToken: s.APIToken, Domain: s.Domain, + Domains: s.Domains, EmailPath: emailPath, AddUserAPI: addUserAPI, }) diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 6889d43..73827bb 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -10,12 +10,13 @@ import ( // MailServiceConfig 邮箱服务配置 type MailServiceConfig struct { - Name string `yaml:"name" json:"name"` - APIBase string `yaml:"api_base" json:"api_base"` - APIToken string `yaml:"api_token" json:"api_token"` - Domain string `yaml:"domain" json:"domain"` - EmailPath string `yaml:"email_path,omitempty" json:"email_path,omitempty"` - AddUserAPI string `yaml:"add_user_api,omitempty" json:"add_user_api,omitempty"` + Name string `yaml:"name" json:"name"` + APIBase string `yaml:"api_base" json:"api_base"` + APIToken string `yaml:"api_token" json:"api_token"` + Domain string `yaml:"domain" json:"domain"` + Domains []string `yaml:"domains,omitempty" json:"domains,omitempty"` // 附加域名列表(同一个API管理的多个域名) + EmailPath string `yaml:"email_path,omitempty" json:"email_path,omitempty"` + AddUserAPI string `yaml:"add_user_api,omitempty" json:"add_user_api,omitempty"` } // Config 应用配置 (实时从数据库读取) diff --git a/backend/internal/mail/service.go b/backend/internal/mail/service.go index e58635f..1a4a281 100644 --- a/backend/internal/mail/service.go +++ b/backend/internal/mail/service.go @@ -100,14 +100,31 @@ func GetRandomService() config.MailServiceConfig { return currentMailServices[rand.Intn(len(currentMailServices))] } +// matchDomain 检查邮箱域名是否匹配服务域名(精确匹配或子域名匹配) +func matchDomain(emailDomain, serviceDomain string) bool { + if serviceDomain == "" { + return false + } + return emailDomain == serviceDomain || strings.HasSuffix(emailDomain, "."+serviceDomain) +} + // GetServiceByDomain 根据域名获取对应的邮箱服务 +// 会同时检查 Domain(主域名)和 Domains(附加域名列表) func GetServiceByDomain(domain string) *config.MailServiceConfig { mailServicesMutex.RLock() defer mailServicesMutex.RUnlock() - for _, s := range currentMailServices { - if s.Domain == domain || strings.HasSuffix(domain, "."+s.Domain) { - return &s + for i := range currentMailServices { + s := ¤tMailServices[i] + // 检查主域名 + if matchDomain(domain, s.Domain) { + return s + } + // 检查附加域名列表 + for _, d := range s.Domains { + if matchDomain(domain, d) { + return s + } } } return nil diff --git a/frontend/src/pages/EmailConfig.tsx b/frontend/src/pages/EmailConfig.tsx index 0efff70..9f0e6d8 100644 --- a/frontend/src/pages/EmailConfig.tsx +++ b/frontend/src/pages/EmailConfig.tsx @@ -78,6 +78,7 @@ export default function EmailConfig() { apiBase: '', apiToken: '', domain: '', + domains: [], }, ]) } @@ -178,7 +179,7 @@ export default function EmailConfig() { {service.name || `服务 ${index + 1}`} - (@{service.domain || '未设置域名'}) + (@{service.domain || '未设置域名'}{service.domains && service.domains.length > 0 ? ` +${service.domains.length}域名` : ''})
@@ -249,6 +250,19 @@ export default function EmailConfig() { 高级设置
+ { + const domains = e.target.value + .split(/[,,]/) + .map(d => d.trim()) + .filter(d => d !== '') + handleUpdateService(index, { domains }) + }} + hint="同一个 API 管理的其他域名(逗号分隔),可用于截取验证码等操作" + />
  • 可以添加多个邮箱服务,系统会轮询使用各个服务
  • 每个服务需要配置独立的 API 地址、Token 和域名
  • -
  • 邮箱域名决定生成的邮箱地址后缀(如 xxx@esyteam.edu.kg)
  • -
  • 验证码会自动从配置的邮箱服务获取
  • +
  • 「邮箱域名」是主域名,决定生成的邮箱地址后缀(如 xxx@esyteam.edu.kg)
  • +
  • 「附加域名」可配置同一 API 管理的其他域名,用于验证码接收等操作
  • 高级设置通常不需要修改,使用默认值即可
  • diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index ed25d47..8b930d7 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -165,7 +165,8 @@ export interface MailServiceConfig { name: string // 服务名称 apiBase: string // API 地址 apiToken: string // API Token - domain: string // 邮箱域名 + domain: string // 主邮箱域名(用于生成邮箱) + domains?: string[] // 附加域名列表(同一API管理的多个域名) emailPath?: string // 获取邮件列表的 API 路径 addUserApi?: string // 创建用户的 API 路径 }