Files
codexautopool/backend/internal/api/codex_proxy.go

253 lines
7.1 KiB
Go

package api
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"codex-pool/internal/database"
"codex-pool/internal/logger"
"codex-pool/internal/proxyutil"
)
// HandleCodexProxies 处理代理池请求
func HandleCodexProxies(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
listCodexProxies(w, r)
case http.MethodPost:
if strings.HasSuffix(r.URL.Path, "/test") {
testCodexProxy(w, r)
return
}
addCodexProxy(w, r)
case http.MethodDelete:
deleteCodexProxy(w, r)
case http.MethodPut:
toggleCodexProxy(w, r)
default:
Error(w, http.StatusMethodNotAllowed, "方法不允许")
}
}
// listCodexProxies 获取代理列表
func listCodexProxies(w http.ResponseWriter, r *http.Request) {
proxies, err := database.Instance.GetCodexProxies()
if err != nil {
Error(w, http.StatusInternalServerError, "获取代理列表失败: "+err.Error())
return
}
stats := database.Instance.GetCodexProxyStats()
Success(w, map[string]interface{}{
"proxies": proxies,
"stats": stats,
})
}
// AddProxyRequest 添加代理请求
type AddProxyRequest struct {
ProxyURL string `json:"proxy_url"`
Description string `json:"description"`
Proxies []string `json:"proxies"` // 批量添加
}
// addCodexProxy 添加代理
func addCodexProxy(w http.ResponseWriter, r *http.Request) {
var req AddProxyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
Error(w, http.StatusBadRequest, "请求参数错误")
return
}
// 批量添加
if len(req.Proxies) > 0 {
// 过滤空行和格式化
var validProxies []string
for _, p := range req.Proxies {
p = strings.TrimSpace(p)
if p != "" {
// 如果没有协议前缀,自动添加 http://
if !strings.HasPrefix(p, "http://") && !strings.HasPrefix(p, "https://") && !strings.HasPrefix(p, "socks5://") {
p = "http://" + p
}
validProxies = append(validProxies, p)
}
}
if len(validProxies) == 0 {
Error(w, http.StatusBadRequest, "没有有效的代理地址")
return
}
count, err := database.Instance.AddCodexProxies(validProxies)
if err != nil {
logger.Error(fmt.Sprintf("批量添加代理失败: %v", err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "批量添加代理失败: "+err.Error())
return
}
logger.Success(fmt.Sprintf("批量添加代理: %d/%d 个成功", count, len(validProxies)), "", "proxy-pool")
Success(w, map[string]interface{}{
"added": count,
"total": len(validProxies),
"message": "批量添加成功",
})
return
}
// 单个添加
if req.ProxyURL == "" {
Error(w, http.StatusBadRequest, "代理地址不能为空")
return
}
proxyURL := strings.TrimSpace(req.ProxyURL)
if !strings.HasPrefix(proxyURL, "http://") && !strings.HasPrefix(proxyURL, "https://") && !strings.HasPrefix(proxyURL, "socks5://") {
proxyURL = "http://" + proxyURL
}
id, err := database.Instance.AddCodexProxy(proxyURL, req.Description)
if err != nil {
if strings.Contains(err.Error(), "UNIQUE constraint failed") {
logger.Warning(fmt.Sprintf("代理已存在: %s", getProxyDisplay(proxyURL)), "", "proxy-pool")
Error(w, http.StatusConflict, "代理地址已存在")
return
}
logger.Error(fmt.Sprintf("添加代理失败: %v", err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "添加代理失败: "+err.Error())
return
}
logger.Success(fmt.Sprintf("添加代理: %s (ID: %d)", getProxyDisplay(proxyURL), id), "", "proxy-pool")
Success(w, map[string]interface{}{
"id": id,
"message": "添加成功",
})
}
// deleteCodexProxy 删除代理
func deleteCodexProxy(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
if idStr == "" {
// 如果没有 id 参数,检查是否要清空所有
if r.URL.Query().Get("all") == "true" {
err := database.Instance.ClearCodexProxies()
if err != nil {
logger.Error(fmt.Sprintf("清空代理池失败: %v", err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "清空代理失败: "+err.Error())
return
}
logger.Success("已清空所有代理", "", "proxy-pool")
Success(w, map[string]string{"message": "已清空所有代理"})
return
}
Error(w, http.StatusBadRequest, "缺少代理 ID")
return
}
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "无效的代理 ID")
return
}
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())
return
}
logger.Info(fmt.Sprintf("删除代理 (ID: %d)", id), "", "proxy-pool")
Success(w, map[string]string{"message": "删除成功"})
}
// toggleCodexProxy 切换代理启用状态
func toggleCodexProxy(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
if idStr == "" {
Error(w, http.StatusBadRequest, "缺少代理 ID")
return
}
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "无效的代理 ID")
return
}
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())
return
}
logger.Info(fmt.Sprintf("切换代理状态 (ID: %d)", id), "", "proxy-pool")
Success(w, map[string]string{"message": "状态已切换"})
}
// testCodexProxy 测试代理连通性
func testCodexProxy(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
if idStr == "" {
Error(w, http.StatusBadRequest, "缺少代理 ID")
return
}
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "无效的代理 ID")
return
}
// 获取代理详情
proxies, err := database.Instance.GetCodexProxies()
if err != nil {
Error(w, http.StatusInternalServerError, "获取代理失败")
return
}
var targetProxy *database.CodexProxy
for _, p := range proxies {
if p.ID == id {
targetProxy = &p
break
}
}
if targetProxy == nil {
Error(w, http.StatusNotFound, "未找到代理")
return
}
proxyDisplay := getProxyDisplay(targetProxy.ProxyURL)
logger.Status(fmt.Sprintf("测试代理中: %s", proxyDisplay), "", "proxy-pool")
// 执行测试
result, err := proxyutil.TestProxy(targetProxy.ProxyURL)
if err != nil {
database.Instance.UpdateCodexProxyTestResult(id, "", false)
logger.Error(fmt.Sprintf("测试代理失败: %s - %v", proxyDisplay, err), "", "proxy-pool")
Error(w, http.StatusInternalServerError, "测试过程出错: "+err.Error())
return
}
// 更新数据库
err = database.Instance.UpdateCodexProxyTestResult(id, result.Location, result.Success)
if err != nil {
Error(w, http.StatusInternalServerError, "更新测试结果失败")
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)
}