package router import ( "encoding/json" "html/template" "net/http" "os" "path/filepath" "strings" "videoweb/app/api/web" "videoweb/app/appg" "videoweb/app/service/configser" "videoweb/app/service/playletser" "videoweb/app/service/seoser" "github.com/gin-gonic/gin" ) func Init() *gin.Engine { setGinMode() engine := gin.Default() engine.HandleMethodNotAllowed = true setupEngine(engine) engine.NoRoute(renderNotFound) engine.NoMethod(renderNotFound) registerM3U8Routes(engine) registerRoutes(engine) return engine } // DHInit 导航站独立端口:仅暴露官方导航页及静态资源。 func DHInit() *gin.Engine { setGinMode() engine := gin.Default() engine.HandleMethodNotAllowed = true setupEngine(engine) engine.StaticFile("/favicon.ico", "./static/favicon.ico") engine.StaticFile("/favicon.svg", "./static/favicon.svg") engine.StaticFile("/favicon.png", "./static/favicon.png") engine.GET("/ui/dialog/:name", web.DialogFragment) engine.GET("/", web.Nav) engine.GET("/nav/", web.Nav) engine.GET("/dh/", web.Nav) engine.NoRoute(renderNotFound) engine.NoMethod(renderNotFound) return engine } func setGinMode() { switch strings.ToLower(strings.TrimSpace(appg.Conf.RunMod)) { case "test": gin.SetMode(gin.TestMode) case "prod": gin.SetMode(gin.ReleaseMode) default: gin.SetMode(gin.DebugMode) } } func setupEngine(engine *gin.Engine) { tplFuncs := playletser.TemplateFuncs() tplFuncs["InJSONScript"] = inJSONScript tplFuncs["jsonStr"] = jsonStr tplFuncs["InTrackerAppId"] = inTrackerAppId engine.SetFuncMap(tplFuncs) engine.LoadHTMLFiles(listHTMLTemplates("templates")...) engine.Use(staticCacheHeaders(), crawlerHeaders()) registerBrowserDiscoveryRoutes(engine) engine.Static("/static", "./static") } // registerBrowserDiscoveryRoutes 处理 Chrome DevTools 的本地工作区探测请求。 // 该请求并非站点页面,返回 204 可避免进入业务 404 页面和产生误导日志。 func registerBrowserDiscoveryRoutes(engine *gin.Engine) { engine.GET("/.well-known/appspecific/com.chrome.devtools.json", func(c *gin.Context) { c.Header("Cache-Control", "public, max-age=86400") c.Header("X-Robots-Tag", "noindex, nofollow") c.Status(http.StatusNoContent) }) } // staticCacheHeaders 为静态资源设置浏览器缓存,减少重复下载。 func staticCacheHeaders() gin.HandlerFunc { return func(c *gin.Context) { if strings.HasPrefix(c.Request.URL.Path, "/static/") { c.Header("Cache-Control", "public, max-age=604800, stale-while-revalidate=86400") } c.Next() } } // crawlerHeaders 防止接口、片段和健康检查等非 HTML 内容进入搜索索引。 func crawlerHeaders() gin.HandlerFunc { return func(c *gin.Context) { path := c.Request.URL.Path if strings.HasPrefix(path, "/api/") || strings.HasPrefix(path, "/ui/") || strings.HasPrefix(path, "/h5/") || path == "/healthz" || path == "/enkey" || path == "/newenkey" { c.Header("X-Robots-Tag", "noindex, nofollow") } c.Next() } } // listHTMLTemplates 收集 templates 下页面模板,跳过遗留广告文件。 func listHTMLTemplates(root string) []string { var files []string _ = filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { if err != nil || d.IsDir() { return err } name := strings.ToLower(d.Name()) if !strings.HasSuffix(name, ".html") { return nil } if name == "banners.html" { return nil } files = append(files, path) return nil }) if len(files) == 0 { panic("no html templates found under " + root) } return files } // inJSONScript 将任意数据序列化为可嵌入