feat: Implement core backend services for team owner management, SQLite persistence, and logging, alongside frontend monitoring and record views.
This commit is contained in:
@@ -92,6 +92,7 @@ func startServer(cfg *config.Config) {
|
||||
// 日志 API
|
||||
mux.HandleFunc("/api/logs", api.CORS(handleGetLogs))
|
||||
mux.HandleFunc("/api/logs/clear", api.CORS(handleClearLogs))
|
||||
mux.HandleFunc("/api/logs/stream", handleLogStream) // SSE 实时日志
|
||||
|
||||
// S2A 代理 API
|
||||
mux.HandleFunc("/api/s2a/test", api.CORS(handleS2ATest))
|
||||
@@ -117,6 +118,10 @@ func startServer(cfg *config.Config) {
|
||||
mux.HandleFunc("/api/team/status", api.CORS(api.HandleTeamProcessStatus))
|
||||
mux.HandleFunc("/api/team/stop", api.CORS(api.HandleTeamProcessStop))
|
||||
|
||||
// 批次记录 API
|
||||
mux.HandleFunc("/api/batch/runs", api.CORS(handleBatchRuns))
|
||||
mux.HandleFunc("/api/batch/stats", api.CORS(handleBatchStats))
|
||||
|
||||
// 监控设置 API
|
||||
mux.HandleFunc("/api/monitor/settings", api.CORS(api.HandleGetMonitorSettings))
|
||||
mux.HandleFunc("/api/monitor/settings/save", api.CORS(api.HandleSaveMonitorSettings))
|
||||
@@ -252,6 +257,84 @@ func handleClearLogs(w http.ResponseWriter, r *http.Request) {
|
||||
api.Success(w, map[string]string{"message": "日志已清空"})
|
||||
}
|
||||
|
||||
// handleBatchRuns 获取批次运行记录
|
||||
func handleBatchRuns(w http.ResponseWriter, r *http.Request) {
|
||||
if database.Instance == nil {
|
||||
api.Error(w, http.StatusInternalServerError, "数据库未初始化")
|
||||
return
|
||||
}
|
||||
|
||||
runs, err := database.Instance.GetBatchRuns(50)
|
||||
if err != nil {
|
||||
api.Error(w, http.StatusInternalServerError, fmt.Sprintf("获取记录失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(w, runs)
|
||||
}
|
||||
|
||||
// handleBatchStats 获取批次统计
|
||||
func handleBatchStats(w http.ResponseWriter, r *http.Request) {
|
||||
if database.Instance == nil {
|
||||
api.Error(w, http.StatusInternalServerError, "数据库未初始化")
|
||||
return
|
||||
}
|
||||
|
||||
stats := database.Instance.GetBatchRunStats()
|
||||
api.Success(w, stats)
|
||||
}
|
||||
|
||||
// handleLogStream SSE 实时日志流
|
||||
func handleLogStream(w http.ResponseWriter, r *http.Request) {
|
||||
// 设置 SSE 响应头
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
// 生成唯一 ID
|
||||
listenerID := fmt.Sprintf("client-%d", time.Now().UnixNano())
|
||||
|
||||
// 订阅日志
|
||||
logChan := logger.AddListener(listenerID)
|
||||
defer logger.RemoveListener(listenerID)
|
||||
|
||||
// 获取 flusher
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
http.Error(w, "SSE not supported", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 发送初始连接确认
|
||||
fmt.Fprintf(w, "data: {\"type\":\"connected\",\"id\":\"%s\"}\n\n", listenerID)
|
||||
flusher.Flush()
|
||||
|
||||
// 监听日志和连接关闭
|
||||
ctx := r.Context()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case entry, ok := <-logChan:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// 发送日志条目
|
||||
data, _ := json.Marshal(map[string]interface{}{
|
||||
"type": "log",
|
||||
"timestamp": entry.Timestamp.Format("15:04:05"),
|
||||
"level": entry.Level,
|
||||
"message": entry.Message,
|
||||
"email": entry.Email,
|
||||
"module": entry.Module,
|
||||
})
|
||||
fmt.Fprintf(w, "data: %s\n\n", data)
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleS2ATest(w http.ResponseWriter, r *http.Request) {
|
||||
if config.Global == nil || config.Global.S2AApiBase == "" {
|
||||
api.Error(w, http.StatusBadRequest, "S2A 配置未设置")
|
||||
|
||||
Reference in New Issue
Block a user