package server import ( "database/sql" "encoding/json" "html/template" "net/http" "github.com/gin-gonic/gin" "opsxapi/server/internal/store" ) type sharedDocument struct { Name string `json:"name"` Endpoints []struct { Method string `json:"method"` Path string `json:"path"` Name string `json:"name"` Description string `json:"description"` Parameters []struct { Name string `json:"name"` In string `json:"in"` Required bool `json:"required"` Description string `json:"description"` Example string `json:"example"` } `json:"parameters"` RequestBody *struct { ContentType string `json:"contentType"` Example string `json:"example"` } `json:"requestBody"` Responses []struct { Status string `json:"status"` Description string `json:"description"` ContentType string `json:"contentType"` Example string `json:"example"` } `json:"responses"` MockURL string `json:"mockUrl"` } `json:"endpoints"` } var sharedDocsTemplate = template.Must(template.New("docs").Parse(`{{.Name}} · API 文档

{{.Name}}

自托管 API 文档 · 只读快照
{{range $i,$e:=.Endpoints}}

{{$e.Name}}

{{$e.Method}} {{$e.Path}}

{{$e.Description}}

{{if $e.Parameters}}

请求参数

{{range $e.Parameters}}{{end}}
参数位置必填示例说明
{{.Name}}{{.In}}{{if .Required}}是{{else}}否{{end}}{{.Example}}{{.Description}}
{{end}}{{with $e.RequestBody}}

请求体 · {{.ContentType}}

{{.Example}}
{{end}}{{range $e.Responses}}

HTTP {{.Status}} · {{.ContentType}}

{{.Description}}

{{.Example}}
{{end}}{{if $e.MockURL}}

Mock 地址

{{$e.MockURL}}

{{end}}
{{end}}
`)) func registerSharedDocs(r *gin.Engine, s *store.Store) { r.GET("/docs/:token", func(c *gin.Context) { record, err := s.PublishedDocByToken(c.Param("token")) if err == sql.ErrNoRows { c.AbortWithStatus(404) return } if err != nil { c.AbortWithStatus(500) return } var doc sharedDocument if json.Unmarshal([]byte(record.Content), &doc) != nil { c.AbortWithStatus(500) return } c.Header("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'") c.Header("X-Content-Type-Options", "nosniff") c.Status(http.StatusOK) c.Header("Content-Type", "text/html; charset=utf-8") _ = sharedDocsTemplate.Execute(c.Writer, doc) }) }