Files
TaskPool/internal/router/router.go
T
admin e6956aa001 Initial commit: TaskPool React panel
- React frontend with route-level code splitting
- Backend rebranded from Baihu to TaskPool
- DB brand migration script and local compatibility
2026-07-26 08:43:52 +08:00

121 lines
4.0 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
}
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)
// =========================================================================
// [ 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
}