feat: OpsxDB 桌面数据库客户端首版入库
含工作台/连库多引擎字段与 SSH·SSL、结果编辑、Redis 键浏览、 Tauri 桌面与 docs/落地页;排除本地构建产物与 sidecar 二进制。
This commit is contained in:
commit
f8740d27f7
240 files changed
+21625
No files matched your search
@@ -0,0 +1,89 @@
|
||||
// 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(`<a class="` + cls + `" href="/docs/` + p.Slug + `">` + p.Title + `</a>`)
|
||||
}
|
||||
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)
|
||||
}
|
||||
Reference in new issue
Block a user