From 800ad7cd60b1d2853a96bfec60ffcf122b822ebb Mon Sep 17 00:00:00 2001 From: Arthur Lu Date: Tue, 29 Apr 2025 17:44:02 +0000 Subject: [PATCH] cleanup various unused code, simplify instance action hiding when node is not online --- app/app.go | 2 +- app/common/meta.go | 10 +- app/common/utils.go | 2 +- go.mod | 2 + web/css/form.css | 2 + web/css/style.css | 5 +- web/html/config.html | 2 +- web/scripts/account.js | 8 +- web/scripts/config.js | 26 +--- web/scripts/index.js | 38 ++--- web/scripts/utils.js | 220 ---------------------------- web/templates/instance-card.go.tmpl | 6 +- 12 files changed, 39 insertions(+), 284 deletions(-) diff --git a/app/app.go b/app/app.go index d64dd57..9d3e1fb 100644 --- a/app/app.go +++ b/app/app.go @@ -10,7 +10,7 @@ import ( "proxmoxaas-dashboard/app/routes" "github.com/gin-gonic/gin" - "github.com/tdewolff/minify" + "github.com/tdewolff/minify/v2" ) func Run() { diff --git a/app/common/meta.go b/app/common/meta.go index 50f539e..ba208aa 100644 --- a/app/common/meta.go +++ b/app/common/meta.go @@ -3,17 +3,19 @@ package common import ( "io" - "github.com/tdewolff/minify" - "github.com/tdewolff/minify/css" - "github.com/tdewolff/minify/html" - "github.com/tdewolff/minify/js" + "github.com/tdewolff/minify/v2" + "github.com/tdewolff/minify/v2/css" + "github.com/tdewolff/minify/v2/html" + "github.com/tdewolff/minify/v2/js" ) +// defines mime type and associated minifier type MimeType struct { Type string Minifier func(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error } +// map file extension to mime types var MimeTypes = map[string]MimeType{ "css": { Type: "text/css", diff --git a/app/common/utils.go b/app/common/utils.go index db0bdb1..c5c9f3a 100644 --- a/app/common/utils.go +++ b/app/common/utils.go @@ -16,7 +16,7 @@ import ( "strings" "github.com/gin-gonic/gin" - "github.com/tdewolff/minify" + "github.com/tdewolff/minify/v2" ) var TMPL *template.Template diff --git a/go.mod b/go.mod index 299e1ac..edbffd7 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,9 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/tdewolff/minify/v2 v2.23.1 // indirect github.com/tdewolff/parse v2.3.4+incompatible // indirect + github.com/tdewolff/parse/v2 v2.7.23 // indirect github.com/tdewolff/test v1.0.11 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect diff --git a/web/css/form.css b/web/css/form.css index 45d246e..6eb5ca4 100644 --- a/web/css/form.css +++ b/web/css/form.css @@ -77,4 +77,6 @@ input[type="radio"] { dialog { max-width: calc(min(50%, 80ch)); + background-color: var(--main-bg-color); + color: var(--main-text-color); } \ No newline at end of file diff --git a/web/css/style.css b/web/css/style.css index 00146c6..4084f79 100644 --- a/web/css/style.css +++ b/web/css/style.css @@ -61,13 +61,10 @@ body { grid-template-rows: auto 1fr; } -main, dialog { +main { max-width: 100vw; background-color: var(--main-bg-color); color: var(--main-text-color); -} - -main { padding: 0 16px 16px; } diff --git a/web/html/config.html b/web/html/config.html index 4e9df58..fb121b8 100644 --- a/web/html/config.html +++ b/web/html/config.html @@ -25,7 +25,7 @@
-

Instances / {{.config.Name}}

+

Instances / {{.config.Name}}

Resources diff --git a/web/scripts/account.js b/web/scripts/account.js index a73ea94..07c17b4 100644 --- a/web/scripts/account.js +++ b/web/scripts/account.js @@ -12,10 +12,10 @@ async function init () { function handlePasswordChangeForm () { const body = ` - - - - + + + + `; const d = dialog("Change Password", body, async (result, form) => { diff --git a/web/scripts/config.js b/web/scripts/config.js index cf4324f..e235c3c 100644 --- a/web/scripts/config.js +++ b/web/scripts/config.js @@ -6,7 +6,6 @@ window.addEventListener("DOMContentLoaded", init); let node; let type; let vmid; -let config; async function init () { setAppearance(); @@ -16,11 +15,6 @@ async function init () { type = uriData.type; vmid = uriData.vmid; - await getConfig(); - - const name = type === "qemu" ? "name" : "hostname"; - document.querySelector("#name").innerHTML = document.querySelector("#name").innerHTML.replace("%{vmname}", config.data[name]); - initVolumes(); initNetworks(); initDevices(); @@ -28,10 +22,6 @@ async function init () { document.querySelector("#exit").addEventListener("click", handleFormExit); } -async function getConfig () { - config = await requestPVE(`/nodes/${node}/${type}/${vmid}/config`, "GET"); -} - class VolumeAction extends HTMLElement { shadowRoot = null; @@ -83,7 +73,7 @@ class VolumeAction extends HTMLElement { const body = `
- +
`; @@ -113,7 +103,7 @@ class VolumeAction extends HTMLElement { - `; + `; dialog(header, body, async (result, form) => { if (result === "confirm") { @@ -135,9 +125,7 @@ class VolumeAction extends HTMLElement { async handleDiskMove () { const content = type === "qemu" ? "images" : "rootdir"; const storage = await requestPVE(`/nodes/${node}/storage`, "GET"); - const header = `Move ${this.dataset.volume}`; - let options = ""; storage.data.forEach((element) => { if (element.content.includes(content)) { @@ -215,9 +203,7 @@ async function refreshVolumes () { async function handleDiskAdd () { const content = type === "qemu" ? "images" : "rootdir"; const storage = await requestPVE(`/nodes/${node}/storage`, "GET"); - const header = "Create New Disk"; - let options = ""; storage.data.forEach((element) => { if (element.content.includes(content)) { @@ -228,7 +214,7 @@ async function handleDiskAdd () { const body = `
- + ${select}
@@ -255,9 +241,7 @@ async function handleDiskAdd () { async function handleCDAdd () { const isos = await requestAPI("/user/vm-isos", "GET"); - const header = "Mount a CDROM"; - const body = `
@@ -341,7 +325,6 @@ class NetworkAction extends HTMLElement { const netID = this.dataset.network; const header = `Delete ${netID}`; const body = ""; - dialog(header, body, async (result, form) => { if (result === "confirm") { setSVGSrc(document.querySelector(`svg[data-network="${netID}"]`), "images/status/loading.svg"); @@ -388,7 +371,6 @@ async function handleNetworkAdd () { body += ""; } body += "
"; - dialog(header, body, async (result, form) => { if (result === "confirm") { const body = { @@ -468,7 +450,6 @@ class DeviceAction extends HTMLElement { const deviceID = this.dataset.device; const header = `Remove Expansion Card ${deviceID}`; const body = ""; - dialog(header, body, async (result, form) => { if (result === "confirm") { this.setStatusLoading(); @@ -514,7 +495,6 @@ async function handleDeviceAdd () { `; - const d = dialog(header, body, async (result, form) => { if (result === "confirm") { const hostpci = form.get("hostpci"); diff --git a/web/scripts/index.js b/web/scripts/index.js index d734946..0e37362 100644 --- a/web/scripts/index.js +++ b/web/scripts/index.js @@ -3,6 +3,20 @@ import { alert, dialog } from "./dialog.js"; import { setupClientSync } from "./clientsync.js"; import wfaInit from "../modules/wfa.js"; +window.addEventListener("DOMContentLoaded", init); + +async function init () { + setAppearance(); + + wfaInit("modules/wfa.wasm"); + initInstances(); + + document.querySelector("#instance-add").addEventListener("click", handleInstanceAdd); + document.querySelector("#vm-search").addEventListener("input", sortInstances); + + setupClientSync(refreshInstances); +} + class InstanceCard extends HTMLElement { actionLock = false; shadowRoot = null; @@ -125,20 +139,12 @@ class InstanceCard extends HTMLElement { if (deleteButton.classList.contains("clickable")) { deleteButton.onclick = this.handleDeleteButton.bind(this); } - - if (this.node.status !== "online") { - powerButton.classList.add("hidden"); - configButton.classList.add("hidden"); - consoleButton.classList.add("hidden"); - deleteButton.classList.add("hidden"); - } } async handlePowerButton () { if (!this.actionLock) { const header = `${this.status === "running" ? "Stop" : "Start"} VM ${this.vmid}`; const body = `

Are you sure you want to ${this.status === "running" ? "stop" : "start"} VM ${this.vmid}

`; - dialog(header, body, async (result, form) => { if (result === "confirm") { this.actionLock = true; @@ -226,22 +232,8 @@ class InstanceCard extends HTMLElement { customElements.define("instance-card", InstanceCard); -window.addEventListener("DOMContentLoaded", init); - -async function init () { - setAppearance(); - - wfaInit("modules/wfa.wasm"); - initInstances(); - - document.querySelector("#instance-add").addEventListener("click", handleInstanceAdd); - document.querySelector("#vm-search").addEventListener("input", sortInstances); - - setupClientSync(refreshInstances); -} - async function getInstancesFragment () { - return await requestDash("/index/instances", "GET") + return await requestDash("/index/instances", "GET"); } async function refreshInstances () { diff --git a/web/scripts/utils.js b/web/scripts/utils.js index 38358ec..edf0b59 100644 --- a/web/scripts/utils.js +++ b/web/scripts/utils.js @@ -1,91 +1,3 @@ -export const resourcesConfig = { - cpu: { - name: "CPU Type", - icon: "images/resources/cpu.svg", - id: "proctype", - unitText: null - }, - cores: { - name: "CPU Amount", - icon: "images/resources/cpu.svg", - id: "cores", - unitText: "Cores" - }, - memory: { - name: "Memory", - icon: "images/resources/ram.svg", - id: "ram", - unitText: "MiB" - }, - swap: { - name: "Swap", - icon: "images/resources/swap.svg", - id: "swap", - unitText: "MiB" - }, - disk: { - actionBarOrder: ["move", "resize", "detach_attach", "delete"], - lxc: { - prefixOrder: ["rootfs", "mp", "unused"], - rootfs: { name: "ROOTFS", icon: "images/resources/drive.svg", actions: ["move", "resize"] }, - mp: { name: "MP", icon: "images/resources/drive.svg", actions: ["detach", "move", "reassign", "resize"] }, - unused: { name: "UNUSED", icon: "images/resources/drive.svg", actions: ["attach", "delete", "reassign"] } - }, - qemu: { - prefixOrder: ["ide", "sata", "scsi", "unused"], - ide: { name: "IDE", icon: "images/resources/disk.svg", actions: ["delete"] }, - sata: { name: "SATA", icon: "images/resources/drive.svg", actions: ["detach", "move", "reassign", "resize"] }, - scsi: { name: "SCSI", icon: "images/resources/drive.svg", actions: ["detach", "move", "reassign", "resize"] }, - unused: { name: "UNUSED", icon: "images/resources/drive.svg", actions: ["attach", "delete", "reassign"] } - }, - actions: { - attach: { - src: "images/actions/disk/attach.svg", - title: "Attach Disk" - }, - detach: { - src: "images/actions/disk/detach.svg", - title: "Detach Disk" - }, - delete: null - } - }, - network: { - name: "Network", - icon: "images/resources/network.svg", - id: "network", - unitText: "MB/s", - prefix: "net" - }, - pci: { - name: "Devices", - icon: "images/resources/device.svg", - id: "devices", - unitText: null, - prefix: "hostpci" - } -}; - -export const bootConfig = { - eligiblePrefixes: ["ide", "sata", "scsi", "net"], - ide: { - icon: "images/resources/disk.svg", - alt: "IDE Bootable Icon" - }, - sata: { - icon: "images/resources/drive.svg", - alt: "SATA Bootable Icon" - }, - scsi: { - icon: "images/resources/drive.svg", - alt: "SCSI Bootable Icon" - }, - net: { - icon: "images/resources/network.svg", - alt: "NET Bootable Icon" - } -}; - export function setCookie (cname, cval) { document.cookie = `${cname}=${cval}`; } @@ -301,135 +213,3 @@ export function setSVGSrc (svgElem, href) { export function setSVGAlt (svgElem, alt) { svgElem.setAttribute("aria-label", alt); } - -/** - * Simple object check. - * @param item - * @returns {boolean} - */ -export function isObject (item) { - return (item && typeof item === "object" && !Array.isArray(item)); -} - -/** - * Deep merge two objects. - * @param target - * @param ...sources - */ -export function mergeDeep (target, ...sources) { - if (!sources.length) return target; - const source = sources.shift(); - - if (isObject(target) && isObject(source)) { - for (const key in source) { - if (isObject(source[key])) { - if (!target[key]) Object.assign(target, { [key]: {} }); - mergeDeep(target[key], source[key]); - } - else { - Object.assign(target, { [key]: source[key] }); - } - } - } - - return mergeDeep(target, ...sources); -} - -/** - * Checks if object or array is empty - * @param {*} obj - * @returns - */ -export function isEmpty (obj) { - if (obj instanceof Array) { - return obj.length === 0; - } - else { - for (const prop in obj) { - if (Object.hasOwn(obj, prop)) { - return false; - } - } - return true; - } -} - -/* -export function addResourceLine (resourceConfig, field, attributesOverride, labelPrefix = null) { - const iconHref = resourceConfig.icon; - const elementType = resourceConfig.element; - const labelText = labelPrefix ? `${labelPrefix} ${resourceConfig.name}` : resourceConfig.name; - const id = resourceConfig.id; - const unitText = resourceConfig.unitText; - const attributes = { ...(resourceConfig.attributes), ...(attributesOverride) }; - - const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - setSVGSrc(icon, iconHref); - setSVGAlt(icon, labelText); - field.append(icon); - - const label = document.createElement("label"); - label.innerText = labelText; - label.htmlFor = id; - field.append(label); - - let element; - - if (elementType === "input") { - element = document.createElement("input"); - for (const k in attributes) { - element.setAttribute(k, attributes[k]); - } - element.id = id; - element.name = id; - element.required = true; - element.classList.add("w3-input"); - element.classList.add("w3-border"); - field.append(element); - } - else if (elementType === "select" || elementType === "multi-select") { - element = document.createElement("select"); - for (const option of attributes.options) { - element.append(new Option(option)); - } - element.value = attributes.value; - element.id = id; - element.name = id; - element.required = true; - element.classList.add("w3-select"); - element.classList.add("w3-border"); - if (elementType === "multi-select") { - element.setAttribute("multiple", true); - } - field.append(element); - } - else if (customElements.get(elementType)) { - element = document.createElement(elementType); - if (attributes.options) { - for (const option of attributes.options) { - element.append(new Option(option)); - } - } - element.value = attributes.value; - element.id = id; - element.name = id; - element.required = true; - field.append(element); - } - - let unit; - - if (unitText) { - unit = document.createElement("p"); - unit.innerText = unitText; - field.append(unit); - } - else { - unit = document.createElement("div"); - unit.classList.add("hidden"); - field.append(unit); - } - - return { icon, label, element, unit }; -} -*/ diff --git a/web/templates/instance-card.go.tmpl b/web/templates/instance-card.go.tmpl index 818365e..ddc209b 100644 --- a/web/templates/instance-card.go.tmpl +++ b/web/templates/instance-card.go.tmpl @@ -38,17 +38,17 @@

{{.NodeStatus}}

- {{if eq .Status "running"}} + {{if and (eq .NodeStatus "online") (eq .Status "running")}} - {{else if eq .Status "stopped"}} + {{else if and (eq .NodeStatus "online") (eq .Status "stopped")}} - {{else if eq .Status "loading"}} + {{else if and (eq .NodeStatus "online") (eq .Status "loading")}}