Compare commits
24 Commits
d95a82f248
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 72b1179fc9 | |||
| 32c23daf9d | |||
| b86be4c749 | |||
| 338ea3342e | |||
| 3b7d3ba01f | |||
| 9e1fca8597 | |||
| c8ca49b85c | |||
| 2877f7709a | |||
| 7db0bea35c | |||
| ff98eb318e | |||
| 05ced39598 | |||
| 4e2b6278d8 | |||
| 75e098b7b4 | |||
| 8c378a3b49 | |||
| 3d5989a946 | |||
| 06afdcec37 | |||
| 2f21b23535 | |||
| e8dd28b519 | |||
| e0c7a53d85 | |||
| 118b7dac53 | |||
| db32f318b9 | |||
| c13a4c8539 | |||
| 8d490cd336 | |||
| 343c149330 |
Submodule ProxmoxAAS-Fabric updated: 9be70e5900...c28620324a
@@ -3,10 +3,9 @@ package app
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"proxmoxaas-dashboard/dist/web" // go will complain here until the first build
|
|
||||||
|
|
||||||
"proxmoxaas-dashboard/app/common"
|
"proxmoxaas-dashboard/app/common"
|
||||||
"proxmoxaas-dashboard/app/routes"
|
"proxmoxaas-dashboard/app/routes"
|
||||||
|
"proxmoxaas-dashboard/dist/web" // go will complain here until the first build
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/tdewolff/minify/v2"
|
"github.com/tdewolff/minify/v2"
|
||||||
@@ -15,6 +14,7 @@ import (
|
|||||||
func Run(configPath *string) {
|
func Run(configPath *string) {
|
||||||
common.Global = common.GetConfig(*configPath)
|
common.Global = common.GetConfig(*configPath)
|
||||||
|
|
||||||
|
// setup static resources
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
m := common.InitMinify()
|
m := common.InitMinify()
|
||||||
@@ -22,10 +22,11 @@ func Run(configPath *string) {
|
|||||||
html := common.MinifyStatic(m, web.Templates)
|
html := common.MinifyStatic(m, web.Templates)
|
||||||
common.TMPL = common.LoadHTMLToGin(router, html)
|
common.TMPL = common.LoadHTMLToGin(router, html)
|
||||||
|
|
||||||
router.GET("/account", routes.HandleGETAccount)
|
// dynamic routes for pages and page fragments
|
||||||
router.GET("/", routes.HandleGETIndex)
|
router.GET("/", routes.HandleGETIndex)
|
||||||
router.GET("/index", routes.HandleGETIndex)
|
router.GET("/index", routes.HandleGETIndex)
|
||||||
router.GET("/index/instances", routes.HandleGETInstancesFragment)
|
router.GET("/index/instances", routes.HandleGETInstancesFragment)
|
||||||
|
router.GET("/account", routes.HandleGETAccount)
|
||||||
router.GET("/config", routes.HandleGETConfig)
|
router.GET("/config", routes.HandleGETConfig)
|
||||||
router.GET("/config/volumes", routes.HandleGETConfigVolumesFragment)
|
router.GET("/config/volumes", routes.HandleGETConfigVolumesFragment)
|
||||||
router.GET("/config/nets", routes.HandleGETConfigNetsFragment)
|
router.GET("/config/nets", routes.HandleGETConfigNetsFragment)
|
||||||
@@ -36,9 +37,11 @@ func Run(configPath *string) {
|
|||||||
router.GET("/login", routes.HandleGETLogin)
|
router.GET("/login", routes.HandleGETLogin)
|
||||||
router.GET("/settings", routes.HandleGETSettings)
|
router.GET("/settings", routes.HandleGETSettings)
|
||||||
|
|
||||||
|
// run on all interfaces with port
|
||||||
log.Fatal(router.Run(fmt.Sprintf("0.0.0.0:%d", common.Global.Port)))
|
log.Fatal(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) {
|
func ServeStatic(router *gin.Engine, m *minify.M) {
|
||||||
css := common.MinifyStatic(m, web.CSS_fs)
|
css := common.MinifyStatic(m, web.CSS_fs)
|
||||||
router.GET("/css/*css", func(c *gin.Context) {
|
router.GET("/css/*css", func(c *gin.Context) {
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ var MimeTypes = map[string]MimeType{
|
|||||||
Type: "image/svg+xml",
|
Type: "image/svg+xml",
|
||||||
Minifier: svg.Minify,
|
Minifier: svg.Minify,
|
||||||
},
|
},
|
||||||
|
"png": {
|
||||||
|
Type: "image/png",
|
||||||
|
Minifier: nil,
|
||||||
|
},
|
||||||
"js": {
|
"js": {
|
||||||
Type: "application/javascript",
|
Type: "application/javascript",
|
||||||
Minifier: js.Minify,
|
Minifier: js.Minify,
|
||||||
@@ -75,6 +79,10 @@ var MimeTypes = map[string]MimeType{
|
|||||||
Type: "image/svg+xml",
|
Type: "image/svg+xml",
|
||||||
Minifier: nil,
|
Minifier: nil,
|
||||||
},
|
},
|
||||||
|
"png": {
|
||||||
|
Type: "image/png",
|
||||||
|
Minifier: nil,
|
||||||
|
},
|
||||||
"js": {
|
"js": {
|
||||||
Type: "application/javascript",
|
Type: "application/javascript",
|
||||||
Minifier: nil,
|
Minifier: nil,
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
|
import "html/template"
|
||||||
|
|
||||||
|
var Global Config
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Port int `json:"listenPort"`
|
Port int `json:"listenPort"`
|
||||||
Organization string `json:"organization"`
|
Organization string `json:"organization"`
|
||||||
@@ -8,11 +12,23 @@ type Config struct {
|
|||||||
API string `json:"apiurl"`
|
API string `json:"apiurl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// variable for html template root
|
||||||
|
// generated from LoadHTMLToGin
|
||||||
|
var TMPL *template.Template
|
||||||
|
|
||||||
|
// static served file type containing data and mimetype
|
||||||
type StaticFile struct {
|
type StaticFile struct {
|
||||||
Data string
|
Data string
|
||||||
MimeType MimeType
|
MimeType MimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parsed vmpath data (ie node/type/vmid)
|
||||||
|
type VMPath struct {
|
||||||
|
Node string
|
||||||
|
Type string
|
||||||
|
VMID string
|
||||||
|
}
|
||||||
|
|
||||||
// type used for templated <select>
|
// type used for templated <select>
|
||||||
type Select struct {
|
type Select struct {
|
||||||
ID string
|
ID string
|
||||||
@@ -27,8 +43,6 @@ type Option struct {
|
|||||||
Display string
|
Display string
|
||||||
}
|
}
|
||||||
|
|
||||||
type RequestType int
|
|
||||||
|
|
||||||
type RequestContext struct {
|
type RequestContext struct {
|
||||||
Cookies map[string]string
|
Cookies map[string]string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,7 @@ import (
|
|||||||
"github.com/tdewolff/minify/v2"
|
"github.com/tdewolff/minify/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var TMPL *template.Template
|
// get config file from configPath
|
||||||
var Global Config
|
|
||||||
|
|
||||||
type VMPath struct {
|
|
||||||
Node string
|
|
||||||
Type string
|
|
||||||
VMID string
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetConfig(configPath string) Config {
|
func GetConfig(configPath string) Config {
|
||||||
content, err := os.ReadFile(configPath)
|
content, err := os.ReadFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -42,6 +34,7 @@ func GetConfig(configPath string) Config {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize minifier using the meta types specified
|
||||||
func InitMinify() *minify.M {
|
func InitMinify() *minify.M {
|
||||||
m := minify.New()
|
m := minify.New()
|
||||||
for _, v := range MimeTypes {
|
for _, v := range MimeTypes {
|
||||||
@@ -125,7 +118,7 @@ func LoadHTMLToGin(engine *gin.Engine, html map[string]StaticFile) *template.Tem
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
tmpl := template.Must(root, LoadAndAddToRoot(engine.FuncMap, root, html))
|
tmpl := template.Must(root, LoadAndAddToRoot(engine.FuncMap, root, html))
|
||||||
engine.SetHTMLTemplate(tmpl)
|
engine.SetHTMLTemplate(root)
|
||||||
return tmpl
|
return tmpl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,14 @@ type Account struct {
|
|||||||
Resources map[string]map[string]any
|
Resources map[string]map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// numerical constraint
|
||||||
type Constraint struct {
|
type Constraint struct {
|
||||||
Max int64
|
Max int64
|
||||||
Used int64
|
Used int64
|
||||||
Avail int64
|
Avail int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// match constraint
|
||||||
type Match struct {
|
type Match struct {
|
||||||
Name string
|
Name string
|
||||||
Match string
|
Match string
|
||||||
@@ -107,6 +109,7 @@ func HandleGETAccount(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for each resource category, create a resource chart
|
||||||
for category, resources := range account.Resources {
|
for category, resources := range account.Resources {
|
||||||
for resource, v := range resources {
|
for resource, v := range resources {
|
||||||
switch t := v.(type) {
|
switch t := v.(type) {
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"proxmoxaas-dashboard/app/common"
|
"proxmoxaas-dashboard/app/common"
|
||||||
|
fabric "proxmoxaas-fabric/app"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
fabric "proxmoxaas-fabric/app"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-viper/mapstructure/v2"
|
"github.com/go-viper/mapstructure/v2"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,13 +10,7 @@ import (
|
|||||||
"github.com/go-viper/mapstructure/v2"
|
"github.com/go-viper/mapstructure/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// used in constructing instance cards in index
|
// primary type used in constructing instance cards in index
|
||||||
type Node struct {
|
|
||||||
Node string `json:"node"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// used in constructing instance cards in index
|
|
||||||
type InstanceCard struct {
|
type InstanceCard struct {
|
||||||
VMID uint
|
VMID uint
|
||||||
Name string
|
Name string
|
||||||
@@ -29,6 +23,12 @@ type InstanceCard struct {
|
|||||||
BackupsPath string
|
BackupsPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// used in constructing instance cards in index
|
||||||
|
type Node struct {
|
||||||
|
Node string `json:"node"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
// used in retriving cluster tasks
|
// used in retriving cluster tasks
|
||||||
type Task struct {
|
type Task struct {
|
||||||
Type string
|
Type string
|
||||||
@@ -92,24 +92,24 @@ func GetClusterResources(auth common.Auth) (map[uint]InstanceCard, map[string]No
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if code != 200 { // if we did not successfully retrieve resources, then return 500 because auth was 1 but was invalid somehow
|
if code != 200 { // if we did not successfully retrieve resources, then return the error because auth was 1 but was invalid somehow
|
||||||
return nil, nil, fmt.Errorf("request to /cluster/resources resulted in %+v", res)
|
return nil, nil, fmt.Errorf("request to /cluster/resources resulted in %+v", res)
|
||||||
}
|
}
|
||||||
|
|
||||||
instances := map[uint]InstanceCard{}
|
instances := map[uint]InstanceCard{}
|
||||||
nodes := map[string]Node{}
|
nodes := map[string]Node{}
|
||||||
|
|
||||||
// if we successfully retrieved the resources, then process it and return index
|
// parse /proxmox/cluster/resources to separate instances and nodes
|
||||||
for _, v := range body["data"].([]any) {
|
for _, v := range body["data"].([]any) {
|
||||||
m := v.(map[string]any)
|
m := v.(map[string]any)
|
||||||
if m["type"] == "node" {
|
if m["type"] == "node" { // if type is node -> parse as Node object
|
||||||
node := Node{}
|
node := Node{}
|
||||||
err := mapstructure.Decode(v, &node)
|
err := mapstructure.Decode(v, &node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
nodes[node.Node] = node
|
nodes[node.Node] = node
|
||||||
} else if m["type"] == "lxc" || m["type"] == "qemu" {
|
} else if m["type"] == "lxc" || m["type"] == "qemu" { // if type is lxc or qemu -> parse as InstanceCard object
|
||||||
instance := InstanceCard{}
|
instance := InstanceCard{}
|
||||||
err := mapstructure.Decode(v, &instance)
|
err := mapstructure.Decode(v, &instance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -118,16 +118,21 @@ func GetClusterResources(auth common.Auth) (map[uint]InstanceCard, map[string]No
|
|||||||
instances[instance.VMID] = instance
|
instances[instance.VMID] = instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// once all basic instance and node stuff is parsed, go back and fill in cross referenced data
|
||||||
for vmid, instance := range instances {
|
for vmid, instance := range instances {
|
||||||
nodestatus := nodes[instance.Node].Status
|
// set instance's node status
|
||||||
instance.NodeStatus = nodestatus
|
instance.NodeStatus = nodes[instance.Node].Status
|
||||||
|
// set instance's config link path
|
||||||
instance.ConfigPath = fmt.Sprintf("config?node=%s&type=%s&vmid=%d", instance.Node, instance.Type, instance.VMID)
|
instance.ConfigPath = fmt.Sprintf("config?node=%s&type=%s&vmid=%d", instance.Node, instance.Type, instance.VMID)
|
||||||
|
// set the instance's console link path
|
||||||
if instance.Type == "qemu" {
|
if instance.Type == "qemu" {
|
||||||
instance.ConsolePath = fmt.Sprintf("%s/?console=kvm&vmid=%d&vmname=%s&node=%s&resize=off&cmd=&novnc=1", common.Global.PVE, instance.VMID, instance.Name, instance.Node)
|
instance.ConsolePath = fmt.Sprintf("%s/?console=kvm&vmid=%d&vmname=%s&node=%s&resize=off&cmd=&novnc=1", common.Global.PVE, instance.VMID, instance.Name, instance.Node)
|
||||||
} else if instance.Type == "lxc" {
|
} else if instance.Type == "lxc" {
|
||||||
instance.ConsolePath = fmt.Sprintf("%s/?console=lxc&vmid=%d&vmname=%s&node=%s&resize=off&cmd=&xtermjs=1", common.Global.PVE, instance.VMID, instance.Name, instance.Node)
|
instance.ConsolePath = fmt.Sprintf("%s/?console=lxc&vmid=%d&vmname=%s&node=%s&resize=off&cmd=&xtermjs=1", common.Global.PVE, instance.VMID, instance.Name, instance.Node)
|
||||||
}
|
}
|
||||||
|
// set the instance's backups link path
|
||||||
instance.BackupsPath = fmt.Sprintf("backups?node=%s&type=%s&vmid=%d", instance.Node, instance.Type, instance.VMID)
|
instance.BackupsPath = fmt.Sprintf("backups?node=%s&type=%s&vmid=%d", instance.Node, instance.Type, instance.VMID)
|
||||||
|
// save back to instances map
|
||||||
instances[vmid] = instance
|
instances[vmid] = instance
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +148,9 @@ func GetClusterResources(auth common.Auth) (map[uint]InstanceCard, map[string]No
|
|||||||
most_recent_task := map[uint]uint{}
|
most_recent_task := map[uint]uint{}
|
||||||
expected_state := map[uint]string{}
|
expected_state := map[uint]string{}
|
||||||
|
|
||||||
|
// iterate through recent user accessible tasks to find the task most recently made on an instance
|
||||||
for _, v := range body["data"].([]any) {
|
for _, v := range body["data"].([]any) {
|
||||||
|
// parse task as Task object
|
||||||
task := Task{}
|
task := Task{}
|
||||||
err := mapstructure.Decode(v, &task)
|
err := mapstructure.Decode(v, &task)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -177,6 +184,7 @@ func GetClusterResources(auth common.Auth) (map[uint]InstanceCard, map[string]No
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// iterate through the instances with recent tasks, refetch their state from a more reliable source
|
||||||
for vmid, expected_state := range expected_state { // for the expected states from recent tasks
|
for vmid, expected_state := range expected_state { // for the expected states from recent tasks
|
||||||
if instances[vmid].Status != expected_state { // if the current node's state from /cluster/resources differs from expected state
|
if instances[vmid].Status != expected_state { // if the current node's state from /cluster/resources differs from expected state
|
||||||
// get /status/current which is updated faster than /cluster/resources
|
// get /status/current which is updated faster than /cluster/resources
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ func GetLoginRealms() ([]Realm, error) {
|
|||||||
|
|
||||||
ctx := common.RequestContext{
|
ctx := common.RequestContext{
|
||||||
Cookies: nil,
|
Cookies: nil,
|
||||||
//Body: map[string]any{},
|
|
||||||
}
|
}
|
||||||
body := map[string]any{}
|
body := map[string]any{}
|
||||||
res, code, err := common.RequestGetAPI("/proxmox/access/domains", ctx, &body)
|
res, code, err := common.RequestGetAPI("/proxmox/access/domains", ctx, &body)
|
||||||
|
|||||||
40
go.mod
40
go.mod
@@ -1,13 +1,12 @@
|
|||||||
module proxmoxaas-dashboard
|
module proxmoxaas-dashboard
|
||||||
|
|
||||||
go 1.25.1
|
go 1.25.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gerow/go-color v0.0.0-20140219113758-125d37f527f1
|
github.com/gerow/go-color v0.0.0-20140219113758-125d37f527f1
|
||||||
github.com/gin-gonic/gin v1.11.0
|
github.com/gin-gonic/gin v1.11.0
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0
|
github.com/go-viper/mapstructure/v2 v2.4.0
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible
|
github.com/tdewolff/minify/v2 v2.24.8
|
||||||
github.com/tdewolff/minify/v2 v2.24.3
|
|
||||||
proxmoxaas-fabric v0.0.0
|
proxmoxaas-fabric v0.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,18 +15,18 @@ replace proxmoxaas-fabric => ./ProxmoxAAS-Fabric
|
|||||||
require (
|
require (
|
||||||
github.com/buger/goterm v1.0.4 // indirect
|
github.com/buger/goterm v1.0.4 // indirect
|
||||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||||
github.com/bytedance/sonic v1.14.1 // indirect
|
github.com/bytedance/sonic v1.14.2 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
github.com/diskfs/go-diskfs v1.7.0 // indirect
|
github.com/diskfs/go-diskfs v1.7.0 // indirect
|
||||||
github.com/djherbis/times v1.6.0 // indirect
|
github.com/djherbis/times v1.6.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
github.com/goccy/go-yaml v1.19.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/jinzhu/copier v0.4.0 // indirect
|
github.com/jinzhu/copier v0.4.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
@@ -39,21 +38,16 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
github.com/quic-go/quic-go v0.54.0 // indirect
|
github.com/quic-go/quic-go v0.57.1 // indirect
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
github.com/tdewolff/parse/v2 v2.8.5 // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.8.3 // indirect
|
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||||
go.uber.org/mock v0.6.0 // indirect
|
go.uber.org/mock v0.6.0 // indirect
|
||||||
golang.org/x/arch v0.21.0 // indirect
|
golang.org/x/arch v0.23.0 // indirect
|
||||||
golang.org/x/crypto v0.42.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/mod v0.28.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/net v0.44.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
golang.org/x/sync v0.17.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
golang.org/x/sys v0.36.0 // indirect
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
golang.org/x/text v0.29.0 // indirect
|
|
||||||
golang.org/x/tools v0.37.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.36.9 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
input, select, textarea {
|
||||||
|
background-color: var(--main-input-bg-color);
|
||||||
|
color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
.input-grid {
|
.input-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 5px 10px;
|
gap: 5px 10px;
|
||||||
@@ -16,7 +21,7 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-grid svg {
|
.input-grid img {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +81,6 @@ input[type="radio"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dialog {
|
dialog {
|
||||||
max-width: calc(min(50%, 80ch));
|
max-width: calc(min(100% - 16px, 80ch));
|
||||||
color: var(--main-text-color);
|
color: var(--main-text-color);
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
--main-text-color: white;
|
--main-text-color: white;
|
||||||
--main-card-bg-color: #202020;
|
--main-card-bg-color: #202020;
|
||||||
--main-card-box-shadow: 0 2px 5px 0 rgb(0 0 0 / 80%), 0 2px 10px 0 rgb(0 0 0 / 80%);
|
--main-card-box-shadow: 0 2px 5px 0 rgb(0 0 0 / 80%), 0 2px 10px 0 rgb(0 0 0 / 80%);
|
||||||
--main-table-header-bg-color: black;
|
|
||||||
--main-input-bg-color: #404040;
|
--main-input-bg-color: #404040;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +19,6 @@
|
|||||||
--main-text-color: black;
|
--main-text-color: black;
|
||||||
--main-card-bg-color: white;
|
--main-card-bg-color: white;
|
||||||
--main-card-box-shadow: 0 2px 5px 0 rgb(0 0 0 / 20%), 0 2px 10px 0 rgb(0 0 0 / 20%);
|
--main-card-box-shadow: 0 2px 5px 0 rgb(0 0 0 / 20%), 0 2px 10px 0 rgb(0 0 0 / 20%);
|
||||||
--main-table-header-bg-color: #808080;
|
|
||||||
--main-input-bg-color: white;
|
--main-input-bg-color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +29,6 @@
|
|||||||
--main-text-color: black;
|
--main-text-color: black;
|
||||||
--main-card-bg-color: white;
|
--main-card-bg-color: white;
|
||||||
--main-card-box-shadow: 0 2px 5px 0 rgb(0 0 0 / 20%), 0 2px 10px 0 rgb(0 0 0 / 20%);
|
--main-card-box-shadow: 0 2px 5px 0 rgb(0 0 0 / 20%), 0 2px 10px 0 rgb(0 0 0 / 20%);
|
||||||
--main-table-header-bg-color: #808080;
|
|
||||||
--main-input-bg-color: white;
|
--main-input-bg-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,18 +37,17 @@
|
|||||||
--main-text-color: white;
|
--main-text-color: white;
|
||||||
--main-card-bg-color: #202020;
|
--main-card-bg-color: #202020;
|
||||||
--main-card-box-shadow: 0 2px 5px 0 rgb(0 0 0 / 80%), 0 2px 10px 0 rgb(0 0 0 / 80%);
|
--main-card-box-shadow: 0 2px 5px 0 rgb(0 0 0 / 80%), 0 2px 10px 0 rgb(0 0 0 / 80%);
|
||||||
--main-table-header-bg-color: black;
|
|
||||||
--main-input-bg-color: #404040;
|
--main-input-bg-color: #404040;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: var(--main-bg-color);
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
html {
|
||||||
font-family: monospace;
|
background-color: var(--main-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -77,33 +73,24 @@ main {
|
|||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
|
||||||
background-color: var(--main-table-header-bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
background-color: var(--main-card-bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
input, select, textarea {
|
|
||||||
background-color: var(--main-input-bg-color);
|
|
||||||
color: var(--main-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
img.clickable, svg.clickable {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
img, svg {
|
img, svg {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
color: var(--main-text-color)
|
color: var(--main-text-color)
|
||||||
}
|
}
|
||||||
|
|
||||||
hr, * {
|
a img {
|
||||||
|
vertical-align: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
border-color: var(--main-text-color);
|
border-color: var(--main-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@@ -114,6 +101,12 @@ hr, * {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-reverse {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
row-gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.wrap {
|
.wrap {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
row-gap: 10px;
|
row-gap: 10px;
|
||||||
@@ -156,18 +149,26 @@ hr, * {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* add hide large class similar to w3-hide-medium and w3-hide-small */
|
/* add hide large class similar to w3-hide-medium and w3-hide-small */
|
||||||
@media (width >=993px) {
|
@media screen and (width >=993px) {
|
||||||
.w3-hide-large {
|
.hide-large {display: none !important;}
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fix edge case in w3-hide-medium where width between 992 and 993 */
|
/* fixes edge case in w3-hide-medium where width between 992 and 993 */
|
||||||
@media (width <=993px) and (width >=601px){
|
@media screen and (width <=993px) and (width >=601px){
|
||||||
.w3-hide-medium{display:none!important}
|
.hide-large {display: none !important;}
|
||||||
|
.hide-medium {display:none !important}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fix edge case in w3-hide-small when width between 600 and 601 */
|
/* fixes edge case in w3-hide-small when width between 600 and 601 */
|
||||||
@media (width <=601px) {
|
@media screen and (width <=601px) {
|
||||||
.w3-hide-small{display:none!important}
|
.hide-large {display: none !important;}
|
||||||
|
.hide-medium {display:none !important}
|
||||||
|
.hide-small {display:none !important}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (width <= 440px) {
|
||||||
|
.hide-large {display: none !important;}
|
||||||
|
.hide-medium {display:none !important}
|
||||||
|
.hide-small {display:none !important}
|
||||||
|
.hide-tiny { display: none !important;}
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
<section class="w3-card w3-padding">
|
<section class="w3-card w3-padding">
|
||||||
<div class="w3-row" style="border-bottom: 1px solid;">
|
<div class="w3-row" style="border-bottom: 1px solid;">
|
||||||
<p class="w3-col l2 m4 s8">Time</p>
|
<p class="w3-col l2 m4 s8">Time</p>
|
||||||
<p class="w3-col l6 m6 w3-hide-small">Notes</p>
|
<p class="w3-col l6 m6 hide-small">Notes</p>
|
||||||
<p class="w3-col l2 w3-hide-medium w3-hide-small">Size</p>
|
<p class="w3-col l2 hide-medium">Size</p>
|
||||||
<p class="w3-col l2 m2 s4">Actions</p>
|
<p class="w3-col l2 m2 s4">Actions</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="backups-container">
|
<div id="backups-container">
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
<script src="scripts/config.js" type="module"></script>
|
<script src="scripts/config.js" type="module"></script>
|
||||||
<script src="scripts/draggable.js" type="module"></script>
|
<script src="scripts/draggable.js" type="module"></script>
|
||||||
<script src="modules/Sortable.min.js"></script>
|
<script src="modules/Sortable.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
|
|
||||||
<link rel="modulepreload" href="scripts/utils.js">
|
<link rel="modulepreload" href="scripts/utils.js">
|
||||||
<link rel="modulepreload" href="scripts/dialog.js">
|
<link rel="modulepreload" href="scripts/dialog.js">
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -8,12 +8,6 @@
|
|||||||
<link rel="modulepreload" href="scripts/dialog.js">
|
<link rel="modulepreload" href="scripts/dialog.js">
|
||||||
<link rel="modulepreload" href="scripts/clientsync.js">
|
<link rel="modulepreload" href="scripts/clientsync.js">
|
||||||
<style>
|
<style>
|
||||||
#instance-container > div {
|
|
||||||
border-bottom: 1px solid white;
|
|
||||||
}
|
|
||||||
#instance-container > div:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
@media screen and (width >= 440px) {
|
@media screen and (width >= 440px) {
|
||||||
#vm-search {
|
#vm-search {
|
||||||
max-width: calc(100% - 10px - 152px);
|
max-width: calc(100% - 10px - 152px);
|
||||||
@@ -24,6 +18,50 @@
|
|||||||
max-width: calc(100% - 10px - 47px);
|
max-width: calc(100% - 10px - 47px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media screen and (width >= 993px) {
|
||||||
|
#instance-table {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(7, auto);
|
||||||
|
grid-column-gap: 1em;
|
||||||
|
grid-row-gap: 0.25em;
|
||||||
|
}
|
||||||
|
#instance-table-header, #instance-container, #instance-table instance-card {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (width <= 993px) and (width >= 601px){
|
||||||
|
#instance-table {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, auto);
|
||||||
|
grid-column-gap: 1em;
|
||||||
|
grid-row-gap: 0.25em;
|
||||||
|
}
|
||||||
|
#instance-table-header, #instance-container, #instance-table instance-card {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (width <= 601px) and (width >= 440px){
|
||||||
|
#instance-table {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, auto);
|
||||||
|
grid-column-gap: 1em;
|
||||||
|
grid-row-gap: 0.25em;
|
||||||
|
}
|
||||||
|
#instance-table-header, #instance-container, #instance-table instance-card {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (width <= 440px) {
|
||||||
|
#instance-table {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, auto);
|
||||||
|
grid-column-gap: 1em;
|
||||||
|
grid-row-gap: 0.25em;
|
||||||
|
}
|
||||||
|
#instance-table-header, #instance-container, #instance-table instance-card {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -36,13 +74,13 @@
|
|||||||
<div class="w3-card w3-padding">
|
<div class="w3-card w3-padding">
|
||||||
<div class="flex row nowrap" style="margin-top: 1em; justify-content: space-between;">
|
<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">
|
<form id="vm-search" role="search" class="flex row nowrap" tabindex="0">
|
||||||
<svg role="img" aria-label="Search Instances"><use href="images/common/search.svg#symb"></use></svg>
|
<img alt="Search Instances" aria-label="Search Instances" src="images/common/search.svg#symb">
|
||||||
<input type="search" id="search" class="w3-input w3-border" style="height: 1lh; max-width: fit-content;" aria-label="search instances by name">
|
<input type="search" id="search" class="w3-input w3-border" style="height: 1lh; max-width: fit-content;" aria-label="search instances by name">
|
||||||
</form>
|
</form>
|
||||||
<!--Add Instance Button & Dialog Template-->
|
<!--Add Instance Button & Dialog Template-->
|
||||||
<button type="button" id="instance-add" class="w3-button" aria-label="create new instance">
|
<button type="button" id="instance-add" class="w3-button" aria-label="create new instance">
|
||||||
<span class="large" style="margin: 0;">Create Instance</span>
|
<span class="large" style="margin: 0;">Create Instance</span>
|
||||||
<svg class="small" style="height: 1lh; width: 1lh;" role="img" aria-label="Create New Instance"><use href="images/actions/instance/add.svg#symb"></use></svg>
|
<img class="small" style="height: 1lh; width: 1lh;" alt="Create Instance" aria-label="Create Instance" src="images/actions/instance/add.svg#symb">
|
||||||
</button>
|
</button>
|
||||||
<template id="create-instance-dialog">
|
<template id="create-instance-dialog">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
@@ -90,16 +128,17 @@
|
|||||||
</dialog>
|
</dialog>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div id="instance-table">
|
||||||
<div class="w3-row w3-hide-small" style="border-bottom: 1px solid;">
|
<div id="instance-table-header">
|
||||||
<p class="w3-col l1 m2 w3-hide-small">ID</p>
|
<p>ID</p>
|
||||||
<p class="w3-col l2 m3 w3-hide-small">Name</p>
|
<p>Name</p>
|
||||||
<p class="w3-col l1 m2 w3-hide-small">Type</p>
|
<p class="hide-tiny">Type</p>
|
||||||
<p class="w3-col l2 m3 w3-hide-small">Status</p>
|
<p class="hide-small">Status</p>
|
||||||
<p class="w3-col l2 w3-hide-medium w3-hide-small">Host Name</p>
|
<p class="hide-medium">Host Name</p>
|
||||||
<p class="w3-col l2 w3-hide-medium w3-hide-small">Host Status</p>
|
<p class="hide-medium">Host Status</p>
|
||||||
<p class="w3-col l2 m2 w3-hide-small">Actions</p>
|
<p>Actions</p>
|
||||||
</div>
|
</div>
|
||||||
|
<hr style="grid-column: 1 / -1; padding: 0; margin: 0;">
|
||||||
<div id="instance-container">
|
<div id="instance-container">
|
||||||
{{range .instances}}
|
{{range .instances}}
|
||||||
{{template "instance-card" .}}
|
{{template "instance-card" .}}
|
||||||
|
|||||||
BIN
web/images/common/blank.png
Normal file
BIN
web/images/common/blank.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 B |
1
web/images/common/blank.svg
Normal file
1
web/images/common/blank.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="symb" role="img" aria-label="cpu" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>
|
||||||
|
After Width: | Height: | Size: 104 B |
4
web/modules/Sortable.min.js
vendored
4
web/modules/Sortable.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
|||||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||||
@@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||||
|
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||||
|
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||||
@@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||||
.w3-hover-none:hover{box-shadow:none!important}
|
.w3-hover-none:hover{box-shadow:none!important}
|
||||||
|
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||||
/* Colors */
|
/* Colors */
|
||||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||||
@@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||||
|
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||||
|
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||||
|
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||||
|
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||||
|
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||||
|
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||||
|
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||||
|
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||||
|
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||||
|
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||||
|
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||||
|
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||||
|
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
import { requestPVE, requestAPI, goToPage, getURIData, setAppearance, setSVGSrc, requestDash } from "./utils.js";
|
import { requestPVE, requestAPI, goToPage, getURIData, setAppearance, setIconSrc, requestDash } from "./utils.js";
|
||||||
import { alert, dialog } from "./dialog.js";
|
import { alert, dialog } from "./dialog.js";
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", init);
|
window.addEventListener("DOMContentLoaded", init);
|
||||||
@@ -48,8 +48,8 @@ class VolumeAction extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setStatusLoading () {
|
async setStatusLoading () {
|
||||||
const svg = document.querySelector(`svg[data-volume="${this.dataset.volume}"]`);
|
const icon = document.querySelector(`img[data-volume="${this.dataset.volume}"]`);
|
||||||
setSVGSrc(svg, "images/status/loading.svg");
|
setIconSrc(icon, "images/status/loading.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleDiskDetach () {
|
async handleDiskDetach () {
|
||||||
@@ -247,8 +247,8 @@ class NetworkAction extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setStatusLoading () {
|
async setStatusLoading () {
|
||||||
const svg = document.querySelector(`svg[data-network="${this.dataset.network}"]`);
|
const icon = document.querySelector(`img[data-network="${this.dataset.network}"]`);
|
||||||
setSVGSrc(svg, "images/status/loading.svg");
|
setIconSrc(icon, "images/status/loading.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleNetworkConfig () {
|
async handleNetworkConfig () {
|
||||||
@@ -277,7 +277,7 @@ class NetworkAction extends HTMLElement {
|
|||||||
const netID = this.dataset.network;
|
const netID = this.dataset.network;
|
||||||
dialog(this.template, async (result, form) => {
|
dialog(this.template, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
setSVGSrc(document.querySelector(`svg[data-network="${netID}"]`), "images/status/loading.svg");
|
setIconSrc(document.querySelector(`svg[data-network="${netID}"]`), "images/status/loading.svg");
|
||||||
const net = `${netID}`;
|
const net = `${netID}`;
|
||||||
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/net/${net}/delete`, "DELETE");
|
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/net/${net}/delete`, "DELETE");
|
||||||
if (result.status !== 200) {
|
if (result.status !== 200) {
|
||||||
@@ -349,8 +349,8 @@ class DeviceAction extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setStatusLoading () {
|
async setStatusLoading () {
|
||||||
const svg = document.querySelector(`svg[data-device="${this.dataset.device}"]`);
|
const icon = document.querySelector(`img[data-device="${this.dataset.device}"]`);
|
||||||
setSVGSrc(svg, "images/status/loading.svg");
|
setIconSrc(icon, "images/status/loading.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleDeviceConfig () {
|
async handleDeviceConfig () {
|
||||||
|
|||||||
@@ -42,7 +42,10 @@ export function dialog (template, onclose = async (result, form) => { }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function alert (message) {
|
export function alert (message) {
|
||||||
|
const dialog = document.querySelector("#alert-dialog");
|
||||||
|
if (dialog == null) {
|
||||||
const dialog = document.createElement("dialog");
|
const dialog = document.createElement("dialog");
|
||||||
|
dialog.id = "alert-dialog";
|
||||||
dialog.innerHTML = `
|
dialog.innerHTML = `
|
||||||
<form method="dialog">
|
<form method="dialog">
|
||||||
<p class="w3-center" style="margin-bottom: 0px;">${message}</p>
|
<p class="w3-center" style="margin-bottom: 0px;">${message}</p>
|
||||||
@@ -52,13 +55,101 @@ export function alert (message) {
|
|||||||
</form>
|
</form>
|
||||||
`;
|
`;
|
||||||
dialog.className = "w3-container w3-card w3-border-0";
|
dialog.className = "w3-container w3-card w3-border-0";
|
||||||
|
|
||||||
document.body.append(dialog);
|
document.body.append(dialog);
|
||||||
dialog.showModal();
|
dialog.showModal();
|
||||||
|
|
||||||
dialog.addEventListener("close", () => {
|
dialog.addEventListener("close", () => {
|
||||||
dialog.parentElement.removeChild(dialog);
|
dialog.parentElement.removeChild(dialog);
|
||||||
});
|
});
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error("Attempted to create a new alert while one already exists!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ErrorDialog extends HTMLElement {
|
||||||
|
shadowRoot = null;
|
||||||
|
dialog = null;
|
||||||
|
errors = null;
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
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">
|
||||||
|
<style>
|
||||||
|
#errors {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
max-height: 20lh;
|
||||||
|
min-height: 20lh;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
#errors * {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
|
<form method="dialog">
|
||||||
|
<p class="w3-large" id="prompt" style="text-align: center;">Error</p>
|
||||||
|
<div id="errors" class="flex column-reverse"></div>
|
||||||
|
<div class="w3-center" id="controls">
|
||||||
|
<button class="w3-button w3-margin" type="submit" value="ok">OK</button>
|
||||||
|
<button class="w3-button w3-margin" type="submit" value="copy">Copy</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
|
`;
|
||||||
|
this.dialog = this.shadowRoot.querySelector("dialog");
|
||||||
|
this.errors = this.shadowRoot.querySelector("#errors");
|
||||||
|
|
||||||
|
for (const control of this.shadowRoot.querySelector("#controls").childNodes) {
|
||||||
|
control.addEventListener("click", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.dialog.close(e.target.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialog.addEventListener("close", () => {
|
||||||
|
if (this.dialog.returnValue === "ok") {}
|
||||||
|
else if (this.dialog.returnValue === "copy") {
|
||||||
|
let errors = "";
|
||||||
|
for (const error of this.errors.childNodes) {
|
||||||
|
errors += `${error.innerText}\n`;
|
||||||
|
}
|
||||||
|
navigator.clipboard.writeText(errors);
|
||||||
|
}
|
||||||
|
this.parentElement.removeChild(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
appendError (error) {
|
||||||
|
error = `${(new Date()).toUTCString()}: ${error}`;
|
||||||
|
const p = document.createElement("p");
|
||||||
|
p.innerText = error;
|
||||||
|
this.errors.appendChild(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
showModal () {
|
||||||
|
this.dialog.showModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("error-dialog", ErrorDialog);
|
||||||
|
|
||||||
|
export function error (message) {
|
||||||
|
let dialog = document.querySelector("error-dialog");
|
||||||
|
if (dialog == null) {
|
||||||
|
dialog = document.createElement("error-dialog");
|
||||||
|
document.body.append(dialog);
|
||||||
|
dialog.appendError(message);
|
||||||
|
dialog.showModal();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dialog.appendError(message);
|
||||||
|
dialog.showModal();
|
||||||
|
}
|
||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const blank = document.createElement("img");
|
const blank = document.createElement("img");
|
||||||
|
blank.src = "images/common/blank.png"; // for whatever reason an svg does NOT work here
|
||||||
|
|
||||||
class DraggableContainer extends HTMLElement {
|
class DraggableContainer extends HTMLElement {
|
||||||
shadowRoot = null;
|
shadowRoot = null;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { requestPVE, requestAPI, setAppearance, getSearchSettings, requestDash, setSVGSrc, setSVGAlt } from "./utils.js";
|
import { requestPVE, requestAPI, setAppearance, getSearchSettings, requestDash, setIconSrc, setIconAlt } from "./utils.js";
|
||||||
import { alert, dialog } from "./dialog.js";
|
import { alert, dialog, error } from "./dialog.js";
|
||||||
import { setupClientSync } from "./clientsync.js";
|
import { setupClientSync } from "./clientsync.js";
|
||||||
import wfaInit from "../modules/wfa.js";
|
import wfaInit from "../modules/wfa.js";
|
||||||
|
|
||||||
@@ -120,6 +120,7 @@ class InstanceCard extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const powerButton = this.shadowRoot.querySelector("#power-btn");
|
const powerButton = this.shadowRoot.querySelector("#power-btn");
|
||||||
|
if (powerButton !== null) {
|
||||||
if (powerButton.classList.contains("clickable")) {
|
if (powerButton.classList.contains("clickable")) {
|
||||||
powerButton.onclick = this.handlePowerButton.bind(this);
|
powerButton.onclick = this.handlePowerButton.bind(this);
|
||||||
powerButton.onkeydown = (event) => {
|
powerButton.onkeydown = (event) => {
|
||||||
@@ -129,8 +130,10 @@ class InstanceCard extends HTMLElement {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const deleteButton = this.shadowRoot.querySelector("#delete-btn");
|
const deleteButton = this.shadowRoot.querySelector("#delete-btn");
|
||||||
|
if (deleteButton !== null) {
|
||||||
if (deleteButton.classList.contains("clickable")) {
|
if (deleteButton.classList.contains("clickable")) {
|
||||||
deleteButton.onclick = this.handleDeleteButton.bind(this);
|
deleteButton.onclick = this.handleDeleteButton.bind(this);
|
||||||
deleteButton.onkeydown = (event) => {
|
deleteButton.onkeydown = (event) => {
|
||||||
@@ -141,15 +144,16 @@ class InstanceCard extends HTMLElement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setStatusLoading () {
|
setStatusLoading () {
|
||||||
this.status = "loading";
|
this.status = "loading";
|
||||||
const statusicon = this.shadowRoot.querySelector("#status");
|
const statusicon = this.shadowRoot.querySelector("#status");
|
||||||
const powerbtn = this.shadowRoot.querySelector("#power-btn");
|
const powerbtn = this.shadowRoot.querySelector("#power-btn");
|
||||||
setSVGSrc(statusicon, "images/status/loading.svg");
|
setIconSrc(statusicon, "images/status/loading.svg");
|
||||||
setSVGAlt(statusicon, "instance is loading");
|
setIconAlt(statusicon, "instance is loading");
|
||||||
setSVGSrc(powerbtn, "images/status/loading.svg");
|
setIconSrc(powerbtn, "images/status/loading.svg");
|
||||||
setSVGAlt(powerbtn, "");
|
setIconAlt(powerbtn, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
async handlePowerButton () {
|
async handlePowerButton () {
|
||||||
@@ -219,7 +223,7 @@ async function getInstancesFragment () {
|
|||||||
async function refreshInstances () {
|
async function refreshInstances () {
|
||||||
let instances = await getInstancesFragment();
|
let instances = await getInstancesFragment();
|
||||||
if (instances.status !== 200) {
|
if (instances.status !== 200) {
|
||||||
alert("Error fetching instances.");
|
error(`Error fetching instances: ${instances.status} ${instances.error !== undefined ? instances.error : ""}`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
instances = instances.data;
|
instances = instances.data;
|
||||||
@@ -253,9 +257,9 @@ function sortInstances () {
|
|||||||
if (substrInc) {
|
if (substrInc) {
|
||||||
const substrStartIndex = item.indexOf(query);
|
const substrStartIndex = item.indexOf(query);
|
||||||
const queryLength = query.length;
|
const queryLength = query.length;
|
||||||
const remaining = item.length - substrInc - queryLength;
|
const remaining = item.length - substrInc - queryLength + 1;
|
||||||
const alignment = `${"X".repeat(substrStartIndex)}${"M".repeat(queryLength)}${"X".repeat(remaining)}`;
|
const alignment = `${"X".repeat(substrStartIndex)}${"M".repeat(queryLength)}${"X".repeat(remaining)}`;
|
||||||
return { score: 1, alignment };
|
return { score: -1, alignment };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const alignment = `${"X".repeat(item.length)}`;
|
const alignment = `${"X".repeat(item.length)}`;
|
||||||
@@ -272,8 +276,8 @@ function sortInstances () {
|
|||||||
};
|
};
|
||||||
criteria = (item, query) => {
|
criteria = (item, query) => {
|
||||||
// lower is better
|
// lower is better
|
||||||
const { score, CIGAR } = global.wfAlign(query, item, penalties, true);
|
const { score, CIGAR } = global.wfa.wfAlign(query, item, penalties, true);
|
||||||
const alignment = global.DecodeCIGAR(CIGAR);
|
const alignment = global.wfa.DecodeCIGAR(CIGAR);
|
||||||
return { score: score / item.length, alignment };
|
return { score: score / item.length, alignment };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,15 +192,10 @@ export function setAppearance () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assumes href is path to svg, and id to grab is #symb
|
// assumes href is path to svg, and id to grab is #symb
|
||||||
export function setSVGSrc (svgElem, href) {
|
export function setIconSrc (icon, path) {
|
||||||
let useElem = svgElem.querySelector("use");
|
icon.setAttribute("src", path);
|
||||||
if (!useElem) {
|
|
||||||
useElem = document.createElementNS("http://www.w3.org/2000/svg", "use");
|
|
||||||
}
|
|
||||||
useElem.setAttribute("href", `${href}#symb`);
|
|
||||||
svgElem.append(useElem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setSVGAlt (svgElem, alt) {
|
export function setIconAlt (icon, alt) {
|
||||||
svgElem.setAttribute("aria-label", alt);
|
icon.setAttribute("alt", alt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
<backup-card data-volid="{{.Volid}}">
|
<backup-card data-volid="{{.Volid}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="modules/w3.css">
|
<link rel="stylesheet" href="modules/w3.css">
|
||||||
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@@ -14,19 +13,15 @@
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
svg {
|
|
||||||
height: 1em;
|
|
||||||
width: 1em;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<div class="w3-row" style="margin-top: 1em; margin-bottom: 1em;">
|
<div class="w3-row" style="margin-top: 1em; margin-bottom: 1em;">
|
||||||
<p class="w3-col l2 m4 s8">{{.TimeFormatted}}</p>
|
<p class="w3-col l2 m4 s8">{{.TimeFormatted}}</p>
|
||||||
<p class="w3-col l6 m6 w3-hide-small">{{.Notes}}</p>
|
<p class="w3-col l6 m6 hide-small">{{.Notes}}</p>
|
||||||
<p class="w3-col l2 w3-hide-medium w3-hide-small">{{.SizeFormatted}}</p>
|
<p class="w3-col l2 hide-medium">{{.SizeFormatted}}</p>
|
||||||
<div class="w3-col l2 m2 s4 flex row nowrap" style="height: 1lh;">
|
<div class="w3-col l2 m2 s4 flex row nowrap" style="height: 1lh;">
|
||||||
<svg id="edit-btn" class="clickable" aria-label="change notes"><use href="images/actions/backups/config.svg#symb"></svg>
|
<img id="edit-btn" class="clickable" alt="change notes" src="images/actions/backups/config.svg#symb">
|
||||||
<svg id="delete-btn" class="clickable" aria-label="delete backup" role="button" tabindex=0><use href="images/actions/backups/delete-active.svg#symb"></svg>
|
<img id="delete-btn" class="clickable" alt="delete backup" role="button" tabindex=0 src="images/actions/backups/delete-active.svg#symb">
|
||||||
<svg id="restore-btn" class="clickable" aria-label="restore from backup" role="button" tabindex=0><use href="images/actions/backups/restore.svg#symb"></svg>
|
<img id="restore-btn" class="clickable" alt="restore from backup" role="button" tabindex=0 src="images/actions/backups/restore.svg#symb">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template id="edit-dialog">
|
<template id="edit-dialog">
|
||||||
@@ -101,7 +96,7 @@
|
|||||||
{{define "backups-add-backup"}}
|
{{define "backups-add-backup"}}
|
||||||
<button type="button" id="backup-add" class="w3-button" aria-label="Create Backup">
|
<button type="button" id="backup-add" class="w3-button" aria-label="Create Backup">
|
||||||
<span class="large" style="margin: 0;">Create Backup</span>
|
<span class="large" style="margin: 0;">Create Backup</span>
|
||||||
<svg class="small" role="img" style="height: 1lh; width: 1lh;" aria-label="Create Backup"><use href="images/actions/network/add.svg#symb"></use></svg>
|
<img class="small" role="img" style="height: 1lh; width: 1lh;" alt="Create Backup" src="images/actions/network/add.svg#symb">
|
||||||
</button>
|
</button>
|
||||||
<template id="create-backup-dialog">
|
<template id="create-backup-dialog">
|
||||||
<link rel="stylesheet" href="modules/w3.css">
|
<link rel="stylesheet" href="modules/w3.css">
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
<title>{{.global.Organization}} - dashboard</title>
|
<title>{{.global.Organization}} - dashboard</title>
|
||||||
<link rel="icon" href="images/favicon.svg" sizes="any" type="image/svg+xml">
|
<link rel="icon" href="images/favicon.svg" sizes="any" type="image/svg+xml">
|
||||||
<link rel="stylesheet" href="modules/w3.css">
|
<link rel="stylesheet" href="modules/w3.css">
|
||||||
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
|
||||||
<script>
|
<script>
|
||||||
window.PVE = "{{.global.PVE}}";
|
window.PVE = "{{.global.PVE}}";
|
||||||
window.API = "{{.global.API}}";
|
window.API = "{{.global.API}}";
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
{{define "proctype-input"}}
|
{{define "proctype-input"}}
|
||||||
<svg aria-label="CPU Type"><use href="images/resources/cpu.svg#symb"></svg>
|
<img alt="CPU Type" src="images/resources/cpu.svg#symb">
|
||||||
<label for="proctype">CPU Type</label>
|
<label for="proctype">CPU Type</label>
|
||||||
{{template "select" .}}
|
{{template "select" .}}
|
||||||
<div></div>
|
<div></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "cores-input"}}
|
{{define "cores-input"}}
|
||||||
<svg aria-label="CPU Amount"><use href="images/resources/cpu.svg#symb"></svg>
|
<img alt="CPU Amount" src="images/resources/cpu.svg#symb">
|
||||||
<label for="cores">CPU Amount</label>
|
<label for="cores">CPU Amount</label>
|
||||||
<input id="cores" name="cores" class="w3-input w3-border" type="number" required value="{{.}}">
|
<input id="cores" name="cores" class="w3-input w3-border" type="number" required value="{{.}}">
|
||||||
<p>Cores</p>
|
<p>Cores</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "memory-input"}}
|
{{define "memory-input"}}
|
||||||
<svg aria-label="Memory Amount"><use href="images/resources/ram.svg#symb"></svg>
|
<img alt="Memory Amount" src="images/resources/ram.svg#symb">
|
||||||
<label for="ram">Memory</label>
|
<label for="ram">Memory</label>
|
||||||
<input id="ram" name="ram" class="w3-input w3-border" type="number" required value="{{.}}">
|
<input id="ram" name="ram" class="w3-input w3-border" type="number" required value="{{.}}">
|
||||||
<p>MiB</p>
|
<p>MiB</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "swap-input"}}
|
{{define "swap-input"}}
|
||||||
<svg aria-label="Swap Amount"><use href="images/resources/swap.svg#symb"></svg>
|
<img alt="Swap Amount" src="images/resources/swap.svg#symb">
|
||||||
<label for="swap">Swap</label>
|
<label for="swap">Swap</label>
|
||||||
<input id="swap" name="swap" class="w3-input w3-border" type="number" required value="{{.}}">
|
<input id="swap" name="swap" class="w3-input w3-border" type="number" required value="{{.}}">
|
||||||
<p>MiB</p>
|
<p>MiB</p>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
{{define "volumes-add-disk"}}
|
{{define "volumes-add-disk"}}
|
||||||
<button type="button" id="disk-add" class="w3-button" aria-label="Add New Disk">
|
<button type="button" id="disk-add" class="w3-button" aria-label="Add New Disk">
|
||||||
<span class="large" style="margin: 0;">Add Disk</span>
|
<span class="large" style="margin: 0;">Add Disk</span>
|
||||||
<svg class="small" role="img" style="height: 1lh; width: 1lh;" aria-label="Add New Disk"><use href="images/actions/disk/add-disk.svg#symb"></use></svg>
|
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New Disk" src="images/actions/disk/add-disk.svg#symb">
|
||||||
</button>
|
</button>
|
||||||
<template id="add-disk-dialog">
|
<template id="add-disk-dialog">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
{{define "volumes-add-cd"}}
|
{{define "volumes-add-cd"}}
|
||||||
<button type="button" id="cd-add" class="w3-button" aria-label="Add New CD">
|
<button type="button" id="cd-add" class="w3-button" aria-label="Add New CD">
|
||||||
<span class="large" style="margin: 0;">Mount CD</span>
|
<span class="large" style="margin: 0;">Mount CD</span>
|
||||||
<svg class="small" role="img" style="height: 1lh; width: 1lh;" aria-label="Add New CDROM"><use href="images/actions/disk/add-cd.svg#symb"></use></svg>
|
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New CDROM" src="images/actions/disk/add-cd.svg#symb">
|
||||||
</button>
|
</button>
|
||||||
<template id="add-cd-dialog">
|
<template id="add-cd-dialog">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "volume-rootfs"}}
|
{{define "volume-rootfs"}}
|
||||||
<svg data-volume={{.Name}} xmlns="http://www.w3.org/2000/svg" aria-label="Drive"><use href="images/resources/drive.svg#symb"></svg>
|
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||||
<p>{{.Name}}</p>
|
<p>{{.Name}}</p>
|
||||||
<p>{{.Volume.File}}</p>
|
<p>{{.Volume.File}}</p>
|
||||||
<div>
|
<div>
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "volume-mp"}}
|
{{define "volume-mp"}}
|
||||||
<svg data-volume={{.Name}} xmlns="http://www.w3.org/2000/svg" aria-label="Drive"><use href="images/resources/drive.svg#symb"></svg>
|
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||||
<p>{{.Name}}</p>
|
<p>{{.Name}}</p>
|
||||||
<p>{{.Volume.File}}</p>
|
<p>{{.Volume.File}}</p>
|
||||||
<div>
|
<div>
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "volume-ide"}}
|
{{define "volume-ide"}}
|
||||||
<svg data-volume={{.Name}} xmlns="http://www.w3.org/2000/svg" aria-label="Drive"><use href="images/resources/drive.svg#symb"></svg>
|
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||||
<p>{{.Name}}</p>
|
<p>{{.Name}}</p>
|
||||||
<p>{{.Volume.File}}</p>
|
<p>{{.Volume.File}}</p>
|
||||||
<div>
|
<div>
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "volume-scsi"}}
|
{{define "volume-scsi"}}
|
||||||
<svg data-volume={{.Name}} xmlns="http://www.w3.org/2000/svg" aria-label="Drive"><use href="images/resources/drive.svg#symb"></svg>
|
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||||
<p>{{.Name}}</p>
|
<p>{{.Name}}</p>
|
||||||
<p>{{.Volume.File}}</p>
|
<p>{{.Volume.File}}</p>
|
||||||
<div>
|
<div>
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "volume-unused"}}
|
{{define "volume-unused"}}
|
||||||
<svg data-volume={{.Name}} xmlns="http://www.w3.org/2000/svg" aria-label="Drive"><use href="images/resources/drive.svg#symb"></svg>
|
<img data-volume={{.Name}} alt="Drive" src="images/resources/drive.svg#symb">
|
||||||
<p>{{.Name}}</p>
|
<p>{{.Name}}</p>
|
||||||
<p>{{.Volume.File}}</p>
|
<p>{{.Volume.File}}</p>
|
||||||
<div>
|
<div>
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
<volume-action data-type="move" data-volume="{{.Name}}">
|
<volume-action data-type="move" data-volume="{{.Name}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg class="clickable" aria-label="Move {{.Name}}"><use href="images/actions/disk/move-active.svg#symb"></svg>
|
<img class="clickable" alt="Move {{.Name}}" src="images/actions/disk/move-active.svg#symb">
|
||||||
<template id="dialog-template">
|
<template id="dialog-template">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
@@ -186,7 +186,7 @@
|
|||||||
<volume-action data-type="none" data-volume="{{.Name}}">
|
<volume-action data-type="none" data-volume="{{.Name}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg aria-label=""><use href="images/actions/disk/move-inactive.svg#symb"></svg>
|
<img alt="" src="images/actions/disk/move-inactive.svg#symb">
|
||||||
</template>
|
</template>
|
||||||
</volume-action>
|
</volume-action>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -195,7 +195,7 @@
|
|||||||
<volume-action data-type="resize" data-volume="{{.Name}}">
|
<volume-action data-type="resize" data-volume="{{.Name}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg class="clickable" aria-label="Resize {{.Name}}"><use href="images/actions/disk/resize-active.svg#symb"></svg>
|
<img class="clickable" alt="Resize {{.Name}}" src="images/actions/disk/resize-active.svg#symb">
|
||||||
<template id="dialog-template">
|
<template id="dialog-template">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
@@ -221,7 +221,7 @@
|
|||||||
<volume-action data-type="none" data-volume="{{.Name}}">
|
<volume-action data-type="none" data-volume="{{.Name}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg aria-label=""><use href="images/actions/disk/resize-inactive.svg#symb"></svg>
|
<img alt="" src="images/actions/disk/resize-inactive.svg#symb">
|
||||||
</template>
|
</template>
|
||||||
</volume-action>
|
</volume-action>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -230,7 +230,7 @@
|
|||||||
<volume-action data-type="delete" data-volume="{{.Name}}">
|
<volume-action data-type="delete" data-volume="{{.Name}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg class="clickable" aria-label="Delete {{.Name}}"><use href="images/actions/disk/delete-active.svg#symb"></svg>
|
<img class="clickable" alt="Delete {{.Name}}" src="images/actions/disk/delete-active.svg#symb">
|
||||||
<template id="dialog-template">
|
<template id="dialog-template">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
@@ -255,7 +255,7 @@
|
|||||||
<volume-action data-type="none" data-volume="{{.Name}}">
|
<volume-action data-type="none" data-volume="{{.Name}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg aria-label=""><use href="images/actions/disk/delete-inactive.svg#symb"></svg>
|
<img alt="" src="images/actions/disk/delete-inactive.svg#symb">
|
||||||
</template>
|
</template>
|
||||||
</volume-action>
|
</volume-action>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<volume-action data-type="attach" data-volume="{{.Name}}">
|
<volume-action data-type="attach" data-volume="{{.Name}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg class="clickable" aria-label="Attach {{.Name}}"><use href="images/actions/disk/attach.svg#symb"></svg>
|
<img class="clickable" alt="Attach {{.Name}}" src="images/actions/disk/attach.svg#symb">
|
||||||
<template id="dialog-template">
|
<template id="dialog-template">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
@@ -297,7 +297,7 @@
|
|||||||
<volume-action data-type="detach" data-volume="{{.Name}}">
|
<volume-action data-type="detach" data-volume="{{.Name}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg class="clickable" aria-label="Detach {{.Name}}"><use href="images/actions/disk/detach.svg#symb"></svg>
|
<img class="clickable" alt="Detach {{.Name}}" src="images/actions/disk/detach.svg#symb">
|
||||||
<template id="dialog-template">
|
<template id="dialog-template">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
@@ -322,7 +322,7 @@
|
|||||||
<volume-action data-type="none">
|
<volume-action data-type="none">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg aria-label=""></svg>
|
<img alt="" src="images/common/blank.svg">
|
||||||
</template>
|
</template>
|
||||||
</volume-action>
|
</volume-action>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -336,7 +336,7 @@
|
|||||||
{{define "nets-add-net"}}
|
{{define "nets-add-net"}}
|
||||||
<button type="button" id="network-add" class="w3-button" aria-label="Add New Network Interface">
|
<button type="button" id="network-add" class="w3-button" aria-label="Add New Network Interface">
|
||||||
<span class="large" style="margin: 0;">Add Network</span>
|
<span class="large" style="margin: 0;">Add Network</span>
|
||||||
<svg class="small" role="img" style="height: 1lh; width: 1lh;" aria-label="Add New Network Interface"><use href="images/actions/network/add.svg#symb"></use></svg>
|
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New Network Interface" src="images/actions/network/add.svg#symb">
|
||||||
</button>
|
</button>
|
||||||
<template id="add-net-dialog">
|
<template id="add-net-dialog">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
@@ -361,14 +361,14 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "net"}}
|
{{define "net"}}
|
||||||
<svg data-network="{{.Net_ID}}" aria-label="Net {{.Net_ID}}"><use href="images/resources/network.svg#symb"></svg>
|
<img data-network="{{.Net_ID}}" alt="Net {{.Net_ID}}" src="images/resources/network.svg#symb">
|
||||||
<p>{{.Net_ID}}</p>
|
<p>{{.Net_ID}}</p>
|
||||||
<p>{{.Value}}</p>
|
<p>{{.Value}}</p>
|
||||||
<div>
|
<div>
|
||||||
<network-action data-type="config" data-network="{{.Net_ID}}" data-value="{{.Value}}">
|
<network-action data-type="config" data-network="{{.Net_ID}}" data-value="{{.Value}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg class="clickable" aria-label="Configure Net {{.Net_ID}}"><use href="images/actions/network/config.svg#symb"></svg>
|
<img class="clickable" alt="Configure Net {{.Net_ID}}" src="images/actions/network/config.svg#symb">
|
||||||
<template id="dialog-template">
|
<template id="dialog-template">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
@@ -390,7 +390,7 @@
|
|||||||
<network-action data-type="delete" data-network="{{.Net_ID}}" data-value="{{.Value}}">
|
<network-action data-type="delete" data-network="{{.Net_ID}}" data-value="{{.Value}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg class="clickable" aria-label="Delete Net {{.Net_ID}}"><use href="images/actions/network/delete-active.svg#symb"></svg>
|
<img class="clickable" alt="Delete Net {{.Net_ID}}" src="images/actions/network/delete-active.svg#symb">
|
||||||
<template id="dialog-template">
|
<template id="dialog-template">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
@@ -421,7 +421,7 @@
|
|||||||
{{define "devices-add-device"}}
|
{{define "devices-add-device"}}
|
||||||
<button type="button" id="device-add" class="w3-button" aria-label="Add New PCIe 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>
|
<span class="large" style="margin: 0;">Add Device</span>
|
||||||
<svg class="small" role="img" style="height: 1lh; width: 1lh;" aria-label="Add New PCIe Device"><use href="images/actions/device/add.svg#symb"></use></svg>
|
<img class="small" style="height: 1lh; width: 1lh;" alt="Add New PCIe Device" src="images/actions/device/add.svg#symb">
|
||||||
</button>
|
</button>
|
||||||
<template id="add-device-dialog">
|
<template id="add-device-dialog">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
@@ -444,14 +444,14 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "device"}}
|
{{define "device"}}
|
||||||
<svg data-device="{{.Device_ID}}" aria-label="Device {{.Device_ID}}"><use href="images/resources/device.svg#symb"></svg>
|
<img data-device="{{.Device_ID}}" alt="Device {{.Device_ID}}" src="images/resources/device.svg#symb">
|
||||||
<p>{{.Device_ID}}</p>
|
<p>{{.Device_ID}}</p>
|
||||||
<p>{{.Device_Name}}</p>
|
<p>{{.Device_Name}}</p>
|
||||||
<div>
|
<div>
|
||||||
<device-action data-type="config" data-device="{{.Device_ID}}" data-value="{{.Value}}">
|
<device-action data-type="config" data-device="{{.Device_ID}}" data-value="{{.Value}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg class="clickable" aria-label="Configure Device {{.Device_ID}}"><use href="images/actions/device/config.svg#symb"></svg>
|
<img class="clickable" alt="Configure Device {{.Device_ID}}" src="images/actions/device/config.svg#symb">
|
||||||
<template id="dialog-template">
|
<template id="dialog-template">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
@@ -473,7 +473,7 @@
|
|||||||
<device-action data-type="delete" data-device="{{.Device_ID}}" data-value="{{.Value}}">
|
<device-action data-type="delete" data-device="{{.Device_ID}}" data-value="{{.Value}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<svg class="clickable" aria-label="Delete Device {{.Device_ID}}"><use href="images/actions/device/delete-active.svg#symb"></svg>
|
<img class="clickable" alt="Delete Device {{.Device_ID}}" src="images/actions/device/delete-active.svg#symb">
|
||||||
<template id="dialog-template">
|
<template id="dialog-template">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
@@ -537,15 +537,15 @@
|
|||||||
{{define "boot-target"}}
|
{{define "boot-target"}}
|
||||||
{{if .volume_id}}
|
{{if .volume_id}}
|
||||||
<div class="draggable-item" data-value="{{.volume_id}}" style="display: grid; grid-template-columns: auto auto 8ch 1fr; column-gap: 10px; align-items: center;">
|
<div class="draggable-item" data-value="{{.volume_id}}" style="display: grid; grid-template-columns: auto auto 8ch 1fr; column-gap: 10px; align-items: center;">
|
||||||
<svg aria-label="Drag"><use href="images/actions/drag.svg#symb"></use></svg>
|
<img style="height: 1em; width: 1em" alt="Drag" src="images/actions/drag.svg#symb">
|
||||||
<svg aria-label="Volume"><use href="images/resources/drive.svg#symb"></use></svg>
|
<img style="height: 1em; width: 1em" alt="Volume" src="images/resources/drive.svg#symb">
|
||||||
<p style="margin: 0px;">{{.volume_id}}</p>
|
<p style="margin: 0px;">{{.volume_id}}</p>
|
||||||
<p style="margin: 0px; overflow: hidden; white-space: nowrap;">{{.file}}</p>
|
<p style="margin: 0px; overflow: hidden; white-space: nowrap;">{{.file}}</p>
|
||||||
</div>
|
</div>
|
||||||
{{else if .net_id}}
|
{{else if .net_id}}
|
||||||
<div class="draggable-item" data-value="{{.net_id}}" style="display: grid; grid-template-columns: auto auto 8ch 1fr; column-gap: 10px; align-items: center;">
|
<div class="draggable-item" data-value="{{.net_id}}" style="display: grid; grid-template-columns: auto auto 8ch 1fr; column-gap: 10px; align-items: center;">
|
||||||
<svg aria-label="Drag"><use href="images/actions/drag.svg#symb"></use></svg>
|
<img style="height: 1em; width: 1em" alt="Drag" src="images/actions/drag.svg#symb">
|
||||||
<svg aria-label="Net"><use href="images/resources/network.svg#symb"></use></svg>
|
<img style="height: 1em; width: 1em" alt="Net" src="images/resources/network.svg#symb">
|
||||||
<p style="margin: 0px;">{{.net_id}}</p>
|
<p style="margin: 0px;">{{.net_id}}</p>
|
||||||
<p style="margin: 0px; overflow: hidden; white-space: nowrap;">{{.value}}</p>
|
<p style="margin: 0px; overflow: hidden; white-space: nowrap;">{{.value}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,81 +2,114 @@
|
|||||||
<instance-card data-type="{{.Type}}" data-status="{{.Status}}" data-vmid="{{.VMID}}" data-name="{{.Name}}" data-node="{{.Node}}" data-nodestatus="{{.NodeStatus}}">
|
<instance-card data-type="{{.Type}}" data-status="{{.Status}}" data-vmid="{{.VMID}}" data-name="{{.Name}}" data-node="{{.Node}}" data-nodestatus="{{.NodeStatus}}">
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="modules/w3.css">
|
<link rel="stylesheet" href="modules/w3.css">
|
||||||
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: fit-content;
|
||||||
}
|
}
|
||||||
a {
|
a, svg, img {
|
||||||
|
line-height: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
svg {
|
a img {
|
||||||
height: 1em;
|
vertical-align: unset;
|
||||||
width: 1em;
|
}
|
||||||
|
#instance-name {
|
||||||
|
overflow-x: hidden;
|
||||||
|
min-width: 0;
|
||||||
|
width: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.flex { /* needed for some reason to avoid a flickering issue on chrome ONLY */
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.row { /* needed for some reason to avoid a flickering issue on chrome ONLY */
|
||||||
|
flex-direction: row;
|
||||||
|
column-gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.nowrap { /* needed for some reason to avoid a flickering issue on chrome ONLY */
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
@media screen and (width >=993px) {
|
||||||
|
.hide-large {display: none !important;}
|
||||||
|
}
|
||||||
|
@media screen and (width <=993px) and (width >=601px){
|
||||||
|
.hide-large {display: none !important;}
|
||||||
|
.hide-medium {display:none !important}
|
||||||
|
}
|
||||||
|
@media screen and (width <=601px) {
|
||||||
|
.hide-large {display: none !important;}
|
||||||
|
.hide-medium {display:none !important}
|
||||||
|
.hide-small {display:none !important}
|
||||||
|
}
|
||||||
|
@media screen and (width <= 440px) {
|
||||||
|
.hide-large {display: none !important;}
|
||||||
|
.hide-medium {display:none !important}
|
||||||
|
.hide-small {display:none !important}
|
||||||
|
.hide-tiny { display: none !important;}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="w3-row" style="margin-top: 1em; margin-bottom: 1em;">
|
<p>{{.VMID}}</p>
|
||||||
<hr class="w3-show-small w3-hide-medium w3-hide-large" style="margin: 0; margin-bottom: 1em;">
|
<p id="instance-name">{{.Name}}</p>
|
||||||
<p class="w3-col l1 m2 s6">{{.VMID}}</p>
|
<p class="hide-small">{{.Type}}</p>
|
||||||
<p class="w3-col l2 m3 s6" id="instance-name">{{.Name}}</p>
|
<div class="flex row nowrap hide-tiny">
|
||||||
<p class="w3-col l1 m2 w3-hide-small">{{.Type}}</p>
|
|
||||||
<div class="w3-col l2 m3 s6 flex row nowrap">
|
|
||||||
{{if eq .Status "running"}}
|
{{if eq .Status "running"}}
|
||||||
<svg id="status" aria-label="instance is running"><use href="images/status/active.svg#symb"></svg>
|
<img id="status" alt="instance is running" src="images/status/active.svg#symb">
|
||||||
{{else if eq .Status "stopped"}}
|
{{else if eq .Status "stopped"}}
|
||||||
<svg id="status" aria-label="instance is stopped"><use href="images/status/inactive.svg#symb"></svg>
|
<img id="status" alt="instance is stopped" src="images/status/inactive.svg#symb">
|
||||||
{{else if eq .Status "loading"}}
|
{{else if eq .Status "loading"}}
|
||||||
<svg id="status" aria-label="instance is loading"><use href="images/status/loading.svg#symb"></svg>
|
<img id="status" alt="instance is loading" src="images/status/loading.svg#symb">
|
||||||
{{else}}
|
{{else}}
|
||||||
<svg id="status" aria-label="instance is loading"><use href="images/status/loading.svg#symb"></svg>
|
<img id="status" alt="instance is loading" src="images/status/loading.svg#symb">
|
||||||
{{end}}
|
{{end}}
|
||||||
<p>{{.Status}}</p>
|
<p>{{.Status}}</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="w3-col l2 w3-hide-medium w3-hide-small">{{.Node}}</p>
|
<p class="hide-medium">{{.Node}}</p>
|
||||||
<div class="w3-col l2 w3-hide-medium w3-hide-small flex row nowrap">
|
<div class="flex row nowrap hide-medium">
|
||||||
{{if eq .NodeStatus "online"}}
|
{{if eq .NodeStatus "online"}}
|
||||||
<svg aria-label="node is online"><use href="images/status/active.svg#symb"></svg>
|
<img alt="node is online" src="images/status/active.svg#symb">
|
||||||
{{else if eq .NodeStatus "offline"}}
|
{{else if eq .NodeStatus "offline"}}
|
||||||
<svg aria-label="node is offline"><use href="images/status/inactive.svg#symb"></svg>
|
<img alt="node is offline" src="images/status/inactive.svg#symb">
|
||||||
{{else if eq .NodeStatus "unknown"}}
|
{{else if eq .NodeStatus "unknown"}}
|
||||||
<svg aria-label="node is offline"><use href="images/status/inactive.svg#symb"></svg>
|
<img alt="node is offline" src="images/status/inactive.svg#symb">
|
||||||
{{else}}
|
{{else}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<p>{{.NodeStatus}}</p>
|
<p>{{.NodeStatus}}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-col l2 m2 s6 flex row nowrap" style="height: 1lh;">
|
<div class="flex row nowrap" style="height: 1lh;">
|
||||||
{{if and (eq .NodeStatus "online") (eq .Status "running")}}
|
{{if and (eq .NodeStatus "online") (eq .Status "running")}}
|
||||||
<svg id="power-btn" class="clickable" aria-label="shutdown instance" role="button" tabindex=0><use href="images/actions/instance/stop.svg#symb"></svg>
|
<img id="power-btn" class="clickable" alt="shutdown instance" role="button" tabindex=0 src="images/actions/instance/stop.svg#symb">
|
||||||
<svg id="configure-btn" aria-disabled="true" role="none"><use href="images/actions/instance/config-inactive.svg#symb"></svg>
|
<img id="configure-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/config-inactive.svg#symb">
|
||||||
<svg id="backup-btn" aria-disabled="true" role="none"><use href="images/actions/instance/backup-inactive.svg#symb"></svg>
|
<img id="backup-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/backup-inactive.svg#symb">
|
||||||
<a href="{{.ConsolePath}}" target="_blank">
|
<a href="{{.ConsolePath}}" target="_blank">
|
||||||
<svg id="console-btn" class="clickable" aria-label="open console"><use href="images/actions/instance/console-active.svg#symb"></svg>
|
<img id="console-btn" class="clickable" alt="open console" src="images/actions/instance/console-active.svg#symb">
|
||||||
</a>
|
</a>
|
||||||
<svg id="delete-btn" aria-disabled="true" role="none"><use href="images/actions/instance/delete-inactive.svg#symb"></svg>
|
<img id="delete-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/delete-inactive.svg#symb">
|
||||||
{{else if and (eq .NodeStatus "online") (eq .Status "stopped")}}
|
{{else if and (eq .NodeStatus "online") (eq .Status "stopped")}}
|
||||||
<svg id="power-btn" class="clickable" aria-label="start instance" role="button" tabindex=0><use href="images/actions/instance/start.svg#symb"></svg>
|
<img id="power-btn" class="clickable" alt="start instance" role="button" tabindex=0 src="images/actions/instance/start.svg#symb">
|
||||||
<a href="{{.ConfigPath}}">
|
<a href="{{.ConfigPath}}">
|
||||||
<svg id="configure-btn" class="clickable" aria-label="change configuration"><use href="images/actions/instance/config-active.svg#symb"></svg>
|
<img id="configure-btn" class="clickable" alt="change configuration" src="images/actions/instance/config-active.svg#symb">
|
||||||
</a>
|
</a>
|
||||||
<a href="{{.BackupsPath}}">
|
<a href="{{.BackupsPath}}">
|
||||||
<svg id="backup-btn" class="clickable" aria-label="manage backups"><use href="images/actions/instance/backup-active.svg#symb"></svg>
|
<img id="backup-btn" class="clickable" alt="manage backups" src="images/actions/instance/backup-active.svg#symb">
|
||||||
</a>
|
</a>
|
||||||
<svg id="console-btn" aria-disabled="true" role="none"><use href="images/actions/instance/console-inactive.svg#symb"></svg>
|
<img id="console-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/console-inactive.svg#symb">
|
||||||
<svg id="delete-btn" class="clickable" aria-label="delete instance" role="button" tabindex=0><use href="images/actions/instance/delete-active.svg#symb"></svg>
|
<img id="delete-btn" class="clickable" alt="delete instance" role="button" tabindex=0 src="images/actions/instance/delete-active.svg#symb">
|
||||||
{{else if and (eq .NodeStatus "online") (eq .Status "loading")}}
|
{{else if and (eq .NodeStatus "online") (eq .Status "loading")}}
|
||||||
<svg id="power-btn" aria-disabled="true" role="none"><use href="images/actions/instance/loading.svg#symb"></svg>
|
<img id="power-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/loading.svg#symb">
|
||||||
<svg id="configure-btn" aria-disabled="true" role="none"><use href="images/actions/instance/config-inactive.svg#symb"></svg>
|
<img id="configure-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/config-inactive.svg#symb">
|
||||||
<svg id="backup-btn" aria-disabled="true" role="none"><use href="images/actions/instance/backup-inactive.svg#symb"></svg>
|
<img id="backup-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/backup-inactive.svg#symb">
|
||||||
<svg id="console-btn" aria-disabled="true" role="none"><use href="images/actions/instance/console-inactive.svg#symb"></svg>
|
<img id="console-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/console-inactive.svg#symb">
|
||||||
<svg id="delete-btn" aria-disabled="true" role="none"><use href="images/actions/instance/delete-inactive.svg#symb"></svg>
|
<img id="delete-btn" alt="" aria-disabled="true" role="none" src="images/actions/instance/delete-inactive.svg#symb">
|
||||||
{{else}}
|
{{else}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<template id="power-dialog">
|
<template id="power-dialog">
|
||||||
<link rel="stylesheet" href="modules/w3.css">
|
<link rel="stylesheet" href="modules/w3.css">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
|||||||
@@ -2,13 +2,8 @@
|
|||||||
<resource-chart>
|
<resource-chart>
|
||||||
<template shadowrootmode="open">
|
<template shadowrootmode="open">
|
||||||
<link rel="stylesheet" href="modules/w3.css">
|
<link rel="stylesheet" href="modules/w3.css">
|
||||||
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<style>
|
<style>
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
#container{
|
#container{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
Reference in New Issue
Block a user