feat: Initialize core application structure with backend configuration, database, API, and a comprehensive frontend UI for account pooling and management.

This commit is contained in:
2026-01-31 01:48:07 +08:00
parent 74bdcae836
commit 92383f2f20
14 changed files with 776 additions and 47 deletions

View File

@@ -82,6 +82,9 @@ func main() {
// 启动自动补号检查器(需在前端开启开关才会实际补号)
api.StartAutoAddService()
// 启动错误账号定期清理服务(需在配置中启用)
api.StartErrorCleanerService()
// 启动服务器
startServer(cfg)
}
@@ -102,6 +105,7 @@ func startServer(cfg *config.Config) {
mux.HandleFunc("/api/s2a/test", api.CORS(handleS2ATest))
mux.HandleFunc("/api/s2a/proxy/", api.CORS(handleS2AProxy)) // 通配代理
mux.HandleFunc("/api/s2a/clean-errors", api.CORS(api.HandleCleanErrorAccounts)) // 清理错误账号
mux.HandleFunc("/api/s2a/cleaner/settings", api.CORS(handleCleanerSettings)) // 清理服务设置
// 邮箱服务 API
mux.HandleFunc("/api/mail/services", api.CORS(handleMailServices))
@@ -128,6 +132,7 @@ func startServer(cfg *config.Config) {
// 批次记录 API
mux.HandleFunc("/api/batch/runs", api.CORS(handleBatchRuns))
mux.HandleFunc("/api/batch/stats", api.CORS(handleBatchStats))
mux.HandleFunc("/api/batch/cleanup", api.CORS(handleBatchCleanup))
// 监控设置 API
mux.HandleFunc("/api/monitor/settings", api.CORS(api.HandleGetMonitorSettings))
@@ -197,6 +202,7 @@ func handleConfig(w http.ResponseWriter, r *http.Request) {
"group_ids": config.Global.GroupIDs,
"proxy_enabled": config.Global.ProxyEnabled,
"default_proxy": config.Global.DefaultProxy,
"site_name": config.Global.SiteName,
"mail_services_count": len(config.Global.MailServices),
"mail_services": config.Global.MailServices,
})
@@ -211,6 +217,7 @@ func handleConfig(w http.ResponseWriter, r *http.Request) {
GroupIDs []int `json:"group_ids"`
ProxyEnabled *bool `json:"proxy_enabled"`
DefaultProxy *string `json:"default_proxy"`
SiteName *string `json:"site_name"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
api.Error(w, http.StatusBadRequest, "请求格式错误")
@@ -239,6 +246,9 @@ func handleConfig(w http.ResponseWriter, r *http.Request) {
if req.DefaultProxy != nil {
config.Global.DefaultProxy = *req.DefaultProxy
}
if req.SiteName != nil {
config.Global.SiteName = *req.SiteName
}
// 保存到数据库 (实时生效)
if err := config.Update(config.Global); err != nil {
@@ -291,6 +301,30 @@ func handleBatchStats(w http.ResponseWriter, r *http.Request) {
api.Success(w, stats)
}
// handleBatchCleanup 清理卡住的 running 状态批次记录
func handleBatchCleanup(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
api.Error(w, http.StatusMethodNotAllowed, "仅支持 POST")
return
}
if database.Instance == nil {
api.Error(w, http.StatusInternalServerError, "数据库未初始化")
return
}
affected, err := database.Instance.CleanupStuckBatchRuns()
if err != nil {
api.Error(w, http.StatusInternalServerError, fmt.Sprintf("清理失败: %v", err))
return
}
api.Success(w, map[string]interface{}{
"message": "清理完成",
"affected": affected,
})
}
// handleLogStream SSE 实时日志流
func handleLogStream(w http.ResponseWriter, r *http.Request) {
// 设置 SSE 响应头
@@ -817,3 +851,62 @@ func getOutboundIP() string {
}
return ""
}
// handleCleanerSettings GET/POST /api/s2a/cleaner/settings - 获取/保存清理服务设置
func handleCleanerSettings(w http.ResponseWriter, r *http.Request) {
if database.Instance == nil {
api.Error(w, http.StatusInternalServerError, "数据库未初始化")
return
}
switch r.Method {
case http.MethodGet:
// 获取清理设置
enabled := false
interval := 3600 // 默认 1 小时
if val, _ := database.Instance.GetConfig("error_clean_enabled"); val == "true" {
enabled = true
}
if val, _ := database.Instance.GetConfig("error_clean_interval"); val != "" {
if v, err := strconv.Atoi(val); err == nil {
interval = v
}
}
api.Success(w, map[string]interface{}{
"enabled": enabled,
"interval": interval,
"status": api.GetCleanerStatus(),
})
case http.MethodPost:
// 保存清理设置
var req struct {
Enabled *bool `json:"enabled"`
Interval *int `json:"interval"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
api.Error(w, http.StatusBadRequest, "请求格式错误")
return
}
if req.Enabled != nil {
database.Instance.SetConfig("error_clean_enabled", strconv.FormatBool(*req.Enabled))
if *req.Enabled {
logger.Success("定期清理错误账号已启用", "", "cleaner")
} else {
logger.Info("定期清理错误账号已禁用", "", "cleaner")
}
}
if req.Interval != nil && *req.Interval >= 60 {
database.Instance.SetConfig("error_clean_interval", strconv.Itoa(*req.Interval))
logger.Info(fmt.Sprintf("清理间隔已设置为 %d 秒", *req.Interval), "", "cleaner")
}
api.Success(w, map[string]string{"message": "清理设置已保存"})
default:
api.Error(w, http.StatusMethodNotAllowed, "不支持的方法")
}
}