337 lines
11 KiB
JavaScript
337 lines
11 KiB
JavaScript
import RFB from '/static/novnc/core/rfb.js';
|
|
|
|
const sidebarHeader = document.getElementById('sidebarHeader');
|
|
const sidebarNav = document.getElementById('sidebarNav');
|
|
const sidebarList = document.getElementById('sidebarList');
|
|
const overlay = document.getElementById('overlay');
|
|
const userBanner = document.getElementById('bannerUserid');
|
|
const mainContent = document.getElementById('mainContent');
|
|
const vmListBtn = document.getElementById('vmListBtn');
|
|
let consoleContainer = document.createElement('div');
|
|
let cachedInstructions = '';
|
|
|
|
const cookies = getCookies();
|
|
|
|
let vmName = "";
|
|
let sbHeaderStatus = "";
|
|
let status = "";
|
|
let vmList = [];
|
|
|
|
userBanner.textContent = cookies['username'];
|
|
|
|
await validatePath();
|
|
|
|
window.addEventListener('popstate', () =>{
|
|
validatePath();
|
|
});
|
|
|
|
async function validatePath() {
|
|
const path = window.location.pathname;
|
|
if (path.startsWith('/vm')) {
|
|
const split = path.split('/');
|
|
vmName = split.pop()
|
|
const access = await validateAccess(vmName);
|
|
} else {
|
|
navigateTo('start');
|
|
}
|
|
}
|
|
|
|
function getVMList() {
|
|
vmList = [];
|
|
fetch('/api/vmlist')
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.access) {
|
|
data.allowed.forEach(vm => {
|
|
const hostname = vm.hostname;
|
|
vmList.push(hostname);
|
|
const li = document.createElement('li');
|
|
const a = document.createElement('a');
|
|
a.classList.add('vm-link');
|
|
li.appendChild(a);
|
|
a.innerText = hostname;
|
|
a.addEventListener('click', () => navigateTo(hostname));
|
|
sidebarList.appendChild(li);
|
|
});
|
|
} else {
|
|
mainContent.innerHTML = "You haven't been assigned any VM"
|
|
}
|
|
}).catch(err => console.error('Error getting VM list:', err));
|
|
};
|
|
|
|
function loadStart() {
|
|
vmName = "";
|
|
sidebarHeader.innerHTML = 'Virtual Machines';
|
|
sidebarList.innerHTML = '';
|
|
mainContent.innerHTML = 'Welcome';
|
|
consoleContainer.innerHTML = '';
|
|
cachedInstructions = '';
|
|
sbHeaderStatus = '';
|
|
status = '';
|
|
getVMList();
|
|
}
|
|
|
|
function navigateTo(vmName) {
|
|
if(vmName === 'start') {
|
|
history.pushState({vm: ''}, '', '/');
|
|
loadStart();
|
|
} else{
|
|
history.pushState({vm: vmName}, '', `/vm/${vmName}`);
|
|
selectVM(vmName);
|
|
}
|
|
}
|
|
|
|
async function validateAccess(vmName){
|
|
fetch(`/api/vmaccess?vm=${vmName}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (!data.access){
|
|
navigateTo('start');
|
|
} else {
|
|
selectVM(vmName);
|
|
}
|
|
})
|
|
.catch(err => console.error('Error validating user access:', err));
|
|
}
|
|
|
|
function selectVM(vm){
|
|
vmName = vm;
|
|
mainContent.innerHTML = '';
|
|
sidebarHeader.innerHTML = '';
|
|
const sbHeaderDiv = document.createElement('div');
|
|
sbHeaderDiv.innerHTML = vmName;
|
|
sbHeaderStatus = document.createElement('div');
|
|
sbHeaderStatus.classList.add('status-indicator');
|
|
sbHeaderStatus.innerHTML = 'Status: ';
|
|
status = document.createElement('span');
|
|
getVMStatus();
|
|
sbHeaderStatus.appendChild(status);
|
|
sidebarHeader.appendChild(sbHeaderDiv);
|
|
sidebarHeader.appendChild(sbHeaderStatus);
|
|
|
|
sidebarList.innerHTML = '';
|
|
const infoli = document.createElement('li');
|
|
const consoleli = document.createElement('li');
|
|
const snapli = document.createElement('li');
|
|
const homeli = document.createElement('li');
|
|
const infoa = document.createElement('a');
|
|
const consolea = document.createElement('a');
|
|
const snapa = document.createElement('a');
|
|
const homea = document.createElement('a');
|
|
infoli.appendChild(infoa);
|
|
consoleli.appendChild(consolea);
|
|
snapli.appendChild(snapa);
|
|
homeli.appendChild(homea);
|
|
infoa.innerText = 'Information';
|
|
infoa.addEventListener('click', () => vmStart());
|
|
consolea.innerText = 'Console';
|
|
consolea.addEventListener('click', () => consolePage());
|
|
snapa.innerText = 'Snapshot';
|
|
snapa.addEventListener('click', () => getSnapshots());
|
|
homea.innerText = 'Back to start';
|
|
homea.addEventListener('click', () => navigateTo('start'));
|
|
homea.classList.add('home-link');
|
|
|
|
sidebarList.appendChild(infoli);
|
|
sidebarList.appendChild(consoleli);
|
|
sidebarList.appendChild(snapli);
|
|
sidebarList.appendChild(homeli);
|
|
|
|
vmStart();
|
|
}
|
|
|
|
function consolePage(){
|
|
mainContent.innerHTML = '';
|
|
consoleContainer.innerHTML = '';
|
|
consoleContainer.className = 'console-container';
|
|
consoleContainer.name = 'console'
|
|
const startBtn = document.createElement('button');
|
|
const stopBtn = document.createElement('button');
|
|
startBtn.innerText = 'Power on';
|
|
stopBtn.innerText = 'Power off';
|
|
startBtn.className = 'vm-button';
|
|
stopBtn.className = 'vm-button';
|
|
startBtn.addEventListener('click', () => sendPowerAction('start'));
|
|
stopBtn.addEventListener('click', () => sendPowerAction('stop'));
|
|
mainContent.appendChild(startBtn);
|
|
mainContent.appendChild(stopBtn);
|
|
mainContent.appendChild(consoleContainer);
|
|
connectToVM();
|
|
}
|
|
|
|
async function connectToVM(){
|
|
fetch(`/api/vncproxy?vm=${vmName}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.ready) {
|
|
setTimeout(() => {
|
|
let wsurl = `wss://${data.hostname}.dsv.su.se/vnc?ticket=${encodeURIComponent(data.ticket)}`;
|
|
const rfb = new RFB(consoleContainer, wsurl, {
|
|
credentials: {
|
|
password: data.ticket
|
|
}
|
|
});
|
|
|
|
rfb.viewOnly = false;
|
|
rfb.scaleViewport = true;
|
|
rfb.background = '#000';
|
|
}, 1000);
|
|
} else {
|
|
throw new Error('Failed with VNC D:');
|
|
}
|
|
})
|
|
.catch(err => console.error('Error connecting to VNC proxy:', err));
|
|
}
|
|
|
|
function sendPowerAction(action){
|
|
fetch(`/api/${action}?vm=${vmName}`)
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
alert(`${action} exectuted!`)
|
|
})
|
|
.catch(err => alert('Error:', + err))
|
|
}
|
|
|
|
function getCookies() {
|
|
var out = new Object();
|
|
const cookies = document.cookie.split('; ');
|
|
cookies.forEach((cookie) => {
|
|
const temp = cookie.split('=');
|
|
const name = temp[0];
|
|
const value = temp.slice(1).join('=');
|
|
out[name] = decodeURIComponent(value);
|
|
});
|
|
return out;
|
|
}
|
|
|
|
async function vmStart() {
|
|
consoleContainer.innerHTML = '';
|
|
mainContent.innerHTML = '';
|
|
if (cachedInstructions === ''){
|
|
fetch(`/api/metadata?vm=${vmName}`)
|
|
.then(res => res.json())
|
|
.then(data => getInstructions(data.instructions))
|
|
.catch(err => alert('Error:', err))
|
|
} else {
|
|
mainContent.innerHTML = cachedInstructions;
|
|
}
|
|
};
|
|
|
|
async function getInstructions(instructions) {
|
|
if (cachedInstructions === '') cachedInstructions = instructions;
|
|
mainContent.innerHTML = instructions;
|
|
|
|
};
|
|
|
|
async function getVMStatus(){
|
|
fetch(`/api/status?vm=${vmName}`)
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if(data) {
|
|
status.textContent = "On";
|
|
sbHeaderStatus.classList.remove('stopped');
|
|
sbHeaderStatus.classList.add('running');
|
|
} else {
|
|
status.textContent = "Off";
|
|
sbHeaderStatus.classList.remove('running');
|
|
sbHeaderStatus.classList.add('stopped');
|
|
}
|
|
})
|
|
.catch(err => console.error(err))
|
|
}
|
|
|
|
async function getSnapshots(){
|
|
mainContent.innerHTML = '';
|
|
consoleContainer.innerHTML = '';
|
|
fetch(`/api/getsnapshots?vm=${vmName}`)
|
|
.then(res => res.json())
|
|
.then(data => displaySnapshots(data))
|
|
.catch(err => console.log(err))
|
|
};
|
|
|
|
function displaySnapshots(data){
|
|
if (Object.keys(data).length === 1){
|
|
mainContent.innerHTML = "There is nothing to show here";
|
|
} else {
|
|
|
|
const listData = Object.entries(data);
|
|
|
|
const table = document.createElement('table');
|
|
const thead = table.createTHead();
|
|
const tbody = table.createTBody();
|
|
|
|
const headRow = thead.insertRow(0);
|
|
const headNameCell = headRow.insertCell(0);
|
|
const headDescriptionCell = headRow.insertCell(1);
|
|
const headDateCell = headRow.insertCell(2);
|
|
const headRollbackCell = headRow.insertCell(3);
|
|
|
|
const [headName, headValue] = listData[0];
|
|
const headRollbackValue = 'Rollback';
|
|
headNameCell.textContent = headName;
|
|
headDescriptionCell.textContent = headValue[0];
|
|
headDateCell.textContent = headValue[1];
|
|
headRollbackCell.textContent = headRollbackValue;
|
|
delete data[headName];
|
|
|
|
let rowInd = 0;
|
|
for (const [key, value] of Object.entries(data)){
|
|
let row = tbody.insertRow(rowInd);
|
|
if (rowInd % 2 === 0 ) row.classList.add('even');
|
|
else row.classList.add('odd');
|
|
const keyCell = row.insertCell(0);
|
|
const descriptionCell = row.insertCell(1);
|
|
const dateCell = row.insertCell(2);
|
|
const rollbackCell = row.insertCell(3);
|
|
keyCell.textContent = key;
|
|
descriptionCell.textContent = value[0];
|
|
const date = new Date(value[1] * 1000);
|
|
const formatted = new Intl.DateTimeFormat("sv-SE", {
|
|
year: "numeric",
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
second: "2-digit",
|
|
hour12: false,
|
|
}).format(date);
|
|
dateCell.textContent = formatted;
|
|
const rollbackBtn = document.createElement('button');
|
|
rollbackBtn.textContent = 'Restore';
|
|
rollbackBtn.dataset.snapname = key;
|
|
rollbackBtn.addEventListener('click', function() {
|
|
const snapname = this.getAttribute('data-snapname');
|
|
rollbackSnapshot(snapname);
|
|
})
|
|
rollbackCell.appendChild(rollbackBtn);
|
|
rowInd++;
|
|
}
|
|
mainContent.appendChild(table);
|
|
}
|
|
};
|
|
|
|
async function rollbackSnapshot(snapname) {
|
|
const confirmRollback = confirm("Are you sure you want to rollback the VM to this snapshot?\nDoing this will remove all data on it and can't be undone.");
|
|
if(!confirmRollback) return;
|
|
|
|
try {
|
|
fetch(`/api/rollback?vm=${vmName}&snap=${snapname}`)
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if(data.success){
|
|
alert(`Succesfully rollbacked ${vmName}`);
|
|
} else {
|
|
alert(`Failed with rollbacked for ${vmName}`);
|
|
}
|
|
})
|
|
|
|
}catch (err) {
|
|
|
|
}
|
|
}
|
|
|
|
setInterval(async () => {
|
|
if (status !== ""){
|
|
getVMStatus();
|
|
}
|
|
}, 10000); |