2 Commits
Author SHA1 Message Date
alu d75260f75c test ci 2026-07-31 23:56:06 +00:00
alu b4fe7263e2 add ingore paths for web component linters
tronnet/workflows: Code Style -- Web Components / style-web (push) Failing after 4s
2026-07-31 23:52:10 +00:00
111 changed files with 1006 additions and 981 deletions

No files matched your search

+15 -14
View File
@@ -1,26 +1,27 @@
.PHONY: test build build-wfa clean clean-wfa clean-web
.PHONY: build test clean wfa-js
build: clean ensure-dist build-wfa
build: clean ensure-dist build-web build-wfa-js
@echo "======================== Building Binary ======================="
# resolve symbolic links in web by copying it into dist/web/
GODEBUG=embedfollowsymlinks=1 CGO_ENABLED=0 go build -tags release -ldflags="-s -w" -v -o dist/ .
CGO_ENABLED=0 go build -tags release -ldflags="-s -w" -v -o dist/ .
test:
GODEBUG=embedfollowsymlinks=1 CGO_ENABLED=0 go run .
build-web: ensure-dist
cp -rL web/ dist/web/
clean: clean-wfa clean-web
build-wfa-js: ensure-dist
$(MAKE) -C WFA-JS
cp -f WFA-JS/dist/* web/modules
clean: clean-wfa-js
@echo "======================== Cleaning Project ======================"
go clean
rm -rf dist/*
clean-wfa-js:
$(MAKE) clean -C WFA-JS
ensure-dist:
mkdir -p dist
clean-web:
build-wfa:
$(MAKE) -C WFA-JS
cp -f WFA-JS/dist/* app/web/modules
clean-wfa:
$(MAKE) clean -C WFA-JS
workflow-init: ensure-dist build-web
cp -r dev_config .
+1 -1
View File
@@ -13,7 +13,7 @@ ProxmoxAAS Dashboard provides users of a proxmox based compute on demand service
## ProxmoxAAS System Installation Overview
The ProxmoxAAS project is large and is split into multiple components. There are four required components, the Dashboard, API, Fabric, and access-manager-api. The installation order should start with the Dashboard and then proceed to the other backend components. This will require some foresight into the setup process.
The ProxmoxAAS project is large and is split into multiple components. There are four required components, the Dashboard, API, Fabric, and access-manager-api. The instalation order should start with the Dashboard and then proceed to the other backend components. This will require some foresight into the setup process.
The supported setup is to use a reverse proxy to serve both the original Proxmox web interface and ProxmoxAAS components. It is possible other setups can work. Rather than provide specific steps to duplicate a certain setup, the steps included are intended as a guideline of steps required for proper function in most setups. Consequently, the examples provided are only to highlight key settings and do not represent complete working configurations. The instructions also assume you have your own domain name which will substitute `domain.net` in some of the configs.
+1 -1
Submodule WFA-JS updated: b0aacd5f9c...f53f344fb3.
+53 -37
View File
@@ -4,55 +4,71 @@ import (
"flag"
"fmt"
"log"
"net/http"
"proxmoxaas-dashboard/app/common"
"proxmoxaas-dashboard/app/routes"
"proxmoxaas-dashboard/app/web"
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
"web" // go will complain here until the first build
"github.com/gin-gonic/gin"
"github.com/tdewolff/minify/v2"
)
func Run() {
configPath := flag.String("config", "config.json", "path to config.json file")
flag.Parse()
common.Global = common.GetConfig(*configPath)
log.Printf("[INFO] Read in config from %s\n", *configPath)
// setup http router
router := http.NewServeMux()
// setup static resources
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
m := common.InitMinify()
// setup static resources under web (css, images, modules, scripts)
static := common.MinifyStatic(m, web.Static)
router.HandleFunc("GET /websrc/{path...}", func(w http.ResponseWriter, r *http.Request) {
path := r.PathValue("path")
data := static[path]
w.Header().Set("Content-Encoding", data.MimeType.Encoding)
w.Header().Set("Content-Type", data.MimeType.Type)
w.Write([]byte(data.Data))
})
// setup template resources
ServeStatic(router, m)
html := common.MinifyStatic(m, web.Templates)
common.TMPL = common.LoadHTML(html)
common.TMPL = common.LoadHTMLToGin(router, html)
// dynamic routes for pages and page fragments
router.HandleFunc("GET /", routes.HandleGETIndex)
router.HandleFunc("GET /index", routes.HandleGETIndex)
router.HandleFunc("GET /index/instances", routes.HandleGETInstancesFragment)
router.HandleFunc("GET /account", routes.HandleGETAccount)
router.HandleFunc("GET /config", routes.HandleGETConfig)
router.HandleFunc("GET /config/volumes", routes.HandleGETConfigVolumesFragment)
router.HandleFunc("GET /config/nets", routes.HandleGETConfigNetsFragment)
router.HandleFunc("GET /config/devices", routes.HandleGETConfigDevicesFragment)
router.HandleFunc("GET /config/boot", routes.HandleGETConfigBootFragment)
router.HandleFunc("GET /backups", routes.HandleGETBackups)
router.HandleFunc("GET /backups/backups", routes.HandleGETBackupsFragment)
router.HandleFunc("GET /login", routes.HandleGETLogin)
router.HandleFunc("GET /settings", routes.HandleGETSettings)
router.GET("/", routes.HandleGETIndex)
router.GET("/index", routes.HandleGETIndex)
router.GET("/index/instances", routes.HandleGETInstancesFragment)
router.GET("/account", routes.HandleGETAccount)
router.GET("/config", routes.HandleGETConfig)
router.GET("/config/volumes", routes.HandleGETConfigVolumesFragment)
router.GET("/config/nets", routes.HandleGETConfigNetsFragment)
router.GET("/config/devices", routes.HandleGETConfigDevicesFragment)
router.GET("/config/boot", routes.HandleGETConfigBootFragment)
router.GET("/backups", routes.HandleGETBackups)
router.GET("/backups/backups", routes.HandleGETBackupsFragment)
router.GET("/login", routes.HandleGETLogin)
router.GET("/settings", routes.HandleGETSettings)
server := paas.LoggingMiddleware(router)
log.Printf("[INFO] starting server listening on 0.0.0.0:%d", common.Global.Port)
log.Fatalf("[ERR ] starting gin router: %s", http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", common.Global.Port), server))
// run on all interfaces with port
log.Fatal("[ERR ] starting gin router: ", router.Run(fmt.Sprintf("0.0.0.0:%d", common.Global.Port)))
}
// setup static resources under web (css, images, modules, scripts)
func ServeStatic(router *gin.Engine, m *minify.M) {
css := common.MinifyStatic(m, web.CSS_fs)
router.GET("/css/*css", func(c *gin.Context) {
path, _ := c.Params.Get("css")
data := css[fmt.Sprintf("css%s", path)]
c.Data(200, data.MimeType.Type, []byte(data.Data))
})
images := common.MinifyStatic(m, web.Images_fs)
router.GET("/images/*image", func(c *gin.Context) {
path, _ := c.Params.Get("image")
data := images[fmt.Sprintf("images%s", path)]
c.Data(200, data.MimeType.Type, []byte(data.Data))
})
modules := common.MinifyStatic(m, web.Modules_fs)
router.GET("/modules/*module", func(c *gin.Context) {
path, _ := c.Params.Get("module")
data := modules[fmt.Sprintf("modules%s", path)]
c.Data(200, data.MimeType.Type, []byte(data.Data))
})
scripts := common.MinifyStatic(m, web.Scripts_fs)
router.GET("/scripts/*script", func(c *gin.Context) {
path, _ := c.Params.Get("script")
data := scripts[fmt.Sprintf("scripts%s", path)]
c.Data(200, data.MimeType.Type, []byte(data.Data))
})
}
-6
View File
@@ -11,7 +11,6 @@ import (
// defines mime type and associated minifier
type MimeType struct {
Type string
Encoding string
Minifier func(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error
}
@@ -45,11 +44,6 @@ var MimeTypes = map[string]MimeType{
Type: "application/wasm",
Minifier: nil,
},
"gz": {
Type: "application/wasm",
Encoding: "gzip",
Minifier: nil,
},
"*": {
Type: "text/plain",
Minifier: nil,
-6
View File
@@ -15,7 +15,6 @@ import (
// defines mime type and associated minifier
type MimeType struct {
Type string
Encoding string
Minifier func(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error
}
@@ -49,11 +48,6 @@ var MimeTypes = map[string]MimeType{
Type: "application/wasm",
Minifier: nil,
},
"gz": {
Type: "application/wasm",
Encoding: "gzip",
Minifier: nil,
},
"*": {
Type: "text/plain",
Minifier: nil,
+26 -43
View File
@@ -16,9 +16,10 @@ import (
"strconv"
"strings"
"github.com/gin-gonic/gin"
"github.com/tdewolff/minify/v2"
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
paas "proxmoxaas-common-lib"
)
// get config file from configPath
@@ -102,8 +103,9 @@ func MinifyStatic(m *minify.M, files embed.FS) map[string]StaticFile {
}
}
func LoadHTML(html map[string]StaticFile) *template.Template {
funcMap := template.FuncMap{
func LoadHTMLToGin(engine *gin.Engine, html map[string]StaticFile) *template.Template {
root := template.New("")
engine.FuncMap = template.FuncMap{
"MapKeys": func(x any, sep string) string {
v := reflect.ValueOf(x)
keys := v.MapKeys()
@@ -131,11 +133,9 @@ func LoadHTML(html map[string]StaticFile) *template.Template {
return dict, nil
},
}
root := template.New("").Funcs(funcMap)
if err := LoadAndAddToRoot(funcMap, root, html); err != nil {
log.Fatal(err)
}
return root
tmpl := template.Must(root, LoadAndAddToRoot(engine.FuncMap, root, html))
engine.SetHTMLTemplate(root)
return tmpl
}
func LoadAndAddToRoot(FuncMap template.FuncMap, root *template.Template, html map[string]StaticFile) error {
@@ -170,9 +170,9 @@ func TemplateMinifier(m *minify.M, w io.Writer, r io.Reader, _ map[string]string
return nil
}
func HandleNonFatalError(w http.ResponseWriter, err error) {
func HandleNonFatalError(c *gin.Context, err error) {
log.Printf("[WARN] encountered a non-fatal error: %s", err.Error())
w.WriteHeader(http.StatusInternalServerError)
c.Status(http.StatusInternalServerError)
}
func RequestGetAPI(path string, auth *paas.Auth, body any) (*http.Response, int, error) {
@@ -221,41 +221,25 @@ func RequestGetAPI(path string, auth *paas.Auth, body any) (*http.Response, int,
return response, response.StatusCode, nil
}
func GetAuthFromRequest(r *http.Request) (paas.Auth, error) {
_, errAuth := r.Cookie("auth")
username, errUsername := r.Cookie("username")
token, errToken := r.Cookie("PVEAuthCookie")
csrf, errCSRF := r.Cookie("CSRFPreventionToken")
access, errAccess := r.Cookie("PAASAccessManagerTicket")
if errUsername != nil || errAuth != nil || errToken != nil ||
errCSRF != nil || errAccess != nil {
return paas.Auth{}, fmt.Errorf(
"error occurred getting user cookies: (auth: %v, token: %v, csrf: %v)",
errAuth, errToken, errCSRF,
)
func GetAuthFromRequest(c *gin.Context) (paas.Auth, error) {
_, errAuth := c.Cookie("auth")
username, errUsername := c.Cookie("username")
token, errToken := c.Cookie("PVEAuthCookie")
csrf, errCSRF := c.Cookie("CSRFPreventionToken")
access, errAccess := c.Cookie("PAASAccessManagerTicket")
if errUsername != nil || errAuth != nil || errToken != nil || errCSRF != nil || errAccess != nil {
return paas.Auth{}, fmt.Errorf("error occured getting user cookies: (auth: %s, token: %s, csrf: %s)", errAuth, errToken, errCSRF)
} else {
return paas.Auth{Username: username, Token: token, CSRF: csrf, AccessManagerTicket: access}, nil
}
}
return paas.Auth{
Username: username.Value,
Token: token.Value,
CSRF: csrf.Value,
AccessManagerTicket: access.Value,
}, nil
}
func GetInstancePathFromRequest(r *http.Request) (paas.InstancePath, error) {
query := r.URL.Query()
req_node := query.Get("node")
req_type := query.Get("type")
req_vmid := query.Get("vmid")
func GetInstancePathFromRequest(c *gin.Context) (paas.InstancePath, error) {
req_node := c.Query("node")
req_type := c.Query("type")
req_vmid := c.Query("vmid")
if req_node == "" || req_type == "" || req_vmid == "" {
return paas.InstancePath{}, fmt.Errorf(
"request missing required values: (node: %s, type: %s, vmid: %s)",
req_node, req_type, req_vmid,
)
return paas.InstancePath{}, fmt.Errorf("request missing required values: (node: %s, type: %s, vmid: %s)", req_node, req_type, req_vmid)
}
vmid_int, err := strconv.ParseUint(req_vmid, 10, 64)
@@ -268,7 +252,6 @@ func GetInstancePathFromRequest(r *http.Request) (paas.InstancePath, error) {
InstanceType: paas.InstanceType(req_type),
InstanceID: paas.InstanceID(vmid_int),
}
return vm_path, nil
}
+8 -8
View File
@@ -3,10 +3,11 @@ package routes
import (
"fmt"
"net/http"
paas "proxmoxaas-common-lib"
"proxmoxaas-dashboard/app/common"
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
"github.com/gerow/go-color"
"github.com/gin-gonic/gin"
"github.com/go-viper/mapstructure/v2"
)
@@ -39,30 +40,29 @@ var Green = color.RGB{
B: 0,
}
func HandleGETAccount(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETAccount(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil {
user, err := GetUserBasic(auth)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
pools, err := GetUserPools(auth)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/account.html", map[string]any{
c.HTML(http.StatusOK, "html/account.html", gin.H{
"global": common.Global,
"page": "account",
"user": user,
"pools": pools,
})
} else {
http.Redirect(w, r, "/login", http.StatusFound) // if user is not authed, redirect user to login page
return
c.Redirect(http.StatusFound, "/login") // if user is not authed, redirect user to login page
}
}
+23 -21
View File
@@ -3,10 +3,11 @@ package routes
import (
"fmt"
"net/http"
paas "proxmoxaas-common-lib"
"proxmoxaas-dashboard/app/common"
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
"time"
"github.com/gin-gonic/gin"
"github.com/go-viper/mapstructure/v2"
)
@@ -19,60 +20,61 @@ type InstanceBackup struct {
TimeFormatted string
}
func HandleGETBackups(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETBackups(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil {
vm_path, err := common.GetInstancePathFromRequest(r)
vm_path, err := common.GetInstancePathFromRequest(c)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
backups, err := GetInstanceBackups(vm_path, auth)
if err != nil {
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance backups: %s", err.Error()))
return
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance backups: %s", err.Error()))
}
config, err := GetInstanceConfig(vm_path, auth) // only used for the VM's name
if err != nil {
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
return
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
}
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/backups.html", map[string]any{
c.HTML(http.StatusOK, "html/backups.html", gin.H{
"global": common.Global,
"page": "backups",
"backups": backups,
"config": config,
})
} else {
http.Redirect(w, r, "/login", http.StatusFound)
return
c.Redirect(http.StatusFound, "/login")
}
}
func HandleGETBackupsFragment(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETBackupsFragment(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil { // user should be authed, try to return index with population
vm_path, err := common.GetInstancePathFromRequest(r)
vm_path, err := common.GetInstancePathFromRequest(c)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
backups, err := GetInstanceBackups(vm_path, auth)
if err != nil {
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance backups: %s", err.Error()))
return
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance backups: %s", err.Error()))
}
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/backups-backups.go.tmpl", map[string]any{
c.Header("Content-Type", "text/plain")
err = common.TMPL.ExecuteTemplate(c.Writer, "html/backups-backups.go.tmpl", gin.H{
"backups": backups,
})
if err != nil {
c.Status(http.StatusInternalServerError)
} else {
c.Status(http.StatusOK)
}
} else { // return 401
w.WriteHeader(http.StatusUnauthorized)
return
c.Status(http.StatusUnauthorized)
}
}
+65 -51
View File
@@ -3,11 +3,12 @@ package routes
import (
"fmt"
"net/http"
paas "proxmoxaas-common-lib"
"proxmoxaas-dashboard/app/common"
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
"slices"
"sort"
"github.com/gin-gonic/gin"
"github.com/go-viper/mapstructure/v2"
)
@@ -30,26 +31,24 @@ type PoolConfig struct {
}
}
func HandleGETConfig(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETConfig(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil {
vm_path, err := common.GetInstancePathFromRequest(r)
vm_path, err := common.GetInstancePathFromRequest(c)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
config, err := GetInstanceConfig(vm_path, auth)
if err != nil {
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
return
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
}
if config.Type == "VM" { // if VM, fetch CPU types from node
config.ProctypeSelect, err = GetCPUTypes(vm_path, config.Pool, auth)
if err != nil {
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting proctypes: %s", err.Error()))
return
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting proctypes: %s", err.Error()))
}
}
for i, cpu := range config.ProctypeSelect.Options {
@@ -58,110 +57,125 @@ func HandleGETConfig(w http.ResponseWriter, r *http.Request) {
}
}
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/config.html", map[string]any{
c.HTML(http.StatusOK, "html/config.html", gin.H{
"global": common.Global,
"page": "config",
"config": config,
})
} else {
http.Redirect(w, r, "/login", http.StatusFound)
return
c.Redirect(http.StatusFound, "/login")
}
}
func HandleGETConfigVolumesFragment(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETConfigVolumesFragment(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil {
vm_path, err := common.GetInstancePathFromRequest(r)
vm_path, err := common.GetInstancePathFromRequest(c)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
config, err := GetInstanceConfig(vm_path, auth)
if err != nil {
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
return
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
}
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/config-volumes.go.tmpl", map[string]any{
c.Header("Content-Type", "text/plain")
err = common.TMPL.ExecuteTemplate(c.Writer, "html/config-volumes.go.tmpl", gin.H{
"config": config,
})
if err != nil {
c.Status(http.StatusInternalServerError)
} else {
w.WriteHeader(http.StatusUnauthorized)
return
c.Status(http.StatusOK)
}
} else {
c.Status(http.StatusUnauthorized)
}
}
func HandleGETConfigNetsFragment(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETConfigNetsFragment(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil {
vm_path, err := common.GetInstancePathFromRequest(r)
vm_path, err := common.GetInstancePathFromRequest(c)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
config, err := GetInstanceConfig(vm_path, auth)
if err != nil {
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
return
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
}
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/config-nets.go.tmpl", map[string]any{
c.Header("Content-Type", "text/plain")
err = common.TMPL.ExecuteTemplate(c.Writer, "html/config-nets.go.tmpl", gin.H{
"config": config,
})
} else { // return 401
w.WriteHeader(http.StatusUnauthorized)
return
if err != nil {
c.Status(http.StatusInternalServerError)
} else {
c.Status(http.StatusOK)
}
} else {
c.Status(http.StatusUnauthorized)
}
}
func HandleGETConfigDevicesFragment(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETConfigDevicesFragment(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil {
vm_path, err := common.GetInstancePathFromRequest(r)
vm_path, err := common.GetInstancePathFromRequest(c)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
config, err := GetInstanceConfig(vm_path, auth)
if err != nil {
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
return
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
}
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/config-devices.go.tmpl", map[string]any{
c.Header("Content-Type", "text/plain")
err = common.TMPL.ExecuteTemplate(c.Writer, "html/config-devices.go.tmpl", gin.H{
"config": config,
})
} else { // return 401
w.WriteHeader(http.StatusUnauthorized)
return
if err != nil {
c.Status(http.StatusInternalServerError)
} else {
c.Status(http.StatusOK)
}
} else {
c.Status(http.StatusUnauthorized)
}
}
func HandleGETConfigBootFragment(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETConfigBootFragment(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil {
vm_path, err := common.GetInstancePathFromRequest(r)
vm_path, err := common.GetInstancePathFromRequest(c)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
config, err := GetInstanceConfig(vm_path, auth)
if err != nil {
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
return
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
}
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/config-boot.go.tmpl", map[string]any{
c.Header("Content-Type", "text/plain")
err = common.TMPL.ExecuteTemplate(c.Writer, "html/config-boot.go.tmpl", gin.H{
"config": config,
})
} else { // return 401
w.WriteHeader(http.StatusUnauthorized)
return
if err != nil {
c.Status(http.StatusInternalServerError)
} else {
c.Status(http.StatusOK)
}
} else {
c.Status(http.StatusUnauthorized)
}
}
+21 -17
View File
@@ -3,10 +3,11 @@ package routes
import (
"fmt"
"net/http"
paas "proxmoxaas-common-lib"
"proxmoxaas-dashboard/app/common"
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
"strconv"
"github.com/gin-gonic/gin"
"github.com/go-viper/mapstructure/v2"
)
@@ -44,43 +45,46 @@ type InstanceStatus struct {
Status string
}
func HandleGETIndex(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETIndex(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil { // user should be authed, try to return index with population
instances, _, err := GetClusterResources(auth)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
page := map[string]any{
page := gin.H{
"global": common.Global,
"page": "index",
"instances": instances,
}
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/index.html", page)
c.HTML(http.StatusOK, "html/index.html", page)
} else { // return index without populating
http.Redirect(w, r, "/login", http.StatusFound)
return
c.Redirect(http.StatusFound, "/login") // if user is not authed, redirect user to login page
}
}
func HandleGETInstancesFragment(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETInstancesFragment(c *gin.Context) {
auth, err := common.GetAuthFromRequest(c)
if err == nil { // user should be authed, try to return index with population
instances, _, err := GetClusterResources(auth)
if err != nil {
common.HandleNonFatalError(w, err)
common.HandleNonFatalError(c, err)
return
}
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/index-instances.go.tmpl", map[string]any{
c.Header("Content-Type", "text/plain")
err = common.TMPL.ExecuteTemplate(c.Writer, "html/index-instances.go.tmpl", gin.H{
"instances": instances,
})
} else { // return 401
w.WriteHeader(http.StatusUnauthorized)
return
if err != nil {
c.Status(http.StatusInternalServerError)
} else {
c.Status(http.StatusOK)
}
} else { // return 401
c.Status(http.StatusUnauthorized)
}
}
func GetClusterResources(auth paas.Auth) (map[uint]InstanceCard, map[string]Node, error) {
+4 -5
View File
@@ -4,8 +4,8 @@ import (
"fmt"
"net/http"
"proxmoxaas-dashboard/app/common"
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
"github.com/gin-gonic/gin"
"github.com/go-viper/mapstructure/v2"
)
@@ -41,11 +41,10 @@ func GetLoginRealms() ([]Realm, error) {
return realms, nil
}
func HandleGETLogin(w http.ResponseWriter, r *http.Request) {
func HandleGETLogin(c *gin.Context) {
realms, err := GetLoginRealms()
if err != nil {
common.HandleNonFatalError(w, err)
return
common.HandleNonFatalError(c, err)
}
sel := common.Select{
@@ -61,7 +60,7 @@ func HandleGETLogin(w http.ResponseWriter, r *http.Request) {
})
}
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/login.html", map[string]any{
c.HTML(http.StatusOK, "html/login.html", gin.H{
"global": common.Global,
"page": "login",
"realms": sel,
+6 -12
View File
@@ -3,24 +3,18 @@ package routes
import (
"net/http"
"proxmoxaas-dashboard/app/common"
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
"github.com/gin-gonic/gin"
)
func HandleGETSettings(w http.ResponseWriter, r *http.Request) {
auth, err := common.GetAuthFromRequest(r)
func HandleGETSettings(c *gin.Context) {
_, err := common.GetAuthFromRequest(c)
if err == nil {
_, err := GetUserBasic(auth)
if err != nil {
common.HandleNonFatalError(w, err)
return
}
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/settings.html", map[string]any{
c.HTML(http.StatusOK, "html/settings.html", gin.H{
"global": common.Global,
"page": "settings",
})
} else {
http.Redirect(w, r, "/login", http.StatusFound)
return
c.Redirect(http.StatusFound, "/login")
}
}
-604
View File
@@ -1,604 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
"use strict";
(() => {
const enosys = () => {
const err = new Error("not implemented");
err.code = "ENOSYS";
return err;
};
if (!globalThis.fs) {
let outputBuf = "";
globalThis.fs = {
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused
writeSync(fd, buf) {
outputBuf += decoder.decode(buf);
const nl = outputBuf.lastIndexOf("\n");
if (nl != -1) {
console.log(outputBuf.substring(0, nl));
outputBuf = outputBuf.substring(nl + 1);
}
return buf.length;
},
write(fd, buf, offset, length, position, callback) {
if (offset !== 0 || length !== buf.length || position !== null) {
callback(enosys());
return;
}
const n = this.writeSync(fd, buf);
callback(null, n);
},
chmod(path, mode, callback) { callback(enosys()); },
chown(path, uid, gid, callback) { callback(enosys()); },
close(fd, callback) { callback(enosys()); },
fchmod(fd, mode, callback) { callback(enosys()); },
fchown(fd, uid, gid, callback) { callback(enosys()); },
fstat(fd, callback) { callback(enosys()); },
fsync(fd, callback) { callback(null); },
ftruncate(fd, length, callback) { callback(enosys()); },
lchown(path, uid, gid, callback) { callback(enosys()); },
link(path, link, callback) { callback(enosys()); },
lstat(path, callback) { callback(enosys()); },
mkdir(path, perm, callback) { callback(enosys()); },
open(path, flags, mode, callback) { callback(enosys()); },
read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
readdir(path, callback) { callback(enosys()); },
readlink(path, callback) { callback(enosys()); },
rename(from, to, callback) { callback(enosys()); },
rmdir(path, callback) { callback(enosys()); },
stat(path, callback) { callback(enosys()); },
symlink(path, link, callback) { callback(enosys()); },
truncate(path, length, callback) { callback(enosys()); },
unlink(path, callback) { callback(enosys()); },
utimes(path, atime, mtime, callback) { callback(enosys()); },
};
}
if (!globalThis.process) {
globalThis.process = {
getuid() { return -1; },
getgid() { return -1; },
geteuid() { return -1; },
getegid() { return -1; },
getgroups() { throw enosys(); },
pid: -1,
ppid: -1,
umask() { throw enosys(); },
cwd() { throw enosys(); },
chdir() { throw enosys(); },
}
}
if (!globalThis.path) {
globalThis.path = {
resolve(...pathSegments) {
return pathSegments.join("/");
}
}
}
if (!globalThis.crypto) {
throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
}
if (!globalThis.performance) {
throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
}
if (!globalThis.TextEncoder) {
throw new Error("globalThis.TextEncoder is not available, polyfill required");
}
if (!globalThis.TextDecoder) {
throw new Error("globalThis.TextDecoder is not available, polyfill required");
}
const encoder = new TextEncoder("utf-8");
const decoder = new TextDecoder("utf-8");
globalThis.Go = class {
constructor() {
this.argv = ["js"];
this.env = {};
this.exit = (code) => {
if (code !== 0) {
console.warn("exit code:", code);
}
};
this._exitPromise = new Promise((resolve) => {
this._resolveExitPromise = resolve;
});
this._pendingEvent = null;
this._scheduledTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
const setInt64 = (addr, v) => {
this.mem.setUint32(addr + 0, v, true);
this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
}
const setInt32 = (addr, v) => {
this.mem.setUint32(addr + 0, v, true);
}
const getInt64 = (addr) => {
const low = this.mem.getUint32(addr + 0, true);
const high = this.mem.getInt32(addr + 4, true);
return low + high * 4294967296;
}
const loadValue = (addr) => {
const f = this.mem.getFloat64(addr, true);
if (f === 0) {
return undefined;
}
if (!isNaN(f)) {
return f;
}
const id = this.mem.getUint32(addr, true);
return this._values[id];
}
const storeValue = (addr, v) => {
const nanHead = 0x7FF80000;
if (typeof v === "number" && v !== 0) {
if (isNaN(v)) {
this.mem.setUint32(addr + 4, nanHead, true);
this.mem.setUint32(addr, 0, true);
return;
}
this.mem.setFloat64(addr, v, true);
return;
}
if (v === undefined) {
this.mem.setFloat64(addr, 0, true);
return;
}
let id = this._ids.get(v);
if (id === undefined) {
id = this._idPool.pop();
if (id === undefined) {
id = this._values.length;
}
this._values[id] = v;
this._goRefCounts[id] = 0;
this._ids.set(v, id);
}
this._goRefCounts[id]++;
let typeFlag = 0;
switch (typeof v) {
case "object":
if (v !== null) {
typeFlag = 1;
}
break;
case "string":
typeFlag = 2;
break;
case "symbol":
typeFlag = 3;
break;
case "function":
typeFlag = 4;
break;
}
this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
this.mem.setUint32(addr, id, true);
}
const loadSlice = (addr) => {
const array = getInt64(addr + 0);
const len = getInt64(addr + 8);
return new Uint8Array(this._inst.exports.mem.buffer, array, len);
}
const loadSliceOfValues = (addr) => {
const array = getInt64(addr + 0);
const len = getInt64(addr + 8);
const a = new Array(len);
for (let i = 0; i < len; i++) {
a[i] = loadValue(array + i * 8);
}
return a;
}
const loadString = (addr) => {
const saddr = getInt64(addr + 0);
const len = getInt64(addr + 8);
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
}
const testCallExport = (a, b) => {
this._inst.exports.testExport0();
return this._inst.exports.testExport(a, b);
}
const timeOrigin = Date.now() - performance.now();
this.importObject = {
_gotest: {
add: (a, b) => a + b,
callExport: testCallExport,
},
gojs: {
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
// This changes the SP, thus we have to update the SP used by the imported function.
// func wasmExit(code int32)
"runtime.wasmExit": (sp) => {
sp >>>= 0;
const code = this.mem.getInt32(sp + 8, true);
this.exited = true;
delete this._inst;
delete this._values;
delete this._goRefCounts;
delete this._ids;
delete this._idPool;
this.exit(code);
},
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
"runtime.wasmWrite": (sp) => {
sp >>>= 0;
const fd = getInt64(sp + 8);
const p = getInt64(sp + 16);
const n = this.mem.getInt32(sp + 24, true);
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
},
// func resetMemoryDataView()
"runtime.resetMemoryDataView": (sp) => {
sp >>>= 0;
this.mem = new DataView(this._inst.exports.mem.buffer);
},
// func nanotime1() int64
"runtime.nanotime1": (sp) => {
sp >>>= 0;
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
},
// func walltime() (sec int64, nsec int32)
"runtime.walltime": (sp) => {
sp >>>= 0;
const msec = (new Date).getTime();
setInt64(sp + 8, msec / 1000);
this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
},
// func scheduleTimeoutEvent(delay int64) int32
"runtime.scheduleTimeoutEvent": (sp) => {
sp >>>= 0;
const id = this._nextCallbackTimeoutID;
this._nextCallbackTimeoutID++;
this._scheduledTimeouts.set(id, setTimeout(
() => {
this._resume();
while (this._scheduledTimeouts.has(id)) {
// for some reason Go failed to register the timeout event, log and try again
// (temporary workaround for https://github.com/golang/go/issues/28975)
console.warn("scheduleTimeoutEvent: missed timeout event");
this._resume();
}
},
getInt64(sp + 8),
));
this.mem.setInt32(sp + 16, id, true);
},
// func clearTimeoutEvent(id int32)
"runtime.clearTimeoutEvent": (sp) => {
sp >>>= 0;
const id = this.mem.getInt32(sp + 8, true);
clearTimeout(this._scheduledTimeouts.get(id));
this._scheduledTimeouts.delete(id);
},
// func getRandomData(r []byte)
"runtime.getRandomData": (sp) => {
sp >>>= 0;
crypto.getRandomValues(loadSlice(sp + 8));
},
// func finalizeRef(v ref)
"syscall/js.finalizeRef": (sp) => {
sp >>>= 0;
const id = this.mem.getUint32(sp + 8, true);
this._goRefCounts[id]--;
if (this._goRefCounts[id] === 0) {
const v = this._values[id];
this._values[id] = null;
this._ids.delete(v);
this._idPool.push(id);
}
},
// func stringVal(value string) ref
"syscall/js.stringVal": (sp) => {
sp >>>= 0;
storeValue(sp + 24, loadString(sp + 8));
},
// func valueGet(v ref, p string) ref
"syscall/js.valueGet": (sp) => {
sp >>>= 0;
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 32, result);
},
// func valueSet(v ref, p string, x ref)
"syscall/js.valueSet": (sp) => {
sp >>>= 0;
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
},
// func valueDelete(v ref, p string)
"syscall/js.valueDelete": (sp) => {
sp >>>= 0;
Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
},
// func valueIndex(v ref, i int) ref
"syscall/js.valueIndex": (sp) => {
sp >>>= 0;
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
},
// valueSetIndex(v ref, i int, x ref)
"syscall/js.valueSetIndex": (sp) => {
sp >>>= 0;
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
},
// func valueCall(v ref, m string, args []ref) (ref, bool)
"syscall/js.valueCall": (sp) => {
sp >>>= 0;
try {
const v = loadValue(sp + 8);
const m = Reflect.get(v, loadString(sp + 16));
const args = loadSliceOfValues(sp + 32);
const result = Reflect.apply(m, v, args);
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 56, result);
this.mem.setUint8(sp + 64, 1);
} catch (err) {
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 56, err);
this.mem.setUint8(sp + 64, 0);
}
},
// func valueInvoke(v ref, args []ref) (ref, bool)
"syscall/js.valueInvoke": (sp) => {
sp >>>= 0;
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
const result = Reflect.apply(v, undefined, args);
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, result);
this.mem.setUint8(sp + 48, 1);
} catch (err) {
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, err);
this.mem.setUint8(sp + 48, 0);
}
},
// func valueNew(v ref, args []ref) (ref, bool)
"syscall/js.valueNew": (sp) => {
sp >>>= 0;
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
const result = Reflect.construct(v, args);
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, result);
this.mem.setUint8(sp + 48, 1);
} catch (err) {
sp = this._inst.exports.getsp() >>> 0; // see comment above
storeValue(sp + 40, err);
this.mem.setUint8(sp + 48, 0);
}
},
// func valueLength(v ref) int
"syscall/js.valueLength": (sp) => {
sp >>>= 0;
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
},
// valuePrepareString(v ref) (ref, int)
"syscall/js.valuePrepareString": (sp) => {
sp >>>= 0;
const str = encoder.encode(String(loadValue(sp + 8)));
storeValue(sp + 16, str);
setInt64(sp + 24, str.length);
},
// valueLoadString(v ref, b []byte)
"syscall/js.valueLoadString": (sp) => {
sp >>>= 0;
const str = loadValue(sp + 8);
loadSlice(sp + 16).set(str);
},
// func valueInstanceOf(v ref, t ref) bool
"syscall/js.valueInstanceOf": (sp) => {
sp >>>= 0;
this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
},
// func copyBytesToGo(dst []byte, src ref) (int, bool)
"syscall/js.copyBytesToGo": (sp) => {
sp >>>= 0;
const dst = loadSlice(sp + 8);
const src = loadValue(sp + 32);
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
this.mem.setUint8(sp + 48, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(sp + 40, toCopy.length);
this.mem.setUint8(sp + 48, 1);
},
// func copyBytesToJS(dst ref, src []byte) (int, bool)
"syscall/js.copyBytesToJS": (sp) => {
sp >>>= 0;
const dst = loadValue(sp + 8);
const src = loadSlice(sp + 16);
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
this.mem.setUint8(sp + 48, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(sp + 40, toCopy.length);
this.mem.setUint8(sp + 48, 1);
},
"debug": (value) => {
console.log(value);
},
}
};
}
async run(instance) {
if (!(instance instanceof WebAssembly.Instance)) {
throw new Error("Go.run: WebAssembly.Instance expected");
}
this._inst = instance;
this.mem = new DataView(this._inst.exports.mem.buffer);
this._values = [ // JS values that Go currently has references to, indexed by reference id
NaN,
0,
null,
true,
false,
globalThis,
this,
];
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
this._ids = new Map([ // mapping from JS values to reference ids
[0, 1],
[null, 2],
[true, 3],
[false, 4],
[globalThis, 5],
[this, 6],
]);
this._idPool = []; // unused ids that have been garbage collected
this.exited = false; // whether the Go program has exited
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
let offset = 4096;
const strPtr = (str) => {
const ptr = offset;
const bytes = encoder.encode(str + "\0");
new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
offset += bytes.length;
if (offset % 8 !== 0) {
offset += 8 - (offset % 8);
}
return ptr;
};
const argc = this.argv.length;
const argvPtrs = [];
this.argv.forEach((arg) => {
argvPtrs.push(strPtr(arg));
});
argvPtrs.push(0);
const keys = Object.keys(this.env).sort();
keys.forEach((key) => {
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
});
argvPtrs.push(0);
const argv = offset;
argvPtrs.forEach((ptr) => {
this.mem.setUint32(offset, ptr, true);
this.mem.setUint32(offset + 4, 0, true);
offset += 8;
});
// The linker guarantees global data starts from at least wasmMinDataAddr.
// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
const wasmMinDataAddr = 4096 + 8192;
if (offset >= wasmMinDataAddr) {
throw new Error("total length of command line and environment variables exceeds limit");
}
this._inst.exports.run(argc, argv);
if (this.exited) {
this._resolveExitPromise();
}
await this._exitPromise;
}
_resume() {
if (this.exited) {
throw new Error("Go program has already exited");
}
this._inst.exports.resume();
if (this.exited) {
this._resolveExitPromise();
}
}
_makeFuncWrapper(id) {
const go = this;
return function () {
const event = { id: id, this: this, args: arguments };
go._pendingEvent = event;
go._resume();
return event.result;
};
}
}
})();
// wasm setup code
export default function init (path) {
if (typeof global === "undefined") {
var global = globalThis;
}
return new Promise ((res) => {
const go = new Go();
const run = (obj) => {
const wasm = obj.instance;
global.wfa = wasm
go.run(wasm);
res(wasm)
}
if ('instantiateStreaming' in WebAssembly) {
WebAssembly.instantiateStreaming(fetch(path), go.importObject).then(function (obj) {
run(obj)
})
} else {
fetch(path).then(resp =>
resp.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
run(obj)
})
)
}
})
}
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
web/modules/*
WFA-JS/*
dist/*
+1 -1
View File
@@ -1,3 +1,3 @@
app/web/modules/*
web/modules/*
WFA-JS/*
dist/*
+1 -1
View File
@@ -4,7 +4,7 @@ import js from "@eslint/js";
export default defineConfig([
js.configs.recommended,
globalIgnores(["dist/", "app/web/modules", "WFA-JS"]),
globalIgnores(["dist/", "web/modules", "WFA-JS"]),
{
languageOptions: {
globals: {
+40 -3
View File
@@ -1,11 +1,48 @@
module proxmoxaas-dashboard
go 1.27.3
go 1.26.5
require (
github.com/gerow/go-color v0.0.0-20140219113758-125d37f527f1
github.com/gin-gonic/gin v1.12.0
github.com/go-viper/mapstructure/v2 v2.5.0
github.com/tdewolff/minify/v2 v2.24.23
github.com/tdewolff/minify/v2 v2.24.13
proxmoxaas-common-lib v0.0.0
web v0.0.0
)
require github.com/tdewolff/parse/v2 v2.8.20 // indirect
replace proxmoxaas-common-lib => ./proxmoxaas-common-lib
replace web => ./dist/web
require (
github.com/bytedance/gopkg v0.1.4 // indirect
github.com/bytedance/sonic v1.15.2 // indirect
github.com/bytedance/sonic/loader v0.5.1 // indirect
github.com/cloudwego/base64x v0.1.7 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gin-contrib/sse v1.1.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.30.3 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.23 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.60.0 // indirect
github.com/tdewolff/parse/v2 v2.8.13 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect
golang.org/x/arch v0.29.0 // indirect
golang.org/x/crypto v0.54.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.40.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
)
File renamed without changes.
File renamed without changes.
+1 -1
View File
@@ -202,7 +202,7 @@ hr {
}
/* fixes edge case in w3-hide-small when width between 600 and 601 */
@media screen and (width <= 601px) and (width >= 440px) {
@media screen and (width <=601px) {
.hide-large {display: none !important;}
.hide-medium {display:none !important}
.hide-small {display:none !important}
+7 -2
View File
@@ -5,11 +5,16 @@ import (
)
//go:embed css/*
var CSS_fs embed.FS
//go:embed images/*
//go:embed images/*/*/*
var Images_fs embed.FS
//go:embed modules/*
var Modules_fs embed.FS
//go:embed scripts/*
var Static embed.FS
var Scripts_fs embed.FS
//go:embed html/*
//go:embed templates/*
+3
View File
@@ -0,0 +1,3 @@
module web
go 1.26.0
@@ -2,9 +2,9 @@
<html lang="en">
<head>
{{template "head" .}}
<script src="websrc/scripts/account.js" type="module"></script>
<link rel="modulepreload" href="websrc/scripts/utils.js">
<link rel="modulepreload" href="websrc/scripts/dialog.js">
<script src="scripts/account.js" type="module"></script>
<link rel="modulepreload" href="scripts/utils.js">
<link rel="modulepreload" href="scripts/dialog.js">
<style>
@media screen and (width >= 1264px){
.resource-container {
@@ -48,9 +48,9 @@
{{end}}
</main>
<template id="change-password-dialog">
<link rel="stylesheet" href="websrc/modules/w3.css">
<link rel="stylesheet" href="websrc/css/style.css">
<link rel="stylesheet" href="websrc/css/form.css">
<link rel="stylesheet" href="modules/w3.css">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/form.css">
<dialog class="w3-container w3-card w3-border-0">
<p class="w3-large" id="prompt">
Change Password
File renamed without changes.
@@ -2,9 +2,9 @@
<html lang="en">
<head>
{{template "head" .}}
<script src="websrc/scripts/backups.js" type="module"></script>
<link rel="modulepreload" href="websrc/scripts/utils.js">
<link rel="modulepreload" href="websrc/scripts/dialog.js">
<script src="scripts/backups.js" type="module"></script>
<link rel="modulepreload" href="scripts/utils.js">
<link rel="modulepreload" href="scripts/dialog.js">
<style>
</style>
</head>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -2,11 +2,11 @@
<html lang="en">
<head>
{{template "head" .}}
<script src="websrc/scripts/config.js" type="module"></script>
<script src="websrc/scripts/draggable.js" type="module"></script>
<script src="websrc/modules/Sortable.min.js"></script>
<link rel="modulepreload" href="websrc/scripts/utils.js">
<link rel="modulepreload" href="websrc/scripts/dialog.js">
<script src="scripts/config.js" type="module"></script>
<script src="scripts/draggable.js" type="module"></script>
<script src="modules/Sortable.min.js"></script>
<link rel="modulepreload" href="scripts/utils.js">
<link rel="modulepreload" href="scripts/dialog.js">
<style>
.input-grid p, .input-grid div {
margin-top: 0;
File renamed without changes.
@@ -2,11 +2,11 @@
<html lang="en">
<head>
{{template "head" .}}
<script src="websrc/scripts/index.js" type="module"></script>
<script src="websrc/modules/wfa.js" type="module"></script>
<link rel="modulepreload" href="websrc/scripts/utils.js">
<link rel="modulepreload" href="websrc/scripts/dialog.js">
<link rel="modulepreload" href="websrc/scripts/clientsync.js">
<script src="scripts/index.js" type="module"></script>
<script src="modules/wfa.js" type="module"></script>
<link rel="modulepreload" href="scripts/utils.js">
<link rel="modulepreload" href="scripts/dialog.js">
<link rel="modulepreload" href="scripts/clientsync.js">
<style>
@media screen and (width >= 440px) {
#vm-search {
@@ -72,13 +72,13 @@
<div class="w3-card w3-padding">
<div class="flex row nowrap" style="margin-top: 1em; justify-content: space-between;">
<form id="vm-search" role="search" class="flex row nowrap" tabindex="0">
<button type="submit" id="submit" class="w3-button" style="padding: 0; width: 1em; height: 1em; line-height: 1em;"><img alt="Search Instances" aria-label="Search Instances" src="websrc/images/common/search.svg#symb"></button>
<button type="submit" id="submit" class="w3-button" style="padding: 0; width: 1em; height: 1em; line-height: 1em;"><img alt="Search Instances" aria-label="Search Instances" src="images/common/search.svg#symb"></button>
<input type="search" id="search" class="w3-input w3-border" style="height: 1lh; max-width: fit-content;" aria-label="search instances by name">
</form>
<!--Add Instance Button & Dialog Template-->
<button type="button" id="instance-add" class="w3-button" aria-label="create new instance">
<span class="large" style="margin: 0;">Create Instance</span>
<img class="small" style="height: 1lh; width: 1lh;" alt="Create Instance" aria-label="Create Instance" src="websrc/images/actions/instance/add.svg#symb">
<img class="small" style="height: 1lh; width: 1lh;" alt="Create Instance" aria-label="Create Instance" src="images/actions/instance/add.svg#symb">
</button>
<template id="create-instance-dialog">
<dialog class="w3-container w3-card w3-border-0">
@@ -2,9 +2,9 @@
<html lang="en">
<head>
{{template "head" .}}
<script src="websrc/scripts/login.js" type="module"></script>
<link rel="modulepreload" href="websrc/scripts/utils.js">
<link rel="modulepreload" href="websrc/scripts/dialog.js">
<script src="scripts/login.js" type="module"></script>
<link rel="modulepreload" href="scripts/utils.js">
<link rel="modulepreload" href="scripts/dialog.js">
</head>
<body>
{{template "header" .}}
@@ -2,8 +2,8 @@
<html lang="en">
<head>
{{template "head" .}}
<script src="websrc/scripts/settings.js" type="module"></script>
<link rel="modulepreload" href="websrc/scripts/utils.js">
<script src="scripts/settings.js" type="module"></script>
<link rel="modulepreload" href="scripts/utils.js">
<style>
label {
display: flex;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+579
View File
@@ -0,0 +1,579 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// This file has been modified for use by the TinyGo compiler.
(() => {
// Map multiple JavaScript environments to a single common API,
// preferring web standards over Node.js API.
//
// Environments considered:
// - Browsers
// - Node.js
// - Electron
// - Parcel
if (typeof global !== "undefined") {
// global already exists
} else if (typeof window !== "undefined") {
window.global = window;
} else if (typeof self !== "undefined") {
self.global = self;
} else {
throw new Error("cannot export Go (neither global, window nor self is defined)");
}
if (!global.require && typeof require !== "undefined") {
global.require = require;
}
if (!global.fs && global.require) {
global.fs = require("node:fs");
}
const enosys = () => {
const err = new Error("not implemented");
err.code = "ENOSYS";
return err;
};
if (!global.fs) {
let outputBuf = "";
global.fs = {
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
writeSync(fd, buf) {
outputBuf += decoder.decode(buf);
const nl = outputBuf.lastIndexOf("\n");
if (nl != -1) {
console.log(outputBuf.substr(0, nl));
outputBuf = outputBuf.substr(nl + 1);
}
return buf.length;
},
write(fd, buf, offset, length, position, callback) {
if (offset !== 0 || length !== buf.length || position !== null) {
callback(enosys());
return;
}
const n = this.writeSync(fd, buf);
callback(null, n);
},
chmod(path, mode, callback) { callback(enosys()); },
chown(path, uid, gid, callback) { callback(enosys()); },
close(fd, callback) { callback(enosys()); },
fchmod(fd, mode, callback) { callback(enosys()); },
fchown(fd, uid, gid, callback) { callback(enosys()); },
fstat(fd, callback) { callback(enosys()); },
fsync(fd, callback) { callback(null); },
ftruncate(fd, length, callback) { callback(enosys()); },
lchown(path, uid, gid, callback) { callback(enosys()); },
link(path, link, callback) { callback(enosys()); },
lstat(path, callback) { callback(enosys()); },
mkdir(path, perm, callback) { callback(enosys()); },
open(path, flags, mode, callback) { callback(enosys()); },
read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
readdir(path, callback) { callback(enosys()); },
readlink(path, callback) { callback(enosys()); },
rename(from, to, callback) { callback(enosys()); },
rmdir(path, callback) { callback(enosys()); },
stat(path, callback) { callback(enosys()); },
symlink(path, link, callback) { callback(enosys()); },
truncate(path, length, callback) { callback(enosys()); },
unlink(path, callback) { callback(enosys()); },
utimes(path, atime, mtime, callback) { callback(enosys()); },
};
}
if (!global.process) {
global.process = {
getuid() { return -1; },
getgid() { return -1; },
geteuid() { return -1; },
getegid() { return -1; },
getgroups() { throw enosys(); },
pid: -1,
ppid: -1,
umask() { throw enosys(); },
cwd() { throw enosys(); },
chdir() { throw enosys(); },
}
}
if (!global.crypto) {
const nodeCrypto = require("node:crypto");
global.crypto = {
getRandomValues(b) {
nodeCrypto.randomFillSync(b);
},
};
}
if (!global.performance) {
global.performance = {
now() {
const [sec, nsec] = process.hrtime();
return sec * 1000 + nsec / 1000000;
},
};
}
if (!global.TextEncoder) {
global.TextEncoder = require("node:util").TextEncoder;
}
if (!global.TextDecoder) {
global.TextDecoder = require("node:util").TextDecoder;
}
// End of polyfills for common API.
const encoder = new TextEncoder("utf-8");
const decoder = new TextDecoder("utf-8");
let reinterpretBuf = new DataView(new ArrayBuffer(8));
var logLine = [];
const wasmExit = {}; // thrown to exit via proc_exit (not an error)
global.Go = class {
constructor() {
this._callbackTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
const mem = () => {
// The buffer may change when requesting more memory.
return new DataView(this._inst.exports.memory.buffer);
}
const unboxValue = (v_ref) => {
reinterpretBuf.setBigInt64(0, v_ref, true);
const f = reinterpretBuf.getFloat64(0, true);
if (f === 0) {
return undefined;
}
if (!isNaN(f)) {
return f;
}
const id = v_ref & 0xffffffffn;
return this._values[id];
}
const loadValue = (addr) => {
let v_ref = mem().getBigUint64(addr, true);
return unboxValue(v_ref);
}
const boxValue = (v) => {
const nanHead = 0x7FF80000n;
if (typeof v === "number") {
if (isNaN(v)) {
return nanHead << 32n;
}
if (v === 0) {
return (nanHead << 32n) | 1n;
}
reinterpretBuf.setFloat64(0, v, true);
return reinterpretBuf.getBigInt64(0, true);
}
switch (v) {
case undefined:
return 0n;
case null:
return (nanHead << 32n) | 2n;
case true:
return (nanHead << 32n) | 3n;
case false:
return (nanHead << 32n) | 4n;
}
let id = this._ids.get(v);
if (id === undefined) {
id = this._idPool.pop();
if (id === undefined) {
id = BigInt(this._values.length);
}
this._values[id] = v;
this._goRefCounts[id] = 0;
this._ids.set(v, id);
}
this._goRefCounts[id]++;
let typeFlag = 1n;
switch (typeof v) {
case "string":
typeFlag = 2n;
break;
case "symbol":
typeFlag = 3n;
break;
case "function":
typeFlag = 4n;
break;
}
return id | ((nanHead | typeFlag) << 32n);
}
const storeValue = (addr, v) => {
let v_ref = boxValue(v);
mem().setBigUint64(addr, v_ref, true);
}
const loadSlice = (array, len, cap) => {
return new Uint8Array(this._inst.exports.memory.buffer, array, len);
}
const loadSliceOfValues = (array, len, cap) => {
const a = new Array(len);
for (let i = 0; i < len; i++) {
a[i] = loadValue(array + i * 8);
}
return a;
}
const loadString = (ptr, len) => {
return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len));
}
const timeOrigin = Date.now() - performance.now();
this.importObject = {
wasi_snapshot_preview1: {
// https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write
fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) {
let nwritten = 0;
if (fd == 1) {
for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) {
let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32
let ptr = mem().getUint32(iov_ptr + 0, true);
let len = mem().getUint32(iov_ptr + 4, true);
nwritten += len;
for (let i=0; i<len; i++) {
let c = mem().getUint8(ptr+i);
if (c == 13) { // CR
// ignore
} else if (c == 10) { // LF
// write line
let line = decoder.decode(new Uint8Array(logLine));
logLine = [];
console.log(line);
} else {
logLine.push(c);
}
}
}
} else {
console.error('invalid file descriptor:', fd);
}
mem().setUint32(nwritten_ptr, nwritten, true);
return 0;
},
fd_close: () => 0, // dummy
fd_fdstat_get: () => 0, // dummy
fd_seek: () => 0, // dummy
proc_exit: (code) => {
this.exited = true;
this.exitCode = code;
this._resolveExitPromise();
throw wasmExit;
},
random_get: (bufPtr, bufLen) => {
crypto.getRandomValues(loadSlice(bufPtr, bufLen));
return 0;
},
},
gojs: {
// func ticks() int64
"runtime.ticks": () => {
return BigInt((timeOrigin + performance.now()) * 1e6);
},
// func sleepTicks(timeout int64)
"runtime.sleepTicks": (timeout) => {
// Do not sleep, only reactivate scheduler after the given timeout.
setTimeout(() => {
if (this.exited) return;
try {
this._inst.exports.go_scheduler();
} catch (e) {
if (e !== wasmExit) throw e;
}
}, Number(timeout)/1e6);
},
// func finalizeRef(v ref)
"syscall/js.finalizeRef": (v_ref) => {
// Note: TinyGo does not support finalizers so this is only called
// for one specific case, by js.go:jsString. and can/might leak memory.
const id = v_ref & 0xffffffffn;
if (this._goRefCounts?.[id] !== undefined) {
this._goRefCounts[id]--;
if (this._goRefCounts[id] === 0) {
const v = this._values[id];
this._values[id] = null;
this._ids.delete(v);
this._idPool.push(id);
}
} else {
console.error("syscall/js.finalizeRef: unknown id", id);
}
},
// func stringVal(value string) ref
"syscall/js.stringVal": (value_ptr, value_len) => {
value_ptr >>>= 0;
const s = loadString(value_ptr, value_len);
return boxValue(s);
},
// func valueGet(v ref, p string) ref
"syscall/js.valueGet": (v_ref, p_ptr, p_len) => {
let prop = loadString(p_ptr, p_len);
let v = unboxValue(v_ref);
let result = Reflect.get(v, prop);
return boxValue(result);
},
// func valueSet(v ref, p string, x ref)
"syscall/js.valueSet": (v_ref, p_ptr, p_len, x_ref) => {
const v = unboxValue(v_ref);
const p = loadString(p_ptr, p_len);
const x = unboxValue(x_ref);
Reflect.set(v, p, x);
},
// func valueDelete(v ref, p string)
"syscall/js.valueDelete": (v_ref, p_ptr, p_len) => {
const v = unboxValue(v_ref);
const p = loadString(p_ptr, p_len);
Reflect.deleteProperty(v, p);
},
// func valueIndex(v ref, i int) ref
"syscall/js.valueIndex": (v_ref, i) => {
return boxValue(Reflect.get(unboxValue(v_ref), i));
},
// valueSetIndex(v ref, i int, x ref)
"syscall/js.valueSetIndex": (v_ref, i, x_ref) => {
Reflect.set(unboxValue(v_ref), i, unboxValue(x_ref));
},
// func valueCall(v ref, m string, args []ref) (ref, bool)
"syscall/js.valueCall": (ret_addr, v_ref, m_ptr, m_len, args_ptr, args_len, args_cap) => {
const v = unboxValue(v_ref);
const name = loadString(m_ptr, m_len);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
try {
const m = Reflect.get(v, name);
storeValue(ret_addr, Reflect.apply(m, v, args));
mem().setUint8(ret_addr + 8, 1);
} catch (err) {
storeValue(ret_addr, err);
mem().setUint8(ret_addr + 8, 0);
}
},
// func valueInvoke(v ref, args []ref) (ref, bool)
"syscall/js.valueInvoke": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
try {
const v = unboxValue(v_ref);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
storeValue(ret_addr, Reflect.apply(v, undefined, args));
mem().setUint8(ret_addr + 8, 1);
} catch (err) {
storeValue(ret_addr, err);
mem().setUint8(ret_addr + 8, 0);
}
},
// func valueNew(v ref, args []ref) (ref, bool)
"syscall/js.valueNew": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
const v = unboxValue(v_ref);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
try {
storeValue(ret_addr, Reflect.construct(v, args));
mem().setUint8(ret_addr + 8, 1);
} catch (err) {
storeValue(ret_addr, err);
mem().setUint8(ret_addr+ 8, 0);
}
},
// func valueLength(v ref) int
"syscall/js.valueLength": (v_ref) => {
return unboxValue(v_ref).length;
},
// valuePrepareString(v ref) (ref, int)
"syscall/js.valuePrepareString": (ret_addr, v_ref) => {
const s = String(unboxValue(v_ref));
const str = encoder.encode(s);
storeValue(ret_addr, str);
mem().setInt32(ret_addr + 8, str.length, true);
},
// valueLoadString(v ref, b []byte)
"syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => {
const str = unboxValue(v_ref);
loadSlice(slice_ptr, slice_len, slice_cap).set(str);
},
// func valueInstanceOf(v ref, t ref) bool
"syscall/js.valueInstanceOf": (v_ref, t_ref) => {
return unboxValue(v_ref) instanceof unboxValue(t_ref);
},
// func copyBytesToGo(dst []byte, src ref) (int, bool)
"syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, src_ref) => {
let num_bytes_copied_addr = ret_addr;
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
const dst = loadSlice(dest_addr, dest_len);
const src = unboxValue(src_ref);
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
mem().setUint8(returned_status_addr, 0); // Return "not ok" status
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
mem().setUint32(num_bytes_copied_addr, toCopy.length, true);
mem().setUint8(returned_status_addr, 1); // Return "ok" status
},
// copyBytesToJS(dst ref, src []byte) (int, bool)
// Originally copied from upstream Go project, then modified:
// https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416
"syscall/js.copyBytesToJS": (ret_addr, dst_ref, src_addr, src_len, src_cap) => {
let num_bytes_copied_addr = ret_addr;
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
const dst = unboxValue(dst_ref);
const src = loadSlice(src_addr, src_len);
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
mem().setUint8(returned_status_addr, 0); // Return "not ok" status
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
mem().setUint32(num_bytes_copied_addr, toCopy.length, true);
mem().setUint8(returned_status_addr, 1); // Return "ok" status
},
}
};
// Go 1.20 uses 'env'. Go 1.21 uses 'gojs'.
// For compatibility, we use both as long as Go 1.20 is supported.
this.importObject.env = this.importObject.gojs;
}
async run(instance) {
this._inst = instance;
this._values = [ // JS values that Go currently has references to, indexed by reference id
NaN,
0,
null,
true,
false,
global,
this,
];
this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
this._ids = new Map(); // mapping from JS values to reference ids
this._idPool = []; // unused ids that have been garbage collected
this.exited = false; // whether the Go program has exited
this.exitCode = 0;
if (this._inst.exports._start) {
let exitPromise = new Promise((resolve, reject) => {
this._resolveExitPromise = resolve;
});
// Run program, but catch the wasmExit exception that's thrown
// to return back here.
try {
this._inst.exports._start();
} catch (e) {
if (e !== wasmExit) throw e;
}
await exitPromise;
return this.exitCode;
} else {
this._inst.exports._initialize();
}
}
_resume() {
if (this.exited) {
throw new Error("Go program has already exited");
}
try {
this._inst.exports.resume();
} catch (e) {
if (e !== wasmExit) throw e;
}
if (this.exited) {
this._resolveExitPromise();
}
}
_makeFuncWrapper(id) {
const go = this;
return function () {
const event = { id: id, this: this, args: arguments };
go._pendingEvent = event;
go._resume();
return event.result;
};
}
}
if (
global.require &&
global.require.main === module &&
global.process &&
global.process.versions &&
!global.process.versions.electron
) {
if (process.argv.length != 3) {
console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
process.exit(1);
}
const go = new Go();
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then(async (result) => {
let exitCode = await go.run(result.instance);
process.exit(exitCode);
}).catch((err) => {
console.error(err);
process.exit(1);
});
}
})();
// wasm setup code
export default function init (path) {
return new Promise ((res) => {
const go = new Go();
const run = (obj) => {
const wasm = obj.instance;
global.wfa = wasm
go.run(wasm);
res(wasm)
}
if ('instantiateStreaming' in WebAssembly) {
WebAssembly.instantiateStreaming(fetch(path), go.importObject).then(function (obj) {
run(obj)
})
} else {
fetch(path).then(resp =>
resp.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
run(obj)
})
)
}
})
}
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
import { requestPVE, requestAPI, goToPage, getURIData, setAppearance, setIconSrc, requestDash, getCookie } from "./utils.js";
import { requestPVE, requestAPI, goToPage, getURIData, setAppearance, setIconSrc, requestDash } from "./utils.js";
import { error, dialog } from "./dialog.js";
window.addEventListener("DOMContentLoaded", init);
@@ -49,7 +49,7 @@ class VolumeAction extends HTMLElement {
async setStatusLoading () {
const icon = document.querySelector(`img[data-volume="${this.dataset.volume}"]`);
setIconSrc(icon, "websrc/images/status/loading.svg");
setIconSrc(icon, "images/status/loading.svg");
}
async handleDiskDetach () {
@@ -221,7 +221,7 @@ async function handleCDAdd () {
}
});
const isos = await requestAPI(`/access/users/${getCookie("username")}/vm-isos`, "GET");
const isos = await requestAPI("/user/vm-isos", "GET");
const select = d.querySelector("#iso-select");
for (const iso of isos.data) {
@@ -248,7 +248,7 @@ class NetworkAction extends HTMLElement {
async setStatusLoading () {
const icon = document.querySelector(`img[data-network="${this.dataset.network}"]`);
setIconSrc(icon, "websrc/images/status/loading.svg");
setIconSrc(icon, "images/status/loading.svg");
}
async handleNetworkConfig () {
@@ -277,7 +277,7 @@ class NetworkAction extends HTMLElement {
const netID = this.dataset.network;
dialog(this.template, async (result, _form) => {
if (result === "confirm") {
setIconSrc(document.querySelector(`svg[data-network="${netID}"]`), "websrc/images/status/loading.svg");
setIconSrc(document.querySelector(`svg[data-network="${netID}"]`), "images/status/loading.svg");
const net = `${netID}`;
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/net/${net}/delete`, "DELETE");
if (result.status !== 200) {
@@ -350,7 +350,7 @@ class DeviceAction extends HTMLElement {
async setStatusLoading () {
const icon = document.querySelector(`img[data-device="${this.dataset.device}"]`);
setIconSrc(icon, "websrc/images/status/loading.svg");
setIconSrc(icon, "images/status/loading.svg");
}
async handleDeviceConfig () {
@@ -50,9 +50,9 @@ class ErrorDialog extends HTMLElement {
super();
this.shadowRoot = this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = `
<link rel="stylesheet" href="websrc/modules/w3.css">
<link rel="stylesheet" href="websrc/css/style.css">
<link rel="stylesheet" href="websrc/css/form.css">
<link rel="stylesheet" href="modules/w3.css">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/form.css">
<style>
#errors {
margin-bottom: 0;
@@ -1,5 +1,5 @@
const blank = document.createElement("img");
blank.src = "websrc/images/common/blank.png"; // for whatever reason an svg does NOT work here
blank.src = "images/common/blank.png"; // for whatever reason an svg does NOT work here
class DraggableContainer extends HTMLElement {
shadowRoot = null;
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff. Show more