package router import ( "io" "io/fs" "net/http" "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" "github.com/gotunnel/internal/server/router/handler" "github.com/gotunnel/internal/server/router/middleware" "github.com/gotunnel/pkg/auth" ) // GinRouter Gin 路由管理器 type GinRouter struct { Engine *gin.Engine } // New 创建 Gin 路由管理器 func New() *GinRouter { gin.SetMode(gin.ReleaseMode) engine := gin.New() return &GinRouter{Engine: engine} } // Handler 返回 http.Handler func (r *GinRouter) Handler() http.Handler { return r.Engine } // SetupRoutes 配置所有路由 func (r *GinRouter) SetupRoutes(app handler.AppInterface, jwtAuth *auth.JWTAuth, username, password string) { engine := r.Engine // 全局中间件 engine.Use(middleware.Recovery()) engine.Use(middleware.Logger()) engine.Use(middleware.CORS()) // Swagger 文档 engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // 认证路由 (无需 JWT) authHandler := handler.NewAuthHandler(username, password, jwtAuth) engine.POST("/api/auth/login", authHandler.Login) engine.GET("/api/auth/check", authHandler.Check) // API 路由 (需要 JWT) api := engine.Group("/api") api.Use(middleware.JWTAuth(jwtAuth)) { // 状态 statusHandler := handler.NewStatusHandler(app) api.GET("/status", statusHandler.GetStatus) api.GET("/update/version", statusHandler.GetVersion) // 客户端管理 clientHandler := handler.NewClientHandler(app) api.GET("/clients", clientHandler.List) api.POST("/clients", clientHandler.Create) api.GET("/client/:id", clientHandler.Get) api.PUT("/client/:id", clientHandler.Update) api.DELETE("/client/:id", clientHandler.Delete) api.POST("/client/:id/push", clientHandler.PushConfig) api.POST("/client/:id/disconnect", clientHandler.Disconnect) api.POST("/client/:id/restart", clientHandler.Restart) api.POST("/client/:id/install-plugins", clientHandler.InstallPlugins) api.POST("/client/:id/plugin/:pluginName/:action", clientHandler.PluginAction) // 配置管理 configHandler := handler.NewConfigHandler(app) api.GET("/config", configHandler.Get) api.PUT("/config", configHandler.Update) api.POST("/config/reload", configHandler.Reload) // 插件管理 pluginHandler := handler.NewPluginHandler(app) api.GET("/plugins", pluginHandler.List) api.POST("/plugin/:name/enable", pluginHandler.Enable) api.POST("/plugin/:name/disable", pluginHandler.Disable) api.GET("/rule-schemas", pluginHandler.GetRuleSchemas) api.GET("/client-plugin/:clientID/:pluginName/config", pluginHandler.GetClientConfig) api.PUT("/client-plugin/:clientID/:pluginName/config", pluginHandler.UpdateClientConfig) // JS 插件管理 jsPluginHandler := handler.NewJSPluginHandler(app) api.GET("/js-plugins", jsPluginHandler.List) api.POST("/js-plugins", jsPluginHandler.Create) api.GET("/js-plugin/:name", jsPluginHandler.Get) api.PUT("/js-plugin/:name", jsPluginHandler.Update) api.DELETE("/js-plugin/:name", jsPluginHandler.Delete) api.POST("/js-plugin/:name/push/:clientID", jsPluginHandler.PushToClient) // 插件商店 storeHandler := handler.NewStoreHandler(app) api.GET("/store/plugins", storeHandler.ListPlugins) api.POST("/store/install", storeHandler.Install) // 更新管理 updateHandler := handler.NewUpdateHandler(app) api.GET("/update/check/server", updateHandler.CheckServer) api.GET("/update/check/client", updateHandler.CheckClient) api.POST("/update/apply/server", updateHandler.ApplyServer) api.POST("/update/apply/client", updateHandler.ApplyClient) } } // SetupStaticFiles 配置静态文件处理 func (r *GinRouter) SetupStaticFiles(staticFS fs.FS) { // 使用 NoRoute 处理 SPA 路由 r.Engine.NoRoute(gin.WrapH(&spaHandler{fs: http.FS(staticFS)})) } // spaHandler SPA 路由处理器 type spaHandler struct { fs http.FileSystem } func (h *spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path f, err := h.fs.Open(path) if err != nil { f, err = h.fs.Open("index.html") if err != nil { http.Error(w, "Not Found", http.StatusNotFound) return } } defer f.Close() stat, err := f.Stat() if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } if stat.IsDir() { f.Close() f, err = h.fs.Open(path + "/index.html") if err != nil { f, _ = h.fs.Open("index.html") } stat, _ = f.Stat() } if seeker, ok := f.(io.ReadSeeker); ok { http.ServeContent(w, r, path, stat.ModTime(), seeker) } } // Re-export types from handler package for backward compatibility type ( ServerInterface = handler.ServerInterface AppInterface = handler.AppInterface ConfigField = handler.ConfigField RuleSchema = handler.RuleSchema PluginInfo = handler.PluginInfo JSPluginInstallRequest = handler.JSPluginInstallRequest )