125 lines
4.2 KiB
Go
125 lines
4.2 KiB
Go
package router
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/engigu/taskpool/internal/controllers"
|
|
"github.com/engigu/taskpool/internal/middleware"
|
|
"github.com/engigu/taskpool/internal/services"
|
|
|
|
"github.com/gin-contrib/pprof"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type Controllers struct {
|
|
Task *controllers.TaskController
|
|
Auth *controllers.AuthController
|
|
Env *controllers.EnvController
|
|
Script *controllers.ScriptController
|
|
Executor *controllers.ExecutorController
|
|
File *controllers.FileController
|
|
Dashboard *controllers.DashboardController
|
|
Log *controllers.LogController
|
|
LogSSE *controllers.LogSSEController
|
|
Terminal *controllers.TerminalController
|
|
Settings *controllers.SettingsController
|
|
Dependency *controllers.DependencyController
|
|
Agent *controllers.AgentController
|
|
Mise *controllers.MiseController
|
|
Notification *controllers.NotificationController
|
|
AppLog *controllers.AppLogController
|
|
SystemWS *controllers.SystemWSController
|
|
WebUI *controllers.WebUIController
|
|
Monitor *controllers.MonitorController
|
|
Interconnect *controllers.InterconnectController
|
|
Data *controllers.DataController
|
|
Install *controllers.InstallController
|
|
MCP *controllers.MCPController
|
|
}
|
|
|
|
func Setup(c *Controllers) *gin.Engine {
|
|
if os.Getenv("GIN_MODE") == "" {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
}
|
|
router := gin.New()
|
|
router.Use(middleware.GinLogger(), middleware.GinRecovery())
|
|
router.Use(middleware.TravelProxyMiddleware())
|
|
|
|
// 获取 URL 前缀
|
|
cfg := services.GetConfig()
|
|
urlPrefix := strings.TrimSuffix(cfg.Server.URLPrefix, "/")
|
|
|
|
// 创建一个路由组,如果有前缀则使用前缀,否则使用根路径
|
|
var root *gin.RouterGroup
|
|
if urlPrefix != "" {
|
|
root = router.Group(urlPrefix)
|
|
} else {
|
|
root = router.Group("")
|
|
}
|
|
|
|
// 按需绑定 Pprof 调试路由 (注册在 root 下以支持 URLPrefix)
|
|
if cfg.Server.PprofEnabled {
|
|
// pprof.RouteRegister 会在传入的路由组下注册 /debug/pprof 等路由
|
|
pprof.RouteRegister(root)
|
|
}
|
|
|
|
// =========================================================================
|
|
// 路由分类组装 (对应 Nginx 的 location 块分发)
|
|
// =========================================================================
|
|
|
|
// 1. [ location /assets ] 静态资源路由
|
|
initStaticRoutes(root)
|
|
|
|
// 3. [ location /api ] 内部 API 路由组
|
|
apiV1 := root.Group("/api/v1")
|
|
initPublicAPIRoutes(apiV1, c) // 公开接口 (无需认证)
|
|
initAuthorizedAPIRoutes(apiV1, c) // 授权接口 (需 JWT)
|
|
|
|
// 4. [ location /api/agent ] Agent 相关 API 路由组
|
|
initAgentAPIRoutes(root, c)
|
|
initOpenAPIV1Routes(root, c)
|
|
|
|
// 5. [ location /mcp ] MCP Server 路由(供 AI Agent 使用)
|
|
initMCPRoutes(root, c)
|
|
|
|
// =========================================================================
|
|
// [ location / ] 全局 404 兜底与 SPA 渲染
|
|
// 对应 Nginx: try_files $uri $uri/ /index.html;
|
|
// =========================================================================
|
|
router.NoRoute(func(ctx *gin.Context) {
|
|
path := ctx.Request.URL.Path
|
|
|
|
// 如果配置了前缀,只处理带前缀的路径
|
|
if urlPrefix != "" && !strings.HasPrefix(path, urlPrefix) {
|
|
ctx.Status(404)
|
|
return
|
|
}
|
|
|
|
// 解析实际的相对路径
|
|
relPath := strings.TrimPrefix(path, urlPrefix)
|
|
if !strings.HasPrefix(relPath, "/") {
|
|
relPath = "/" + relPath
|
|
}
|
|
|
|
// 拦截器:不该返回 index.html 的情况
|
|
// 如果该请求被识别为 API 请求、静态资源请求,或者是带有明确文件后缀(如 .js / .css / .png)的物理文件请求
|
|
// 都不应该返回 SPA 页面(会报前端 MIME 类型错误),而是直接掐断返回 404
|
|
hasAnyExt := false
|
|
if idx := strings.LastIndex(relPath, "."); idx > 0 && len(relPath)-idx < 6 {
|
|
// 简单判断是否有后缀(如 .js, .css)
|
|
hasAnyExt = true
|
|
}
|
|
|
|
if strings.HasPrefix(relPath, "/api/") || strings.HasPrefix(relPath, "/assets/") || strings.HasPrefix(relPath, "/debug/") || hasAnyExt {
|
|
ctx.String(404, "404 Not Found")
|
|
return
|
|
}
|
|
|
|
// 其他所有有效的前端页面路径(如 /tasks, /settings),都返回 index.html 交给 vue-router 处理
|
|
serveSPA(ctx, urlPrefix, 200)
|
|
})
|
|
|
|
return router
|
|
}
|