From 5fb2b2b55ef39c0a0787d402159c45303090ae53 Mon Sep 17 00:00:00 2001 From: Ethan Li Date: Mon, 9 Nov 2026 16:26:50 +0100 Subject: [PATCH 1/2] Import changes to templates client from machine-portal --- README.md | 8 +++ internal/app/server/server.go | 3 +- internal/clients/templates/client.go | 22 ++++++-- internal/clients/templates/conf.go | 9 +++- .../clients/templates/overlayfs-errors.go | 11 ++++ .../clients/templates/overlayfs-structures.go | 40 ++++++++++++++ .../server => clients/templates}/overlayfs.go | 54 ++++--------------- 7 files changed, 96 insertions(+), 51 deletions(-) create mode 100644 internal/clients/templates/overlayfs-errors.go create mode 100644 internal/clients/templates/overlayfs-structures.go rename internal/{app/server => clients/templates}/overlayfs.go (82%) diff --git a/README.md b/README.md index 2541d3a..34bb0db 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,14 @@ TEMPLATES_PATH=custom-templates ./machine-admin server TEMPLATES_PATH=custom-templates make run-server ``` +Note that running `make runlive` will cause your `TEMPLATES_PATH` environment variable to be ignored, so that the templates directory at `web/templates` (relative to the root of this repository) is always used. + +You can also provide multiple directories delimited by colons, for example: +```bash +TEMPLATES_PATH=~/.local/share/machine-admin/template-overrides:custom-templates ./machine-admin server +``` +Then the directories will be overlaid (like in Linux overlay filesystems) from left to right, with the leftmost directory on top. + #### HTTP Server You can override the default port (`3001`) or base path (`/`) of the HTTP server with the `HTTP_PORT` and `HTTP_BASEPATH` environment variables, respectively. For example, you could run the web server on port 3002 with base path `/admin/panel/` by running the following command: diff --git a/internal/app/server/server.go b/internal/app/server/server.go index 71aee9d..9b57d4a 100644 --- a/internal/app/server/server.go +++ b/internal/app/server/server.go @@ -23,6 +23,7 @@ import ( "github.com/openUC2/machine-admin/internal/app/server/routes" "github.com/openUC2/machine-admin/internal/app/server/routes/assets" "github.com/openUC2/machine-admin/internal/app/server/tmplfunc" + "github.com/openUC2/machine-admin/internal/clients/templates" "github.com/openUC2/machine-admin/web" ) @@ -41,7 +42,7 @@ func New(config conf.Config, logger godest.Logger) (s *Server, err error) { } s.Embeds = web.NewEmbeds() - templatesOverlay := &OverlayFS{ + templatesOverlay := &templates.OverlayFS{ Upper: s.Globals.Base.Templates.GetFS(), Lower: s.Embeds.TemplatesFS, } diff --git a/internal/clients/templates/client.go b/internal/clients/templates/client.go index 16637e9..dec2c40 100644 --- a/internal/clients/templates/client.go +++ b/internal/clients/templates/client.go @@ -17,9 +17,25 @@ func NewClient(c Config) *Client { } func (c *Client) GetFS() fs.FS { - path := c.Config.Path - if path == "" { + paths := c.Config.Paths + switch len(paths) { + case 0: return nil + case 1: + return os.DirFS(paths[0]) } - return os.DirFS(path) + + overlay := &OverlayFS{} + suboverlay := overlay + for i, path := range paths { + suboverlay.Upper = os.DirFS(path) + if i+1 == len(paths)-1 { + suboverlay.Lower = os.DirFS(paths[i+1]) + break + } + nextSuboverlay := &OverlayFS{} + suboverlay.Lower = nextSuboverlay + suboverlay = nextSuboverlay + } + return overlay } diff --git a/internal/clients/templates/conf.go b/internal/clients/templates/conf.go index b361585..7aaac3c 100644 --- a/internal/clients/templates/conf.go +++ b/internal/clients/templates/conf.go @@ -1,19 +1,24 @@ package templates import ( + "strings" + "github.com/sargassum-world/godest/env" ) const envPrefix = "TEMPLATES_" type Config struct { - Path string + Paths []string } func GetConfig() (c Config, err error) { // This is a file path specific to ImSwitch OS const defaultPath = "" - c.Path = env.GetString(envPrefix+"PATH", defaultPath) + c.Paths = strings.Split(env.GetString(envPrefix+"PATH", defaultPath), ":") + if len(c.Paths) == 1 && c.Paths[0] == "" { + c.Paths = nil + } return c, nil } diff --git a/internal/clients/templates/overlayfs-errors.go b/internal/clients/templates/overlayfs-errors.go new file mode 100644 index 0000000..7d66295 --- /dev/null +++ b/internal/clients/templates/overlayfs-errors.go @@ -0,0 +1,11 @@ +package templates + +const ( + PathErrorOpLstat = "lstat" + PathErrorOpOpen = "open" + PathErrorOpRead = "read" + PathErrorOpReadDir = "readdir" + PathErrorOpResolve = "resolve" + PathErrorOpStat = "stat" + PathErrorOpSub = "sub" +) diff --git a/internal/clients/templates/overlayfs-structures.go b/internal/clients/templates/overlayfs-structures.go new file mode 100644 index 0000000..f87c30e --- /dev/null +++ b/internal/clients/templates/overlayfs-structures.go @@ -0,0 +1,40 @@ +package templates + +import ( + "iter" + "maps" +) + +// Set + +type Set[Node comparable] map[Node]struct{} + +// Add adds the node to the set. If the node was already in the set, nothing changes. +func (s Set[Node]) Add(n ...Node) { + for _, node := range n { + s[node] = struct{}{} + } +} + +// Has checks whether the node is already in the set. +func (s Set[Node]) Has(n Node) bool { + _, ok := s[n] + return ok +} + +// Difference creates a new set with the difference between the set whose method is called and the +// provided set. +func (s Set[Node]) Difference(t Set[Node]) Set[Node] { + difference := make(Set[Node]) + for node := range s { + if !t.Has(node) { + difference.Add(node) + } + } + return difference +} + +// All returns an iterator over all elements in s. +func (s Set[Node]) All() iter.Seq[Node] { + return maps.Keys(s) +} diff --git a/internal/app/server/overlayfs.go b/internal/clients/templates/overlayfs.go similarity index 82% rename from internal/app/server/overlayfs.go rename to internal/clients/templates/overlayfs.go index 5ceeb4e..7ec90c3 100644 --- a/internal/app/server/overlayfs.go +++ b/internal/clients/templates/overlayfs.go @@ -1,10 +1,8 @@ -package server +package templates import ( "cmp" "io/fs" - "iter" - "maps" "path" "slices" @@ -34,7 +32,7 @@ func (f *OverlayFS) Open(name string) (fs.File, error) { switch { default: return nil, &fs.PathError{ - Op: "open", + Op: PathErrorOpOpen, Path: name, Err: errors.Wrapf(err, "couldn't open file %s in upper", name), } @@ -42,7 +40,7 @@ func (f *OverlayFS) Open(name string) (fs.File, error) { file, err = f.Lower.Open(name) if err != nil { return nil, &fs.PathError{ - Op: "open", + Op: PathErrorOpOpen, Path: name, Err: errors.Wrapf(err, "couldn't open file %s in lower", name), } @@ -91,14 +89,14 @@ func (f *OverlayFS) ReadDir(name string) (entries []fs.DirEntry, err error) { if err == nil { if !info.IsDir() { return nil, &fs.PathError{ - Op: "read", + Op: PathErrorOpRead, Path: name, Err: errors.Wrapf(err, "%s is a non-directory file in upper", name), } } if entries, err = fs.ReadDir(f.Upper, name); err != nil { return nil, &fs.PathError{ - Op: "read", + Op: PathErrorOpRead, Path: name, Err: errors.Wrapf(err, "couldn't read directory %s in upper", name), } @@ -112,7 +110,7 @@ func (f *OverlayFS) ReadDir(name string) (entries []fs.DirEntry, err error) { if err == nil { if !info.IsDir() { return nil, &fs.PathError{ - Op: "read", + Op: PathErrorOpRead, Path: name, Err: errors.Wrapf(err, "%s is a non-directory file in lower", name), } @@ -120,7 +118,7 @@ func (f *OverlayFS) ReadDir(name string) (entries []fs.DirEntry, err error) { lowerEntries, err := fs.ReadDir(f.Lower, name) if err != nil { return nil, &fs.PathError{ - Op: "read", + Op: PathErrorOpRead, Path: name, Err: errors.Wrapf(err, "couldn't read directory %s in lower", name), } @@ -177,7 +175,7 @@ func (f *OverlayFS) Stat(name string) (fs.FileInfo, error) { switch { default: return nil, &fs.PathError{ - Op: "stat", + Op: PathErrorOpStat, Path: name, Err: errors.Wrapf(err, "couldn't stat file %s in upper", name), } @@ -185,7 +183,7 @@ func (f *OverlayFS) Stat(name string) (fs.FileInfo, error) { info, err := fs.Stat(f.Lower, name) if err != nil { return nil, &fs.PathError{ - Op: "stat", + Op: PathErrorOpStat, Path: name, Err: errors.Wrapf(err, "couldn't stat file %s in lower", name), } @@ -195,37 +193,3 @@ func (f *OverlayFS) Stat(name string) (fs.FileInfo, error) { return info, nil } } - -// Set - -type Set[Node comparable] map[Node]struct{} - -// Add adds the node to the set. If the node was already in the set, nothing changes. -func (s Set[Node]) Add(n ...Node) { - for _, node := range n { - s[node] = struct{}{} - } -} - -// Has checks whether the node is already in the set. -func (s Set[Node]) Has(n Node) bool { - _, ok := s[n] - return ok -} - -// Difference creates a new set with the difference between the set whose method is called and the -// provided set. -func (s Set[Node]) Difference(t Set[Node]) Set[Node] { - difference := make(Set[Node]) - for node := range s { - if !t.Has(node) { - difference.Add(node) - } - } - return difference -} - -// All returns an iterator over all elements in s. -func (s Set[Node]) All() iter.Seq[Node] { - return maps.Keys(s) -} From c68677d08cea2c7cb174ae9e506696f50e216a1d Mon Sep 17 00:00:00 2001 From: Ethan Li Date: Mon, 9 Nov 2026 16:47:39 +0100 Subject: [PATCH 2/2] Resolve merge artifacts --- internal/app/server/overlayfs-errors.go | 11 ------ internal/app/server/overlayfs-structures.go | 40 --------------------- 2 files changed, 51 deletions(-) delete mode 100644 internal/app/server/overlayfs-errors.go delete mode 100644 internal/app/server/overlayfs-structures.go diff --git a/internal/app/server/overlayfs-errors.go b/internal/app/server/overlayfs-errors.go deleted file mode 100644 index b33b10d..0000000 --- a/internal/app/server/overlayfs-errors.go +++ /dev/null @@ -1,11 +0,0 @@ -package server - -const ( - PathErrorOpLstat = "lstat" - PathErrorOpOpen = "open" - PathErrorOpRead = "read" - PathErrorOpReadDir = "readdir" - PathErrorOpResolve = "resolve" - PathErrorOpStat = "stat" - PathErrorOpSub = "sub" -) diff --git a/internal/app/server/overlayfs-structures.go b/internal/app/server/overlayfs-structures.go deleted file mode 100644 index 1985b60..0000000 --- a/internal/app/server/overlayfs-structures.go +++ /dev/null @@ -1,40 +0,0 @@ -package server - -import ( - "iter" - "maps" -) - -// Set - -type Set[Node comparable] map[Node]struct{} - -// Add adds the node to the set. If the node was already in the set, nothing changes. -func (s Set[Node]) Add(n ...Node) { - for _, node := range n { - s[node] = struct{}{} - } -} - -// Has checks whether the node is already in the set. -func (s Set[Node]) Has(n Node) bool { - _, ok := s[n] - return ok -} - -// Difference creates a new set with the difference between the set whose method is called and the -// provided set. -func (s Set[Node]) Difference(t Set[Node]) Set[Node] { - difference := make(Set[Node]) - for node := range s { - if !t.Has(node) { - difference.Add(node) - } - } - return difference -} - -// All returns an iterator over all elements in s. -func (s Set[Node]) All() iter.Seq[Node] { - return maps.Keys(s) -}