466 lines
14 KiB
JavaScript
466 lines
14 KiB
JavaScript
/*
|
||
* This file is part of Linode Manager Classic.
|
||
*
|
||
* Linode Manager Classic is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation, either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* Linode Manager Classic is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
import { settings, elements, apiGet, apiPost, parseParams, regionNames, setupHeader, timeString } from "/global.js";
|
||
|
||
(function()
|
||
{
|
||
// Element names specific to this page
|
||
elements.backupAge = "backup-age";
|
||
elements.backupConfigs = "backup-configs";
|
||
elements.backupDate = "backup-date";
|
||
elements.backupDisks = "backup-disks";
|
||
elements.backupLabel = "backup-label";
|
||
elements.backupLocation = "backup-location";
|
||
elements.backupSize = "backup-size";
|
||
elements.backupType = "backup-type";
|
||
elements.destLabel = "dest-label";
|
||
elements.destLinodes = "dest-linodes";
|
||
elements.destLocation = "dest-location";
|
||
elements.destPlan = "dest-plan";
|
||
elements.destSpace = "dest-space";
|
||
elements.linodeLabel = "linode-label";
|
||
elements.linodeTag = "linode-tag";
|
||
elements.linodeTagLink = "linode-tag-link";
|
||
elements.loading = "loading-linodes";
|
||
elements.lmcRow = "lmc-tr3";
|
||
elements.newLinode = "new-linode";
|
||
elements.restorePrefix = "restore-";
|
||
elements.selectPrefix = "select-";
|
||
elements.subnav = "subnav-link";
|
||
elements.subnavActive = "subnav-link-active";
|
||
elements.unallocatedPrefix = "unallocated-";
|
||
|
||
// Data recieved from API calls
|
||
var data = {};
|
||
data.backup = {};
|
||
data.linode = {};
|
||
data.linodes = [];
|
||
data.region = {};
|
||
data.types = [];
|
||
|
||
// Static references to UI elements
|
||
var ui = {};
|
||
ui.backupAge = {};
|
||
ui.backupConfigs = {};
|
||
ui.backupDate = {};
|
||
ui.backupDisks = {};
|
||
ui.backupLabel = {};
|
||
ui.backupLocation = {};
|
||
ui.backupSize = {};
|
||
ui.backupType = {};
|
||
ui.destLabel = {};
|
||
ui.destLinodes = {};
|
||
ui.destLocation = {};
|
||
ui.destPlan = {};
|
||
ui.destSpace = {};
|
||
ui.linodeLabel = {};
|
||
ui.linodeTag = {};
|
||
ui.linodeTagLink = {};
|
||
ui.loading = {};
|
||
ui.newLinode = {};
|
||
|
||
// Temporary state
|
||
var state = {};
|
||
state.haveTypes = false;
|
||
|
||
// Generate a linode table row
|
||
var createLinodeRow = function(linode)
|
||
{
|
||
var row = document.createElement("tr");
|
||
row.className = elements.lmcRow;
|
||
|
||
var label = document.createElement("td");
|
||
label.innerHTML = linode.label;
|
||
row.appendChild(label);
|
||
|
||
var plan = document.createElement("td");
|
||
if (linode.type) {
|
||
plan.innerHTML = linode.type;
|
||
for (var i = 0; i < data.types.length; i++) {
|
||
if (data.types[i].id == linode.type) {
|
||
plan.innerHTML = data.types[i].label;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
plan.innerHTML = "Unknown";
|
||
}
|
||
if (plan.innerHTML == "")
|
||
translatePlan(linode.type, plan);
|
||
row.appendChild(plan);
|
||
|
||
var location = document.createElement("td");
|
||
if (data.region.label && data.region.label.length)
|
||
location.innerHTML = data.region.label;
|
||
else if (regionNames[linode.region])
|
||
location.innerHTML = regionNames[linode.region];
|
||
else
|
||
location.innerHTML = linode.region;
|
||
row.appendChild(location);
|
||
|
||
var freeSpace = document.createElement("td");
|
||
freeSpace.id = elements.unallocatedPrefix + linode.id;
|
||
row.appendChild(freeSpace);
|
||
|
||
var select = document.createElement("td");
|
||
select.id = elements.selectPrefix + linode.id;
|
||
row.appendChild(select);
|
||
|
||
return row;
|
||
};
|
||
|
||
// Callback for backup details API call
|
||
var displayBackup = function(response)
|
||
{
|
||
data.backup = response;
|
||
|
||
// Populate info table
|
||
if (data.backup.label)
|
||
ui.backupLabel.innerHTML = data.backup.label;
|
||
|
||
var now = new Date();
|
||
var backupStart = new Date(data.backup.created + "Z");
|
||
ui.backupDate.innerHTML = backupStart.toLocaleString();
|
||
ui.backupAge.innerHTML = timeString(now - backupStart, true);
|
||
|
||
ui.backupType.innerHTML = data.backup.type;
|
||
|
||
if (regionNames[data.backup.region]) {
|
||
ui.backupLocation.innerHTML = regionNames[data.backup.region];
|
||
ui.destLocation.innerHTML = regionNames[data.backup.region];
|
||
} else {
|
||
ui.backupLocation.innerHTML = data.backup.region;
|
||
ui.destLocation.innerHTML = data.backup.region;
|
||
}
|
||
apiGet("/regions/" + data.backup.region, displayRegion, null);
|
||
|
||
for (var i = 0; i < data.backup.configs.length; i++) {
|
||
var li = document.createElement("li");
|
||
li.innerHTML = data.backup.configs[i];
|
||
ui.backupConfigs.appendChild(li);
|
||
}
|
||
|
||
data.backup.totalSize = 0;
|
||
for (var i = 0; i < data.backup.disks.length; i++) {
|
||
data.backup.totalSize += data.backup.disks[i].size;
|
||
var li = document.createElement("li");
|
||
li.innerHTML = data.backup.disks[i].label + " (" + data.backup.disks[i].filesystem + ") – " + data.backup.disks[i].size + "MB";
|
||
ui.backupDisks.appendChild(li);
|
||
}
|
||
|
||
ui.backupSize.innerHTML = data.backup.totalSize + " MB";
|
||
|
||
if (state.haveTypes)
|
||
insertTypes();
|
||
};
|
||
|
||
// Callback for linode details API call
|
||
var displayDetails = function(response)
|
||
{
|
||
data.linode = response;
|
||
|
||
// Set page title and header stuff
|
||
document.title += " // " + data.linode.label;
|
||
ui.linodeLabel.innerHTML = data.linode.label;
|
||
if (data.linode.tags.length == 1) {
|
||
ui.linodeTagLink.href = "/linodes?tag=" + data.linode.tags[0];
|
||
ui.linodeTagLink.innerHTML = "(" + data.linode.tags[0] + ")";
|
||
ui.linodeTag.style.display = "inline";
|
||
}
|
||
};
|
||
|
||
// Callback for linode disks API call
|
||
var displayDisks = function(response)
|
||
{
|
||
// Find the linode this response is for
|
||
var lid = parseInt(response['_endpoint'].split("/")[3]);
|
||
var index = -1;
|
||
for (var i = 0; i < data.linodes.length; i++) {
|
||
if (data.linodes[i].id == lid) {
|
||
index = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (index == -1)
|
||
return;
|
||
|
||
// Add disks to array
|
||
data.linodes[index].disks = data.linodes[index].disks.concat(response.data);
|
||
|
||
// Request the next page if there are more pages
|
||
if (response.page != response.pages) {
|
||
apiGet("/linode/instances/" + data.linodes[index].id + "/disks?page=" + (response.page + 1), displayDisks, null);
|
||
return;
|
||
}
|
||
|
||
// Calculate Linode's free space
|
||
var free = data.linodes[index].specs.disk;
|
||
for (var i = 0; i < data.linodes[index].disks.length; i++)
|
||
free -= data.linodes[index].disks[i].size;
|
||
|
||
// Update table
|
||
var freeCell = document.getElementById(elements.unallocatedPrefix + data.linodes[index].id);
|
||
var selectCell = document.getElementById(elements.selectPrefix + data.linodes[index].id);
|
||
freeCell.innerHTML = free + " MB";
|
||
if (free >= data.backup.totalSize) {
|
||
var restoreLink = document.createElement("a");
|
||
restoreLink.id = elements.restorePrefix + data.linodes[index].id;
|
||
restoreLink.href = "#";
|
||
restoreLink.innerHTML = "Restore to this Linode";
|
||
restoreLink.addEventListener("click", handleRestore);
|
||
selectCell.appendChild(restoreLink);
|
||
} else {
|
||
selectCell.innerHTML = "--- not enough free space ---";
|
||
}
|
||
};
|
||
|
||
// Callback for linodes API call
|
||
var displayLinodes = function(response)
|
||
{
|
||
// Add linodes to array
|
||
data.linodes = data.linodes.concat(response.data);
|
||
|
||
// Request the next page if there are more pages
|
||
if (response.page != response.pages) {
|
||
var filter = {
|
||
"region": data.backup.region
|
||
};
|
||
apiGet("/linode/instances?page=" + (response.page + 1), displayLinodes, filter);
|
||
return;
|
||
}
|
||
|
||
ui.loading.remove();
|
||
for (var i = 0; i < data.linodes.length; i++) {
|
||
// Create row in the table
|
||
ui.destLinodes.appendChild(createLinodeRow(data.linodes[i]));
|
||
// Get the linode's disks
|
||
data.linodes[i].disks = [];
|
||
apiGet("/linode/instances/" + data.linodes[i].id + "/disks", displayDisks, null);
|
||
}
|
||
};
|
||
|
||
// Callback for region API call
|
||
var displayRegion = function(response)
|
||
{
|
||
data.region = response;
|
||
|
||
if (data.region.label && data.region.label.length) {
|
||
ui.backupLocation.innerHTML = response.label;
|
||
ui.destLocation.innerHTML = response.label;
|
||
}
|
||
};
|
||
|
||
// Callback for linode types API call
|
||
var displayTypes = function(response)
|
||
{
|
||
// Add types to array
|
||
data.types = data.types.concat(response.data);
|
||
|
||
// Request the next page if there are more pages
|
||
if (response.page != response.pages) {
|
||
apiGet("/linode/types?page=" + (response.page + 1), displayTypes, null);
|
||
return;
|
||
}
|
||
|
||
state.haveTypes = true;
|
||
if (data.backup.id)
|
||
insertTypes();
|
||
};
|
||
|
||
// Handler for create linode button
|
||
var handleCreate = function(event)
|
||
{
|
||
if (event.currentTarget.disabled)
|
||
return;
|
||
|
||
// Find the selected type
|
||
var index = -1;
|
||
for (var i = 0; i < data.types.length; i++) {
|
||
if (data.types[i].id == ui.destPlan.value) {
|
||
index = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (index == -1)
|
||
return;
|
||
|
||
if (!confirm("Create a new " + data.types[i].label + " from this backup? This new instance will cost $" + data.types[i].price.monthly.toFixed(2) + " per month and backups will be enabled per your global account setting."))
|
||
return;
|
||
|
||
var req = {
|
||
"label": ui.destLabel.value,
|
||
"type": ui.destPlan.value,
|
||
"region": data.backup.region,
|
||
"backup_id": data.backup.id
|
||
};
|
||
var callback = function(response)
|
||
{
|
||
location.href = "/linodes/dashboard?lid=" + response.id;
|
||
};
|
||
apiPost("/linode/instances", req, callback);
|
||
};
|
||
|
||
// Handler for backup restore links
|
||
var handleRestore = function(event)
|
||
{
|
||
// Find the linode associated with this link
|
||
var lid = parseInt(event.currentTarget.id.replace(elements.restorePrefix, ""));
|
||
var index = -1;
|
||
for (var i = 0; i < data.linodes.length; i++) {
|
||
if (data.linodes[i].id == lid) {
|
||
index = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (index == -1)
|
||
return;
|
||
if (!confirm("Restore to Linode " + data.linodes[index].label + "?"))
|
||
return;
|
||
|
||
var req = {
|
||
"linode_id": lid,
|
||
"overwrite": false
|
||
};
|
||
|
||
var callback = function(response)
|
||
{
|
||
location.href = "/linodes/dashboard?lid=" + lid;
|
||
};
|
||
apiPost("/linode/instances/" + data.params.lid + "/backups/" + data.backup.id + "/restore", req, callback);
|
||
};
|
||
|
||
// Insert linode types into selector
|
||
var insertTypes = function()
|
||
{
|
||
for (var i = 0; i < data.types.length; i++) {
|
||
if (data.types[i].disk < data.backup.totalSize)
|
||
continue;
|
||
|
||
var option = document.createElement("option");
|
||
option.value = data.types[i].id;
|
||
option.innerHTML = data.types[i].label;
|
||
ui.destPlan.appendChild(option);
|
||
}
|
||
|
||
updateSpace(null);
|
||
ui.newLinode.disabled = false;
|
||
|
||
var filter = {
|
||
"region": data.backup.region
|
||
};
|
||
apiGet("/linode/instances", displayLinodes, filter);
|
||
};
|
||
|
||
// Initial setup
|
||
var setup = function()
|
||
{
|
||
// Parse URL parameters
|
||
data.params = parseParams();
|
||
|
||
// We need a Linode ID, so die if we don't have it
|
||
if (!data.params.lid) {
|
||
alert("No Linode ID supplied!");
|
||
return;
|
||
}
|
||
|
||
// We also need a backup ID
|
||
if (!data.params.bid) {
|
||
alert("No Backup ID supplied!");
|
||
return;
|
||
}
|
||
|
||
setupHeader();
|
||
|
||
// Update links on page to include proper Linode ID
|
||
var anchors = document.getElementsByTagName("a");
|
||
for (var i = 0; i < anchors.length; i++)
|
||
anchors[i].href = anchors[i].href.replace("lid=0", "lid=" + data.params.lid);
|
||
|
||
// Highlight the backups subnav link
|
||
var subnavLinks = document.getElementsByClassName(elements.subnav);
|
||
for (var i = 0; i < subnavLinks.length; i++) {
|
||
if (subnavLinks[i].pathname == "/linodes/backups")
|
||
subnavLinks[i].className += " " + elements.subnavActive;
|
||
}
|
||
|
||
// Get element references
|
||
ui.backupAge = document.getElementById(elements.backupAge);
|
||
ui.backupConfigs = document.getElementById(elements.backupConfigs);
|
||
ui.backupDate = document.getElementById(elements.backupDate);
|
||
ui.backupDisks = document.getElementById(elements.backupDisks);
|
||
ui.backupLabel = document.getElementById(elements.backupLabel);
|
||
ui.backupLocation = document.getElementById(elements.backupLocation);
|
||
ui.backupSize = document.getElementById(elements.backupSize);
|
||
ui.backupType = document.getElementById(elements.backupType);
|
||
ui.destLabel = document.getElementById(elements.destLabel);
|
||
ui.destLinodes = document.getElementById(elements.destLinodes);
|
||
ui.destLocation = document.getElementById(elements.destLocation);
|
||
ui.destPlan = document.getElementById(elements.destPlan);
|
||
ui.destSpace = document.getElementById(elements.destSpace);
|
||
ui.linodeLabel = document.getElementById(elements.linodeLabel);
|
||
ui.linodeTag = document.getElementById(elements.linodeTag);
|
||
ui.linodeTagLink = document.getElementById(elements.linodeTagLink);
|
||
ui.loading = document.getElementById(elements.loading);
|
||
ui.newLinode = document.getElementById(elements.newLinode);
|
||
|
||
// Register event handlers
|
||
ui.destPlan.addEventListener("input", updateSpace);
|
||
ui.newLinode.addEventListener("click", handleCreate);
|
||
|
||
// Get data from API
|
||
apiGet("/linode/instances/" + data.params.lid, displayDetails, null);
|
||
apiGet("/linode/instances/" + data.params.lid + "/backups/" + data.params.bid, displayBackup, null);
|
||
apiGet("/linode/types", displayTypes, null);
|
||
};
|
||
|
||
// Update the given element with the given type's label
|
||
var translatePlan = function(type, el)
|
||
{
|
||
var callback = function(response)
|
||
{
|
||
el.innerHTML = response.label;
|
||
};
|
||
|
||
apiGet("/linode/types/" + type, callback, null);
|
||
};
|
||
|
||
// Update the price display
|
||
var updateSpace = function(event)
|
||
{
|
||
// Find the selected type
|
||
var type = null;
|
||
for (var i = 0; i < data.types.length; i++) {
|
||
if (data.types[i].id == ui.destPlan.value) {
|
||
type = data.types[i];
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!type)
|
||
return;
|
||
|
||
ui.destSpace.innerHTML = type.disk;
|
||
};
|
||
|
||
// Attach onload handler
|
||
window.addEventListener("load", setup);
|
||
})();
|