diff --git a/scripts/config.js b/scripts/config.js index 586a223..256b8c3 100644 --- a/scripts/config.js +++ b/scripts/config.js @@ -1,5 +1,5 @@ -import {requestPVE, requestAPI, goToPage, getURIData, resources_config, setTitleAndHeader} from "./utils.js"; -import {alert, dialog} from "./dialog.js"; +import { requestPVE, requestAPI, goToPage, getURIData, resources_config, setTitleAndHeader } from "./utils.js"; +import { alert, dialog } from "./dialog.js"; window.addEventListener("DOMContentLoaded", init); // do the dumb thing where the disk config refreshes every second @@ -11,13 +11,13 @@ let type; let vmid; let config; -async function init () { +async function init() { setTitleAndHeader(); let cookie = document.cookie; if (cookie === "") { goToPage("login.html"); } - + let uriData = getURIData(); node = uriData.node; type = uriData.type; @@ -32,27 +32,27 @@ async function init () { document.querySelector("#exit").addEventListener("click", handleFormExit); } -function getOrdered(keys){ - let ordered_keys = Object.keys(keys).sort((a,b) => {parseInt(a) - parseInt(b)}); // ordered integer list +function getOrdered(keys) { + let ordered_keys = Object.keys(keys).sort((a, b) => { parseInt(a) - parseInt(b) }); // ordered integer list return ordered_keys; } -async function getConfig () { +async function getConfig() { config = await requestPVE(`/nodes/${node}/${type}/${vmid}/config`, "GET"); } -function populateResources () { +function populateResources() { let name = type === "qemu" ? "name" : "hostname"; document.querySelector("#name").innerHTML = document.querySelector("#name").innerHTML.replace("%{vmname}", config.data[name]); - addResourceLine("resources", "images/resources/cpu.svg", "Cores", {type: "number", value: config.data.cores, min: 1, max: 8192}, "Threads"); - addResourceLine("resources", "images/resources/ram.svg", "Memory", {type: "number", value: config.data.memory, min: 16, step: 1}, "MiB"); - + addResourceLine("resources", "images/resources/cpu.svg", "Cores", { type: "number", value: config.data.cores, min: 1, max: 8192 }, "Threads"); + addResourceLine("resources", "images/resources/ram.svg", "Memory", { type: "number", value: config.data.memory, min: 16, step: 1 }, "MiB"); + if (type === "lxc") { - addResourceLine("resources", "images/resources/swap.svg", "Swap", {type: "number", value: config.data.swap, min: 0, step: 1}, "MiB"); + addResourceLine("resources", "images/resources/swap.svg", "Swap", { type: "number", value: config.data.swap, min: 0, step: 1 }, "MiB"); } } -function addResourceLine (fieldset, iconHref, labelText, inputAttr, unitText=null) { +function addResourceLine(fieldset, iconHref, labelText, inputAttr, unitText = null) { let field = document.querySelector(`#${fieldset}`); let icon = document.createElement("img"); @@ -83,9 +83,9 @@ function addResourceLine (fieldset, iconHref, labelText, inputAttr, unitText=nul } } -function populateDisk () { +function populateDisk() { document.querySelector("#disks").innerHTML = ""; - for(let i = 0; i < diskMetaData[type].prefixOrder.length; i++){ + for (let i = 0; i < diskMetaData[type].prefixOrder.length; i++) { let prefix = diskMetaData[type].prefixOrder[i]; let busName = diskMetaData[type][prefix].name; let disks = {}; @@ -108,12 +108,12 @@ function populateDisk () { } } -function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) { +function addDiskLine(fieldset, busPrefix, busName, device, diskDetails) { let field = document.querySelector(`#${fieldset}`); let diskName = `${busName} ${device}`; let diskID = `${busPrefix}${device}`; - + // Set the disk icon, either drive.svg or disk.svg let icon = document.createElement("img"); icon.src = diskMetaData[type][busPrefix].icon; @@ -138,12 +138,12 @@ function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) { diskMetaData.actionBarOrder.forEach((element) => { let action = document.createElement("img"); action.classList.add("clickable"); - if (element === "detach_attach" && diskMetaData[type][busPrefix].actions.includes("attach")){ // attach + if (element === "detach_attach" && diskMetaData[type][busPrefix].actions.includes("attach")) { // attach action.src = "images/actions/disk/attach.svg"; action.title = "Attach Disk"; action.addEventListener("click", handleDiskAttach); } - else if (element === "detach_attach" && diskMetaData[type][busPrefix].actions.includes("detach")){ // detach + else if (element === "detach_attach" && diskMetaData[type][busPrefix].actions.includes("detach")) { // detach action.src = "images/actions/disk/detach.svg"; action.title = "Detach Disk"; action.addEventListener("click", handleDiskDetach); @@ -154,7 +154,7 @@ function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) { action.title = "Delete Disk"; if (active === "active") { action.addEventListener("click", handleDiskDelete); - } + } } else { let active = diskMetaData[type][busPrefix].actions.includes(element) ? "active" : "inactive"; // resize @@ -176,7 +176,7 @@ function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) { field.append(actionDiv); } -async function handleDiskDetach () { +async function handleDiskDetach() { let header = `Detach ${this.dataset.disk}`; let body = `

Are you sure you want to detach disk

${this.dataset.disk}

`; dialog(header, body, async (result, form) => { @@ -202,7 +202,7 @@ async function handleDiskDetach () { }); } -async function handleDiskAttach () { +async function handleDiskAttach() { let diskImage = config.data[this.dataset.disk]; let header = `Attach ${diskImage}`; @@ -211,7 +211,7 @@ async function handleDiskAttach () { dialog(header, body, async (result, form) => { if (result === "confirm") { let device = form.get("device"); - document.querySelector(`img[data-disk="${this.dataset.disk}"]`).src = "images/status/loading.svg"; + document.querySelector(`img[data-disk="${this.dataset.disk}"]`).src = "images/status/loading.svg"; let body = { node: node, type: type, @@ -233,8 +233,8 @@ async function handleDiskAttach () { }); } -async function handleDiskResize () { - let header = `Resize ${this.dataset.disk}`; +async function handleDiskResize() { + let header = `Resize ${this.dataset.disk}`; let body = ``; dialog(header, body, async (result, form) => { @@ -252,7 +252,7 @@ async function handleDiskResize () { await getConfig(); populateDisk(); } - else{ + else { alert(result.error); await getConfig(); populateDisk(); @@ -261,7 +261,7 @@ async function handleDiskResize () { }); } -async function handleDiskMove () { +async function handleDiskMove() { let content = type === "qemu" ? "images" : "rootdir"; let storage = await requestPVE(`/nodes/${node}/storage`, "GET"); @@ -269,7 +269,7 @@ async function handleDiskMove () { let options = ""; storage.data.forEach((element) => { - if (element.content.includes(content)){ + if (element.content.includes(content)) { options += `"`; } }); @@ -289,7 +289,7 @@ async function handleDiskMove () { vmid: vmid, disk: this.dataset.disk, storage: form.get("storage-select"), - delete: form.get("delete-check") === "on" ? "1": "0" + delete: form.get("delete-check") === "on" ? "1" : "0" } let result = await requestAPI("/instance/disk/move", "POST", body); if (result.status === 200) { @@ -305,7 +305,7 @@ async function handleDiskMove () { }); } -async function handleDiskDelete () { +async function handleDiskDelete() { let header = `Delete ${this.dataset.disk}`; let body = `

Are you sure you want to delete disk

${this.dataset.disk}

`; @@ -332,15 +332,15 @@ async function handleDiskDelete () { }); } -async function handleDiskAdd () { +async function handleDiskAdd() { let content = type === "qemu" ? "images" : "rootdir"; let storage = await requestPVE(`/nodes/${node}/storage`, "GET"); - + let header = "Create New Disk"; let options = ""; storage.data.forEach((element) => { - if (element.content.includes(content)){ + if (element.content.includes(content)) { options += `"`; } }); @@ -376,15 +376,15 @@ async function handleDiskAdd () { }); } -async function handleCDAdd () { +async function handleCDAdd() { let content = "iso"; let storage = await requestPVE(`/nodes/${node}/storage`, "GET"); - + let header = `Add a CDROM`; let storageOptions = ""; storage.data.forEach((element) => { - if (element.content.includes(content)){ + if (element.content.includes(content)) { storageOptions += `"`; } }); @@ -422,7 +422,7 @@ async function handleCDAdd () { let storage = document.querySelector("#storage-select").value; let ISOSelect = document.querySelector("#iso-select"); ISOSelect.innerHTML = ``; - let isos = await requestPVE(`/nodes/${node}/storage/${storage}/content`, "GET", {content: content}); + let isos = await requestPVE(`/nodes/${node}/storage/${storage}/content`, "GET", { content: content }); isos.data.forEach((element) => { if (element.content.includes(content)) { ISOSelect.append(new Option(element.volid.replace(`${storage}:${content}/`, ""), element.volid)); @@ -431,7 +431,7 @@ async function handleCDAdd () { }); } -function populateNetworks () { +function populateNetworks() { document.querySelector("#networks").innerHTML = ""; let networks = {}; let prefix = networkMetaData.prefix; @@ -446,7 +446,7 @@ function populateNetworks () { }); } -function addNetworkLine (fieldset, prefix, netID, netDetails) { +function addNetworkLine(fieldset, prefix, netID, netDetails) { let field = document.querySelector(`#${fieldset}`); let icon = document.createElement("img"); @@ -481,12 +481,12 @@ function addNetworkLine (fieldset, prefix, netID, netDetails) { field.append(actionDiv); } -async function handleNetworkConfig () { +async function handleNetworkConfig() { let netID = this.dataset.network; let netDetails = this.dataset.netvals; let header = `Edit ${netID}`; let body = ``; - + let d = dialog(header, body, async (result, form) => { if (result === "confirm") { document.querySelector(`img[data-network="${netID}"]`).src = "images/status/loading.svg"; @@ -502,7 +502,7 @@ async function handleNetworkConfig () { await getConfig(); populateNetworks(); } - else{ + else { alert(result.error); await getConfig(); populateNetworks(); @@ -513,7 +513,7 @@ async function handleNetworkConfig () { d.querySelector("#rate").value = netDetails.split("rate=")[1].split(",")[0]; } -async function handleFormExit () { +async function handleFormExit() { let body = { node: node, type: type, diff --git a/scripts/dialog.js b/scripts/dialog.js index 40fe005..7d8e21e 100644 --- a/scripts/dialog.js +++ b/scripts/dialog.js @@ -1,4 +1,4 @@ -export function dialog (header, body, callback = async (result, form) => {}) { +export function dialog(header, body, callback = async (result, form) => { }) { let dialog = document.createElement("dialog"); dialog.innerHTML = `

@@ -23,7 +23,7 @@ export function dialog (header, body, callback = async (result, form) => {}) { return dialog; } -export function alert (message) { +export function alert(message) { let dialog = document.createElement("dialog"); dialog.innerHTML = `
diff --git a/scripts/index.js b/scripts/index.js index 4ac0784..56c2c22 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -1,23 +1,23 @@ -import {requestPVE, requestAPI, goToPage, goToURL, instances_config, nodes_config, setTitleAndHeader} from "./utils.js"; -import {alert, dialog} from "./dialog.js"; -import {PVE} from "../vars.js" +import { requestPVE, requestAPI, goToPage, goToURL, instances_config, nodes_config, setTitleAndHeader } from "./utils.js"; +import { alert, dialog } from "./dialog.js"; +import { PVE } from "../vars.js" window.addEventListener("DOMContentLoaded", init); -async function init () { +async function init() { setTitleAndHeader(); let cookie = document.cookie; if (cookie === "") { goToPage("login.html"); } - + await populateInstances(); let addInstanceBtn = document.querySelector("#instance-add"); addInstanceBtn.addEventListener("click", handleInstanceAdd); } -async function populateInstances () { +async function populateInstances() { let resources = await requestPVE("/cluster/resources", "GET"); let instanceContainer = document.getElementById("instance-container"); let instances = []; @@ -26,7 +26,7 @@ async function populateInstances () { if (element.type === "lxc" || element.type === "qemu") { let nodeName = element.node; let nodeStatus = resources.data.find(item => item.node === nodeName && item.type === "node").status; - element.node = {name: nodeName, status: nodeStatus}; + element.node = { name: nodeName, status: nodeStatus }; instances.push(element); } }); @@ -58,14 +58,14 @@ async function populateInstances () { `; - for(let i = 0; i < instances.length; i++) { + for (let i = 0; i < instances.length; i++) { let newInstance = new Instance(); newInstance.data = instances[i]; instanceContainer.append(newInstance.shadowElement); } } -async function handleInstanceAdd () { +async function handleInstanceAdd() { let header = "Create New Instance"; let body = ` @@ -130,7 +130,7 @@ async function handleInstanceAdd () { let typeSelect = d.querySelector("#type"); typeSelect.selectedIndex = -1; typeSelect.addEventListener("change", () => { - if(typeSelect.value === "qemu") { + if (typeSelect.value === "qemu") { d.querySelectorAll(".container-specific").forEach((element) => { element.classList.add("none"); element.disabled = true; @@ -198,7 +198,7 @@ async function handleInstanceAdd () { } export class Instance { - constructor () { + constructor() { let shadowRoot = document.createElement("div"); shadowRoot.classList.add("w3-row"); @@ -235,7 +235,7 @@ export class Instance { this.actionLock = false; } - set data (data) { + set data(data) { if (data.status === "unknown") { data.status = "stopped"; } @@ -247,7 +247,7 @@ export class Instance { this.update(); } - update () { + update() { let vmidParagraph = this.shadowElement.querySelector("#instance-id"); vmidParagraph.innerText = this.vmid; @@ -306,9 +306,9 @@ export class Instance { } } - async handlePowerButton () { + async handlePowerButton() { - if(!this.actionLock) { + if (!this.actionLock) { let header = `${this.status === "running" ? "Stop" : "Start"} VM ${this.vmid}`; let body = `

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

${this.vmid}

` @@ -322,13 +322,13 @@ export class Instance { this.update(); - let result = await requestPVE(`/nodes/${this.node.name}/${this.type}/${this.vmid}/status/${targetAction}`, "POST", {node: this.node.name, vmid: this.vmid}); + let result = await requestPVE(`/nodes/${this.node.name}/${this.type}/${this.vmid}/status/${targetAction}`, "POST", { node: this.node.name, vmid: this.vmid }); const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay)); while (true) { let taskStatus = await requestPVE(`/nodes/${this.node.name}/tasks/${result.data}/status`, "GET"); - if(taskStatus.data.status === "stopped" && taskStatus.data.exitstatus === "OK") { // task stopped and was successful + if (taskStatus.data.status === "stopped" && taskStatus.data.exitstatus === "OK") { // task stopped and was successful this.status = targetStatus; this.update(); this.actionLock = false; @@ -341,7 +341,7 @@ export class Instance { this.actionLock = false; break; } - else{ // task has not stopped + else { // task has not stopped await waitFor(1000); } } @@ -350,21 +350,21 @@ export class Instance { } } - handleConfigButton () { + handleConfigButton() { if (!this.actionLock && this.status === "stopped") { // if the action lock is false, and the node is stopped, then navigate to the conig page with the node infor in the search query - goToPage("config.html", {node: this.node.name, type: this.type, vmid: this.vmid}); + goToPage("config.html", { node: this.node.name, type: this.type, vmid: this.vmid }); } } - handleConsoleButton () { + handleConsoleButton() { if (!this.actionLock && this.status === "running") { - let data = {console: `${this.type === "qemu" ? "kvm" : "lxc"}`, vmid: this.vmid, vmname: this.name, node: this.node.name, resize: "off", cmd: ""}; + let data = { console: `${this.type === "qemu" ? "kvm" : "lxc"}`, vmid: this.vmid, vmname: this.name, node: this.node.name, resize: "off", cmd: "" }; data[`${this.type === "qemu" ? "novnc" : "xtermjs"}`] = 1; goToURL(PVE, data, true); } } - handleDeleteButton () { + handleDeleteButton() { if (!this.actionLock && this.status === "stopped") { let header = `Delete VM ${this.vmid}`; diff --git a/scripts/login.js b/scripts/login.js index 37c3332..a803c81 100644 --- a/scripts/login.js +++ b/scripts/login.js @@ -1,9 +1,9 @@ -import {requestTicket, goToPage, deleteAllCookies, requestPVE, setTitleAndHeader} from "./utils.js"; -import {alert} from "./dialog.js"; +import { requestTicket, goToPage, deleteAllCookies, requestPVE, setTitleAndHeader } from "./utils.js"; +import { alert } from "./dialog.js"; window.addEventListener("DOMContentLoaded", init); -async function init (){ +async function init() { setTitleAndHeader(); await deleteAllCookies(); let formSubmitButton = document.querySelector("#submit"); @@ -11,7 +11,7 @@ async function init (){ let realmSelect = document.querySelector("#realm"); realms.data.forEach((element) => { realmSelect.add(new Option(element.comment, element.realm)); - if("default" in element && element.default === 1){ + if ("default" in element && element.default === 1) { realmSelect.value = element.realm; } }); @@ -23,7 +23,7 @@ async function init (){ formSubmitButton.innerText = "Authenticating..."; let ticket = await requestTicket(formData.get("username"), formData.get("password"), formData.get("realm")); if (ticket.status === 200) { - formSubmitButton.innerText = "LOGIN"; + formSubmitButton.innerText = "LOGIN"; goToPage("index.html"); } else if (ticket.status === 401) { diff --git a/scripts/utils.js b/scripts/utils.js index 5f5a2ef..973270a 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -1,19 +1,19 @@ -import {API, organization} from "/vars.js"; +import { API, organization } from "/vars.js"; export const resources_config = { 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"]} + 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", "unused"], - ide: {name: "IDE", icon: "images/resources/disk.svg", actions: ["delete"]}, - sata: {name: "SATA", icon: "images/resources/drive.svg", actions: ["detach", "move", "reassign", "resize"]}, - unused: {name: "UNUSED", icon: "images/resources/drive.svg", actions: ["attach", "delete", "reassign"]} + ide: { name: "IDE", icon: "images/resources/disk.svg", actions: ["delete"] }, + sata: { name: "SATA", icon: "images/resources/drive.svg", actions: ["detach", "move", "reassign", "resize"] }, + unused: { name: "UNUSED", icon: "images/resources/drive.svg", actions: ["attach", "delete", "reassign"] } } }, network: { @@ -79,7 +79,7 @@ export function getCookie(cname) { let name = cname + "="; let decodedCookie = decodeURIComponent(document.cookie); let ca = decodedCookie.split(";"); - for(let i = 0; i < ca.length; i++) { + for (let i = 0; i < ca.length; i++) { let c = ca[i]; while (c.charAt(0) === " ") { c = c.substring(1); @@ -91,12 +91,12 @@ export function getCookie(cname) { return ""; } -export async function requestTicket (username, password, realm) { - let response = await requestAPI("/ticket", "POST", {username: `${username}@${realm}`, password: password}, false); +export async function requestTicket(username, password, realm) { + let response = await requestAPI("/ticket", "POST", { username: `${username}@${realm}`, password: password }, false); return response; } -export async function requestPVE (path, method, body = null) { +export async function requestPVE(path, method, body = null) { let prms = new URLSearchParams(body); let content = { method: method, @@ -106,7 +106,7 @@ export async function requestPVE (path, method, body = null) { "Content-Type": "application/x-www-form-urlencoded" } } - if(method === "POST") { + if (method === "POST") { content.body = prms.toString(); content.headers.CSRFPreventionToken = getCookie("CSRFPreventionToken"); } @@ -115,7 +115,7 @@ export async function requestPVE (path, method, body = null) { return response; } -export async function requestAPI (path, method, body = null) { +export async function requestAPI(path, method, body = null) { let prms = new URLSearchParams(body); let content = { method: method, @@ -136,7 +136,7 @@ export async function requestAPI (path, method, body = null) { return response; } -async function request (url, content) { +async function request(url, content) { let response = await fetch(url, content); let data = null; @@ -148,8 +148,8 @@ async function request (url, content) { data = null; } - if(!response.ok){ - return {status: response.status, error: data ? data.error : response.status}; + if (!response.ok) { + return { status: response.status, error: data ? data.error : response.status }; } else { data.status = response.status; @@ -157,9 +157,9 @@ async function request (url, content) { } } -export function goToPage (page, data={}, newwindow = false) { +export function goToPage(page, data = {}, newwindow = false) { let url = new URL(`https://${window.location.host}/${page}`); - for(let k in data) { + for (let k in data) { url.searchParams.append(k, data[k]); } @@ -171,9 +171,9 @@ export function goToPage (page, data={}, newwindow = false) { } } -export function goToURL (href, data={}, newwindow = false) { +export function goToURL(href, data = {}, newwindow = false) { let url = new URL(href); - for(let k in data) { + for (let k in data) { url.searchParams.append(k, data[k]); } @@ -185,16 +185,16 @@ export function goToURL (href, data={}, newwindow = false) { } } -export function getURIData () { +export function getURIData() { let url = new URL(window.location.href); return Object.fromEntries(url.searchParams); } -export async function deleteAllCookies () { +export async function deleteAllCookies() { await requestAPI("/ticket", "DELETE"); } -export function setTitleAndHeader () { +export function setTitleAndHeader() { document.title = `${organization} - client`; document.querySelector("h1").innerText = organization; } \ No newline at end of file