package server import ( "fmt" "net/http" "path/filepath" "time" "agent/internal/agent" "github.com/gin-gonic/gin" ) // NewRouter 组装全部路由:静态资源、会话直达、/api/v1 REST、/ws WebSocket。 // 返回 (gin.Engine, *Server):*Server 供 main.go 在托管模式下调用 StartBenchmark 自动触发评测。 func NewRouter(cfg agent.Config, store *agent.SessionStore, dockerCfg *agent.DockerConfigStore, apiCfg *agent.APIConfigStore) (*gin.Engine, *Server) { s := &Server{ cfg: cfg, store: store, agent: agent.NewAgent(cfg, store, dockerCfg, apiCfg), dockerCfg: dockerCfg, apiCfg: apiCfg, } gin.SetMode(gin.ReleaseMode) r := gin.New() r.Use(gin.Recovery(), requestLogger()) staticDir := filepath.Join(cfg.Workspace, "web", "static") indexFile := filepath.Join(staticDir, "index.html") r.Static("/static", staticDir) r.StaticFile("/", indexFile) // 会话直达:/ 返回前端页面,由前端 JS 从路径解析会话 ID r.GET("/:sessionId", func(c *gin.Context) { if !sessionIDPattern.MatchString(c.Param("sessionId")) { c.Status(http.StatusNotFound) return } c.File(indexFile) }) v1 := r.Group("/api/v1") v1.GET("/health", s.health) // 授权相关接口始终放行:前端据此判断是否需要授权 / 提交授权码。 // 其余 API 与 WebSocket 统一挂 authRequired(未启用授权时自动放行)。 auth := v1.Group("/auth") auth.GET("/status", s.authStatus) auth.POST("/login", s.authLogin) authed := v1.Group("") authed.Use(s.authRequired) authed.GET("/config", s.config) authed.GET("/docker/config", s.dockerConfig) authed.PUT("/docker/config", s.setDockerConfig) authed.GET("/llm/config", s.llmConfig) authed.PUT("/llm/config", s.setLLMConfig) authed.GET("/sessions", s.listSessions) authed.POST("/sessions", s.createSession) authed.GET("/sessions/:id", s.getSession) authed.GET("/sessions/:id/messages", s.sessionMessages) authed.DELETE("/sessions/:id", s.deleteSession) authed.DELETE("/sessions/:id/messages", s.clearSession) authed.GET("/coop/tasks", s.coopTasks) // WebSocket 实时通道:Agent 流式输出 / 状态 / 协作通知(同样受授权保护) r.GET("/ws", s.authRequired, s.ws) return r, s } // requestLogger 轻量请求日志中间件:统一 [http] 前缀,便于与 [coop] 等日志区分。 func requestLogger() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() c.Next() gin.DefaultWriter.Write([]byte(fmt.Sprintf( "[http] %s | %s %s | %s | %d\n", time.Now().Format("2006/01/02 15:04:05"), c.Request.Method, c.Request.URL.Path, time.Since(start).Round(time.Millisecond).String(), c.Writer.Status(), ))) } }