feat: Implement CodexAuth proxy pool backend and a new sidebar component with navigation.

This commit is contained in:
2026-02-03 03:35:43 +08:00
parent 9b4838b604
commit f867e20c0e
2 changed files with 25 additions and 3 deletions

View File

@@ -2,11 +2,13 @@ package api
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"codex-pool/internal/database" "codex-pool/internal/database"
"codex-pool/internal/logger"
"codex-pool/internal/proxyutil" "codex-pool/internal/proxyutil"
) )
@@ -83,10 +85,12 @@ func addCodexProxy(w http.ResponseWriter, r *http.Request) {
count, err := database.Instance.AddCodexProxies(validProxies) count, err := database.Instance.AddCodexProxies(validProxies)
if err != nil { if err != nil {
logger.Error(fmt.Sprintf("批量添加代理失败: %v", err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "批量添加代理失败: "+err.Error()) Error(w, http.StatusInternalServerError, "批量添加代理失败: "+err.Error())
return return
} }
logger.Success(fmt.Sprintf("批量添加代理: %d/%d 个成功", count, len(validProxies)), "", "proxy-pool")
Success(w, map[string]interface{}{ Success(w, map[string]interface{}{
"added": count, "added": count,
"total": len(validProxies), "total": len(validProxies),
@@ -109,13 +113,16 @@ func addCodexProxy(w http.ResponseWriter, r *http.Request) {
id, err := database.Instance.AddCodexProxy(proxyURL, req.Description) id, err := database.Instance.AddCodexProxy(proxyURL, req.Description)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "UNIQUE constraint failed") { if strings.Contains(err.Error(), "UNIQUE constraint failed") {
logger.Warning(fmt.Sprintf("代理已存在: %s", getProxyDisplay(proxyURL)), "", "proxy-pool")
Error(w, http.StatusConflict, "代理地址已存在") Error(w, http.StatusConflict, "代理地址已存在")
return return
} }
logger.Error(fmt.Sprintf("添加代理失败: %v", err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "添加代理失败: "+err.Error()) Error(w, http.StatusInternalServerError, "添加代理失败: "+err.Error())
return return
} }
logger.Success(fmt.Sprintf("添加代理: %s (ID: %d)", getProxyDisplay(proxyURL), id), "", "proxy-pool")
Success(w, map[string]interface{}{ Success(w, map[string]interface{}{
"id": id, "id": id,
"message": "添加成功", "message": "添加成功",
@@ -130,9 +137,11 @@ func deleteCodexProxy(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("all") == "true" { if r.URL.Query().Get("all") == "true" {
err := database.Instance.ClearCodexProxies() err := database.Instance.ClearCodexProxies()
if err != nil { if err != nil {
logger.Error(fmt.Sprintf("清空代理池失败: %v", err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "清空代理失败: "+err.Error()) Error(w, http.StatusInternalServerError, "清空代理失败: "+err.Error())
return return
} }
logger.Success("已清空所有代理", "", "proxy-pool")
Success(w, map[string]string{"message": "已清空所有代理"}) Success(w, map[string]string{"message": "已清空所有代理"})
return return
} }
@@ -147,10 +156,12 @@ func deleteCodexProxy(w http.ResponseWriter, r *http.Request) {
} }
if err := database.Instance.DeleteCodexProxy(id); err != nil { if err := database.Instance.DeleteCodexProxy(id); err != nil {
logger.Error(fmt.Sprintf("删除代理失败 (ID: %d): %v", id, err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "删除代理失败: "+err.Error()) Error(w, http.StatusInternalServerError, "删除代理失败: "+err.Error())
return return
} }
logger.Info(fmt.Sprintf("删除代理 (ID: %d)", id), "", "proxy-pool")
Success(w, map[string]string{"message": "删除成功"}) Success(w, map[string]string{"message": "删除成功"})
} }
@@ -169,10 +180,12 @@ func toggleCodexProxy(w http.ResponseWriter, r *http.Request) {
} }
if err := database.Instance.ToggleCodexProxy(id); err != nil { if err := database.Instance.ToggleCodexProxy(id); err != nil {
logger.Error(fmt.Sprintf("切换代理状态失败 (ID: %d): %v", id, err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "切换代理状态失败: "+err.Error()) Error(w, http.StatusInternalServerError, "切换代理状态失败: "+err.Error())
return return
} }
logger.Info(fmt.Sprintf("切换代理状态 (ID: %d)", id), "", "proxy-pool")
Success(w, map[string]string{"message": "状态已切换"}) Success(w, map[string]string{"message": "状态已切换"})
} }
@@ -210,11 +223,14 @@ func testCodexProxy(w http.ResponseWriter, r *http.Request) {
return return
} }
// 执行测试 (异步执行以防前端超时,但用户想要同步结果,所以这里同步执行) proxyDisplay := getProxyDisplay(targetProxy.ProxyURL)
// 因为是在管理后台点击15s 超时是可以接受的 logger.Status(fmt.Sprintf("测试代理中: %s", proxyDisplay), "", "proxy-pool")
// 执行测试
result, err := proxyutil.TestProxy(targetProxy.ProxyURL) result, err := proxyutil.TestProxy(targetProxy.ProxyURL)
if err != nil { if err != nil {
database.Instance.UpdateCodexProxyTestResult(id, "", false) database.Instance.UpdateCodexProxyTestResult(id, "", false)
logger.Error(fmt.Sprintf("测试代理失败: %s - %v", proxyDisplay, err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "测试过程出错: "+err.Error()) Error(w, http.StatusInternalServerError, "测试过程出错: "+err.Error())
return return
} }
@@ -226,5 +242,11 @@ func testCodexProxy(w http.ResponseWriter, r *http.Request) {
return return
} }
if result.Success {
logger.Success(fmt.Sprintf("测试代理成功: %s -> %s (%s)", proxyDisplay, result.IP, result.Location), "", "proxy-pool")
} else {
logger.Error(fmt.Sprintf("测试代理失败: %s - %s", proxyDisplay, result.Error), "", "proxy-pool")
}
Success(w, result) Success(w, result)
} }

View File

@@ -46,7 +46,7 @@ const navItems: NavItem[] = [
{ to: '/config', icon: Cog, label: '配置概览' }, { to: '/config', icon: Cog, label: '配置概览' },
{ to: '/config/s2a', icon: Server, label: 'S2A 配置' }, { to: '/config/s2a', icon: Server, label: 'S2A 配置' },
{ to: '/config/email', icon: Mail, label: '邮箱配置' }, { to: '/config/email', icon: Mail, label: '邮箱配置' },
{ to: '/config/codex-proxy', icon: Globe, label: '代理池' }, { to: '/config/codex-proxy', icon: Globe, label: 'CodexAuth代理池' },
] ]
}, },
] ]