From cfceb3213488d2781997f8ec5d2c19e8b7228e99 Mon Sep 17 00:00:00 2001 From: Arthur Lu Date: Tue, 25 Feb 2025 21:35:11 +0000 Subject: [PATCH] implement basic web server for dashboard, use templates to do basic SSR on head and header --- .gitignore | 5 +- Makefile | 15 ++++ app/app.go | 124 ++++++++++++++++++++++++++++++ app/utils.go | 28 +++++++ configs/.htmlvalidate.json | 8 ++ configs/template.config.json | 6 ++ go.mod | 3 + init/proxmoxaas-dashboard.service | 11 +++ package.json | 6 +- proxmoxaas-dashboard.go | 9 +++ web/embed.go | 43 +++++++++++ web/html/account.html | 24 +----- web/html/index.html | 20 +---- web/html/instance.html | 20 +---- web/html/login.html | 19 +---- web/html/settings.html | 22 +----- web/scripts/clientsync.js | 3 +- web/scripts/index.js | 3 +- web/scripts/utils.js | 6 +- web/template.vars.js | 3 - web/templates/base.html | 31 ++++++++ 21 files changed, 302 insertions(+), 107 deletions(-) create mode 100644 Makefile create mode 100644 app/app.go create mode 100644 app/utils.go create mode 100644 configs/.htmlvalidate.json create mode 100644 configs/template.config.json create mode 100644 go.mod create mode 100644 init/proxmoxaas-dashboard.service create mode 100644 proxmoxaas-dashboard.go create mode 100644 web/embed.go delete mode 100644 web/template.vars.js create mode 100644 web/templates/base.html diff --git a/.gitignore b/.gitignore index 8c9746f..b663c6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -vars.js +**/config.json **/package-lock.json -**/node_modules \ No newline at end of file +**/node_modules +dist/* \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cb543ea --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +.PHONY: build test clean + +build: clean + @echo "======================== Building Binary =======================" +# resolve symbolic links in web by copying it into dist/web/ + cp -rL web/ dist/web/ + CGO_ENABLED=0 go build -ldflags="-s -w" -o dist/ . + +test: clean + go run . + +clean: + @echo "======================== Cleaning Project ======================" + go clean + rm -rf dist/* diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..39e98cb --- /dev/null +++ b/app/app.go @@ -0,0 +1,124 @@ +package app + +import ( + "flag" + "fmt" + "io/fs" + "log" + "net/http" + embed "proxmoxaas-dashboard/dist/web" // go will complain here until the first build + "text/template" +) + +var html map[string]*template.Template + +func ParseTemplates() { + html = make(map[string]*template.Template) + fs.WalkDir(embed.HTML, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() { // if it is a html file, parse with all the template files + v, err := fs.ReadFile(embed.HTML, path) + if err != nil { + log.Fatalf("error reading html file %s: %s", path, err.Error()) + } + t := template.New(d.Name()) + t, err = t.Parse(string(v)) + if err != nil { + log.Fatalf("error parsing html file %s: %s", path, err.Error()) + } + fs.WalkDir(embed.Templates, ".", func(path string, e fs.DirEntry, err error) error { + if err != nil { + return err + } + if !e.IsDir() { // if it is a template file, parse it + v, err = fs.ReadFile(embed.Templates, path) + if err != nil { + log.Fatalf("error reading template file %s: %s", path, err.Error()) + } + t, err = t.Parse(string(v)) + if err != nil { + log.Fatalf("error parsing template file %s: %s", path, err.Error()) + } + } + return nil + }) + html[d.Name()] = t + } + return nil + }) + +} + +func ServeStatic() { + http.Handle("/css/", http.FileServerFS(embed.CSS_fs)) + http.Handle("/images/", http.FileServerFS(embed.Images_fs)) + http.Handle("/modules/", http.FileServerFS(embed.Modules_fs)) + http.Handle("/scripts/", http.FileServerFS(embed.Scripts_fs)) +} + +func Run() { + configPath := flag.String("config", "config.json", "path to config.json file") + flag.Parse() + + global := GetConfig(*configPath) + + ParseTemplates() + + http.HandleFunc("/account.html", func(w http.ResponseWriter, r *http.Request) { + global.Page = "account" + err := html["account.html"].Execute(w, global) + if err != nil { + log.Fatal(err.Error()) + } + }) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + global.Page = "index" + err := html["index.html"].Execute(w, global) + if err != nil { + log.Fatal(err.Error()) + } + }) + + http.HandleFunc("/index.html", func(w http.ResponseWriter, r *http.Request) { + global.Page = "index" + err := html["index.html"].Execute(w, global) + if err != nil { + log.Fatal(err.Error()) + } + }) + + http.HandleFunc("/instance.html", func(w http.ResponseWriter, r *http.Request) { + global.Page = "instance" + err := html["instance.html"].Execute(w, global) + if err != nil { + log.Fatal(err.Error()) + } + }) + + http.HandleFunc("/login.html", func(w http.ResponseWriter, r *http.Request) { + global.Page = "login" + err := html["login.html"].Execute(w, global) + if err != nil { + log.Fatal(err.Error()) + } + }) + + http.HandleFunc("/settings.html", func(w http.ResponseWriter, r *http.Request) { + global.Page = "settings" + err := html["settings.html"].Execute(w, global) + if err != nil { + log.Fatal(err.Error()) + } + }) + + ServeStatic() + + log.Printf("Starting HTTP server at port: %d\n", global.Port) + err := http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", global.Port), nil) + if err != nil { + log.Fatal(err) + } +} diff --git a/app/utils.go b/app/utils.go new file mode 100644 index 0000000..4eda294 --- /dev/null +++ b/app/utils.go @@ -0,0 +1,28 @@ +package app + +import ( + "encoding/json" + "log" + "os" +) + +type Config struct { + Port int `json:"listenPort"` + Organization string `json:"organization"` + PVE string `json:"pveurl"` + API string `json:"apiurl"` + Page string +} + +func GetConfig(configPath string) Config { + content, err := os.ReadFile(configPath) + if err != nil { + log.Fatal("Error when opening config file: ", err) + } + var config Config + err = json.Unmarshal(content, &config) + if err != nil { + log.Fatal("Error during parsing config file: ", err) + } + return config +} diff --git a/configs/.htmlvalidate.json b/configs/.htmlvalidate.json new file mode 100644 index 0000000..830dceb --- /dev/null +++ b/configs/.htmlvalidate.json @@ -0,0 +1,8 @@ +{ + "extends": [ + "html-validate:recommended" + ], + "rules": { + "no-inline-style": "off" + } +} \ No newline at end of file diff --git a/configs/template.config.json b/configs/template.config.json new file mode 100644 index 0000000..24fb653 --- /dev/null +++ b/configs/template.config.json @@ -0,0 +1,6 @@ +{ + "listenPort": 8080, + "organization": "myorg", + "apiurl": "https://paas.mydomain.example/api", + "pveurl": "https://pve.mydomain.example" +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e6c2959 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module proxmoxaas-dashboard + +go 1.23.2 diff --git a/init/proxmoxaas-dashboard.service b/init/proxmoxaas-dashboard.service new file mode 100644 index 0000000..d0042dc --- /dev/null +++ b/init/proxmoxaas-dashboard.service @@ -0,0 +1,11 @@ +[Unit] +Description=proxmoxaas-dashboard +After=network.target +[Service] +WorkingDirectory=/ +ExecStart=//promoxaas-dashboard +Restart=always +RestartSec=10 +Type=simple +[Install] +WantedBy=default.target \ No newline at end of file diff --git a/package.json b/package.json index 3b93f72..0af43fd 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "Front-end for ProxmoxAAS", "type": "module", "scripts": { - "lint": "html-validator --continue; stylelint --formatter verbose --fix css/*.css; DEBUG=eslint:cli-engine eslint --fix scripts/", - "update-modules": "rm -rf modules/wfa.js modules/wfa.wasm; curl https://git.tronnet.net/alu/WFA-JS/releases/download/latest/wfa.js -o modules/wfa.js; curl https://git.tronnet.net/alu/WFA-JS/releases/download/latest/wfa.wasm -o modules/wfa.wasm" + "lint": "html-validate --config configs/.htmlvalidate.json web/html/*; stylelint --config configs/.stylelintrc.json --formatter verbose --fix web/css/*.css; DEBUG=eslint:cli-engine eslint --config configs/.eslintrc.json --fix web/scripts/", + "update-modules": "rm -rf web/modules/wfa.js web/modules/wfa.wasm; curl https://git.tronnet.net/alu/WFA-JS/releases/download/latest/wfa.js -o web/modules/wfa.js; curl https://git.tronnet.net/alu/WFA-JS/releases/download/latest/wfa.wasm -o web/modules/wfa.wasm" }, "devDependencies": { "eslint": "^8.43.0", @@ -15,6 +15,6 @@ "eslint-plugin-promise": "^6.1.1", "stylelint": "^15.9.0", "stylelint-config-standard": "^33.0.0", - "w3c-html-validator": "^1.4.0" + "html-validate": "^9.4.0" } } diff --git a/proxmoxaas-dashboard.go b/proxmoxaas-dashboard.go new file mode 100644 index 0000000..0b70b51 --- /dev/null +++ b/proxmoxaas-dashboard.go @@ -0,0 +1,9 @@ +package main + +import ( + app "proxmoxaas-dashboard/app" +) + +func main() { + app.Run() +} diff --git a/web/embed.go b/web/embed.go new file mode 100644 index 0000000..028277d --- /dev/null +++ b/web/embed.go @@ -0,0 +1,43 @@ +package embed + +import ( + "embed" +) + +//go:embed css/* +var CSS_fs embed.FS + +//go:embed images/* +var Images_fs embed.FS + +//go:embed modules/* +var Modules_fs embed.FS + +//go:embed scripts/* +var Scripts_fs embed.FS + +//go:embed html/* +var HTML embed.FS + +//go:embed templates/* +var Templates embed.FS + +/* +//go:embed html/account.html +var Account string + +//go:embed html/index.html +var Index string + +//go:embed html/instance.html +var Instance string + +//go:embed html/login.html +var Login string + +//go:embed html/settings.html +var Settings string + +//go:embed templates/base.html +var Base string +*/ diff --git a/web/html/account.html b/web/html/account.html index 8bac9ca..555b99e 100644 --- a/web/html/account.html +++ b/web/html/account.html @@ -1,15 +1,7 @@ - - - {{.Organization}} - dashboard - - - - - - + {{template "head" .}} @@ -43,15 +35,7 @@
-

{{.Organization}}

- - - + {{template "header" .}}

Account

@@ -65,8 +49,8 @@

Password

- -
+ +

Cluster Resources

diff --git a/web/html/index.html b/web/html/index.html index d26622c..e68009e 100644 --- a/web/html/index.html +++ b/web/html/index.html @@ -1,15 +1,7 @@ - - - {{.Organization}} - dashboard - - - - - - + {{template "head" .}}