package middleware import ( "bufio" "bytes" "net" "net/http" "strconv" "strings" swaggerFiles "github.com/swaggo/files" "github.com/swaggo/swag" "github.com/gin-gonic/gin" ) func SwaggerHandler() gin.HandlerFunc { return func(c *gin.Context) { rawPath := c.Request.URL.Path relPath := strings.TrimPrefix(rawPath, "/swagger") if relPath == "" || relPath == "/" { c.Request.URL.Path = "/index.html" } else { c.Request.URL.Path = relPath } isCSS := strings.HasSuffix(relPath, "swagger-ui.css") isInitializer := strings.HasSuffix(relPath, "swagger-initializer.js") isDocJSON := strings.HasSuffix(relPath, "doc.json") if isDocJSON { doc, _ := swag.ReadDoc() c.Header("Content-Type", "application/json; charset=utf-8") c.Writer.WriteHeader(200) c.Writer.Write([]byte(doc)) c.Request.URL.Path = rawPath return } if isInitializer { c.Header("Content-Type", "application/javascript; charset=utf-8") c.Writer.WriteHeader(200) c.Writer.Write([]byte(chineseInitializer)) c.Request.URL.Path = rawPath return } if isCSS { crw := &captureResponseWriter{ ginWriter: c.Writer, code: 200, } c.Writer = crw swaggerFiles.Handler.ServeHTTP(crw, c.Request) c.Writer = crw.ginWriter if crw.code == 200 && crw.buf.Len() > 0 { body := append(crw.buf.Bytes(), []byte(brandingCSS)...) c.Header("Content-Length", strconv.Itoa(len(body))) c.Header("Content-Type", "text/css; charset=utf-8") c.Writer.WriteHeader(crw.code) c.Writer.Write(body) c.Request.URL.Path = rawPath return } if crw.buf.Len() > 0 { c.Writer.WriteHeader(crw.code) c.Writer.Write(crw.buf.Bytes()) c.Request.URL.Path = rawPath return } c.Writer.WriteHeader(crw.code) c.Request.URL.Path = rawPath return } swaggerFiles.Handler.ServeHTTP(c.Writer, c.Request) c.Request.URL.Path = rawPath } } type captureResponseWriter struct { ginWriter gin.ResponseWriter code int buf bytes.Buffer } func (w *captureResponseWriter) Header() http.Header { return w.ginWriter.Header() } func (w *captureResponseWriter) Write(b []byte) (int, error) { return w.buf.Write(b) } func (w *captureResponseWriter) WriteHeader(code int) { w.code = code } func (w *captureResponseWriter) WriteString(s string) (int, error) { return w.buf.WriteString(s) } func (w *captureResponseWriter) Status() int { return w.code } func (w *captureResponseWriter) Size() int { return w.buf.Len() } func (w *captureResponseWriter) Written() bool { return w.buf.Len() > 0 } func (w *captureResponseWriter) WriteHeaderNow() {} func (w *captureResponseWriter) Pusher() http.Pusher { return nil } func (w *captureResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { return w.ginWriter.Hijack() } func (w *captureResponseWriter) CloseNotify() <-chan bool { return w.ginWriter.CloseNotify() } func (w *captureResponseWriter) Flush() {} const chineseInitializer = ` window.onload = function() { window.ui = SwaggerUIBundle({ url: "./doc.json", dom_id: '#swagger-ui', deepLinking: true, presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset ], plugins: [ SwaggerUIBundle.plugins.DownloadUrl ], layout: "StandaloneLayout", validatorUrl: null, defaultModelsExpandDepth: 1, defaultModelExpandDepth: 1, docExpansion: "list", filter: true, showExtensions: true, showCommonExtensions: true, withCredentials: false, persistAuthorization: true, tryItOutEnabled: false, requestSnippetsEnabled: true, displayRequestDuration: true, syntaxHighlight: { activate: true, theme: "agate" } }); setTimeout(function() { const translations = { 'Available authorizations': '可用授权', 'Close': '关闭', 'Scopes for': '作用域', 'Authorize': '授权', 'Cancel': '取消', 'Authorized': '已授权', 'Logout': '登出', 'Edit': '编辑', 'Select a definition': '选择定义', 'Search': '搜索', 'Top': '顶部', 'Parameters': '参数', 'Responses': '响应', 'Try it out': '试一试', 'Clear': '清除', 'Download': '下载', 'Show/Hide': '显示/隐藏', 'List Operations': '列表操作', 'Expand Operations': '展开操作', 'Models': '模型' }; document.querySelectorAll('span, button, div, label').forEach(function(el) { const text = el.textContent.trim(); if (translations[text]) { el.textContent = translations[text]; } }); document.title = 'MengStack API 文档'; }, 500); }; ` const brandingCSS = ` /* MengStack API branding — replace Swagger SmartBear logo */ .topbar { display: flex !important; align-items: center !important; background-color: #1b1b1f !important; padding: 8px 20px !important; min-height: 50px !important; position: relative !important; } .topbar > * { visibility: hidden !important; width: 0 !important; height: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; flex: 0 !important; } .topbar::before { content: "MengStack API \6587\6863" !important; visibility: visible !important; width: auto !important; height: auto !important; overflow: visible !important; flex: 0 0 auto !important; color: #fff !important; font-size: 20px !important; font-weight: 700 !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; letter-spacing: 1px !important; text-decoration: none !important; position: absolute !important; left: 20px !important; top: 50% !important; transform: translateY(-50%) !important; } .topbar::after { content: "v0.3.0" !important; visibility: visible !important; width: auto !important; height: auto !important; overflow: visible !important; flex: 0 0 auto !important; color: rgba(255,255,255,0.5) !important; font-size: 12px !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; position: absolute !important; left: 230px !important; top: 50% !important; transform: translateY(-50%) !important; } .swagger-ui { margin-top: 0 !important; } `