87 lines
2.7 KiB
Go
87 lines
2.7 KiB
Go
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)
|
||
|
||
// 会话直达:/<sessionId> 返回前端页面,由前端 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(),
|
||
)))
|
||
}
|
||
}
|