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 将任意数据序列化为可嵌入