1 Commits
Author SHA1 Message Date
alu e19d41ca22 code cleanup 2026-07-24 22:03:25 +00:00
22 changed files with 143 additions and 127 deletions
+2
View File
@@ -1,3 +1,5 @@
**/config.json **/config.json
**/package-lock.json
**/node_modules
dist/* dist/*
go.sum go.sum
+5 -12
View File
@@ -1,14 +1,13 @@
.PHONY: build build-web build-wfa-js clean clean-wfa-js ensure-dist workflow-init .PHONY: build test clean wfa-js
build: clean ensure-dist build-web build-wfa-js build: clean build-wfa-js
@echo "======================== Building Binary =======================" @echo "======================== Building Binary ======================="
mkdir -p dist
# resolve symbolic links in web by copying it into dist/web/ # resolve symbolic links in web by copying it into dist/web/
cp -rL web/ dist/web/
CGO_ENABLED=0 go build -tags release -ldflags="-s -w" -v -o dist/ . CGO_ENABLED=0 go build -tags release -ldflags="-s -w" -v -o dist/ .
build-web: ensure-dist build-wfa-js:
cp -rL web/ dist/web/
build-wfa-js: ensure-dist
$(MAKE) -C WFA-JS $(MAKE) -C WFA-JS
cp -f WFA-JS/dist/* web/modules cp -f WFA-JS/dist/* web/modules
@@ -19,9 +18,3 @@ clean: clean-wfa-js
clean-wfa-js: clean-wfa-js:
$(MAKE) clean -C WFA-JS $(MAKE) clean -C WFA-JS
ensure-dist:
mkdir -p dist
workflow-init: ensure-dist build-web
cp -r dev_config/. .
+1 -1
Submodule WFA-JS updated: f53f344fb3...f8f8636cc3
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"log" "log"
"proxmoxaas-dashboard/app/common" "proxmoxaas-dashboard/app/common"
"proxmoxaas-dashboard/app/routes" "proxmoxaas-dashboard/app/routes"
"web" // go will complain here until the first build "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"
-3
View File
@@ -1,3 +0,0 @@
web/modules/*
WFA-JS/*
dist/*
-3
View File
@@ -1,3 +0,0 @@
web/modules/*
WFA-JS/*
dist/*
+25 -29
View File
@@ -1,33 +1,29 @@
import { defineConfig, globalIgnores } from "eslint/config"; import { defineConfig } from "eslint/config";
import globals from "globals"; import globals from "globals";
import js from "@eslint/js"; import js from "@eslint/js";
export default defineConfig([ export default defineConfig([js.configs.recommended,{
js.configs.recommended, languageOptions: {
globalIgnores(["dist/", "web/modules", "WFA-JS"]), globals: {
{ ...globals.browser,
languageOptions: {
globals: {
...globals.browser,
},
ecmaVersion: "latest",
sourceType: "module",
}, },
rules: { ecmaVersion: "latest",
"no-tabs": ["error", { sourceType: "module",
allowIndentationTabs: true, },
}], rules: {
indent: ["error", "tab"], "no-tabs": ["error", {
"linebreak-style": ["error", "unix"], allowIndentationTabs: true,
quotes: ["error", "double"], }],
semi: ["error", "always"], indent: ["error", "tab"],
"brace-style": ["error", "stroustrup", { allowSingleLine: false }], "linebreak-style": ["error", "unix"],
"no-unused-vars": ["warn", { quotes: ["error", "double"],
"argsIgnorePattern": "^_", semi: ["error", "always"],
"varsIgnorePattern": "^_", "brace-style": ["error", "stroustrup", { allowSingleLine: false }],
"caughtErrorsIgnorePattern": "^_" "no-unused-vars": ["warn", {
}], "argsIgnorePattern": "^_",
"prefer-const": ["error"] "varsIgnorePattern": "^_",
}, "caughtErrorsIgnorePattern": "^_"
} }],
]); "prefer-const": ["error"]
},
}]);
-2
View File
@@ -8,11 +8,9 @@ require (
github.com/go-viper/mapstructure/v2 v2.5.0 github.com/go-viper/mapstructure/v2 v2.5.0
github.com/tdewolff/minify/v2 v2.24.13 github.com/tdewolff/minify/v2 v2.24.13
proxmoxaas-common-lib v0.0.0 proxmoxaas-common-lib v0.0.0
web v0.0.0
) )
replace proxmoxaas-common-lib => ./proxmoxaas-common-lib replace proxmoxaas-common-lib => ./proxmoxaas-common-lib
replace web => ./dist/web
require ( require (
github.com/bytedance/gopkg v0.1.4 // indirect github.com/bytedance/gopkg v0.1.4 // indirect
+18
View File
@@ -0,0 +1,18 @@
{
"name": "proxmoxaas-dashboard",
"version": "1.0.0",
"description": "Front-end for ProxmoxAAS",
"type": "module",
"scripts": {
"lint": "html-validate --config dev_config/.htmlvalidate.json web/html/*; stylelint --config dev_config/.stylelintrc.json --formatter verbose --fix web/css/*.css; DEBUG=eslint:cli-engine eslint --config dev_config/eslint.config.mjs --fix web/scripts/"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "^10.0.1",
"eslint": "^10.2.1",
"globals": "^17.5.0",
"html-validate": "^9.4.0",
"stylelint": "^15.9.0",
"stylelint-config-standard": "^33.0.0"
}
}
-13
View File
@@ -130,19 +130,6 @@ hr {
align-items: center; align-items: center;
} }
.row-reverse {
flex-direction: row-reverse;
row-gap: 10px;
align-items: center;
}
.column {
flex-direction: column;
row-gap: 10px;
align-items: center;
}
.column-reverse { .column-reverse {
flex-direction: column-reverse; flex-direction: column-reverse;
row-gap: 10px; row-gap: 10px;
-3
View File
@@ -1,3 +0,0 @@
module web
go 1.26.0
+2 -2
View File
@@ -58,9 +58,9 @@
<div id="body"> <div id="body">
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form"> <form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
<label for="new-password">New Password</label> <label for="new-password">New Password</label>
<input class="w3-input w3-border" id="new-password" name="new-password" type="password" autocomplete="new-password" required> <input class="w3-input w3-border" id="new-password" name="new-password" type="password" required>
<label for="confirm-password">Confirm Password</label> <label for="confirm-password">Confirm Password</label>
<input class="w3-input w3-border" id="confirm-password" name="confirm-password" type="password" autocomplete="new-password" required> <input class="w3-input w3-border" id="confirm-password" name="confirm-password" type="password" required>
</form> </form>
</div> </div>
<div id="controls" class="w3-center w3-container"> <div id="controls" class="w3-center w3-container">
+2 -2
View File
@@ -114,9 +114,9 @@
<label class="container-specific none" for="rootfs-size">ROOTFS Size (GiB)</label> <label class="container-specific none" for="rootfs-size">ROOTFS Size (GiB)</label>
<input class="w3-input w3-border container-specific none" name="rootfs-size" id="rootfs-size" type="number" min="0" max="131072" required disabled> <input class="w3-input w3-border container-specific none" name="rootfs-size" id="rootfs-size" type="number" min="0" max="131072" required disabled>
<label class="container-specific none" for="password">Password</label> <label class="container-specific none" for="password">Password</label>
<input class="w3-input w3-border container-specific none" name="password" id="password" type="password" autocomplete="new-password" required disabled> <input class="w3-input w3-border container-specific none" name="password" id="password" type="password" required disabled>
<label class="container-specific none" for="confirm-password">Confirm Password</label> <label class="container-specific none" for="confirm-password">Confirm Password</label>
<input class="w3-input w3-border container-specific none" name="confirm-password" id="confirm-password" type="password" autocomplete="new-password" required disabled> <input class="w3-input w3-border container-specific none" name="confirm-password" id="confirm-password" type="password" required disabled>
</form> </form>
</div> </div>
<div id="controls" class="w3-center w3-container"> <div id="controls" class="w3-center w3-container">
+1 -1
View File
@@ -560,7 +560,7 @@ export default function init (path) {
const wasm = obj.instance; const wasm = obj.instance;
global.wfa = wasm global.wfa = wasm
go.run(wasm); go.run(wasm);
res(wasm) res()
} }
if ('instantiateStreaming' in WebAssembly) { if ('instantiateStreaming' in WebAssembly) {
WebAssembly.instantiateStreaming(fetch(path), go.importObject).then(function (obj) { WebAssembly.instantiateStreaming(fetch(path), go.importObject).then(function (obj) {
+2 -2
View File
@@ -1,5 +1,5 @@
import { requestAPI, setAppearance } from "./utils.js"; import { requestAPI, setAppearance } from "./utils.js";
import { dialog, error } from "./dialog.js"; import { dialog } from "./dialog.js";
window.addEventListener("DOMContentLoaded", init); window.addEventListener("DOMContentLoaded", init);
@@ -15,7 +15,7 @@ function handlePasswordChangeButton () {
if (result === "confirm") { if (result === "confirm") {
const result = await requestAPI("/access/password", "POST", { password: form.get("new-password") }); const result = await requestAPI("/access/password", "POST", { password: form.get("new-password") });
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to change password but got: ${result.error}`); alert(`Attempted to change password but got: ${result.error}`);
} }
} }
}); });
+11 -7
View File
@@ -1,5 +1,5 @@
import { requestAPI, getURIData, setAppearance, requestDash } from "./utils.js"; import { requestAPI, getURIData, setAppearance, requestDash } from "./utils.js";
import { error, dialog } from "./dialog.js"; import { alert, dialog } from "./dialog.js";
window.addEventListener("DOMContentLoaded", init); window.addEventListener("DOMContentLoaded", init);
@@ -74,7 +74,7 @@ class BackupCard extends HTMLElement {
}; };
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/backup/notes`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/backup/notes`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to edit backup but got: ${result.error}`); alert(`Attempted to edit backup but got: ${result.error}`);
} }
refreshBackups(); refreshBackups();
} }
@@ -90,7 +90,7 @@ class BackupCard extends HTMLElement {
}; };
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/backup`, "DELETE", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/backup`, "DELETE", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to delete backup but got: ${result.error}`); alert(`Attempted to delete backup but got: ${result.error}`);
} }
refreshBackups(); refreshBackups();
} }
@@ -106,7 +106,7 @@ class BackupCard extends HTMLElement {
}; };
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/backup/restore`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/backup/restore`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to delete backup but got: ${result.error}`); alert(`Attempted to delete backup but got: ${result.error}`);
} }
refreshBackups(); refreshBackups();
} }
@@ -116,10 +116,14 @@ class BackupCard extends HTMLElement {
customElements.define("backup-card", BackupCard); customElements.define("backup-card", BackupCard);
async function getBackupsFragment () {
return await requestDash(`/backups/backups?node=${node}&type=${type}&vmid=${vmid}`, "GET");
}
async function refreshBackups () { async function refreshBackups () {
let backups = await requestDash(`/backups/backups?node=${node}&type=${type}&vmid=${vmid}`, "GET"); let backups = await getBackupsFragment();
if (backups.status !== 200) { if (backups.status !== 200) {
error("Error fetching backups."); alert("Error fetching backups.");
} }
else { else {
backups = backups.data; backups = backups.data;
@@ -137,7 +141,7 @@ async function handleBackupAddButton () {
}; };
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/backup`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/backup`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to create backup but got: ${result.error}`); alert(`Attempted to create backup but got: ${result.error}`);
} }
refreshBackups(); refreshBackups();
} }
+2 -3
View File
@@ -1,5 +1,4 @@
import { getSetting, requestAPI } from "./utils.js"; import { getSetting, requestAPI } from "./utils.js";
import { error } from "./dialog.js";
export async function setupClientSync (callback) { export async function setupClientSync (callback) {
const scheme = getSetting("sync-scheme"); const scheme = getSetting("sync-scheme");
@@ -32,12 +31,12 @@ export async function setupClientSync (callback) {
callback(); callback();
} }
else { else {
error("clientsync: recieved unexpected message from server, closing socket."); console.error("clientsync: recieved unexpected message from server, closing socket.");
socket.close(); socket.close();
} }
}); });
} }
else { else {
error(`clientsync: unsupported scheme ${scheme} selected.`); console.error(`clientsync: unsupported scheme ${scheme} selected.`);
} }
} }
+21 -21
View File
@@ -1,5 +1,5 @@
import { requestPVE, requestAPI, goToPage, getURIData, setAppearance, setIconSrc, requestDash } from "./utils.js"; import { requestPVE, requestAPI, goToPage, getURIData, setAppearance, setIconSrc, requestDash } from "./utils.js";
import { error, dialog } from "./dialog.js"; import { alert, dialog } from "./dialog.js";
window.addEventListener("DOMContentLoaded", init); window.addEventListener("DOMContentLoaded", init);
@@ -59,7 +59,7 @@ class VolumeAction extends HTMLElement {
this.setStatusLoading(); this.setStatusLoading();
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/detach`, "POST"); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/detach`, "POST");
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to detach ${disk} but got: ${result.error}`); alert(`Attempted to detach ${disk} but got: ${result.error}`);
} }
refreshVolumes(); refreshVolumes();
refreshBoot(); refreshBoot();
@@ -80,7 +80,7 @@ class VolumeAction extends HTMLElement {
const disk = `${prefix}${device}`; const disk = `${prefix}${device}`;
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/attach`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/attach`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to attach ${this.dataset.volume} to ${disk} but got: ${result.error}`); alert(`Attempted to attach ${this.dataset.volume} to ${disk} but got: ${result.error}`);
} }
refreshVolumes(); refreshVolumes();
refreshBoot(); refreshBoot();
@@ -98,7 +98,7 @@ class VolumeAction extends HTMLElement {
}; };
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/resize`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/resize`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to resize ${disk} but got: ${result.error}`); alert(`Attempted to resize ${disk} but got: ${result.error}`);
} }
refreshVolumes(); refreshVolumes();
refreshBoot(); refreshBoot();
@@ -117,7 +117,7 @@ class VolumeAction extends HTMLElement {
}; };
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/move`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/move`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to move ${disk} to ${body.storage} but got: ${result.error}`); alert(`Attempted to move ${disk} to ${body.storage} but got: ${result.error}`);
} }
refreshVolumes(); refreshVolumes();
refreshBoot(); refreshBoot();
@@ -141,7 +141,7 @@ class VolumeAction extends HTMLElement {
this.setStatusLoading(); this.setStatusLoading();
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/delete`, "DELETE"); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/delete`, "DELETE");
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to delete ${disk} but got: ${result.error}`); alert(`Attempted to delete ${disk} but got: ${result.error}`);
} }
refreshVolumes(); refreshVolumes();
refreshBoot(); refreshBoot();
@@ -162,7 +162,7 @@ async function initVolumes () {
async function refreshVolumes () { async function refreshVolumes () {
let volumes = await requestDash(`/config/volumes?node=${node}&type=${type}&vmid=${vmid}`, "GET"); let volumes = await requestDash(`/config/volumes?node=${node}&type=${type}&vmid=${vmid}`, "GET");
if (volumes.status !== 200) { if (volumes.status !== 200) {
error("Error fetching instance volumes."); alert("Error fetching instance volumes.");
} }
else { else {
volumes = volumes.data; volumes = volumes.data;
@@ -186,7 +186,7 @@ async function handleDiskAdd () {
const disk = `${prefix}${id}`; const disk = `${prefix}${id}`;
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/create`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/create`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to create ${disk} but got: ${result.error}`); alert(`Attempted to create ${disk} but got: ${result.error}`);
} }
refreshVolumes(); refreshVolumes();
refreshBoot(); refreshBoot();
@@ -214,7 +214,7 @@ async function handleCDAdd () {
const disk = `ide${form.get("device")}`; const disk = `ide${form.get("device")}`;
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/create`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/disk/${disk}/create`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to mount ${body.iso} to ${disk} but got: result.error`); alert(`Attempted to mount ${body.iso} to ${disk} but got: result.error`);
} }
refreshVolumes(); refreshVolumes();
refreshBoot(); refreshBoot();
@@ -263,7 +263,7 @@ class NetworkAction extends HTMLElement {
const net = `${netID}`; const net = `${netID}`;
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/net/${net}/modify`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/net/${net}/modify`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to change ${net} but got: ${result.error}`); alert(`Attempted to change ${net} but got: ${result.error}`);
} }
refreshNetworks(); refreshNetworks();
refreshBoot(); refreshBoot();
@@ -281,7 +281,7 @@ class NetworkAction extends HTMLElement {
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) {
error(`Attempted to delete ${net} but got: ${result.error}`); alert(`Attempted to delete ${net} but got: ${result.error}`);
} }
refreshNetworks(); refreshNetworks();
refreshBoot(); refreshBoot();
@@ -299,7 +299,7 @@ async function initNetworks () {
async function refreshNetworks () { async function refreshNetworks () {
let nets = await requestDash(`/config/nets?node=${node}&type=${type}&vmid=${vmid}`, "GET"); let nets = await requestDash(`/config/nets?node=${node}&type=${type}&vmid=${vmid}`, "GET");
if (nets.status !== 200) { if (nets.status !== 200) {
error("Error fetching instance nets."); alert("Error fetching instance nets.");
} }
else { else {
nets = nets.data; nets = nets.data;
@@ -324,7 +324,7 @@ async function handleNetworkAdd () {
const net = `net${id}`; const net = `net${id}`;
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/net/${net}/create`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/net/${net}/create`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to create ${net} but got: ${result.error}`); alert(`Attempted to create ${net} but got: ${result.error}`);
} }
refreshNetworks(); refreshNetworks();
refreshBoot(); refreshBoot();
@@ -367,7 +367,7 @@ class DeviceAction extends HTMLElement {
const device = `${deviceID}`; const device = `${deviceID}`;
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/pci/${device}/modify`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/pci/${device}/modify`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to add ${device} but got: ${result.error}`); alert(`Attempted to add ${device} but got: ${result.error}`);
} }
refreshDevices(); refreshDevices();
} }
@@ -389,7 +389,7 @@ class DeviceAction extends HTMLElement {
const device = `${deviceID}`; const device = `${deviceID}`;
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/pci/${device}/delete`, "DELETE"); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/pci/${device}/delete`, "DELETE");
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to delete ${device} but got: ${result.error}`); alert(`Attempted to delete ${device} but got: ${result.error}`);
} }
refreshDevices(); refreshDevices();
} }
@@ -408,7 +408,7 @@ async function initDevices () {
async function refreshDevices () { async function refreshDevices () {
let devices = await requestDash(`/config/devices?node=${node}&type=${type}&vmid=${vmid}`, "GET"); let devices = await requestDash(`/config/devices?node=${node}&type=${type}&vmid=${vmid}`, "GET");
if (devices.status !== 200) { if (devices.status !== 200) {
error("Error fetching instance devices."); alert("Error fetching instance devices.");
} }
else { else {
devices = devices.data; devices = devices.data;
@@ -431,7 +431,7 @@ async function handleDeviceAdd () {
const deviceID = `hostpci${hostpci}`; const deviceID = `hostpci${hostpci}`;
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/pci/${deviceID}/create`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/pci/${deviceID}/create`, "POST", body);
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to add ${body.device} but got: ${result.error}`); alert(`Attempted to add ${body.device} but got: ${result.error}`);
} }
refreshDevices(); refreshDevices();
} }
@@ -447,12 +447,12 @@ async function handleDeviceAdd () {
async function refreshBoot () { async function refreshBoot () {
let boot = await requestDash(`/config/boot?node=${node}&type=${type}&vmid=${vmid}`, "GET"); let boot = await requestDash(`/config/boot?node=${node}&type=${type}&vmid=${vmid}`, "GET");
if (boot.status !== 200) { if (boot.status !== 200) {
error("Error fetching instance boot order."); alert("Error fetching instance boot order.");
} }
else if (type === "qemu") { else if (type === "qemu") {
boot = boot.data; boot = boot.data;
const container = document.querySelector("#boot-order"); const order = document.querySelector("#boot-order");
container.setHTMLUnsafe(boot); order.setHTMLUnsafe(boot);
} }
} }
@@ -474,6 +474,6 @@ async function handleFormExit (event) {
goToPage("index"); goToPage("index");
} }
else { else {
error(`Attempted to set basic resources but got: ${result.error}`); alert(`Attempted to set basic resources but got: ${result.error}`);
} }
} }
+31 -6
View File
@@ -41,6 +41,34 @@ export function dialog (template, onclose = async (_result, _form) => { }) {
return dialog; return dialog;
} }
export function alert (message) {
const dialog = document.querySelector("#alert-dialog");
if (dialog == null) {
const dialog = document.createElement("dialog");
dialog.id = "alert-dialog";
dialog.innerHTML = `
<form method="dialog">
<p class="w3-large" id="prompt">Alert</p>
<p class="w3-center" style="margin-bottom: 0;">${message}</p>
<div class="w3-center">
<button class="w3-button w3-margin" id="submit">OK</button>
</div>
</form>
`;
dialog.className = "w3-container w3-card w3-border-0";
document.body.append(dialog);
dialog.showModal();
dialog.addEventListener("close", () => {
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 { class ErrorDialog extends HTMLElement {
shadowRoot = null; shadowRoot = null;
dialog = null; dialog = null;
@@ -59,18 +87,15 @@ class ErrorDialog extends HTMLElement {
max-height: 20lh; max-height: 20lh;
min-height: 20lh; min-height: 20lh;
overflow-y: scroll; overflow-y: scroll;
padding-left: 12px;
padding-right: 12px;
} }
#errors * { #errors * {
margin: 0; margin: 0;
width: 100%;
} }
</style> </style>
<dialog class="w3-container w3-card w3-border-0"> <dialog class="w3-container w3-card w3-border-0">
<form method="dialog"> <form method="dialog">
<p class="w3-large" id="prompt">Error</p> <p class="w3-large" id="prompt">Error</p>
<div id="errors" class="flex column"></div> <div id="errors" class="flex column-reverse"></div>
<div class="w3-center" id="controls"> <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="ok">OK</button>
<button class="w3-button w3-margin" type="submit" value="copy">Copy</button> <button class="w3-button w3-margin" type="submit" value="copy">Copy</button>
@@ -96,7 +121,7 @@ class ErrorDialog extends HTMLElement {
} }
navigator.clipboard.writeText(errors); navigator.clipboard.writeText(errors);
} }
this.dialog.close(); this.parentElement.removeChild(this);
}); });
} }
@@ -116,7 +141,7 @@ customElements.define("error-dialog", ErrorDialog);
export function error (message) { export function error (message) {
let dialog = document.querySelector("error-dialog"); let dialog = document.querySelector("error-dialog");
if (dialog === null) { if (dialog == null) {
dialog = document.createElement("error-dialog"); dialog = document.createElement("error-dialog");
document.body.append(dialog); document.body.append(dialog);
dialog.appendError(message); dialog.appendError(message);
+12 -10
View File
@@ -1,16 +1,14 @@
import { requestPVE, requestAPI, setAppearance, getSetting, requestDash, setIconSrc, setIconAlt } from "./utils.js"; import { requestPVE, requestAPI, setAppearance, getSetting, requestDash, setIconSrc, setIconAlt } from "./utils.js";
import { dialog, error } 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";
window.addEventListener("DOMContentLoaded", init); window.addEventListener("DOMContentLoaded", init);
var wfa;
async function init () { async function init () {
setAppearance(); setAppearance();
wfa = await wfaInit("modules/wfa.wasm"); wfaInit("modules/wfa.wasm");
initInstances(); initInstances();
document.querySelector("#instance-add").addEventListener("click", handleInstanceAddButton); document.querySelector("#instance-add").addEventListener("click", handleInstanceAddButton);
@@ -177,7 +175,7 @@ class InstanceCard extends HTMLElement {
break; break;
} }
else if (taskStatus.data.status === "stopped") { // task stopped but was not successful else if (taskStatus.data.status === "stopped") { // task stopped but was not successful
error(`Attempted to ${targetAction} ${this.vmid} but got: ${taskStatus.data.exitstatus}`); alert(`Attempted to ${targetAction} ${this.vmid} but got: ${taskStatus.data.exitstatus}`);
break; break;
} }
else { // task has not stopped else { // task has not stopped
@@ -205,7 +203,7 @@ class InstanceCard extends HTMLElement {
const result = await requestAPI(`/cluster/${this.node.name}/${this.type}/${this.vmid}/delete`, "DELETE"); const result = await requestAPI(`/cluster/${this.node.name}/${this.type}/${this.vmid}/delete`, "DELETE");
if (result.status !== 200) { if (result.status !== 200) {
error(`Attempted to delete ${this.vmid} but got: ${result.error}`); alert(`Attempted to delete ${this.vmid} but got: ${result.error}`);
} }
this.actionLock = false; this.actionLock = false;
@@ -218,8 +216,12 @@ class InstanceCard extends HTMLElement {
customElements.define("instance-card", InstanceCard); customElements.define("instance-card", InstanceCard);
async function getInstancesFragment () {
return await requestDash("/index/instances", "GET");
}
async function refreshInstances () { async function refreshInstances () {
let instances = await requestDash("/index/instances", "GET"); let instances = await getInstancesFragment();
if (instances.status !== 200) { if (instances.status !== 200) {
error(`Error fetching instances: ${instances.status} ${instances.error !== undefined ? instances.error : ""}`); error(`Error fetching instances: ${instances.status} ${instances.error !== undefined ? instances.error : ""}`);
} }
@@ -274,8 +276,8 @@ function sortInstances () {
}; };
criteria = (item, query) => { criteria = (item, query) => {
// lower is better // lower is better
const { score, CIGAR } = wfa.wfAlign(query, item, penalties, true); const { score, CIGAR } = global.wfa.wfAlign(query, item, penalties, true);
const alignment = wfa.DecodeCIGAR(CIGAR); const alignment = global.wfa.DecodeCIGAR(CIGAR);
return { score: score / item.length, alignment }; return { score: score / item.length, alignment };
}; };
} }
@@ -335,7 +337,7 @@ async function handleInstanceAddButton () {
refreshInstances(); refreshInstances();
} }
else { else {
error(`Attempted to create new instance ${vmid} but got: ${result.error}`); alert(`Attempted to create new instance ${vmid} but got: ${result.error}`);
refreshInstances(); refreshInstances();
} }
} }
+6 -5
View File
@@ -1,12 +1,11 @@
import { goToPage, setAppearance, requestAPI } from "./utils.js"; import { goToPage, setAppearance, requestAPI } from "./utils.js";
import { error } from "./dialog.js"; import { alert } from "./dialog.js";
window.addEventListener("DOMContentLoaded", init); window.addEventListener("DOMContentLoaded", init);
async function init () { async function init () {
await deleteAllCookies(); await deleteAllCookies();
setAppearance(); setAppearance();
const formSubmitButton = document.querySelector("#submit"); const formSubmitButton = document.querySelector("#submit");
formSubmitButton.addEventListener("click", async (e) => { formSubmitButton.addEventListener("click", async (e) => {
e.preventDefault(); e.preventDefault();
@@ -20,16 +19,18 @@ async function init () {
goToPage("index"); goToPage("index");
} }
else if (ticket.status === 401) { else if (ticket.status === 401) {
error("Authenticaton failed."); alert("Authenticaton failed.");
formSubmitButton.innerText = "LOGIN"; formSubmitButton.innerText = "LOGIN";
} }
else if (ticket.status === 408) { else if (ticket.status === 408) {
error("Network error."); alert("Network error.");
formSubmitButton.innerText = "LOGIN"; formSubmitButton.innerText = "LOGIN";
} }
else { else {
error(`An error occured: ${JSON.stringify(ticket)}`); alert("An error occured.");
console.error(ticket);
formSubmitButton.innerText = "LOGIN"; formSubmitButton.innerText = "LOGIN";
console.error(ticket.error);
} }
}); });
} }