From 2e10b900fabe33eb15fd36acdf7e8445454b4e7f Mon Sep 17 00:00:00 2001 From: kyx236 Date: Fri, 30 Jan 2026 15:47:18 +0800 Subject: [PATCH] feat: establish core backend API server with S2A proxy, configuration, and mail services, alongside frontend email configuration page and toast component. --- backend/cmd/main.go | 56 +++++++++++++++++++++++- frontend/src/components/common/Toast.tsx | 2 +- frontend/src/pages/EmailConfig.tsx | 23 ++++++++-- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/backend/cmd/main.go b/backend/cmd/main.go index d676059..7e92838 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -355,7 +355,61 @@ func handleMailServices(w http.ResponseWriter, r *http.Request) { } api.Success(w, safeServices) case "POST": - api.Error(w, http.StatusNotImplemented, "更新邮箱服务配置暂未实现") + 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"` + } `json:"services"` + } + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + logger.Error(fmt.Sprintf("解析邮箱服务配置失败: %v", err), "", "mail") + api.Error(w, http.StatusBadRequest, "解析请求失败") + return + } + + // 转换为 config.MailServiceConfig + var services []config.MailServiceConfig + for _, s := range req.Services { + emailPath := s.EmailPath + if emailPath == "" { + emailPath = "/api/public/emailList" + } + addUserAPI := s.AddUserAPI + if addUserAPI == "" { + addUserAPI = "/api/public/addUser" + } + services = append(services, config.MailServiceConfig{ + Name: s.Name, + APIBase: s.APIBase, + APIToken: s.APIToken, + Domain: s.Domain, + EmailPath: emailPath, + AddUserAPI: addUserAPI, + }) + } + + // 更新邮箱服务配置 + mail.Init(services) + + // 保存到全局配置 + if config.Global != nil { + config.Global.MailServices = services + } + + logger.Success(fmt.Sprintf("邮箱服务配置已保存: %d 个服务", len(services)), "", "mail") + for _, s := range services { + logger.Info(fmt.Sprintf(" - %s (%s) @ %s", s.Name, s.Domain, s.APIBase), "", "mail") + } + + api.Success(w, map[string]interface{}{ + "message": fmt.Sprintf("已保存 %d 个邮箱服务配置", len(services)), + "count": len(services), + }) default: api.Error(w, http.StatusMethodNotAllowed, "不支持的方法") } diff --git a/frontend/src/components/common/Toast.tsx b/frontend/src/components/common/Toast.tsx index 7a53e04..c6cc03b 100644 --- a/frontend/src/components/common/Toast.tsx +++ b/frontend/src/components/common/Toast.tsx @@ -78,7 +78,7 @@ function ToastContainer({ toasts, onRemove }: ToastContainerProps) { if (toasts.length === 0) return null return ( -
+
{toasts.map((toast) => ( ))} diff --git a/frontend/src/pages/EmailConfig.tsx b/frontend/src/pages/EmailConfig.tsx index 2b6410f..dc000dd 100644 --- a/frontend/src/pages/EmailConfig.tsx +++ b/frontend/src/pages/EmailConfig.tsx @@ -1,13 +1,15 @@ import { useState, useEffect } from 'react' import { CheckCircle, Save, Mail, Plus, Trash2, TestTube, Loader2, Settings, Server } from 'lucide-react' -import { Card, CardHeader, CardTitle, CardContent, Button, Input } from '../components/common' +import { Card, CardHeader, CardTitle, CardContent, Button, Input, useToast } from '../components/common' import { useConfig } from '../hooks/useConfig' import type { MailServiceConfig } from '../types' export default function EmailConfig() { const { config, updateEmailConfig } = useConfig() + const toast = useToast() const [saved, setSaved] = useState(false) + const [saving, setSaving] = useState(false) const [services, setServices] = useState(config.email?.services || []) const [testingIndex, setTestingIndex] = useState(null) const [testResults, setTestResults] = useState>({}) @@ -20,6 +22,12 @@ export default function EmailConfig() { }, [config.email?.services]) const handleSave = async () => { + if (services.length === 0) { + toast.warning('请至少添加一个邮箱服务') + return + } + + setSaving(true) // 保存到前端 context updateEmailConfig({ services }) @@ -31,13 +39,20 @@ export default function EmailConfig() { body: JSON.stringify({ services }), }) - if (res.ok) { + const data = await res.json() + + if (res.ok && data.code === 0) { setSaved(true) setTimeout(() => setSaved(false), 2000) + toast.success(`保存成功: ${data.data.message}`) + } else { + toast.error('保存失败: ' + (data.message || '未知错误')) } } catch (error) { console.error('保存失败:', error) + toast.error('保存失败: ' + (error instanceof Error ? error.message : '网络错误')) } + setSaving(false) } const handleAddService = () => { @@ -121,9 +136,11 @@ export default function EmailConfig() {