-
diff --git a/login.html b/login.html
index 4bd9f5c..68dfee8 100644
--- a/login.html
+++ b/login.html
@@ -3,7 +3,7 @@
-
proxmox - client
+
proxmox - dashboard
diff --git a/package.json b/package.json
index 1f2d4a7..21fb0ce 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "proxmoxaas-client",
+ "name": "proxmoxaas-dashboard",
"version": "0.0.1",
"description": "Front-end for ProxmoxAAS",
"type": "module",
diff --git a/scripts/index.js b/scripts/index.js
index 6903822..df539ae 100644
--- a/scripts/index.js
+++ b/scripts/index.js
@@ -1,6 +1,5 @@
-import { requestPVE, requestAPI, goToPage, goToURL, instancesConfig, nodesConfig, setTitleAndHeader } from "./utils.js";
+import { requestPVE, requestAPI, goToPage, setTitleAndHeader } from "./utils.js";
import { alert, dialog } from "./dialog.js";
-import { PVE } from "../vars.js";
import { setupClientSync } from "./clientsync.js";
window.addEventListener("DOMContentLoaded", init);
@@ -12,10 +11,13 @@ async function init () {
goToPage("login.html");
}
- const addInstanceBtn = document.querySelector("#instance-add");
- addInstanceBtn.addEventListener("click", handleInstanceAdd);
+ document.querySelector("#instance-add").addEventListener("click", handleInstanceAdd);
setupClientSync(populateInstances);
+
+ document.querySelector("#vm-search").addEventListener("input", () => {
+
+ });
}
async function populateInstances () {
@@ -197,225 +199,3 @@ async function handleInstanceAdd () {
d.querySelector("#vmid").min = userCluster.vmid.min;
d.querySelector("#vmid").max = userCluster.vmid.max;
}
-
-class InstanceCard extends HTMLElement {
- constructor () {
- super();
- this.attachShadow({ mode: "open" });
- this.shadowRoot.innerHTML = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- this.actionLock = false;
- }
-
- get data () {
- return {
- type: this.type,
- status: this.status,
- vmid: this.status,
- name: this.name,
- node: this.node
- };
- }
-
- set data (data) {
- if (data.status === "unknown") {
- data.status = "stopped";
- }
- this.type = data.type;
- this.status = data.status;
- this.vmid = data.vmid;
- this.name = data.name;
- this.node = data.node;
- this.update();
- }
-
- update () {
- const vmidParagraph = this.shadowRoot.querySelector("#instance-id");
- vmidParagraph.innerText = this.vmid;
-
- const nameParagraph = this.shadowRoot.querySelector("#instance-name");
- nameParagraph.innerText = this.name ? this.name : "";
-
- const typeParagraph = this.shadowRoot.querySelector("#instance-type");
- typeParagraph.innerText = this.type;
-
- const statusParagraph = this.shadowRoot.querySelector("#instance-status");
- statusParagraph.innerText = this.status;
-
- const statusIcon = this.shadowRoot.querySelector("#instance-status-icon");
- statusIcon.src = instancesConfig[this.status].status.src;
- statusIcon.alt = instancesConfig[this.status].status.alt;
-
- const nodeNameParagraph = this.shadowRoot.querySelector("#node-name");
- nodeNameParagraph.innerText = this.node.name;
-
- const nodeStatusParagraph = this.shadowRoot.querySelector("#node-status");
- nodeStatusParagraph.innerText = this.node.status;
-
- const nodeStatusIcon = this.shadowRoot.querySelector("#node-status-icon");
- nodeStatusIcon.src = nodesConfig[this.node.status].status.src;
- nodeStatusIcon.alt = nodesConfig[this.node.status].status.src;
-
- const powerButton = this.shadowRoot.querySelector("#power-btn");
- powerButton.src = instancesConfig[this.status].power.src;
- powerButton.alt = instancesConfig[this.status].power.alt;
- powerButton.title = instancesConfig[this.status].power.alt;
- if (instancesConfig[this.status].power.clickable) {
- powerButton.classList.add("clickable");
- powerButton.onclick = this.handlePowerButton.bind(this);
- }
-
- const configButton = this.shadowRoot.querySelector("#configure-btn");
- configButton.src = instancesConfig[this.status].config.src;
- configButton.alt = instancesConfig[this.status].config.alt;
- configButton.title = instancesConfig[this.status].config.alt;
- if (instancesConfig[this.status].config.clickable) {
- configButton.classList.add("clickable");
- configButton.onclick = this.handleConfigButton.bind(this);
- }
-
- const consoleButton = this.shadowRoot.querySelector("#console-btn");
- consoleButton.src = instancesConfig[this.status].console.src;
- consoleButton.alt = instancesConfig[this.status].console.alt;
- consoleButton.title = instancesConfig[this.status].console.alt;
- if (instancesConfig[this.status].console.clickable) {
- consoleButton.classList.add("clickable");
- consoleButton.onclick = this.handleConsoleButton.bind(this);
- }
-
- const deleteButton = this.shadowRoot.querySelector("#delete-btn");
- deleteButton.src = instancesConfig[this.status].delete.src;
- deleteButton.alt = instancesConfig[this.status].delete.alt;
- deleteButton.title = instancesConfig[this.status].delete.alt;
- if (instancesConfig[this.status].delete.clickable) {
- deleteButton.classList.add("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;
- const targetAction = this.status === "running" ? "stop" : "start";
- const targetStatus = this.status === "running" ? "stopped" : "running";
- const prevStatus = this.status;
- this.status = "loading";
-
- this.update();
-
- const 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) {
- const 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
- this.status = targetStatus;
- this.update();
- this.actionLock = false;
- break;
- }
- else if (taskStatus.data.status === "stopped") { // task stopped but was not successful
- this.status = prevStatus;
- alert(`attempted to ${targetAction} ${this.vmid} but process returned stopped:${result.data.exitstatus}`);
- this.update();
- this.actionLock = false;
- break;
- }
- else { // task has not stopped
- await waitFor(1000);
- }
- }
- }
- });
- }
- }
-
- 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 });
- }
- }
-
- handleConsoleButton () {
- if (!this.actionLock && this.status === "running") {
- const 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 () {
- if (!this.actionLock && this.status === "stopped") {
- const header = `Delete VM ${this.vmid}`;
- const body = `
Are you sure you want to delete VM
${this.vmid}
`;
-
- dialog(header, body, async (result, form) => {
- if (result === "confirm") {
- this.actionLock = true;
- this.status = "loading";
- this.update();
-
- const action = {};
- action.purge = 1;
- action["destroy-unreferenced-disks"] = 1;
-
- const result = await requestAPI(`/cluster/${this.node.name}/${this.type}/${this.vmid}/delete`, "DELETE");
- if (result.status === 200) {
- this.parentElement.removeChild(this);
- }
- else {
- alert(result.error);
- this.status = this.prevStatus;
- this.update();
- this.actionLock = false;
- }
- }
- });
- }
- }
-}
-
-customElements.define("instance-card", InstanceCard);
diff --git a/scripts/instance.js b/scripts/instance.js
new file mode 100644
index 0000000..b7b1127
--- /dev/null
+++ b/scripts/instance.js
@@ -0,0 +1,231 @@
+import { requestPVE, requestAPI, goToPage, goToURL, instancesConfig, nodesConfig } from "./utils.js";
+import { PVE } from "../vars.js";
+import { dialog } from "./dialog.js";
+
+class InstanceCard extends HTMLElement {
+ constructor () {
+ super();
+ this.attachShadow({ mode: "open" });
+ this.shadowRoot.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ this.actionLock = false;
+ }
+
+ get data () {
+ return {
+ type: this.type,
+ status: this.status,
+ vmid: this.status,
+ name: this.name,
+ node: this.node
+ };
+ }
+
+ set data (data) {
+ if (data.status === "unknown") {
+ data.status = "stopped";
+ }
+ this.type = data.type;
+ this.status = data.status;
+ this.vmid = data.vmid;
+ this.name = data.name;
+ this.node = data.node;
+ this.update();
+ }
+
+ update () {
+ const vmidParagraph = this.shadowRoot.querySelector("#instance-id");
+ vmidParagraph.innerText = this.vmid;
+
+ const nameParagraph = this.shadowRoot.querySelector("#instance-name");
+ nameParagraph.innerText = this.name ? this.name : "";
+
+ const typeParagraph = this.shadowRoot.querySelector("#instance-type");
+ typeParagraph.innerText = this.type;
+
+ const statusParagraph = this.shadowRoot.querySelector("#instance-status");
+ statusParagraph.innerText = this.status;
+
+ const statusIcon = this.shadowRoot.querySelector("#instance-status-icon");
+ statusIcon.src = instancesConfig[this.status].status.src;
+ statusIcon.alt = instancesConfig[this.status].status.alt;
+
+ const nodeNameParagraph = this.shadowRoot.querySelector("#node-name");
+ nodeNameParagraph.innerText = this.node.name;
+
+ const nodeStatusParagraph = this.shadowRoot.querySelector("#node-status");
+ nodeStatusParagraph.innerText = this.node.status;
+
+ const nodeStatusIcon = this.shadowRoot.querySelector("#node-status-icon");
+ nodeStatusIcon.src = nodesConfig[this.node.status].status.src;
+ nodeStatusIcon.alt = nodesConfig[this.node.status].status.src;
+
+ const powerButton = this.shadowRoot.querySelector("#power-btn");
+ powerButton.src = instancesConfig[this.status].power.src;
+ powerButton.alt = instancesConfig[this.status].power.alt;
+ powerButton.title = instancesConfig[this.status].power.alt;
+ if (instancesConfig[this.status].power.clickable) {
+ powerButton.classList.add("clickable");
+ powerButton.onclick = this.handlePowerButton.bind(this);
+ }
+
+ const configButton = this.shadowRoot.querySelector("#configure-btn");
+ configButton.src = instancesConfig[this.status].config.src;
+ configButton.alt = instancesConfig[this.status].config.alt;
+ configButton.title = instancesConfig[this.status].config.alt;
+ if (instancesConfig[this.status].config.clickable) {
+ configButton.classList.add("clickable");
+ configButton.onclick = this.handleConfigButton.bind(this);
+ }
+
+ const consoleButton = this.shadowRoot.querySelector("#console-btn");
+ consoleButton.src = instancesConfig[this.status].console.src;
+ consoleButton.alt = instancesConfig[this.status].console.alt;
+ consoleButton.title = instancesConfig[this.status].console.alt;
+ if (instancesConfig[this.status].console.clickable) {
+ consoleButton.classList.add("clickable");
+ consoleButton.onclick = this.handleConsoleButton.bind(this);
+ }
+
+ const deleteButton = this.shadowRoot.querySelector("#delete-btn");
+ deleteButton.src = instancesConfig[this.status].delete.src;
+ deleteButton.alt = instancesConfig[this.status].delete.alt;
+ deleteButton.title = instancesConfig[this.status].delete.alt;
+ if (instancesConfig[this.status].delete.clickable) {
+ deleteButton.classList.add("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;
+ const targetAction = this.status === "running" ? "stop" : "start";
+ const targetStatus = this.status === "running" ? "stopped" : "running";
+ const prevStatus = this.status;
+ this.status = "loading";
+
+ this.update();
+
+ const 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) {
+ const 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
+ this.status = targetStatus;
+ this.update();
+ this.actionLock = false;
+ break;
+ }
+ else if (taskStatus.data.status === "stopped") { // task stopped but was not successful
+ this.status = prevStatus;
+ alert(`attempted to ${targetAction} ${this.vmid} but process returned stopped:${result.data.exitstatus}`);
+ this.update();
+ this.actionLock = false;
+ break;
+ }
+ else { // task has not stopped
+ await waitFor(1000);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ 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 });
+ }
+ }
+
+ handleConsoleButton () {
+ if (!this.actionLock && this.status === "running") {
+ const 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 () {
+ if (!this.actionLock && this.status === "stopped") {
+ const header = `Delete VM ${this.vmid}`;
+ const body = `
Are you sure you want to delete VM
${this.vmid}
`;
+
+ dialog(header, body, async (result, form) => {
+ if (result === "confirm") {
+ this.actionLock = true;
+ this.status = "loading";
+ this.update();
+
+ const action = {};
+ action.purge = 1;
+ action["destroy-unreferenced-disks"] = 1;
+
+ const result = await requestAPI(`/cluster/${this.node.name}/${this.type}/${this.vmid}/delete`, "DELETE");
+ if (result.status === 200) {
+ this.parentElement.removeChild(this);
+ }
+ else {
+ alert(result.error);
+ this.status = this.prevStatus;
+ this.update();
+ this.actionLock = false;
+ }
+ }
+ });
+ }
+ }
+}
+
+customElements.define("instance-card", InstanceCard);
diff --git a/scripts/utils.js b/scripts/utils.js
index 0e360aa..ca4ddab 100644
--- a/scripts/utils.js
+++ b/scripts/utils.js
@@ -235,7 +235,7 @@ export function goToPage (page, data = {}, newwindow = false) {
}
if (newwindow) {
- window.open(url, `${organization} - client`, "height=480,width=848");
+ window.open(url, `${organization} - dashboard`, "height=480,width=848");
}
else {
window.location.assign(url.toString());
@@ -249,7 +249,7 @@ export function goToURL (href, data = {}, newwindow = false) {
}
if (newwindow) {
- window.open(url, `${organization} - client`, "height=480,width=848");
+ window.open(url, `${organization} - dashboard`, "height=480,width=848");
}
else {
window.location.assign(url.toString());
@@ -266,6 +266,6 @@ export async function deleteAllCookies () {
}
export function setTitleAndHeader () {
- document.title = `${organization} - client`;
+ document.title = `${organization} - dashboard`;
document.querySelector("h1").innerText = organization;
}
diff --git a/settings.html b/settings.html
index 5518ac3..40d7459 100644
--- a/settings.html
+++ b/settings.html
@@ -3,7 +3,7 @@
-
proxmox - client
+
proxmox - dashboard
diff --git a/template.vars.js b/template.vars.js
index 70df2df..747f239 100644
--- a/template.vars.js
+++ b/template.vars.js
@@ -1,3 +1,3 @@
-export const API = "https://client.mydomain.example/api"; // the proxmox-aas api
+export const API = "https://dashboard.mydomain.example/api"; // the proxmox-aas api
export const PVE = "https://pve.mydomain.example"; // the proxmox api
export const organization = "mydomain"; // org name used in page title and nav bar