Files
TaskPool/internal/router/router.go
T
admin 9191fccab3
Build and Deploy / build-and-push (push) Successful in 53s
fix(router): use CIDR to trust all proxies for Host header validation
2026-07-27 03:15:53 +08:00

131 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()
// 信任所有反向代理(Docker/Nginx 场景)
// 使用 CIDR "0.0.0.0/0" 信任所有 IPv4"::/0" 信任所有 IPv6
// 避免 "Forbidden: invalid Host header" 错误
router.SetTrustedProxies([]string{"0.0.0.0/0", "::/0"})
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
}