/** * VirtFusion Direct Provisioning Module - Client JavaScript * * Handles client-side interactions for server management including: * - Server data display * - Power management (boot, shutdown, restart, power off) * - Control panel login (SSO) * - Password reset * - Server rebuild * - OS template loading * - Traffic statistics * - Backup listing * - VNC management * - Server naming */ // ========================================================================= // Progress Indicator // ========================================================================= var _vfProgressTimer = null; function vfShowProgress(label) { var startTime = Date.now(); $("#vf-action-progress-text").text(label); $("#vf-action-progress-timer").text("0s"); $("#vf-action-progress").show(); _vfProgressTimer = setInterval(function () { var elapsed = Math.floor((Date.now() - startTime) / 1000); $("#vf-action-progress-timer").text(elapsed + "s"); }, 1000); } function vfHideProgress() { if (_vfProgressTimer) { clearInterval(_vfProgressTimer); _vfProgressTimer = null; } $("#vf-action-progress").hide(); } function vfServerData(serviceId, systemUrl) { $("#vf-server-info-error").hide(); $.ajax({ type: "GET", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=serverData" }).done(function (response) { if (response.success) { $("#vf-rename-input").val(response.data.name); $("#vf-data-server-hostname").text(response.data.hostname); $("#vf-data-server-memory").text(response.data.memory); $("#vf-data-server-traffic").text(response.data.traffic); $("#vf-data-server-traffic-used").text(response.data.trafficUsed || "-"); $("#vf-data-server-storage").text(response.data.storage); $("#vf-data-server-cpu").text(response.data.cpu); var pn = response.data.primaryNetwork || {}; $("#vf-data-server-ipv4").text(pn.ipv4 || "-"); $("#vf-data-server-ipv6").text(pn.ipv6 || "-"); // Update status badge var statusBadge = $("#vf-status-badge"); var status = (response.data.status || "unknown").toLowerCase(); statusBadge.text(status.charAt(0).toUpperCase() + status.slice(1)); statusBadge.removeClass("vf-badge-active vf-badge-suspended vf-badge-awaiting"); if (status === "active" || status === "running") { statusBadge.addClass("vf-badge-active"); } else if (status === "suspended") { statusBadge.addClass("vf-badge-suspended"); } else { statusBadge.addClass("vf-badge-awaiting"); } // Show/hide VNC panel based on API response if (response.data.vncEnabled) { $("#vf-vnc-panel").show(); } // Populate resources panel var d = response.data; $("#vf-res-memory").text(d.memory || "-"); $("#vf-res-cpu").text(d.cpu || "-"); $("#vf-res-storage").text(d.storage || "-"); var trafficUsed = d.trafficUsedRaw || 0; var trafficTotal = d.trafficRaw || 0; if (trafficTotal > 0) { $("#vf-res-traffic").text(trafficUsed + " / " + trafficTotal + " GB"); var pct = Math.min(100, Math.round((trafficUsed / trafficTotal) * 100)); $("#vf-res-traffic-bar").css("width", pct + "%").removeClass("bg-danger bg-warning"); if (pct > 90) { $("#vf-res-traffic-bar").addClass("bg-danger"); } else if (pct > 70) { $("#vf-res-traffic-bar").addClass("bg-warning"); } } else { $("#vf-res-traffic").text(d.traffic || "Unlimited"); $("#vf-res-traffic-bar").css("width", "0%"); } var speedIn = d.networkSpeedInboundRaw || 0; var speedOut = d.networkSpeedOutboundRaw || 0; if (speedIn > 0 || speedOut > 0) { $("#vf-res-network-speed").text(speedIn + " / " + speedOut + " Mbps"); } else { $("#vf-res-network-speed").text("-"); } $("#vf-resources-panel").show(); // Populate network panel from server data var ipv4List = $("#vf-ipv4-list"); var ipv6List = $("#vf-ipv6-list"); ipv4List.empty(); ipv6List.empty(); var net = response.data.primaryNetwork || {}; var ipv4Arr = net.ipv4Unformatted || []; var ipv6Arr = net.ipv6Unformatted || []; if (ipv4Arr.length > 0) { $.each(ipv4Arr, function (i, ip) { var row = $('
'); row.append('' + $('').text(ip).html() + ''); row.append(vfCopyButton(ip)); ipv4List.append(row); }); } else { ipv4List.append('No IPv4 addresses'); } if (ipv6Arr.length > 0) { $.each(ipv6Arr, function (i, subnet) { var row = $(''); row.append('' + $('').text(subnet).html() + ''); row.append(vfCopyButton(subnet)); ipv6List.append(row); }); } else { ipv6List.append('No IPv6 subnets'); } $("#vf-network-content").show(); $("#vf-server-info").show(); } else { $("#vf-server-info-error").show(); $("#vf-server-info").hide(); } }).fail(function () { $("#vf-server-info-error").show(); }).always(function () { $("#vf-server-info-loader-container").hide(); }); } function vfServerDataAdmin(serviceId, systemUrl) { $("#vf-loader").show(); $("#vf-server-info").hide(); $("#vf-server-info-error").hide(); $.ajax({ type: "GET", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/admin.php?serviceID=" + encodeURIComponent(serviceId) + "&action=serverData" }).done(function (response) { if (response.success) { $("#vf-data-server-name").text(response.data.name); $("#vf-data-server-hostname").text(response.data.hostname); $("#vf-data-server-memory").text(response.data.memory); $("#vf-data-server-traffic").text(response.data.traffic); $("#vf-data-server-storage").text(response.data.storage); $("#vf-data-server-cpu").text(response.data.cpu); var pnAdmin = response.data.primaryNetwork || {}; $("#vf-data-server-ipv4").text(pnAdmin.ipv4 || "-"); $("#vf-data-server-ipv6").text(pnAdmin.ipv6 || "-"); $("#vf-server-info").show(); } else { $("#vf-server-info-error").show(); $("#vf-server-info-error-message").text("Unable to retrieve server information."); $("#vf-server-info").hide(); } }).fail(function () { $("#vf-server-info-error").show(); }).always(function () { $("#vf-loader").hide(); }); } function vfUserPasswordReset(serviceId, systemUrl) { $("#vf-password-reset-button-spinner").show(); $("#vf-password-reset-error").hide(); $("#vf-password-reset-success").hide(); $.ajax({ type: "POST", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=resetPassword" }).done(function (response) { if (response.success) { $("#vf-password-reset-success").show(); $("#vf-data-user-email").text(response.data.email); $("#vf-data-user-password").text(response.data.password); } else { $("#vf-password-reset-error").show(); } }).fail(function () { $("#vf-password-reset-error").show(); }).always(function () { $("#vf-password-reset-button-spinner").hide(); }); } function vfLoginAsServerOwner(serviceId, systemUrl, newWindow) { newWindow = newWindow !== false; vfLoginError(false); $("#vf-login-button").prop("disabled", true); $("#vf-login-button-spinner").show(); $.ajax({ type: "GET", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=loginAsServerOwner" }).done(function (response) { if (response.success && response.token_url) { if (newWindow) { window.open(response.token_url); } else { window.location.href = response.token_url; } } else { vfLoginError(true); } }).fail(function () { vfLoginError(true); }).always(function () { $("#vf-login-button-spinner").hide(); $("#vf-login-button").prop("disabled", false); }); } function vfLoginError(show, message) { message = message || "Unable to open the control panel. Please try again later."; if (show) { $("#vf-login-error").text(message); $("#vf-login-error").show(); } else { $("#vf-login-error").hide(); } } function vfPowerAction(serviceId, systemUrl, action) { var btn = $("#vf-power-" + action); var spinner = btn.find(".vf-btn-spinner"); var alertDiv = $("#vf-power-alert"); // Disable all power buttons during action $(".vf-btn-power").prop("disabled", true); spinner.show(); alertDiv.hide(); var actionLabels = { boot: "Starting", shutdown: "Shutting down", restart: "Restarting", poweroff: "Forcing off" }; $.ajax({ type: "POST", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=powerAction", data: { powerAction: action } }).done(function (response) { if (response.success) { alertDiv.removeClass("alert-danger").addClass("alert-success"); alertDiv.text(response.data.message || (actionLabels[action] + " server...")); } else { alertDiv.removeClass("alert-success").addClass("alert-danger"); alertDiv.text("Power action failed. Please try again."); } alertDiv.show(); }).fail(function () { alertDiv.removeClass("alert-success").addClass("alert-danger"); alertDiv.text("An error occurred. Please try again."); alertDiv.show(); }).always(function () { spinner.hide(); // Cooldown: keep buttons disabled for 3 seconds setTimeout(function () { $(".vf-btn-power").prop("disabled", false); }, 3000); }); } var vfOsBrandColors = { "ubuntu": "#E95420", "debian": "#A81D33", "rocky": "#10B981", "centos": "#932279", "almalinux": "#0F4266", "alma": "#0F4266", "windows": "#0078D4", "fedora": "#51A2DA", "arch": "#1793D1", "opensuse": "#73BA25", "suse": "#73BA25", "freebsd": "#AB2B28", "oracle": "#F80000", "rhel": "#EE0000", "red hat": "#EE0000", "cloudlinux": "#0095D9", "gentoo": "#54487A", "slackware": "#000", "nixos": "#7EBAE4", "alpine": "#0D597F" }; function vfGetBrandColor(name) { var lower = (name || "").toLowerCase(); for (var key in vfOsBrandColors) { if (lower.indexOf(key) !== -1) return vfOsBrandColors[key]; } return "#6c757d"; } function vfRenderOsGallery(container, data, hiddenInput) { var $container = $(container); $container.empty(); if (!data || !data.categories || data.categories.length === 0) { $container.append($('').text("No templates available")); $container.show(); return; } $.each(data.categories, function (ci, category) { var section = $('').attr("data-category", ci); var brandColor = vfGetBrandColor(category.name); // Accordion header var header = $(''); var iconSpan = $('').css("background", brandColor).text((category.name || "?")[0].toUpperCase()); var titleSpan = $('').text(category.name + " (" + category.templates.length + ")"); var arrow = $('' + (ci === 0 ? '▼' : '▶') + ''); header.append(iconSpan).append(titleSpan).append(arrow); section.append(header); // Collapsible grid — first category open by default var grid = $(''); if (ci !== 0) grid.hide(); header.on("click", function () { var isVisible = grid.is(":visible"); grid.slideToggle(200); arrow.html(isVisible ? '▶' : '▼'); }); $.each(category.templates, function (ti, tpl) { var label = tpl.name + (tpl.version ? " " + tpl.version : "") + (tpl.variant ? " " + tpl.variant : ""); var card = $('') .attr("data-id", tpl.id) .attr("data-search", label.toLowerCase()); if (tpl.eol) card.addClass("vf-os-card-eol"); var iconDiv = $('').css("background", brandColor); iconDiv.append($('').text((tpl.name || "?")[0].toUpperCase())); card.append(iconDiv); card.append($('').text(tpl.name)); card.append($('').text((tpl.version || "") + (tpl.variant ? " " + tpl.variant : ""))); if (tpl.eol) { card.append($('').text("EOL")); } card.on("click", function () { $container.find(".vf-os-card").removeClass("vf-os-card-selected"); $(this).addClass("vf-os-card-selected"); $(hiddenInput).val(tpl.id); var details = $("#vf-os-details"); details.empty(); details.append($('').text(label)); if (tpl.description) { details.append($('').text(tpl.description)); } details.show(); }); grid.append(card); }); section.append(grid); $container.append(section); }); $container.show(); } function vfLoadOsTemplates(serviceId, systemUrl) { $.ajax({ type: "GET", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=osTemplates" }).done(function (response) { $("#vf-os-gallery-loader").hide(); if (response.success && response.data) { vfRenderOsGallery("#vf-os-gallery", response.data, "#vf-rebuild-os"); // Bind search after gallery is rendered $("#vf-os-search").on("keyup", function () { var query = $(this).val().toLowerCase(); $("#vf-os-gallery .vf-os-card").each(function () { var match = $(this).data("search").indexOf(query) !== -1; $(this).toggle(match); }); $("#vf-os-gallery .vf-os-category").each(function () { var hasVisible = $(this).find(".vf-os-card:visible").length > 0; $(this).toggle(hasVisible); }); }); } else { $("#vf-os-gallery").append($('').text("No templates available")).show(); } }).fail(function () { $("#vf-os-gallery-loader").hide(); $("#vf-os-gallery").append($('').text("Error loading templates")).show(); }); } function vfRebuildServer(serviceId, systemUrl) { var osId = $("#vf-rebuild-os").val(); var alertDiv = $("#vf-rebuild-alert"); if (!osId) { alertDiv.removeClass("alert-success").addClass("alert-danger"); alertDiv.text("Please select an operating system."); alertDiv.show(); return; } if (!confirm("Are you sure you want to rebuild this server? ALL DATA WILL BE ERASED. This action cannot be undone.")) { return; } $("#vf-rebuild-button").prop("disabled", true); $("#vf-rebuild-spinner").show(); alertDiv.hide(); vfShowProgress("Rebuilding server..."); $.ajax({ type: "POST", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=rebuild", data: { osId: osId } }).done(function (response) { if (response.success) { alertDiv.removeClass("alert-danger").addClass("alert-success"); alertDiv.text(response.data.message || "Server rebuild initiated. You will receive an email when the process is complete."); } else { alertDiv.removeClass("alert-success").addClass("alert-danger"); alertDiv.text("Rebuild failed. Please try again."); } alertDiv.show(); }).fail(function () { alertDiv.removeClass("alert-success").addClass("alert-danger"); alertDiv.text("An error occurred. Please try again."); alertDiv.show(); }).always(function () { vfHideProgress(); $("#vf-rebuild-spinner").hide(); // Cooldown: keep button disabled for 30 seconds after rebuild setTimeout(function () { $("#vf-rebuild-button").prop("disabled", false); }, 30000); }); } function impersonateServerOwner(serviceId, systemUrl) { $.ajax({ type: "GET", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/admin.php?serviceID=" + encodeURIComponent(serviceId) + "&action=impersonateServerOwner" }).done(function (response) { if (response.success && response.user) { window.open(response.url + "/_imp/in/" + response.user.id + "/-"); } }); } // ========================================================================= // VNC Console // ========================================================================= function vfOpenVnc(serviceId, systemUrl) { var btn = $("#vf-vnc-button"); var spinner = $("#vf-vnc-spinner"); var alertDiv = $("#vf-vnc-alert"); btn.prop("disabled", true); spinner.show(); alertDiv.hide(); // Open window immediately in click context to avoid popup blockers var vncWindow = window.open("", "_blank"); if (!vncWindow) { alertDiv.removeClass("alert-success").addClass("alert-danger"); alertDiv.text("Popup blocked. Please allow popups for this site and try again."); alertDiv.show(); spinner.hide(); btn.prop("disabled", false); return; } $.ajax({ type: "GET", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=vnc" }).done(function (response) { if (response.success && response.data) { var data = response.data.data || response.data; if (data.url) { vncWindow.location.href = data.url; } else if (data.host && data.port) { // Build noVNC URL if available var vncUrl = "https://" + data.host + ":" + data.port; if (data.token) { vncUrl += "?token=" + encodeURIComponent(data.token); } vncWindow.location.href = vncUrl; } else { vncWindow.close(); alertDiv.removeClass("alert-danger").addClass("alert-success"); alertDiv.text("VNC session is ready. Check your VirtFusion control panel for access."); alertDiv.show(); } } else { vncWindow.close(); alertDiv.removeClass("alert-success").addClass("alert-danger"); alertDiv.text("VNC console is not available."); alertDiv.show(); } }).fail(function () { vncWindow.close(); alertDiv.removeClass("alert-success").addClass("alert-danger"); alertDiv.text("An error occurred. The server may be powered off."); alertDiv.show(); }).always(function () { spinner.hide(); btn.prop("disabled", false); }); } function vfToggleVnc(serviceId, systemUrl, enabled) { var toggle = $("#vf-vnc-toggle"); toggle.prop("disabled", true); $.ajax({ type: "POST", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=toggleVnc", data: { enabled: enabled ? "1" : "0" } }).done(function (response) { if (response.success) { if (enabled && response.data) { var data = response.data.data || response.data; if (data.ip || data.host) { $("#vf-vnc-ip").text(data.ip || data.host || "-"); $("#vf-vnc-port").text(data.port || "-"); $("#vf-vnc-details").show(); } } else { $("#vf-vnc-details").hide(); } } else { toggle.prop("checked", !enabled); } }).fail(function () { toggle.prop("checked", !enabled); }).always(function () { toggle.prop("disabled", false); }); } function vfCopyVncPassword(serviceId, systemUrl) { var confirmSpan = $("#vf-vnc-copy-confirm"); $.ajax({ type: "GET", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=vnc" }).done(function (response) { if (response.success && response.data) { var data = response.data.data || response.data; var password = data.password || ""; if (password) { navigator.clipboard.writeText(password).then(function () { confirmSpan.text("Copied!").show(); setTimeout(function () { confirmSpan.hide(); }, 2000); }).catch(function () { confirmSpan.text("Copy failed").show(); setTimeout(function () { confirmSpan.hide(); }, 2000); }); } } }); } // ========================================================================= // Self Service — Credit & Usage // ========================================================================= function vfLoadSelfServiceUsage(serviceId, systemUrl) { $.ajax({ type: "GET", dataType: "json", url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=selfServiceUsage" }).done(function (response) { if (response.success && response.data) { var data = response.data.data || response.data; // Credit balance var balance = "-"; if (data.credit !== undefined) { balance = parseFloat(data.credit).toFixed(2); } else if (data.balance !== undefined) { balance = parseFloat(data.balance).toFixed(2); } $("#vf-ss-credit-balance").text(balance); // Usage breakdown var tbody = $("#vf-ss-usage-table"); tbody.empty(); var items = data.usage || data.items || []; if (Array.isArray(items) && items.length > 0) { $.each(items, function (i, item) { var desc = item.description || item.name || item.server || "Item"; var cost = item.cost !== undefined ? parseFloat(item.cost).toFixed(2) : "-"; tbody.append('