feat: Initialize backend API server with S2A proxy, mail service management, team processing, and add frontend email configuration page.

This commit is contained in:
2026-01-30 15:14:01 +08:00
parent 95ff84d15e
commit 1fd984e1ab
4 changed files with 86 additions and 4 deletions

View File

@@ -1,6 +1,7 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
@@ -361,10 +362,84 @@ func handleMailServices(w http.ResponseWriter, r *http.Request) {
}
func handleTestMailService(w http.ResponseWriter, r *http.Request) {
api.Success(w, map[string]interface{}{
"connected": true,
"message": "邮箱服务测试成功",
})
if r.Method != http.MethodPost {
api.Error(w, http.StatusMethodNotAllowed, "仅支持 POST")
return
}
var req struct {
Name string `json:"name"`
APIBase string `json:"api_base"`
APIToken string `json:"api_token"`
Domain string `json:"domain"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
api.Error(w, http.StatusBadRequest, "解析请求失败")
return
}
logger.Info(fmt.Sprintf("测试邮箱服务: %s (%s)", req.Name, req.Domain), "", "mail")
logger.Info(fmt.Sprintf("API Base: %s", req.APIBase), "", "mail")
// 创建测试请求
testEmail := "test@" + req.Domain
payload := map[string]interface{}{
"list": []map[string]string{
{"email": testEmail, "password": "TestPass123#"},
},
}
jsonData, _ := json.Marshal(payload)
apiURL := req.APIBase + "/api/public/addUser"
httpReq, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
if err != nil {
logger.Error(fmt.Sprintf("创建请求失败: %v", err), "", "mail")
api.Error(w, http.StatusInternalServerError, "创建请求失败")
return
}
httpReq.Header.Set("Authorization", req.APIToken)
httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(httpReq)
if err != nil {
logger.Error(fmt.Sprintf("请求失败: %v", err), "", "mail")
api.Error(w, http.StatusBadGateway, fmt.Sprintf("连接失败: %v", err))
return
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
bodyStr := string(bodyBytes)
logger.Info(fmt.Sprintf("响应状态: %d", resp.StatusCode), "", "mail")
logger.Info(fmt.Sprintf("响应内容: %s", bodyStr), "", "mail")
// 解析响应
var result struct {
Code int `json:"code"`
Message string `json:"message"`
}
if err := json.Unmarshal(bodyBytes, &result); err != nil {
logger.Error(fmt.Sprintf("解析响应失败: %v", err), "", "mail")
api.Error(w, http.StatusInternalServerError, "解析响应失败")
return
}
// 判断结果
if result.Code == 200 || strings.Contains(result.Message, "exist") {
logger.Success(fmt.Sprintf("邮箱服务测试成功: %s", req.Name), "", "mail")
api.Success(w, map[string]interface{}{
"connected": true,
"message": "邮箱服务连接成功",
"response": result,
})
} else {
logger.Error(fmt.Sprintf("邮箱服务测试失败: %s - %s", req.Name, result.Message), "", "mail")
api.Error(w, http.StatusBadRequest, fmt.Sprintf("API 错误: %s", result.Message))
}
}
func handleGetOwners(w http.ResponseWriter, r *http.Request) {

View File

@@ -258,6 +258,7 @@ func processSingleTeam(idx int, req TeamProcessRequest) TeamProcessResult {
if owner.AccountID != "" {
// 直接使用数据库中存储的 account_id
teamID = owner.AccountID
inviter.SetAccountID(teamID) // 必须设置到 inviter 中
logger.Info(fmt.Sprintf("%s 使用已存储的 Team ID: %s", logPrefix, teamID), owner.Email, "team")
} else {
// 如果没有存储,才请求 API 获取

View File

@@ -54,6 +54,11 @@ func NewWithProxy(accessToken, proxy string) *TeamInviter {
}
}
// SetAccountID 手动设置 account_id当已有存储值时使用
func (t *TeamInviter) SetAccountID(accountID string) {
t.accountID = accountID
}
// GetAccountID 获取 Team 的 account_id (workspace_id)
func (t *TeamInviter) GetAccountID() (string, error) {
req, _ := http.NewRequest("GET", "https://chatgpt.com/backend-api/accounts/check/v4-2023-04-27", nil)

View File

@@ -76,6 +76,7 @@ export default function EmailConfig() {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: service.name,
api_base: service.apiBase,
api_token: service.apiToken,
domain: service.domain,