// Package router ... // // Description : api_doc ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2025-02-16 13:19 package router import ( "net/http" "path/filepath" "strings" "git.zhangdeman.cn/zhangdeman/api-doc/enums" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/gin/router/theme" "github.com/gin-gonic/gin" knife4goFiles "github.com/go-webtools/knife4go" knife4goGin "github.com/go-webtools/knife4go/gin" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" ) // NewSwaggerUI ... func NewSwaggerUI(docTitle string, docBaseUri string, uiTheme consts.SwaggerUITheme) *SwaggerUI { return &SwaggerUI{ baseUri: docBaseUri, docTitle: docTitle, uiTheme: uiTheme, } } type SwaggerUI struct { baseUri string docTitle string uiTheme consts.SwaggerUITheme // 文档主题, swaggerUI / knife4go, 默认 knife4go } // Handler 访问文档的接口处理 func (su *SwaggerUI) Handler() func(ctx *gin.Context) { switch su.uiTheme { case consts.SwaggerUIThemeKnife4go: return su.HandleKnife4goUI() case consts.SwaggerUIThemeYDocLucky: // YDoc-Lucky-UI 主题处理 return su.HandleLuckyUI() case consts.SwaggerUIThemeDefault: return su.HandleSwaggerUI() case consts.SwaggerUIThemeRedocFree: // redoc免费版 return su.HandleRedocFreeUI() case consts.SwaggerUIThemeElements: return su.HandleElements() default: return su.HandleSwaggerUI() } } func (su *SwaggerUI) HandleElements() gin.HandlerFunc { return func(ctx *gin.Context) { fileRealPath := strings.TrimPrefix(ctx.Request.RequestURI, su.baseUri) byteData, _ := theme.ElementsFiles.ReadFile(filepath.Join("elements", fileRealPath)) if strings.Contains(ctx.Request.RequestURI, ".html") { byteData = []byte(strings.ReplaceAll(string(byteData), "{{BASE_URI}}", su.baseUri)) byteData = []byte(strings.ReplaceAll(string(byteData), "{{DOC_TITLE}}", su.docTitle)) byteData = []byte(strings.ReplaceAll(string(byteData), "{{DOC_PATH}}", "doc.json")) ctx.Header(consts.HeaderKeyContentType.String(), "text/html; charset=utf-8") ctx.String(http.StatusOK, string(byteData)) ctx.Abort() return } uriArr := strings.Split(ctx.Request.RequestURI, ".") contentType := "text/" + uriArr[len(uriArr)-1] if strings.HasSuffix(ctx.Request.RequestURI, "png") { contentType = "image/png" } else if strings.HasSuffix(ctx.Request.RequestURI, "js") { contentType = "application/javascript" } ctx.Header(consts.HeaderKeyContentType.String(), contentType) ctx.String(http.StatusOK, string(byteData)) ctx.Abort() } } // HandleLuckyUI ... func (su *SwaggerUI) HandleLuckyUI() func(ctx *gin.Context) { // su.router.StaticFS(su.baseUri+"/assets", http.FS(ydocUIFiles)) return func(ctx *gin.Context) { fileRealPath := strings.TrimPrefix(ctx.Request.RequestURI, su.baseUri) byteData, _ := theme.YdocUIFiles.ReadFile(filepath.Join(enums.SwaggerUIThemeYDocLucky.String(), fileRealPath)) if strings.HasSuffix(ctx.Request.RequestURI, "html") { byteData = []byte(strings.ReplaceAll(string(byteData), "{{BASE_URI}}", su.baseUri)) } uriArr := strings.Split(ctx.Request.RequestURI, ".") contentType := "text/" + uriArr[len(uriArr)-1] if strings.HasSuffix(ctx.Request.RequestURI, "png") { contentType = "image/png" } else if strings.HasSuffix(ctx.Request.RequestURI, "js") { contentType = "application/javascript" } ctx.Header(consts.HeaderKeyContentType.String(), contentType) ctx.String(http.StatusOK, string(byteData)) ctx.Abort() } } // HandleKnife4goUI ... func (su *SwaggerUI) HandleKnife4goUI() func(ctx *gin.Context) { resetOption := func(cfg *knife4goGin.Config) { if nil == cfg { return } cfg.Title = "服务接口文档" } return knife4goGin.WrapHandler(knife4goFiles.Handler, resetOption) } // HandleSwaggerUI ... func (su *SwaggerUI) HandleSwaggerUI() func(ctx *gin.Context) { return ginSwagger.WrapHandler(swaggerFiles.Handler) } // HandleRedocFreeUI 处理redoc_free主题 func (su *SwaggerUI) HandleRedocFreeUI() func(ctx *gin.Context) { return func(ctx *gin.Context) { // TODO : 这部分数据支持外部传参替换 replaceTable := map[string]string{ "{{DOC_TITLE}}": su.docTitle, "{{CSS_FAMILY}}": "https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700", "{{DOC_PATH}}": "doc.json", "{{REDOC_STANDALONE_JS}}": "https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js", } for k, v := range replaceTable { theme.RedocFreeIndexContent = strings.ReplaceAll(theme.RedocFreeIndexContent, k, v) } ctx.Header(consts.HeaderKeyContentType.String(), "text/html; charset=utf-8") ctx.String(http.StatusOK, theme.RedocFreeIndexContent) ctx.Abort() } }