231 lines
5.8 KiB
Go
231 lines
5.8 KiB
Go
package api
|
||
|
||
import (
|
||
"encoding/json"
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"codex-pool/internal/database"
|
||
"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 {
|
||
Error(w, http.StatusInternalServerError, "批量添加代理失败: "+err.Error())
|
||
return
|
||
}
|
||
|
||
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") {
|
||
Error(w, http.StatusConflict, "代理地址已存在")
|
||
return
|
||
}
|
||
Error(w, http.StatusInternalServerError, "添加代理失败: "+err.Error())
|
||
return
|
||
}
|
||
|
||
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 {
|
||
Error(w, http.StatusInternalServerError, "清空代理失败: "+err.Error())
|
||
return
|
||
}
|
||
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 {
|
||
Error(w, http.StatusInternalServerError, "删除代理失败: "+err.Error())
|
||
return
|
||
}
|
||
|
||
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 {
|
||
Error(w, http.StatusInternalServerError, "切换代理状态失败: "+err.Error())
|
||
return
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
// 执行测试 (异步执行以防前端超时,但用户想要同步结果,所以这里同步执行)
|
||
// 因为是在管理后台点击,15s 超时是可以接受的
|
||
result, err := proxyutil.TestProxy(targetProxy.ProxyURL)
|
||
if err != nil {
|
||
database.Instance.UpdateCodexProxyTestResult(id, "", false)
|
||
Error(w, http.StatusInternalServerError, "测试过程出错: "+err.Error())
|
||
return
|
||
}
|
||
|
||
// 更新数据库
|
||
err = database.Instance.UpdateCodexProxyTestResult(id, result.Location, result.Success)
|
||
if err != nil {
|
||
Error(w, http.StatusInternalServerError, "更新测试结果失败")
|
||
return
|
||
}
|
||
|
||
Success(w, result)
|
||
}
|