// Package site 在 /home(及 /)提供产品官网单页(SSR,资源嵌入二进制)。 package site import ( "embed" "net/http" "strings" "github.com/gin-gonic/gin" ) //go:embed template.html terms.html assets var files embed.FS // Handler 官网服务 type Handler struct { html string terms string } func New() *Handler { raw, err := files.ReadFile("template.html") if err != nil { panic("site: template.html 缺失: " + err.Error()) } terms, err := files.ReadFile("terms.html") if err != nil { panic("site: terms.html 缺失: " + err.Error()) } return &Handler{html: string(raw), terms: string(terms)} } // Register 注册路由:/ 与 /home 均渲染官网 func (h *Handler) Register(r *gin.Engine) { r.GET("/", h.Page) r.GET("/home", h.Page) r.GET("/index.html", h.Page) r.GET("/site/assets/*file", h.Asset) r.GET("/terms", h.Terms) } // Terms 渲染服务条款 func (h *Handler) Terms(c *gin.Context) { c.Header("Content-Type", "text/html; charset=utf-8") c.Header("Cache-Control", "no-cache") _, _ = c.Writer.WriteString(h.terms) } // Page 渲染官网 func (h *Handler) Page(c *gin.Context) { c.Header("Content-Type", "text/html; charset=utf-8") c.Header("Cache-Control", "no-cache") _, _ = c.Writer.WriteString(h.html) } // Asset 静态资源 func (h *Handler) Asset(c *gin.Context) { name := strings.TrimPrefix(c.Param("file"), "/") if strings.Contains(name, "..") { c.Status(http.StatusBadRequest) return } data, err := files.ReadFile("assets/" + name) if err != nil { c.Status(http.StatusNotFound) return } switch { case strings.HasSuffix(name, ".css"): c.Header("Content-Type", "text/css; charset=utf-8") case strings.HasSuffix(name, ".js"): c.Header("Content-Type", "application/javascript; charset=utf-8") case strings.HasSuffix(name, ".svg"): c.Header("Content-Type", "image/svg+xml") default: c.Header("Content-Type", "application/octet-stream") } c.Header("Cache-Control", "public, max-age=3600") c.Data(http.StatusOK, "", data) }