feat: Implement Max RPM tracking API, Codex API authentication, and a new dashboard pool status component.
This commit is contained in:
@@ -114,6 +114,9 @@ func startServer(cfg *config.Config) {
|
||||
mux.HandleFunc("/api/s2a/clean-errors", api.CORS(api.HandleCleanErrorAccounts)) // 清理错误账号
|
||||
mux.HandleFunc("/api/s2a/cleaner/settings", api.CORS(handleCleanerSettings)) // 清理服务设置
|
||||
|
||||
// 统计 API
|
||||
mux.HandleFunc("/api/stats/max-rpm", api.CORS(api.HandleGetMaxRPM)) // 今日最高 RPM
|
||||
|
||||
// 邮箱服务 API
|
||||
mux.HandleFunc("/api/mail/services", api.CORS(handleMailServices))
|
||||
mux.HandleFunc("/api/mail/services/test", api.CORS(handleTestMailService))
|
||||
@@ -495,15 +498,75 @@ func handleLogStream(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func handleS2ATest(w http.ResponseWriter, r *http.Request) {
|
||||
if config.Global == nil || config.Global.S2AApiBase == "" {
|
||||
api.Error(w, http.StatusBadRequest, "S2A 配置未设置")
|
||||
api.Error(w, http.StatusBadRequest, "S2A API 地址未设置")
|
||||
return
|
||||
}
|
||||
|
||||
// 简单测试连接
|
||||
api.Success(w, map[string]interface{}{
|
||||
"connected": true,
|
||||
"message": "S2A 配置已就绪",
|
||||
})
|
||||
if config.Global.S2AAdminKey == "" {
|
||||
api.Error(w, http.StatusBadRequest, "S2A Admin Key 未设置")
|
||||
return
|
||||
}
|
||||
|
||||
// 请求 S2A 仪表盘接口
|
||||
dashboardURL := config.Global.S2AApiBase + "/api/v1/admin/dashboard"
|
||||
req, err := http.NewRequest("GET", dashboardURL, nil)
|
||||
if err != nil {
|
||||
api.Error(w, http.StatusInternalServerError, "创建请求失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 设置认证头
|
||||
adminKey := config.Global.S2AAdminKey
|
||||
req.Header.Set("Authorization", "Bearer "+adminKey)
|
||||
req.Header.Set("X-API-Key", adminKey)
|
||||
req.Header.Set("X-Admin-Key", adminKey)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("S2A 连接测试失败: %v", err), "", "s2a")
|
||||
api.Error(w, http.StatusBadGateway, fmt.Sprintf("无法连接 S2A: %v", err))
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
|
||||
// 检查响应状态
|
||||
if resp.StatusCode == 200 {
|
||||
// 解析仪表盘数据
|
||||
var dashboardData map[string]interface{}
|
||||
if err := json.Unmarshal(bodyBytes, &dashboardData); err == nil {
|
||||
logger.Success("S2A 连接测试成功", "", "s2a")
|
||||
api.Success(w, map[string]interface{}{
|
||||
"connected": true,
|
||||
"message": "S2A 连接成功",
|
||||
"dashboard": dashboardData,
|
||||
})
|
||||
return
|
||||
}
|
||||
// 解析失败但状态码 200,仍然认为连接成功
|
||||
logger.Success("S2A 连接测试成功", "", "s2a")
|
||||
api.Success(w, map[string]interface{}{
|
||||
"connected": true,
|
||||
"message": "S2A 连接成功",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 认证失败
|
||||
if resp.StatusCode == 401 || resp.StatusCode == 403 {
|
||||
logger.Error("S2A 认证失败: Admin Key 无效", "", "s2a")
|
||||
api.Error(w, http.StatusUnauthorized, "S2A Admin Key 无效")
|
||||
return
|
||||
}
|
||||
|
||||
// 其他错误
|
||||
logger.Error(fmt.Sprintf("S2A 响应错误: %d", resp.StatusCode), "", "s2a")
|
||||
api.Error(w, http.StatusBadGateway, fmt.Sprintf("S2A 响应错误: %d", resp.StatusCode))
|
||||
}
|
||||
|
||||
// handleS2AProxy 代理 S2A API 请求
|
||||
@@ -569,6 +632,16 @@ func handleS2AProxy(w http.ResponseWriter, r *http.Request) {
|
||||
// 记录响应状态(仅显示状态码和长度)
|
||||
logger.Info(fmt.Sprintf("S2A 响应: status=%d, len=%d", resp.StatusCode, len(bodyBytes)), "", "proxy")
|
||||
|
||||
// 如果是 dashboard 请求且成功,提取 RPM 并更新最高值
|
||||
if resp.StatusCode == 200 && strings.Contains(targetPath, "dashboard") {
|
||||
rpm := api.ExtractRPMFromDashboard(bodyBytes)
|
||||
if rpm > 0 {
|
||||
if api.UpdateMaxRPM(rpm) {
|
||||
logger.Info(fmt.Sprintf("更新今日最高 RPM: %d", rpm), "", "stats")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 复制响应头
|
||||
for key, values := range resp.Header {
|
||||
for _, value := range values {
|
||||
|
||||
Reference in New Issue
Block a user