// Package docs:用户文档骨架(嵌入 HTML,侧栏+正文密度对齐工作台)。
package docs
import (
"embed"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
//go:embed content assets template.html
var files embed.FS
type page struct {
Slug string
Title string
Body string
}
var order = []page{
{Slug: "install", Title: "安装与启动"},
{Slug: "connect", Title: "连库入门"},
{Slug: "workbench", Title: "工作台简介"},
{Slug: "faq", Title: "常见问题"},
}
type Handler struct {
tpl string
}
func New() *Handler {
raw, err := files.ReadFile("template.html")
if err != nil {
panic("docs: template.html 缺失: " + err.Error())
}
return &Handler{tpl: string(raw)}
}
func (h *Handler) Register(r *gin.Engine) {
r.GET("/docs", func(c *gin.Context) { c.Redirect(http.StatusFound, "/docs/install") })
r.GET("/docs/:slug", h.Page)
r.GET("/docs/assets/*file", h.Asset)
}
func (h *Handler) Page(c *gin.Context) {
slug := c.Param("slug")
bodyBytes, err := files.ReadFile("content/" + slug + ".html")
if err != nil {
c.Status(http.StatusNotFound)
return
}
title := slug
nav := strings.Builder{}
for _, p := range order {
cls := "nav-link"
if p.Slug == slug {
cls += " active"
title = p.Title
}
nav.WriteString(`` + p.Title + ``)
}
html := h.tpl
html = strings.ReplaceAll(html, "{{TITLE}}", title)
html = strings.ReplaceAll(html, "{{NAV}}", nav.String())
html = strings.ReplaceAll(html, "{{BODY}}", string(bodyBytes))
c.Header("Content-Type", "text/html; charset=utf-8")
c.Header("Cache-Control", "no-cache")
_, _ = c.Writer.WriteString(html)
}
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
}
ct := "application/octet-stream"
if strings.HasSuffix(name, ".css") {
ct = "text/css; charset=utf-8"
}
c.Header("Content-Type", ct)
c.Data(http.StatusOK, ct, data)
}