move addResourceLine to utils,

localize  page specific resource line values in page script
This commit is contained in:
Arthur Lu 2024-08-31 12:31:29 +00:00
parent 746136724f
commit 912cee33c7
2 changed files with 187 additions and 73 deletions

View File

@ -1,11 +1,11 @@
import { requestPVE, requestAPI, goToPage, getURIData, resourcesConfig, setTitleAndHeader, bootConfig, setAppearance, setSVGSrc, setSVGAlt } from "./utils.js"; import { requestPVE, requestAPI, goToPage, getURIData, resourcesConfig, setTitleAndHeader, bootConfig, setAppearance, setSVGSrc, setSVGAlt, mergeDeep, addResourceLine } from "./utils.js";
import { alert, dialog } from "./dialog.js"; import { alert, dialog } from "./dialog.js";
window.addEventListener("DOMContentLoaded", init); // do the dumb thing where the disk config refreshes every second window.addEventListener("DOMContentLoaded", init); // do the dumb thing where the disk config refreshes every second
const diskMetaData = resourcesConfig.disk; const diskMetaData = resourcesConfig.disk;
const networkMetaData = resourcesConfig.network; const networkMetaData = resourcesConfig.network;
const pcieMetaData = resourcesConfig.pcie; const pcieMetaData = resourcesConfig.pci;
const bootMetaData = bootConfig; const bootMetaData = bootConfig;
let node; let node;
@ -13,6 +13,33 @@ let type;
let vmid; let vmid;
let config; let config;
const resourceInputTypes = { // input types for each resource for config page
cpu: {
element: "select",
attributes: {}
},
cores: {
element: "input",
attributes: {
type: "number"
}
},
memory: {
element: "input",
attributes: {
type: "number"
}
},
swap: {
element: "input",
attributes: {
type: "number"
}
}
};
const resourcesConfigPage = mergeDeep({}, resourcesConfig, resourceInputTypes);
async function init () { async function init () {
setAppearance(); setAppearance();
setTitleAndHeader(); setTitleAndHeader();
@ -28,6 +55,9 @@ async function init () {
await getConfig(); await getConfig();
const name = type === "qemu" ? "name" : "hostname";
document.querySelector("#name").innerHTML = document.querySelector("#name").innerHTML.replace("%{vmname}", config.data[name]);
populateResources(); populateResources();
populateDisk(); populateDisk();
populateNetworks(); populateNetworks();
@ -49,8 +79,7 @@ async function getConfig () {
} }
async function populateResources () { async function populateResources () {
const name = type === "qemu" ? "name" : "hostname"; const field = document.querySelector("#resources");
document.querySelector("#name").innerHTML = document.querySelector("#name").innerHTML.replace("%{vmname}", config.data[name]);
if (type === "qemu") { if (type === "qemu") {
const global = await requestAPI("/global/config/resources"); const global = await requestAPI("/global/config/resources");
const user = await requestAPI("/user/config/resources"); const user = await requestAPI("/user/config/resources");
@ -76,63 +105,12 @@ async function populateResources () {
return a.localeCompare(b); return a.localeCompare(b);
}); });
} }
addResourceLine("resources", "images/resources/cpu.svg", "select", "CPU Type", "proctype", { value: config.data.cpu, options }); addResourceLine(resourcesConfigPage, field, "cpu", { value: config.data.cpu, options });
} }
addResourceLine("resources", "images/resources/cpu.svg", "input", "CPU Amount", "cores", { type: "number", value: config.data.cores, min: 1, max: 8192 }, "Cores"); addResourceLine(resourcesConfigPage, field, "cores", { value: config.data.cores, min: 1, max: 8192 });
addResourceLine("resources", "images/resources/ram.svg", "input", "Memory", "ram", { type: "number", value: config.data.memory, min: 16, step: 1 }, "MiB"); addResourceLine(resourcesConfigPage, field, "memory", { value: config.data.memory, min: 16, step: 1 });
if (type === "lxc") { if (type === "lxc") {
addResourceLine("resources", "images/resources/swap.svg", "input", "Swap", "swap", { type: "number", value: config.data.swap, min: 0, step: 1 }, "MiB"); addResourceLine(resourcesConfigPage, field, "swap", { value: config.data.swap, min: 0, step: 1 });
}
}
function addResourceLine (fieldset, iconHref, type, labelText, id, attributes, unitText = null) {
const field = document.querySelector(`#${fieldset}`);
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);
if (type === "input") {
const input = document.createElement("input");
for (const k in attributes) {
input.setAttribute(k, attributes[k]);
}
input.id = id;
input.name = id;
input.required = true;
input.classList.add("w3-input");
input.classList.add("w3-border");
field.append(input);
}
else if (type === "select") {
const select = document.createElement("select");
for (const option of attributes.options) {
select.append(new Option(option));
}
select.value = attributes.value;
select.id = id;
select.name = id;
select.required = true;
select.classList.add("w3-select");
select.classList.add("w3-border");
field.append(select);
}
if (unitText) {
const unit = document.createElement("p");
unit.innerText = unitText;
field.append(unit);
}
else {
const unit = document.createElement("div");
unit.classList.add("hidden");
field.append(unit);
} }
} }
@ -256,7 +234,7 @@ async function handleDiskAttach () {
const body = ` const 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="device">${type === "qemu" ? "SATA" : "MP"}</label> <label for="device">${type === "qemu" ? "SATA" : "MP"}</label>
<input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="${type === "qemu" ? "5" : "255"}" required></input> <input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="${type === "qemu" ? "5" : "255"}" required>
</form> </form>
`; `;
@ -285,7 +263,7 @@ async function handleDiskResize () {
const body = ` const 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="size-increment">Size Increment (GiB)</label> <label for="size-increment">Size Increment (GiB)</label>
<input class="w3-input w3-border" name="size-increment" id="size-increment" type="number" min="0" max="131072"></input> <input class="w3-input w3-border" name="size-increment" id="size-increment" type="number" min="0" max="131072">
</form> </form>
`; `;
@ -383,9 +361,9 @@ async function handleDiskAdd () {
const body = ` const 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="device">${type === "qemu" ? "SATA" : "MP"}</label><input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="${type === "qemu" ? "5" : "255"}" value="0" required></input> <label for="device">${type === "qemu" ? "SATA" : "MP"}</label><input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="${type === "qemu" ? "5" : "255"}" value="0" required>
${select} ${select}
<label for="size">Size (GiB)</label><input class="w3-input w3-border" name="size" id="size" type="number" min="0" max="131072" required></input> <label for="size">Size (GiB)</label><input class="w3-input w3-border" name="size" id="size" type="number" min="0" max="131072" required>
</form> </form>
`; `;
@ -416,7 +394,7 @@ async function handleCDAdd () {
const body = ` const 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="device">IDE</label><input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="3" required></input> <label for="device">IDE</label><input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="3" required>
<label for="iso-select">Image</label><select class="w3-select w3-border" name="iso-select" id="iso-select" required></select> <label for="iso-select">Image</label><select class="w3-select w3-border" name="iso-select" id="iso-select" required></select>
</form> </form>
`; `;
@ -466,7 +444,7 @@ function addNetworkLine (fieldset, prefix, netID, netDetails) {
const field = document.querySelector(`#${fieldset}`); const field = document.querySelector(`#${fieldset}`);
const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
setSVGSrc(icon, "images/resources/network.svg"); setSVGSrc(icon, networkMetaData.icon);
setSVGAlt(icon, `${prefix}${netID}`); setSVGAlt(icon, `${prefix}${netID}`);
icon.dataset.network = netID; icon.dataset.network = netID;
icon.dataset.values = netDetails; icon.dataset.values = netDetails;
@ -566,7 +544,7 @@ async function handleNetworkAdd () {
<label for="rate">Rate Limit (MB/s)</label><input type="number" id="rate" name="rate" class="w3-input w3-border"> <label for="rate">Rate Limit (MB/s)</label><input type="number" id="rate" name="rate" class="w3-input w3-border">
`; `;
if (type === "lxc") { if (type === "lxc") {
body += "<label for=\"name\">Interface Name</label><input type=\"text\" id=\"name\" name=\"name\" class=\"w3-input w3-border\"></input>"; body += "<label for=\"name\">Interface Name</label><input type=\"text\" id=\"name\" name=\"name\" class=\"w3-input w3-border\">";
} }
body += "</form>"; body += "</form>";
@ -616,7 +594,7 @@ function addDeviceLine (fieldset, prefix, deviceID, deviceDetails, deviceName) {
const field = document.querySelector(`#${fieldset}`); const field = document.querySelector(`#${fieldset}`);
const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
setSVGSrc(icon, "images/resources/device.svg"); setSVGSrc(icon, pcieMetaData.icon);
setSVGAlt(icon, `${prefix}${deviceID}`); setSVGAlt(icon, `${prefix}${deviceID}`);
icon.dataset.device = deviceID; icon.dataset.device = deviceID;
icon.dataset.values = deviceDetails; icon.dataset.values = deviceDetails;
@ -783,8 +761,8 @@ function addBootLine (container, data, before = null) {
item.data = data; item.data = data;
item.innerHTML = ` item.innerHTML = `
<div style="display: grid; grid-template-columns: auto auto 8ch 1fr; column-gap: 10px; align-items: center;"> <div style="display: grid; grid-template-columns: auto auto 8ch 1fr; column-gap: 10px; align-items: center;">
<svg id="drag" role="application" aria-label="drag icon"><title>drag icon</title><use xlink:href="images/actions/drag.svg#symb"></use></svg> <svg id="drag" role="application" aria-label="drag icon"><title>drag icon</title><use href="images/actions/drag.svg#symb"></use></svg>
<svg role="application" aria-label="${bootMetaData[data.prefix].alt}"><title>${bootMetaData[data.prefix].alt}</title><use xlink:href="${bootMetaData[data.prefix].icon}#symb"></use></svg> <svg role="application" aria-label="${bootMetaData[data.prefix].alt}"><title>${bootMetaData[data.prefix].alt}</title><use href="${bootMetaData[data.prefix].icon}#symb"></use></svg>
<p style="margin: 0px;">${data.id}</p> <p style="margin: 0px;">${data.id}</p>
<p style="margin: 0px; overflow-x: hidden; white-space: nowrap;">${data.detail}</p> <p style="margin: 0px; overflow-x: hidden; white-space: nowrap;">${data.detail}</p>
</div> </div>
@ -850,8 +828,6 @@ async function handleFormExit () {
} }
const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/resources`, "POST", body); const result = await requestAPI(`/cluster/${node}/${type}/${vmid}/resources`, "POST", body);
if (result.status === 200) { if (result.status === 200) {
await getConfig();
populateDisk();
goToPage("index.html"); goToPage("index.html");
} }
else { else {

View File

@ -1,6 +1,30 @@
import { API, organization } from "../vars.js"; import { API, organization } from "../vars.js";
export const resourcesConfig = { 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: { disk: {
actionBarOrder: ["move", "resize", "detach_attach", "delete"], actionBarOrder: ["move", "resize", "detach_attach", "delete"],
lxc: { lxc: {
@ -28,9 +52,17 @@ export const resourcesConfig = {
} }
}, },
network: { network: {
name: "Network",
icon: "images/resources/network.svg",
id: "network",
unitText: "MB/s",
prefix: "net" prefix: "net"
}, },
pcie: { pci: {
name: "Devices",
icon: "images/resources/device.svg",
id: "devices",
unitText: null,
prefix: "hostpci" prefix: "hostpci"
} }
}; };
@ -335,10 +367,116 @@ export function setSVGSrc (svgElem, href) {
if (!useElem) { if (!useElem) {
useElem = document.createElementNS("http://www.w3.org/2000/svg", "use"); useElem = document.createElementNS("http://www.w3.org/2000/svg", "use");
} }
useElem.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", `${href}#symb`); useElem.setAttribute("href", `${href}#symb`);
svgElem.append(useElem); svgElem.append(useElem);
} }
export function setSVGAlt (svgElem, alt) { export function setSVGAlt (svgElem, alt) {
svgElem.setAttribute("aria-label", 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);
}
export function addResourceLine (config, field, resourceType, attributesOverride, labelPrefix = null) {
const resourceConfig = config[resourceType];
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);
if (elementType === "input") {
const input = document.createElement("input");
for (const k in attributes) {
input.setAttribute(k, attributes[k]);
}
input.id = id;
input.name = id;
input.required = true;
input.classList.add("w3-input");
input.classList.add("w3-border");
field.append(input);
}
else if (elementType === "select" || elementType === "multi-select") {
const select = document.createElement("select");
for (const option of attributes.options) {
select.append(new Option(option));
}
select.value = attributes.value;
select.id = id;
select.name = id;
select.required = true;
select.classList.add("w3-select");
select.classList.add("w3-border");
if (elementType === "multi-select") {
select.setAttribute("multiple", true);
}
field.append(select);
}
else if (customElements.get(elementType)) {
const elem = document.createElement(elementType);
if (attributes.options) {
for (const option of attributes.options) {
elem.append(new Option(option));
}
}
elem.value = attributes.value;
elem.id = id;
elem.name = id;
elem.required = true;
field.append(elem);
}
if (unitText) {
const unit = document.createElement("p");
unit.innerText = unitText;
field.append(unit);
}
else {
const unit = document.createElement("div");
unit.classList.add("hidden");
field.append(unit);
}
}