From 032cd1336d33cb503fe521c8f9c51af105364dc4 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 2 Sep 2025 07:11:00 +0200 Subject: [PATCH] console: display missing images in documentation And don't embed SVG. This is wasteful. --- console/assets.go | 12 ++++++++-- console/data/docs/99-changelog.md | 1 + console/docs.go | 21 ++++------------- console/docs_test.go | 39 ++++++++++++++++++++++++++++++- 4 files changed, 54 insertions(+), 19 deletions(-) diff --git a/console/assets.go b/console/assets.go index 6a2d503a..6c98a265 100644 --- a/console/assets.go +++ b/console/assets.go @@ -14,13 +14,21 @@ import ( var embeddedAssets embed.FS func (c *Component) assetsHandlerFunc(w http.ResponseWriter, req *http.Request) { - assets := c.embedOrLiveFS(embeddedAssets, "data/frontend") upath := req.URL.Path if !strings.HasPrefix(upath, "/") { upath = "/" + upath req.URL.Path = upath } - // Serve assets using a file server + + // Serve /doc/images + if strings.HasPrefix(upath, "/docs/images/") { + docs := c.embedOrLiveFS(embeddedDocs, "data/docs") + http.ServeFileFS(w, req, docs, req.URL.Path[len("/docs/images/"):]) + http.FileServer(http.FS(docs)).ServeHTTP(w, req) + } + + // Serve /assets + assets := c.embedOrLiveFS(embeddedAssets, "data/frontend") if strings.HasPrefix(upath, "/assets/") { http.FileServer(http.FS(assets)).ServeHTTP(w, req) return diff --git a/console/data/docs/99-changelog.md b/console/data/docs/99-changelog.md index 02943ad1..ee2dbdde 100644 --- a/console/data/docs/99-changelog.md +++ b/console/data/docs/99-changelog.md @@ -12,6 +12,7 @@ identified with a specific icon: ## Unreleased +- 🩹 *console*: display missing images in documentation - 🌱 *build*: accept building with a not up-to-date toolchain - 🌱 *docker*: update ClickHouse to 25.8 (not mandatory) diff --git a/console/docs.go b/console/docs.go index 5019a127..7db9555b 100644 --- a/console/docs.go +++ b/console/docs.go @@ -6,7 +6,6 @@ package console import ( "bytes" "embed" - "encoding/base64" "fmt" "io" "io/fs" @@ -116,7 +115,7 @@ func (c *Component) docsHandlerFunc(gc *gin.Context) { parser.WithAutoHeadingID(), parser.WithASTTransformers( util.Prioritized(&internalLinkTransformer{}, 500), - util.Prioritized(&imageEmbedder{docs}, 500), + util.Prioritized(&imageLinkTransformer{docs}, 500), ), ), ) @@ -152,11 +151,11 @@ func (r *internalLinkTransformer) Transform(node *ast.Document, _ text.Reader, _ ast.Walk(node, replaceLinks) } -type imageEmbedder struct { +type imageLinkTransformer struct { root fs.FS } -func (r *imageEmbedder) Transform(node *ast.Document, _ text.Reader, _ parser.Context) { +func (r *imageLinkTransformer) Transform(node *ast.Document, _ text.Reader, _ parser.Context) { replaceLinks := func(n ast.Node, entering bool) (ast.WalkStatus, error) { if !entering { return ast.WalkContinue, nil @@ -164,19 +163,9 @@ func (r *imageEmbedder) Transform(node *ast.Document, _ text.Reader, _ parser.Co switch node := n.(type) { case *ast.Image: path := string(node.Destination) - if strings.Contains(path, "/") || !strings.HasSuffix(path, ".svg") { - break + if !strings.Contains(path, "/") { + node.Destination = []byte(fmt.Sprintf("images/%s", path)) } - f, err := r.root.Open(path) - if err != nil { - break - } - content, err := io.ReadAll(f) - if err != nil { - break - } - encoded := fmt.Sprintf("data:image/svg+xml;base64,%s", base64.StdEncoding.EncodeToString(content)) - node.Destination = []byte(encoded) } return ast.WalkContinue, nil } diff --git a/console/docs_test.go b/console/docs_test.go index 7bb5eaac..a44594ef 100644 --- a/console/docs_test.go +++ b/console/docs_test.go @@ -9,6 +9,8 @@ import ( "net/http" "strings" "testing" + + "akvorado/common/helpers" ) func TestServeDocs(t *testing.T) { @@ -22,7 +24,7 @@ func TestServeDocs(t *testing.T) { Expect string }{ {"usage", `configuration section`}, - {"intro", `data:image/svg`}, + {"intro", `images/design.svg`}, } for _, tc := range cases { t.Run(fmt.Sprintf("%s-%s", name, tc.Path), func(t *testing.T) { @@ -50,3 +52,38 @@ func TestServeDocs(t *testing.T) { } } } + +func TestServeImages(t *testing.T) { + for _, live := range []bool{false, true} { + name := "livefs" + if !live { + name = "embeddedfs" + } + + t.Run(name, func(t *testing.T) { + conf := DefaultConfiguration() + conf.ServeLiveFS = live + _, h, _, _ := NewMock(t, conf) + + resp, err := http.Get(fmt.Sprintf("http://%s/docs/images/design.svg", + h.LocalAddr())) + if err != nil { + t.Fatalf("GET /docs/images/design.svg:\n%+v", err) + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + t.Errorf("GET /docs/images/design.svg: got status code %d, not 200", + resp.StatusCode) + } + expected := `` + got := make([]byte, len(expected)) + if _, err := io.ReadFull(resp.Body, got); err != nil { + t.Fatalf("GET /docs/images/design.svg ReadFull() error:\n%+v", err) + } + if diff := helpers.Diff(string(got), expected); diff != "" { + t.Errorf("GET /docs/images/design.svg:\n%s", + diff) + } + }) + } +}