|
|
@@ -3,6 +3,7 @@ package controller
|
|
|
import (
|
|
|
"bytes"
|
|
|
"embed"
|
|
|
+ "encoding/json"
|
|
|
htmlpkg "html"
|
|
|
"net/http"
|
|
|
"strings"
|
|
|
@@ -34,10 +35,42 @@ func ServeOpenAPISpec(c *gin.Context) {
|
|
|
c.JSON(http.StatusNotFound, gin.H{"success": false, "msg": "openapi.json not found"})
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
+ // The embedded spec ships with `servers: [{url: "/"}]`. When the panel runs
|
|
|
+ // under a non-root web base path, Swagger UI "Try it out" and external
|
|
|
+ // generators must target that prefix, so rewrite the single server entry to
|
|
|
+ // the runtime base path before serving.
|
|
|
+ if basePath := c.GetString("base_path"); basePath != "" && basePath != "/" {
|
|
|
+ if rebuilt, err := withServerBasePath(body, basePath); err != nil {
|
|
|
+ logger.Warning("openapi.json: could not inject base path:", err)
|
|
|
+ } else {
|
|
|
+ body = rebuilt
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
c.Header("Cache-Control", "public, max-age=300")
|
|
|
c.Data(http.StatusOK, "application/json; charset=utf-8", body)
|
|
|
}
|
|
|
|
|
|
+// withServerBasePath rewrites the spec's `servers` entry so requests target the
|
|
|
+// panel's configured web base path. Only the top-level `servers` field is
|
|
|
+// replaced; every other field is preserved verbatim via json.RawMessage.
|
|
|
+func withServerBasePath(spec []byte, basePath string) ([]byte, error) {
|
|
|
+ var doc map[string]json.RawMessage
|
|
|
+ if err := json.Unmarshal(spec, &doc); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ servers, err := json.Marshal([]map[string]string{{
|
|
|
+ "url": strings.TrimSuffix(basePath, "/"),
|
|
|
+ "description": "Current panel",
|
|
|
+ }})
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ doc["servers"] = servers
|
|
|
+ return json.Marshal(doc)
|
|
|
+}
|
|
|
+
|
|
|
func serveDistPage(c *gin.Context, name string) {
|
|
|
body, err := distFS.ReadFile("dist/" + name)
|
|
|
if err != nil {
|