From 3fe226938c211fd2d0cc07674789d705a798e91a Mon Sep 17 00:00:00 2001 From: Amy Gale Ruth Bowersox Date: Sun, 14 Sep 2025 15:19:47 -0600 Subject: [PATCH] work in progress: image serving --- ui/amcontext.go | 12 ++++++++ ui/images.go | 36 ++++++++++++++++++++++ ui/render_wrap.go | 2 ++ ui/static_images/powered-by-amsterdam.png | Bin 0 -> 14280 bytes 4 files changed, 50 insertions(+) create mode 100644 ui/images.go create mode 100644 ui/static_images/powered-by-amsterdam.png diff --git a/ui/amcontext.go b/ui/amcontext.go index 5aa3320..fd13693 100644 --- a/ui/amcontext.go +++ b/ui/amcontext.go @@ -18,8 +18,10 @@ import ( type AmContext interface { RC() int + OutputType() string Render(string) error SubRender(string) ([]byte, error) + SetOutputType(string) SetRC(int) URLPath() string VarMap() jet.VarMap @@ -29,12 +31,17 @@ type amContext struct { echoContext echo.Context httprc int rendervars jet.VarMap + outputType string } func (c *amContext) RC() int { return c.httprc } +func (c *amContext) OutputType() string { + return c.outputType +} + func (c *amContext) Render(name string) error { return c.echoContext.Render(c.httprc, name, c) } @@ -49,6 +56,10 @@ func (c *amContext) SubRender(name string) ([]byte, error) { return buf.Bytes(), err } +func (c *amContext) SetOutputType(typ string) { + c.outputType = typ +} + func (c *amContext) SetRC(rc int) { c.httprc = rc } @@ -66,6 +77,7 @@ func NewAmContext(ctxt echo.Context) AmContext { echoContext: ctxt, httprc: http.StatusOK, rendervars: make(jet.VarMap), + outputType: "", } ctxt.Set("amsterdam_context", &rc) return &rc diff --git a/ui/images.go b/ui/images.go new file mode 100644 index 0000000..a8a3863 --- /dev/null +++ b/ui/images.go @@ -0,0 +1,36 @@ +/* + * Amsterdam Web Communities System + * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package ui + +import ( + "embed" + "fmt" + "mime" + "net/http" + "strings" +) + +//go:embed static_images/* +var static_images embed.FS + +func AmServeImage(ctxt AmContext) (string, any, error) { + components := strings.SplitAfter(ctxt.URLPath(), "/") + var err error = nil + if len(components) == 2 && components[0] == "builtin" { + var b []byte + b, err = static_images.ReadFile(fmt.Sprintf("static_images/%s", components[1])) + if err == nil { + mtype := mime.TypeByExtension(components[1][strings.LastIndex(components[1], "."):]) + ctxt.SetOutputType(mtype) + return "bytes", b, nil + } + } + ctxt.SetRC(http.StatusNotFound) + return "string", fmt.Sprintf("File not found: %s", ctxt.URLPath()), err +} diff --git a/ui/render_wrap.go b/ui/render_wrap.go index 38e44c3..730dbd4 100644 --- a/ui/render_wrap.go +++ b/ui/render_wrap.go @@ -20,6 +20,8 @@ func AmWrap(myfunc func(AmContext) (string, any, error)) echo.HandlerFunc { what, rc, err := myfunc(amctxt) if err == nil { switch what { + case "bytes": + err = ctxt.Blob(amctxt.RC(), amctxt.OutputType(), rc.([]byte)) case "string": err = ctxt.String(amctxt.RC(), fmt.Sprintf("%v", rc)) case "template": diff --git a/ui/static_images/powered-by-amsterdam.png b/ui/static_images/powered-by-amsterdam.png new file mode 100644 index 0000000000000000000000000000000000000000..47cef0913e1dd6f04ef600b58f9d3dc22d9b64bd GIT binary patch literal 14280 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