Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6b49f0c85 | ||
|
|
b38c381cf0 | ||
|
|
33766d1205 | ||
|
|
a13331e5a5 | ||
|
|
40dc935eea | ||
|
|
382d207e0e | ||
|
|
3eca3da157 | ||
|
|
4743bcf756 | ||
|
|
73b58a843d | ||
|
|
e0ef1b598b | ||
|
|
f6164b3096 | ||
|
|
5c658db34e | ||
|
|
98790dbe3b | ||
|
|
db8ed2e830 | ||
|
|
f3aa769fb7 | ||
|
|
b92aabb052 | ||
|
|
77f26a3e2c | ||
|
|
9458840292 | ||
|
|
ed4cf064e3 | ||
|
|
c816d15b1a | ||
|
|
2fced6bd11 | ||
|
|
b2bb8ae69e | ||
|
|
c5067f1696 | ||
|
|
de8f5368a5 | ||
|
|
2a6641f03c | ||
|
|
a5b20519d4 | ||
|
|
e000a90456 | ||
|
|
b5c8576ab5 | ||
|
|
e2039a15b1 | ||
|
|
492ae56d48 | ||
|
|
033017864e |
No files matched your search
@@ -1,27 +1,26 @@
|
||||
.PHONY: build build-web build-wfa-js clean clean-wfa-js ensure-dist workflow-init
|
||||
.PHONY: test build build-wfa clean clean-wfa clean-web
|
||||
|
||||
build: clean ensure-dist build-web build-wfa-js
|
||||
build: clean ensure-dist build-wfa
|
||||
@echo "======================== Building Binary ======================="
|
||||
# resolve symbolic links in web by copying it into dist/web/
|
||||
CGO_ENABLED=0 go build -tags release -ldflags="-s -w" -v -o dist/ .
|
||||
GODEBUG=embedfollowsymlinks=1 CGO_ENABLED=0 go build -tags release -ldflags="-s -w" -v -o dist/ .
|
||||
|
||||
build-web: ensure-dist
|
||||
cp -rL web/ dist/web/
|
||||
test:
|
||||
GODEBUG=embedfollowsymlinks=1 CGO_ENABLED=0 go run .
|
||||
|
||||
build-wfa-js: ensure-dist
|
||||
$(MAKE) -C WFA-JS
|
||||
cp -f WFA-JS/dist/* web/modules
|
||||
|
||||
clean: clean-wfa-js
|
||||
clean: clean-wfa clean-web
|
||||
@echo "======================== Cleaning Project ======================"
|
||||
go clean
|
||||
rm -rf dist/*
|
||||
|
||||
clean-wfa-js:
|
||||
$(MAKE) clean -C WFA-JS
|
||||
|
||||
ensure-dist:
|
||||
mkdir -p dist
|
||||
|
||||
workflow-init: ensure-dist build-web
|
||||
cp -r dev_config/. .
|
||||
clean-web:
|
||||
|
||||
build-wfa:
|
||||
$(MAKE) -C WFA-JS
|
||||
cp -f WFA-JS/dist/* app/web/modules
|
||||
|
||||
clean-wfa:
|
||||
$(MAKE) clean -C WFA-JS
|
||||
@@ -13,19 +13,19 @@ 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 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 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 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.
|
||||
|
||||
We will assume different hosts for each component which are accessible by unique host names and public URL addresses. Below is a table of references in the setup instructions across all components. You will need to substitute real addresses and hostnames for these in configurations.
|
||||
|
||||
| component | internal address | external address |
|
||||
| --- | --- | --- |
|
||||
| Proxmox VE API | pve.local | pve.domain.net |
|
||||
| ProxmoxAAS-Dashboard | dashboard.local | paas.domain.net |
|
||||
| ProxmoxAAS-API | api.local | paas.domain.net/api/ |
|
||||
| ProxmoxAAS-Fabric | fabric.local | N/A |
|
||||
| access-manager-api | access.local | N/A|
|
||||
| component | internal address | external address | default port |
|
||||
| --- | --- | --- | --- |
|
||||
| Proxmox VE API | pve.local | pve.domain.net | 8006 |
|
||||
| ProxmoxAAS-Dashboard | dashboard.local | paas.domain.net | 8080 |
|
||||
| ProxmoxAAS-API | api.local | paas.domain.net/api | 8081 |
|
||||
| ProxmoxAAS-Fabric | fabric.local | N/A | 8082 |
|
||||
| access-manager-api | access.local | N/A| 8083 |
|
||||
|
||||
## Prerequisites - Dashboard
|
||||
- Proxmox VE Cluster (v7.0+)
|
||||
@@ -47,18 +47,18 @@ After this step, the Dashboard should be available on the `ProxmoxAAS-Dashboard`
|
||||
|
||||
## Installation - API
|
||||
|
||||
To install the API component, go to [ProxmoxAAS-API](https://git.tronnet.net/tronnet/ProxmoxAAS-API). This is required for the app to function. The API installation will also have steps for setting up the reverse proxy server.
|
||||
To install the API component, go to [ProxmoxAAS-API](https://git.tronnet.net/tronnet/ProxmoxAAS-API). This is required for the app to function.
|
||||
|
||||
## Installation - Fabric
|
||||
|
||||
To install the Fabric component, go to [ProxmoxAAS-Fabric](https://git.tronnet.net/tronnet/ProxmoxAAS-Fabric). This is required for the app to function. The Fabric installation will also have steps for setting up the reverse proxy server.
|
||||
To install the Fabric component, go to [ProxmoxAAS-Fabric](https://git.tronnet.net/tronnet/ProxmoxAAS-Fabric). This is required for the app to function.
|
||||
|
||||
## Installation - access-manager-api
|
||||
|
||||
To install the access-manager-api component, go to [access-manager-api](https://git.tronnet.net/tronnet/access-manager-api). This is required for the app to function. The access-manager-api installation will also have steps for setting up the reverse proxy server.
|
||||
To install the access-manager-api component, go to [access-manager-api](https://git.tronnet.net/tronnet/access-manager-api). This is required for the app to function.
|
||||
|
||||
## Installation - Reverse Proxy
|
||||
1. Configure nginx or preferred reverse proxy to reverse proxy the dashboard. The configuration should include at least the following, ensuring that the configured ports are adjusted appropriately:
|
||||
1. Configure nginx or preferred reverse proxy to reverse proxy the dashboard. A snippet is show for the minimal configuration assuming the default ports.
|
||||
```
|
||||
server {
|
||||
listen 443 ssl;
|
||||
@@ -75,4 +75,4 @@ server {
|
||||
```
|
||||
2. Start nginx with the new configurations
|
||||
|
||||
The dashboard, API, and Fabric should be avaliable and fully functional.
|
||||
The PAAS suite should be avaliable and fully functional.
|
||||
+1
-1
Submodule WFA-JS updated: f53f344fb3...b0aacd5f9c.
+38
-54
@@ -4,71 +4,55 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"proxmoxaas-dashboard/app/common"
|
||||
"proxmoxaas-dashboard/app/routes"
|
||||
"web" // go will complain here until the first build
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tdewolff/minify/v2"
|
||||
"proxmoxaas-dashboard/app/web"
|
||||
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
|
||||
)
|
||||
|
||||
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()
|
||||
ServeStatic(router, m)
|
||||
html := common.MinifyStatic(m, web.Templates)
|
||||
common.TMPL = common.LoadHTMLToGin(router, html)
|
||||
|
||||
// dynamic routes for pages and page fragments
|
||||
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)
|
||||
|
||||
// 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))
|
||||
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
|
||||
html := common.MinifyStatic(m, web.Templates)
|
||||
common.TMPL = common.LoadHTML(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)
|
||||
|
||||
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))
|
||||
}
|
||||
@@ -11,6 +11,7 @@ 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
|
||||
}
|
||||
|
||||
@@ -44,6 +45,11 @@ var MimeTypes = map[string]MimeType{
|
||||
Type: "application/wasm",
|
||||
Minifier: nil,
|
||||
},
|
||||
"gz": {
|
||||
Type: "application/wasm",
|
||||
Encoding: "gzip",
|
||||
Minifier: nil,
|
||||
},
|
||||
"*": {
|
||||
Type: "text/plain",
|
||||
Minifier: nil,
|
||||
|
||||
@@ -15,6 +15,7 @@ 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
|
||||
}
|
||||
|
||||
@@ -48,6 +49,11 @@ var MimeTypes = map[string]MimeType{
|
||||
Type: "application/wasm",
|
||||
Minifier: nil,
|
||||
},
|
||||
"gz": {
|
||||
Type: "application/wasm",
|
||||
Encoding: "gzip",
|
||||
Minifier: nil,
|
||||
},
|
||||
"*": {
|
||||
Type: "text/plain",
|
||||
Minifier: nil,
|
||||
|
||||
+43
-26
@@ -16,10 +16,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tdewolff/minify/v2"
|
||||
|
||||
paas "proxmoxaas-common-lib"
|
||||
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
|
||||
)
|
||||
|
||||
// get config file from configPath
|
||||
@@ -103,9 +102,8 @@ func MinifyStatic(m *minify.M, files embed.FS) map[string]StaticFile {
|
||||
}
|
||||
}
|
||||
|
||||
func LoadHTMLToGin(engine *gin.Engine, html map[string]StaticFile) *template.Template {
|
||||
root := template.New("")
|
||||
engine.FuncMap = template.FuncMap{
|
||||
func LoadHTML(html map[string]StaticFile) *template.Template {
|
||||
funcMap := template.FuncMap{
|
||||
"MapKeys": func(x any, sep string) string {
|
||||
v := reflect.ValueOf(x)
|
||||
keys := v.MapKeys()
|
||||
@@ -133,9 +131,11 @@ func LoadHTMLToGin(engine *gin.Engine, html map[string]StaticFile) *template.Tem
|
||||
return dict, nil
|
||||
},
|
||||
}
|
||||
tmpl := template.Must(root, LoadAndAddToRoot(engine.FuncMap, root, html))
|
||||
engine.SetHTMLTemplate(root)
|
||||
return tmpl
|
||||
root := template.New("").Funcs(funcMap)
|
||||
if err := LoadAndAddToRoot(funcMap, root, html); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
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(c *gin.Context, err error) {
|
||||
func HandleNonFatalError(w http.ResponseWriter, err error) {
|
||||
log.Printf("[WARN] encountered a non-fatal error: %s", err.Error())
|
||||
c.Status(http.StatusInternalServerError)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func RequestGetAPI(path string, auth *paas.Auth, body any) (*http.Response, int, error) {
|
||||
@@ -221,25 +221,41 @@ func RequestGetAPI(path string, auth *paas.Auth, body any) (*http.Response, int,
|
||||
return response, response.StatusCode, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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 GetInstancePathFromRequest(c *gin.Context) (paas.InstancePath, error) {
|
||||
req_node := c.Query("node")
|
||||
req_type := c.Query("type")
|
||||
req_vmid := c.Query("vmid")
|
||||
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")
|
||||
|
||||
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)
|
||||
@@ -252,6 +268,7 @@ func GetInstancePathFromRequest(c *gin.Context) (paas.InstancePath, error) {
|
||||
InstanceType: paas.InstanceType(req_type),
|
||||
InstanceID: paas.InstanceID(vmid_int),
|
||||
}
|
||||
|
||||
return vm_path, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,10 @@ 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"
|
||||
)
|
||||
|
||||
@@ -40,29 +39,30 @@ var Green = color.RGB{
|
||||
B: 0,
|
||||
}
|
||||
|
||||
func HandleGETAccount(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETAccount(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil {
|
||||
user, err := GetUserBasic(auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
pools, err := GetUserPools(auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "html/account.html", gin.H{
|
||||
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/account.html", map[string]any{
|
||||
"global": common.Global,
|
||||
"page": "account",
|
||||
"user": user,
|
||||
"pools": pools,
|
||||
})
|
||||
} else {
|
||||
c.Redirect(http.StatusFound, "/login") // if user is not authed, redirect user to login page
|
||||
http.Redirect(w, r, "/login", http.StatusFound) // if user is not authed, redirect user to login page
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-23
@@ -3,11 +3,10 @@ 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"
|
||||
)
|
||||
|
||||
@@ -20,61 +19,60 @@ type InstanceBackup struct {
|
||||
TimeFormatted string
|
||||
}
|
||||
|
||||
func HandleGETBackups(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETBackups(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil {
|
||||
vm_path, err := common.GetInstancePathFromRequest(c)
|
||||
vm_path, err := common.GetInstancePathFromRequest(r)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
backups, err := GetInstanceBackups(vm_path, auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance backups: %s", err.Error()))
|
||||
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance backups: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
config, err := GetInstanceConfig(vm_path, auth) // only used for the VM's name
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "html/backups.html", gin.H{
|
||||
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/backups.html", map[string]any{
|
||||
"global": common.Global,
|
||||
"page": "backups",
|
||||
"backups": backups,
|
||||
"config": config,
|
||||
})
|
||||
} else {
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func HandleGETBackupsFragment(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETBackupsFragment(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil { // user should be authed, try to return index with population
|
||||
vm_path, err := common.GetInstancePathFromRequest(c)
|
||||
vm_path, err := common.GetInstancePathFromRequest(r)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
backups, err := GetInstanceBackups(vm_path, auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance backups: %s", err.Error()))
|
||||
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance backups: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "text/plain")
|
||||
err = common.TMPL.ExecuteTemplate(c.Writer, "html/backups-backups.go.tmpl", gin.H{
|
||||
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/backups-backups.go.tmpl", map[string]any{
|
||||
"backups": backups,
|
||||
})
|
||||
if err != nil {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
} else {
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
} else { // return 401
|
||||
c.Status(http.StatusUnauthorized)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+51
-65
@@ -3,12 +3,11 @@ 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"
|
||||
)
|
||||
|
||||
@@ -31,24 +30,26 @@ type PoolConfig struct {
|
||||
}
|
||||
}
|
||||
|
||||
func HandleGETConfig(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETConfig(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil {
|
||||
vm_path, err := common.GetInstancePathFromRequest(c)
|
||||
vm_path, err := common.GetInstancePathFromRequest(r)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := GetInstanceConfig(vm_path, auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
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(c, fmt.Errorf("error encountered getting proctypes: %s", err.Error()))
|
||||
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting proctypes: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
for i, cpu := range config.ProctypeSelect.Options {
|
||||
@@ -57,125 +58,110 @@ func HandleGETConfig(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "html/config.html", gin.H{
|
||||
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/config.html", map[string]any{
|
||||
"global": common.Global,
|
||||
"page": "config",
|
||||
"config": config,
|
||||
})
|
||||
} else {
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func HandleGETConfigVolumesFragment(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETConfigVolumesFragment(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil {
|
||||
vm_path, err := common.GetInstancePathFromRequest(c)
|
||||
vm_path, err := common.GetInstancePathFromRequest(r)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := GetInstanceConfig(vm_path, auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "text/plain")
|
||||
err = common.TMPL.ExecuteTemplate(c.Writer, "html/config-volumes.go.tmpl", gin.H{
|
||||
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/config-volumes.go.tmpl", map[string]any{
|
||||
"config": config,
|
||||
})
|
||||
if err != nil {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
} else {
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
} else {
|
||||
c.Status(http.StatusUnauthorized)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func HandleGETConfigNetsFragment(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETConfigNetsFragment(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil {
|
||||
vm_path, err := common.GetInstancePathFromRequest(c)
|
||||
vm_path, err := common.GetInstancePathFromRequest(r)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := GetInstanceConfig(vm_path, auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "text/plain")
|
||||
err = common.TMPL.ExecuteTemplate(c.Writer, "html/config-nets.go.tmpl", gin.H{
|
||||
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/config-nets.go.tmpl", map[string]any{
|
||||
"config": config,
|
||||
})
|
||||
if err != nil {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
} else {
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
} else {
|
||||
c.Status(http.StatusUnauthorized)
|
||||
} else { // return 401
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func HandleGETConfigDevicesFragment(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETConfigDevicesFragment(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil {
|
||||
vm_path, err := common.GetInstancePathFromRequest(c)
|
||||
vm_path, err := common.GetInstancePathFromRequest(r)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := GetInstanceConfig(vm_path, auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "text/plain")
|
||||
err = common.TMPL.ExecuteTemplate(c.Writer, "html/config-devices.go.tmpl", gin.H{
|
||||
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/config-devices.go.tmpl", map[string]any{
|
||||
"config": config,
|
||||
})
|
||||
if err != nil {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
} else {
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
} else {
|
||||
c.Status(http.StatusUnauthorized)
|
||||
} else { // return 401
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func HandleGETConfigBootFragment(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETConfigBootFragment(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil {
|
||||
vm_path, err := common.GetInstancePathFromRequest(c)
|
||||
vm_path, err := common.GetInstancePathFromRequest(r)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := GetInstanceConfig(vm_path, auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
common.HandleNonFatalError(w, fmt.Errorf("error encountered getting instance config: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "text/plain")
|
||||
err = common.TMPL.ExecuteTemplate(c.Writer, "html/config-boot.go.tmpl", gin.H{
|
||||
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/config-boot.go.tmpl", map[string]any{
|
||||
"config": config,
|
||||
})
|
||||
if err != nil {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
} else {
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
} else {
|
||||
c.Status(http.StatusUnauthorized)
|
||||
} else { // return 401
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-20
@@ -3,11 +3,10 @@ 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"
|
||||
)
|
||||
|
||||
@@ -45,46 +44,43 @@ type InstanceStatus struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
func HandleGETIndex(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETIndex(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil { // user should be authed, try to return index with population
|
||||
instances, _, err := GetClusterResources(auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
page := gin.H{
|
||||
page := map[string]any{
|
||||
"global": common.Global,
|
||||
"page": "index",
|
||||
"instances": instances,
|
||||
}
|
||||
c.HTML(http.StatusOK, "html/index.html", page)
|
||||
|
||||
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/index.html", page)
|
||||
} else { // return index without populating
|
||||
c.Redirect(http.StatusFound, "/login") // if user is not authed, redirect user to login page
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func HandleGETInstancesFragment(c *gin.Context) {
|
||||
auth, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETInstancesFragment(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil { // user should be authed, try to return index with population
|
||||
instances, _, err := GetClusterResources(auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
c.Header("Content-Type", "text/plain")
|
||||
err = common.TMPL.ExecuteTemplate(c.Writer, "html/index-instances.go.tmpl", gin.H{
|
||||
|
||||
paas.HTTPExecuteHTMLFragmentTemplate(w, common.TMPL, "html/index-instances.go.tmpl", map[string]any{
|
||||
"instances": instances,
|
||||
})
|
||||
if err != nil {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
} else {
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
} else { // return 401
|
||||
c.Status(http.StatusUnauthorized)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func GetClusterResources(auth paas.Auth) (map[uint]InstanceCard, map[string]Node, error) {
|
||||
|
||||
+5
-4
@@ -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,10 +41,11 @@ func GetLoginRealms() ([]Realm, error) {
|
||||
return realms, nil
|
||||
}
|
||||
|
||||
func HandleGETLogin(c *gin.Context) {
|
||||
func HandleGETLogin(w http.ResponseWriter, r *http.Request) {
|
||||
realms, err := GetLoginRealms()
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(c, err)
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
sel := common.Select{
|
||||
@@ -60,7 +61,7 @@ func HandleGETLogin(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "html/login.html", gin.H{
|
||||
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/login.html", map[string]any{
|
||||
"global": common.Global,
|
||||
"page": "login",
|
||||
"realms": sel,
|
||||
|
||||
+12
-6
@@ -3,18 +3,24 @@ package routes
|
||||
import (
|
||||
"net/http"
|
||||
"proxmoxaas-dashboard/app/common"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
paas "proxmoxaas-dashboard/proxmoxaas-common-lib"
|
||||
)
|
||||
|
||||
func HandleGETSettings(c *gin.Context) {
|
||||
_, err := common.GetAuthFromRequest(c)
|
||||
func HandleGETSettings(w http.ResponseWriter, r *http.Request) {
|
||||
auth, err := common.GetAuthFromRequest(r)
|
||||
if err == nil {
|
||||
c.HTML(http.StatusOK, "html/settings.html", gin.H{
|
||||
_, err := GetUserBasic(auth)
|
||||
if err != nil {
|
||||
common.HandleNonFatalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
paas.HTTPExecuteHTMLTemplate(w, common.TMPL, "html/settings.html", map[string]any{
|
||||
"global": common.Global,
|
||||
"page": "settings",
|
||||
})
|
||||
} else {
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -202,7 +202,7 @@ hr {
|
||||
}
|
||||
|
||||
/* fixes edge case in w3-hide-small when width between 600 and 601 */
|
||||
@media screen and (width <=601px) {
|
||||
@media screen and (width <= 601px) and (width >= 440px) {
|
||||
.hide-large {display: none !important;}
|
||||
.hide-medium {display:none !important}
|
||||
.hide-small {display:none !important}
|
||||
@@ -5,16 +5,11 @@ import (
|
||||
)
|
||||
|
||||
//go:embed css/*
|
||||
var CSS_fs embed.FS
|
||||
|
||||
//go:embed images/*
|
||||
var Images_fs embed.FS
|
||||
|
||||
//go:embed images/*/*/*
|
||||
//go:embed modules/*
|
||||
var Modules_fs embed.FS
|
||||
|
||||
//go:embed scripts/*
|
||||
var Scripts_fs embed.FS
|
||||
var Static embed.FS
|
||||
|
||||
//go:embed html/*
|
||||
//go:embed templates/*
|
||||
@@ -2,9 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
{{template "head" .}}
|
||||
<script src="scripts/account.js" type="module"></script>
|
||||
<link rel="modulepreload" href="scripts/utils.js">
|
||||
<link rel="modulepreload" href="scripts/dialog.js">
|
||||
<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">
|
||||
<style>
|
||||
@media screen and (width >= 1264px){
|
||||
.resource-container {
|
||||
@@ -48,9 +48,9 @@
|
||||
{{end}}
|
||||
</main>
|
||||
<template id="change-password-dialog">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/form.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<link rel="stylesheet" href="websrc/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="scripts/backups.js" type="module"></script>
|
||||
<link rel="modulepreload" href="scripts/utils.js">
|
||||
<link rel="modulepreload" href="scripts/dialog.js">
|
||||
<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">
|
||||
<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="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">
|
||||
<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">
|
||||
<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="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">
|
||||
<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">
|
||||
<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="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="websrc/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="images/actions/instance/add.svg#symb">
|
||||
<img class="small" style="height: 1lh; width: 1lh;" alt="Create Instance" aria-label="Create Instance" src="websrc/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="scripts/login.js" type="module"></script>
|
||||
<link rel="modulepreload" href="scripts/utils.js">
|
||||
<link rel="modulepreload" href="scripts/dialog.js">
|
||||
<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">
|
||||
</head>
|
||||
<body>
|
||||
{{template "header" .}}
|
||||
@@ -2,8 +2,8 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
{{template "head" .}}
|
||||
<script src="scripts/settings.js" type="module"></script>
|
||||
<link rel="modulepreload" href="scripts/utils.js">
|
||||
<script src="websrc/scripts/settings.js" type="module"></script>
|
||||
<link rel="modulepreload" href="websrc/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.
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.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,604 @@
|
||||
// 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)
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
Executable
BIN
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 } from "./utils.js";
|
||||
import { requestPVE, requestAPI, goToPage, getURIData, setAppearance, setIconSrc, requestDash, getCookie } 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, "images/status/loading.svg");
|
||||
setIconSrc(icon, "websrc/images/status/loading.svg");
|
||||
}
|
||||
|
||||
async handleDiskDetach () {
|
||||
@@ -221,7 +221,7 @@ async function handleCDAdd () {
|
||||
}
|
||||
});
|
||||
|
||||
const isos = await requestAPI("/user/vm-isos", "GET");
|
||||
const isos = await requestAPI(`/access/users/${getCookie("username")}/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, "images/status/loading.svg");
|
||||
setIconSrc(icon, "websrc/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}"]`), "images/status/loading.svg");
|
||||
setIconSrc(document.querySelector(`svg[data-network="${netID}"]`), "websrc/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, "images/status/loading.svg");
|
||||
setIconSrc(icon, "websrc/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="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/form.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<link rel="stylesheet" href="websrc/css/form.css">
|
||||
<style>
|
||||
#errors {
|
||||
margin-bottom: 0;
|
||||
@@ -1,5 +1,5 @@
|
||||
const blank = document.createElement("img");
|
||||
blank.src = "images/common/blank.png"; // for whatever reason an svg does NOT work here
|
||||
blank.src = "websrc/images/common/blank.png"; // for whatever reason an svg does NOT work here
|
||||
|
||||
class DraggableContainer extends HTMLElement {
|
||||
shadowRoot = null;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { requestPVE, requestAPI, setAppearance, getSetting, requestDash, setIconSrc, setIconAlt } from "./utils.js";
|
||||
import { requestPVE, requestAPI, setAppearance, getSetting, requestDash, setIconSrc, setIconAlt, getCookie } from "./utils.js";
|
||||
import { dialog, error } from "./dialog.js";
|
||||
import { setupClientSync } from "./clientsync.js";
|
||||
import wfaInit from "../modules/wfa.js";
|
||||
@@ -10,7 +10,7 @@ var wfa;
|
||||
async function init () {
|
||||
setAppearance();
|
||||
|
||||
wfa = await wfaInit("modules/wfa.wasm");
|
||||
wfa = await wfaInit("websrc/modules/wfa.wasm.gz");
|
||||
initInstances();
|
||||
|
||||
document.querySelector("#instance-add").addEventListener("click", handleInstanceAddButton);
|
||||
@@ -152,9 +152,9 @@ class InstanceCard extends HTMLElement {
|
||||
this.status = "loading";
|
||||
const statusicon = this.shadowRoot.querySelector("#status");
|
||||
const powerbtn = this.shadowRoot.querySelector("#power-btn");
|
||||
setIconSrc(statusicon, "images/status/loading.svg");
|
||||
setIconAlt(statusicon, "instance is loading");
|
||||
setIconSrc(powerbtn, "images/status/loading.svg");
|
||||
setIconSrc(statusicon, "websrc/images/status/loading.svg");
|
||||
setIconAlt(statusicon, "websrc/instance is loading");
|
||||
setIconSrc(powerbtn, "websrc/images/status/loading.svg");
|
||||
setIconAlt(powerbtn, "");
|
||||
}
|
||||
|
||||
@@ -268,15 +268,16 @@ function sortInstances () {
|
||||
else if (searchCriteria === "fuzzy") {
|
||||
const penalties = {
|
||||
m: 0,
|
||||
x: 1,
|
||||
o: 0,
|
||||
x: 2,
|
||||
o: 3,
|
||||
e: 1
|
||||
};
|
||||
criteria = (item, query) => {
|
||||
// lower is better
|
||||
const { score, CIGAR } = wfa.wfAlign(query, item, penalties, true);
|
||||
const alignment = wfa.DecodeCIGAR(CIGAR);
|
||||
return { score: score / item.length, alignment };
|
||||
const maxlength = Math.max(item.length, query.length);
|
||||
return { score: score / maxlength, alignment };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -418,7 +419,7 @@ async function handleInstanceAddButton () {
|
||||
// setup templateImage depending on selected image storage
|
||||
const templateImage = d.querySelector("#template-image");
|
||||
// add template images to selector
|
||||
const templates = await requestAPI("/user/ct-templates", "GET");
|
||||
const templates = await requestAPI(`/access/users/${getCookie("username")}/ct-templates`, "GET");
|
||||
for (const template of templates.data) {
|
||||
templateImage.append(new Option(template.name, template.volid));
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,8 +1,8 @@
|
||||
{{define "backup-card"}}
|
||||
<backup-card data-volid="{{.Volid}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
@@ -19,15 +19,15 @@
|
||||
<p class="w3-col l6 m6 hide-small">{{.Notes}}</p>
|
||||
<p class="w3-col l2 hide-medium">{{.SizeFormatted}}</p>
|
||||
<div class="w3-col l2 m2 s4 flex row nowrap" style="height: 1lh;">
|
||||
<img id="edit-btn" class="clickable" alt="change notes" src="images/actions/backups/config.svg#symb">
|
||||
<img id="delete-btn" class="clickable" alt="delete backup" role="button" tabindex=0 src="images/actions/backups/delete-active.svg#symb">
|
||||
<img id="restore-btn" class="clickable" alt="restore from backup" role="button" tabindex=0 src="images/actions/backups/restore.svg#symb">
|
||||
<img id="edit-btn" class="clickable" alt="change notes" src="websrc/images/actions/backups/config.svg#symb">
|
||||
<img id="delete-btn" class="clickable" alt="delete backup" role="button" tabindex=0 src="websrc/images/actions/backups/delete-active.svg#symb">
|
||||
<img id="restore-btn" class="clickable" alt="restore from backup" role="button" tabindex=0 src="websrc/images/actions/backups/restore.svg#symb">
|
||||
</div>
|
||||
</div>
|
||||
<template id="edit-dialog">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/form.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<link rel="stylesheet" href="websrc/css/form.css">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
Edit Backup
|
||||
@@ -45,9 +45,9 @@
|
||||
</dialog>
|
||||
</template>
|
||||
<template id="delete-dialog">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/form.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<link rel="stylesheet" href="websrc/css/form.css">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
Delete Backup
|
||||
@@ -66,9 +66,9 @@
|
||||
</dialog>
|
||||
</template>
|
||||
<template id="restore-dialog">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/form.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<link rel="stylesheet" href="websrc/css/form.css">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
Restore From Backup?
|
||||
@@ -96,12 +96,12 @@
|
||||
{{define "backups-add-backup"}}
|
||||
<button type="button" id="backup-add" class="w3-button" aria-label="Create Backup">
|
||||
<span class="large" style="margin: 0;">Create Backup</span>
|
||||
<img class="small" role="img" style="height: 1lh; width: 1lh;" alt="Create Backup" src="images/actions/network/add.svg#symb">
|
||||
<img class="small" role="img" style="height: 1lh; width: 1lh;" alt="Create Backup" src="websrc/images/actions/network/add.svg#symb">
|
||||
</button>
|
||||
<template id="create-backup-dialog">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/form.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<link rel="stylesheet" href="websrc/css/form.css">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
Create Backup
|
||||
@@ -3,16 +3,16 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.global.Organization}} - dashboard</title>
|
||||
<link rel="icon" href="images/favicon.svg" sizes="any" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="icon" href="websrc/images/favicon.svg" sizes="any" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<link rel="stylesheet" href="websrc/css/nav.css">
|
||||
<link rel="stylesheet" href="websrc/css/form.css">
|
||||
<script>
|
||||
window.PVE = "{{.global.PVE}}";
|
||||
window.API = "{{.global.API}}";
|
||||
window.DASH = "{{.global.DASH}}";
|
||||
</script>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/nav.css">
|
||||
<link rel="stylesheet" href="css/form.css">
|
||||
{{end}}
|
||||
|
||||
{{/* <header> common across all pages */}}
|
||||
@@ -1,26 +1,26 @@
|
||||
{{define "proctype-input"}}
|
||||
<img alt="CPU Type" src="images/resources/cpu.svg#symb">
|
||||
<img alt="CPU Type" src="websrc/images/resources/cpu.svg#symb">
|
||||
<label for="proctype">CPU Type</label>
|
||||
{{template "select" .}}
|
||||
<div></div>
|
||||
{{end}}
|
||||
|
||||
{{define "cores-input"}}
|
||||
<img alt="CPU Amount" src="images/resources/cpu.svg#symb">
|
||||
<img alt="CPU Amount" src="websrc/images/resources/cpu.svg#symb">
|
||||
<label for="cores">CPU Amount</label>
|
||||
<input id="cores" name="cores" class="w3-input w3-border" type="number" required value="{{.}}">
|
||||
<p>Cores</p>
|
||||
{{end}}
|
||||
|
||||
{{define "memory-input"}}
|
||||
<img alt="Memory Amount" src="images/resources/ram.svg#symb">
|
||||
<img alt="Memory Amount" src="websrc/images/resources/ram.svg#symb">
|
||||
<label for="ram">Memory</label>
|
||||
<input id="ram" name="ram" class="w3-input w3-border" type="number" required value="{{.}}">
|
||||
<p>MiB</p>
|
||||
{{end}}
|
||||
|
||||
{{define "swap-input"}}
|
||||
<img alt="Swap Amount" src="images/resources/swap.svg#symb">
|
||||
<img alt="Swap Amount" src="websrc/images/resources/swap.svg#symb">
|
||||
<label for="swap">Swap</label>
|
||||
<input id="swap" name="swap" class="w3-input w3-border" type="number" required value="{{.}}">
|
||||
<p>MiB</p>
|
||||
@@ -46,7 +46,7 @@
|
||||
{{define "volumes-add-disk"}}
|
||||
<button type="button" id="disk-add" class="w3-button" aria-label="Add New Disk">
|
||||
<span class="large" style="margin: 0;">Add Disk</span>
|
||||
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New Disk" src="images/actions/disk/add-disk.svg#symb">
|
||||
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New Disk" src="websrc/images/actions/disk/add-disk.svg#symb">
|
||||
</button>
|
||||
<template id="add-disk-dialog">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
@@ -75,7 +75,7 @@
|
||||
{{define "volumes-add-cd"}}
|
||||
<button type="button" id="cd-add" class="w3-button" aria-label="Add New CD">
|
||||
<span class="large" style="margin: 0;">Mount CD</span>
|
||||
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New CDROM" src="images/actions/disk/add-cd.svg#symb">
|
||||
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New CDROM" src="websrc/images/actions/disk/add-cd.svg#symb">
|
||||
</button>
|
||||
<template id="add-cd-dialog">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
@@ -97,7 +97,7 @@
|
||||
{{end}}
|
||||
|
||||
{{define "volume-rootfs"}}
|
||||
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||
<img data-volume={{.Name}} alt="Drive" src="websrc/images/resources/drive.svg#symb">
|
||||
<p>{{.Name}}</p>
|
||||
<p>{{.Volume.File}}</p>
|
||||
<div>
|
||||
@@ -109,7 +109,7 @@
|
||||
{{end}}
|
||||
|
||||
{{define "volume-mp"}}
|
||||
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||
<img data-volume={{.Name}} alt="Drive" src="websrc/images/resources/drive.svg#symb">
|
||||
<p>{{.Name}}</p>
|
||||
<p>{{.Volume.File}}</p>
|
||||
<div>
|
||||
@@ -121,7 +121,7 @@
|
||||
{{end}}
|
||||
|
||||
{{define "volume-ide"}}
|
||||
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||
<img data-volume={{.Name}} alt="Drive" src="websrc/images/resources/drive.svg#symb">
|
||||
<p>{{.Name}}</p>
|
||||
<p>{{.Volume.File}}</p>
|
||||
<div>
|
||||
@@ -133,7 +133,7 @@
|
||||
{{end}}
|
||||
|
||||
{{define "volume-scsi"}}
|
||||
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||
<img data-volume={{.Name}} alt="Drive" src="websrc/images/resources/drive.svg#symb">
|
||||
<p>{{.Name}}</p>
|
||||
<p>{{.Volume.File}}</p>
|
||||
<div>
|
||||
@@ -145,7 +145,7 @@
|
||||
{{end}}
|
||||
|
||||
{{define "volume-unused"}}
|
||||
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||
<img data-volume={{.Name}} alt="Drive" src="websrc/images/resources/drive.svg#symb">
|
||||
<p>{{.Name}}</p>
|
||||
<p>{{.Volume.File}}</p>
|
||||
<div>
|
||||
@@ -159,8 +159,8 @@
|
||||
{{define "volume-action-move"}}
|
||||
<volume-action data-type="move" data-volume="{{.Name}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img class="clickable" alt="Move {{.Name}}" src="images/actions/disk/move-active.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img class="clickable" alt="Move {{.Name}}" src="websrc/images/actions/disk/move-active.svg#symb">
|
||||
<template id="dialog-template">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
@@ -185,8 +185,8 @@
|
||||
{{define "volume-action-move-inactive"}}
|
||||
<volume-action data-type="none" data-volume="{{.Name}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img alt="" src="images/actions/disk/move-inactive.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img alt="" src="websrc/images/actions/disk/move-inactive.svg#symb">
|
||||
</template>
|
||||
</volume-action>
|
||||
{{end}}
|
||||
@@ -194,8 +194,8 @@
|
||||
{{define "volume-action-resize"}}
|
||||
<volume-action data-type="resize" data-volume="{{.Name}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img class="clickable" alt="Resize {{.Name}}" src="images/actions/disk/resize-active.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img class="clickable" alt="Resize {{.Name}}" src="websrc/images/actions/disk/resize-active.svg#symb">
|
||||
<template id="dialog-template">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
@@ -220,8 +220,8 @@
|
||||
{{define "volume-action-resize-inactive"}}
|
||||
<volume-action data-type="none" data-volume="{{.Name}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img alt="" src="images/actions/disk/resize-inactive.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img alt="" src="websrc/images/actions/disk/resize-inactive.svg#symb">
|
||||
</template>
|
||||
</volume-action>
|
||||
{{end}}
|
||||
@@ -229,8 +229,8 @@
|
||||
{{define "volume-action-delete"}}
|
||||
<volume-action data-type="delete" data-volume="{{.Name}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img class="clickable" alt="Delete {{.Name}}" src="images/actions/disk/delete-active.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img class="clickable" alt="Delete {{.Name}}" src="websrc/images/actions/disk/delete-active.svg#symb">
|
||||
<template id="dialog-template">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
@@ -254,8 +254,8 @@
|
||||
{{define "volume-action-delete-inactive"}}
|
||||
<volume-action data-type="none" data-volume="{{.Name}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img alt="" src="images/actions/disk/delete-inactive.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img alt="" src="websrc/images/actions/disk/delete-inactive.svg#symb">
|
||||
</template>
|
||||
</volume-action>
|
||||
{{end}}
|
||||
@@ -263,8 +263,8 @@
|
||||
{{define "volume-action-attach"}}
|
||||
<volume-action data-type="attach" data-volume="{{.Name}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img class="clickable" alt="Attach {{.Name}}" src="images/actions/disk/attach.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img class="clickable" alt="Attach {{.Name}}" src="websrc/images/actions/disk/attach.svg#symb">
|
||||
<template id="dialog-template">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
@@ -296,8 +296,8 @@
|
||||
{{define "volume-action-detach"}}
|
||||
<volume-action data-type="detach" data-volume="{{.Name}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img class="clickable" alt="Detach {{.Name}}" src="images/actions/disk/detach.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img class="clickable" alt="Detach {{.Name}}" src="websrc/images/actions/disk/detach.svg#symb">
|
||||
<template id="dialog-template">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
@@ -321,8 +321,8 @@
|
||||
{{define "volume-action-none"}}
|
||||
<volume-action data-type="none">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img alt="" src="images/common/blank.svg">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img alt="" src="websrc/images/common/blank.svg">
|
||||
</template>
|
||||
</volume-action>
|
||||
{{end}}
|
||||
@@ -336,7 +336,7 @@
|
||||
{{define "nets-add-net"}}
|
||||
<button type="button" id="network-add" class="w3-button" aria-label="Add New Network Interface">
|
||||
<span class="large" style="margin: 0;">Add Network</span>
|
||||
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New Network Interface" src="images/actions/network/add.svg#symb">
|
||||
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New Network Interface" src="websrc/images/actions/network/add.svg#symb">
|
||||
</button>
|
||||
<template id="add-net-dialog">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
@@ -361,14 +361,14 @@
|
||||
{{end}}
|
||||
|
||||
{{define "net"}}
|
||||
<img data-network="{{.Net_ID}}" alt="Net {{.Net_ID}}" src="images/resources/network.svg#symb">
|
||||
<img data-network="{{.Net_ID}}" alt="Net {{.Net_ID}}" src="websrc/images/resources/network.svg#symb">
|
||||
<p>{{.Net_ID}}</p>
|
||||
<p>{{.Value}}</p>
|
||||
<div>
|
||||
<network-action data-type="config" data-network="{{.Net_ID}}" data-value="{{.Value}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img class="clickable" alt="Configure Net {{.Net_ID}}" src="images/actions/network/config.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img class="clickable" alt="Configure Net {{.Net_ID}}" src="websrc/images/actions/network/config.svg#symb">
|
||||
<template id="dialog-template">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
@@ -389,8 +389,8 @@
|
||||
</network-action>
|
||||
<network-action data-type="delete" data-network="{{.Net_ID}}" data-value="{{.Value}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img class="clickable" alt="Delete Net {{.Net_ID}}" src="images/actions/network/delete-active.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img class="clickable" alt="Delete Net {{.Net_ID}}" src="websrc/images/actions/network/delete-active.svg#symb">
|
||||
<template id="dialog-template">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
@@ -421,7 +421,7 @@
|
||||
{{define "devices-add-device"}}
|
||||
<button type="button" id="device-add" class="w3-button" aria-label="Add New PCIe Device">
|
||||
<span class="large" style="margin: 0;">Add Device</span>
|
||||
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New PCIe Device" src="images/actions/device/add.svg#symb">
|
||||
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New PCIe Device" src="websrc/images/actions/device/add.svg#symb">
|
||||
</button>
|
||||
<template id="add-device-dialog">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
@@ -444,14 +444,14 @@
|
||||
{{end}}
|
||||
|
||||
{{define "device"}}
|
||||
<img data-device="{{.Device_ID}}" alt="Device {{.Device_ID}}" src="images/resources/device.svg#symb">
|
||||
<img data-device="{{.Device_ID}}" alt="Device {{.Device_ID}}" src="websrc/images/resources/device.svg#symb">
|
||||
<p>{{.Device_ID}}</p>
|
||||
<p>{{.Device_Name}}</p>
|
||||
<div>
|
||||
<device-action data-type="config" data-device="{{.Device_ID}}" data-value="{{.Device_ID}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img class="clickable" alt="Configure Device {{.Device_ID}}" src="images/actions/device/config.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img class="clickable" alt="Configure Device {{.Device_ID}}" src="websrc/images/actions/device/config.svg#symb">
|
||||
<template id="dialog-template">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
@@ -472,8 +472,8 @@
|
||||
</device-action>
|
||||
<device-action data-type="delete" data-device="{{.Device_ID}}" data-value="{{.Device_ID}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<img class="clickable" alt="Delete Device {{.Device_ID}}" src="images/actions/device/delete-active.svg#symb">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<img class="clickable" alt="Delete Device {{.Device_ID}}" src="websrc/images/actions/device/delete-active.svg#symb">
|
||||
<template id="dialog-template">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
@@ -544,15 +544,15 @@
|
||||
{{define "boot-target"}}
|
||||
{{if .volume_id}}
|
||||
<div class="draggable-item" data-value="{{.volume_id}}" style="display: grid; grid-template-columns: auto auto 8ch 1fr; column-gap: 0.5em; align-items: center;">
|
||||
<img alt="Drag" src="images/actions/drag.svg#symb">
|
||||
<img alt="Volume" src="images/resources/drive.svg#symb">
|
||||
<img alt="Drag" src="websrc/images/actions/drag.svg#symb">
|
||||
<img alt="Volume" src="websrc/images/resources/drive.svg#symb">
|
||||
<p class="volume-id">{{.volume_id}}</p>
|
||||
<p class="volume-file">{{.file}}</p>
|
||||
</div>
|
||||
{{else if .net_id}}
|
||||
<div class="draggable-item" data-value="{{.net_id}}" style="display: grid; grid-template-columns: auto auto 8ch 1fr; column-gap: 0.5em; align-items: center;">
|
||||
<img alt="Drag" src="images/actions/drag.svg#symb">
|
||||
<img alt="Net" src="images/resources/network.svg#symb">
|
||||
<img alt="Drag" src="websrc/images/actions/drag.svg#symb">
|
||||
<img alt="Net" src="websrc/images/resources/network.svg#symb">
|
||||
<p class="volume-id">{{.net_id}}</p>
|
||||
<p class="volume-file">{{.value}}</p>
|
||||
</div>
|
||||
@@ -1,8 +1,8 @@
|
||||
{{define "instance-card"}}
|
||||
<instance-card data-type="{{.Type}}" data-status="{{.Status}}" data-vmid="{{.VMID}}" data-name="{{.Name}}" data-node="{{.Node}}" data-nodestatus="{{.NodeStatus}}">
|
||||
<template shadowrootmode="open">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
@@ -60,60 +60,60 @@
|
||||
<p class="hide-small">{{.Type}}</p>
|
||||
<div class="flex row nowrap hide-tiny">
|
||||
{{if eq .Status "running"}}
|
||||
<img id="status" alt="instance is running" src="images/status/active.svg#symb">
|
||||
<img id="status" alt="instance is running" src="websrc/images/status/active.svg#symb">
|
||||
{{else if eq .Status "stopped"}}
|
||||
<img id="status" alt="instance is stopped" src="images/status/inactive.svg#symb">
|
||||
<img id="status" alt="instance is stopped" src="websrc/images/status/inactive.svg#symb">
|
||||
{{else if eq .Status "loading"}}
|
||||
<img id="status" alt="instance is loading" src="images/status/loading.svg#symb">
|
||||
<img id="status" alt="instance is loading" src="websrc/images/status/loading.svg#symb">
|
||||
{{else}}
|
||||
<img id="status" alt="instance is loading" src="images/status/loading.svg#symb">
|
||||
<img id="status" alt="instance is loading" src="websrc/images/status/loading.svg#symb">
|
||||
{{end}}
|
||||
<p>{{.Status}}</p>
|
||||
</div>
|
||||
<p class="hide-medium">{{.Node}}</p>
|
||||
<div class="flex row nowrap hide-medium">
|
||||
{{if eq .NodeStatus "online"}}
|
||||
<img alt="node is online" src="images/status/active.svg#symb">
|
||||
<img alt="node is online" src="websrc/images/status/active.svg#symb">
|
||||
{{else if eq .NodeStatus "offline"}}
|
||||
<img alt="node is offline" src="images/status/inactive.svg#symb">
|
||||
<img alt="node is offline" src="websrc/images/status/inactive.svg#symb">
|
||||
{{else if eq .NodeStatus "unknown"}}
|
||||
<img alt="node is offline" src="images/status/inactive.svg#symb">
|
||||
<img alt="node is offline" src="websrc/images/status/inactive.svg#symb">
|
||||
{{else}}
|
||||
{{end}}
|
||||
<p>{{.NodeStatus}}</p>
|
||||
</div>
|
||||
<div class="flex row nowrap">
|
||||
{{if and (eq .NodeStatus "online") (eq .Status "running")}}
|
||||
<img id="power-btn" class="clickable" alt="shutdown instance" role="button" tabindex=0 src="images/actions/instance/stop.svg#symb">
|
||||
<img id="configure-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/config-inactive.svg#symb">
|
||||
<img id="backup-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/backup-inactive.svg#symb">
|
||||
<img id="power-btn" class="clickable" alt="shutdown instance" role="button" tabindex=0 src="websrc/images/actions/instance/stop.svg#symb">
|
||||
<img id="configure-btn" alt="" aria-disabled="true" role="none" src="websrc/images/actions/instance/config-inactive.svg#symb">
|
||||
<img id="backup-btn" alt="" aria-disabled="true" role="none" src="websrc/images/actions/instance/backup-inactive.svg#symb">
|
||||
<a href="{{.ConsolePath}}" target="_blank">
|
||||
<img id="console-btn" class="clickable" alt="open console" src="images/actions/instance/console-active.svg#symb">
|
||||
<img id="console-btn" class="clickable" alt="open console" src="websrc/images/actions/instance/console-active.svg#symb">
|
||||
</a>
|
||||
<img id="delete-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/delete-inactive.svg#symb">
|
||||
<img id="delete-btn" alt="" aria-disabled="true" role="none" src="websrc/images/actions/instance/delete-inactive.svg#symb">
|
||||
{{else if and (eq .NodeStatus "online") (eq .Status "stopped")}}
|
||||
<img id="power-btn" class="clickable" alt="start instance" role="button" tabindex=0 src="images/actions/instance/start.svg#symb">
|
||||
<img id="power-btn" class="clickable" alt="start instance" role="button" tabindex=0 src="websrc/images/actions/instance/start.svg#symb">
|
||||
<a href="{{.ConfigPath}}">
|
||||
<img id="configure-btn" class="clickable" alt="change configuration" src="images/actions/instance/config-active.svg#symb">
|
||||
<img id="configure-btn" class="clickable" alt="change configuration" src="websrc/images/actions/instance/config-active.svg#symb">
|
||||
</a>
|
||||
<a href="{{.BackupsPath}}">
|
||||
<img id="backup-btn" class="clickable" alt="manage backups" src="images/actions/instance/backup-active.svg#symb">
|
||||
<img id="backup-btn" class="clickable" alt="manage backups" src="websrc/images/actions/instance/backup-active.svg#symb">
|
||||
</a>
|
||||
<img id="console-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/console-inactive.svg#symb">
|
||||
<img id="delete-btn" class="clickable" alt="delete instance" role="button" tabindex=0 src="images/actions/instance/delete-active.svg#symb">
|
||||
<img id="console-btn" alt="" aria-disabled="true" role="none" src="websrc/images/actions/instance/console-inactive.svg#symb">
|
||||
<img id="delete-btn" class="clickable" alt="delete instance" role="button" tabindex=0 src="websrc/images/actions/instance/delete-active.svg#symb">
|
||||
{{else if and (eq .NodeStatus "online") (eq .Status "loading")}}
|
||||
<img id="power-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/loading.svg#symb">
|
||||
<img id="configure-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/config-inactive.svg#symb">
|
||||
<img id="backup-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/backup-inactive.svg#symb">
|
||||
<img id="console-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/console-inactive.svg#symb">
|
||||
<img id="delete-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/delete-inactive.svg#symb">
|
||||
<img id="power-btn" alt="" aria-disabled="true" role="none" src="websrc/images/actions/instance/loading.svg#symb">
|
||||
<img id="configure-btn" alt="" aria-disabled="true" role="none" src="websrc/images/actions/instance/config-inactive.svg#symb">
|
||||
<img id="backup-btn" alt="" aria-disabled="true" role="none" src="websrc/images/actions/instance/backup-inactive.svg#symb">
|
||||
<img id="console-btn" alt="" aria-disabled="true" role="none" src="websrc/images/actions/instance/console-inactive.svg#symb">
|
||||
<img id="delete-btn" alt="" aria-disabled="true" role="none" src="websrc/images/actions/instance/delete-inactive.svg#symb">
|
||||
{{else}}
|
||||
{{end}}
|
||||
</div>
|
||||
<template id="power-dialog">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/form.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<link rel="stylesheet" href="websrc/css/form.css">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
{{if eq .Status "running"}}
|
||||
@@ -140,9 +140,9 @@
|
||||
</dialog>
|
||||
</template>
|
||||
<template id="delete-dialog">
|
||||
<link rel="stylesheet" href="modules/w3.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/form.css">
|
||||
<link rel="stylesheet" href="websrc/modules/w3.css">
|
||||
<link rel="stylesheet" href="websrc/css/style.css">
|
||||
<link rel="stylesheet" href="websrc/css/form.css">
|
||||
<dialog class="w3-container w3-card w3-border-0">
|
||||
<p class="w3-large" id="prompt">
|
||||
Delete {{.VMID}}
|
||||
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user