package routes import ( "fmt" "net/http" "time" "git.x2erp.com/qdy/go-base/authbase" "git.x2erp.com/qdy/go-base/webx/router" "git.x2erp.com/qdy/go-svc-code/internal/opencode" ) // RegisterLogStreamRoutes 注册日志流路由 func RegisterLogStreamRoutes(ws *router.RouterService, process *opencode.Process) { // 日志流路由 // 注意:这个路由需要直接处理 HTTP 流,不能使用标准的 router 包装 // 我们将注册一个原始的 HTTP 处理器到 gin 引擎 // 这个函数将由 main.go 中的额外注册调用 } // LogStreamHandler 日志流的 HTTP 处理器(已包含TokenAuth认证) func LogStreamHandler(process *opencode.Process) http.HandlerFunc { // 创建内部处理器 handler := func(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", "*") flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming unsupported", http.StatusInternalServerError) return } // 获取日志通道 logChan := process.GetLogs() if logChan == nil { http.Error(w, "日志通道不可用", http.StatusInternalServerError) return } // 发送初始消息 fmt.Fprintf(w, "data: 连接到 opencode 日志流\n\n") flusher.Flush() // 监听日志通道 for { select { case log, ok := <-logChan: if !ok { // 通道关闭 fmt.Fprintf(w, "data: 日志流已结束\n\n") flusher.Flush() return } // 发送日志 fmt.Fprintf(w, "data: %s\n\n", log) flusher.Flush() case <-r.Context().Done(): // 客户端断开连接 return case <-time.After(30 * time.Second): // 发送心跳保持连接 fmt.Fprintf(w, ": heartbeat\n\n") flusher.Flush() } } } // 包装TokenAuth中间件 return authbase.TokenAuth(http.HandlerFunc(handler)).ServeHTTP }