Add firewall, network, VNC, backup, resource management and UsageUpdate

New features implemented:
- Firewall management: enable/disable, status display, apply rules
- IP address management: add/remove IPv4 and IPv6 with client UI
- VNC console access integration (VirtFusion v6.1.0+)
- Backup plan assignment/removal via API
- Resource modification: in-place memory/CPU/traffic changes
- UsageUpdate cron: automated bandwidth and disk usage sync to WHMCS
- Dry run validation: test server creation config before provisioning
- Admin "Validate Server Config" button for dry run testing

Client area additions:
- Firewall panel with enable/disable/apply controls and status badge
- Network panel with IPv4/IPv6 listing, add, and remove buttons
- VNC Console panel with browser-based access
- All panels load asynchronously with spinner indicators

Comprehensive README rewrite with:
- Table of contents, requirements matrix, step-by-step installation
- Detailed configuration guide for all features
- Theme compatibility documentation (Six, Twenty-One, Lagom)
- Complete API endpoints reference organized by category
- UsageUpdate cron documentation with data format details
- Troubleshooting tables for common issues
- Known issues section covering version requirements
- Security architecture documentation
- File structure reference

https://claude.ai/code/session_01TCsJ4WZCGuEX3zqh1tQ2zx
This commit is contained in:
Claude
2026-02-07 12:43:02 +00:00
parent c93072b1c6
commit cad1af18c1
8 changed files with 1628 additions and 118 deletions

View File

@@ -123,6 +123,22 @@
margin: 10px;
}
/* Network / IP Management */
.vf-ip-row {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 0;
}
.vf-ip-address {
font-family: monospace;
font-size: 0.9rem;
}
.vf-ip-remove {
font-size: 0.7rem;
padding: 0.15rem 0.4rem;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.vf-power-buttons {
@@ -131,4 +147,7 @@
.vf-btn-power {
width: 100%;
}
.vf-ip-row {
flex-wrap: wrap;
}
}

View File

@@ -259,3 +259,245 @@ function impersonateServerOwner(serviceId, systemUrl) {
}
});
}
// =========================================================================
// Firewall Management
// =========================================================================
function vfLoadFirewallStatus(serviceId, systemUrl) {
$.ajax({
type: "GET",
dataType: "json",
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=firewallStatus"
}).done(function (response) {
if (response.success) {
var badge = $("#vf-firewall-badge");
var data = response.data;
var enabled = data && data.data && data.data.enabled;
if (enabled) {
badge.text("Enabled").addClass("vf-badge-active");
} else {
badge.text("Disabled").addClass("vf-badge-awaiting");
}
$("#vf-firewall-content").show();
} else {
$("#vf-firewall-badge").text("Unknown").addClass("vf-badge-awaiting");
$("#vf-firewall-content").show();
}
}).fail(function () {
$("#vf-firewall-badge").text("Unavailable").addClass("vf-badge-awaiting");
$("#vf-firewall-content").show();
}).always(function () {
$("#vf-firewall-loader").hide();
});
}
function vfFirewallAction(serviceId, systemUrl, action) {
var btnId = {
firewallEnable: "#vf-firewall-enable",
firewallDisable: "#vf-firewall-disable",
firewallApplyRules: "#vf-firewall-apply"
};
var btn = $(btnId[action]);
var spinner = btn.find(".vf-btn-spinner");
var alertDiv = $("#vf-firewall-alert");
btn.prop("disabled", true);
spinner.show();
alertDiv.hide();
$.ajax({
type: "GET",
dataType: "json",
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=" + encodeURIComponent(action)
}).done(function (response) {
if (response.success) {
alertDiv.removeClass("alert-danger").addClass("alert-success");
alertDiv.text(response.data.message || "Firewall action completed.");
// Refresh status badge
vfLoadFirewallStatus(serviceId, systemUrl);
} else {
alertDiv.removeClass("alert-success").addClass("alert-danger");
alertDiv.text(response.errors || "Firewall action failed.");
}
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();
btn.prop("disabled", false);
});
}
// =========================================================================
// Network / IP Management
// =========================================================================
function vfLoadServerIPs(serviceId, systemUrl) {
$.ajax({
type: "GET",
dataType: "json",
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=serverIPs"
}).done(function (response) {
if (response.success) {
var ipv4List = $("#vf-ipv4-list");
var ipv6List = $("#vf-ipv6-list");
ipv4List.empty();
ipv6List.empty();
if (response.data.ipv4 && response.data.ipv4.length > 0) {
$.each(response.data.ipv4, function (i, ip) {
var row = $('<div class="vf-ip-row"></div>');
row.append('<span class="vf-ip-address">' + $('<span>').text(ip).html() + '</span>');
if (i > 0) {
row.append(' <button class="btn btn-sm btn-outline-danger vf-ip-remove" onclick="vfRemoveIP(\'' + serviceId + '\',\'' + systemUrl + '\',\'removeIPv4\',\'' + encodeURIComponent(ip) + '\')">Remove</button>');
}
ipv4List.append(row);
});
} else {
ipv4List.append('<span class="text-muted">No IPv4 addresses</span>');
}
if (response.data.ipv6 && response.data.ipv6.length > 0) {
$.each(response.data.ipv6, function (i, subnet) {
var row = $('<div class="vf-ip-row"></div>');
row.append('<span class="vf-ip-address">' + $('<span>').text(subnet).html() + '</span>');
row.append(' <button class="btn btn-sm btn-outline-danger vf-ip-remove" onclick="vfRemoveIP(\'' + serviceId + '\',\'' + systemUrl + '\',\'removeIPv6\',\'' + encodeURIComponent(subnet) + '\')">Remove</button>');
ipv6List.append(row);
});
} else {
ipv6List.append('<span class="text-muted">No IPv6 subnets</span>');
}
$("#vf-network-content").show();
} else {
$("#vf-network-content").show();
$("#vf-ipv4-list").html('<span class="text-muted">Unable to load</span>');
$("#vf-ipv6-list").html('<span class="text-muted">Unable to load</span>');
}
}).fail(function () {
$("#vf-network-content").show();
$("#vf-ipv4-list").html('<span class="text-muted">Unable to load</span>');
$("#vf-ipv6-list").html('<span class="text-muted">Unable to load</span>');
}).always(function () {
$("#vf-network-loader").hide();
});
}
function vfAddIP(serviceId, systemUrl, action) {
var btn = $("#vf-add-" + (action === "addIPv4" ? "ipv4" : "ipv6"));
var spinner = btn.find(".vf-btn-spinner");
var alertDiv = $("#vf-network-alert");
btn.prop("disabled", true);
spinner.show();
alertDiv.hide();
$.ajax({
type: "GET",
dataType: "json",
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=" + encodeURIComponent(action)
}).done(function (response) {
if (response.success) {
alertDiv.removeClass("alert-danger").addClass("alert-success");
alertDiv.text(response.data.message || "IP address added successfully.");
alertDiv.show();
// Refresh IP list
vfLoadServerIPs(serviceId, systemUrl);
} else {
alertDiv.removeClass("alert-success").addClass("alert-danger");
alertDiv.text(response.errors || "Failed to add IP address.");
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();
btn.prop("disabled", false);
});
}
function vfRemoveIP(serviceId, systemUrl, action, identifier) {
if (!confirm("Are you sure you want to remove this IP address?")) {
return;
}
var alertDiv = $("#vf-network-alert");
alertDiv.hide();
var paramName = action === "removeIPv4" ? "ip" : "subnet";
$.ajax({
type: "GET",
dataType: "json",
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=" + encodeURIComponent(action) + "&" + paramName + "=" + identifier
}).done(function (response) {
if (response.success) {
alertDiv.removeClass("alert-danger").addClass("alert-success");
alertDiv.text(response.data.message || "IP address removed successfully.");
alertDiv.show();
vfLoadServerIPs(serviceId, systemUrl);
} else {
alertDiv.removeClass("alert-success").addClass("alert-danger");
alertDiv.text(response.errors || "Failed to remove IP address.");
alertDiv.show();
}
}).fail(function () {
alertDiv.removeClass("alert-success").addClass("alert-danger");
alertDiv.text("An error occurred. Please try again.");
alertDiv.show();
});
}
// =========================================================================
// 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();
$.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) {
window.open(data.url, "_blank");
} 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);
}
window.open(vncUrl, "_blank");
} else {
alertDiv.removeClass("alert-danger").addClass("alert-success");
alertDiv.text("VNC session is ready. Check your VirtFusion control panel for access.");
alertDiv.show();
}
} else {
alertDiv.removeClass("alert-success").addClass("alert-danger");
alertDiv.text(response.errors || "VNC console is not available.");
alertDiv.show();
}
}).fail(function () {
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);
});
}

View File

@@ -168,6 +168,93 @@
</div>
</div>
{* Firewall Management Panel *}
<div class="panel card panel-default mb-3">
<div class="panel-heading card-header">
<h3 class="panel-title card-title m-0">
Firewall
<span id="vf-firewall-badge" class="vf-badge" style="float: right;"></span>
</h3>
</div>
<div class="panel-body card-body p-4">
<div id="vf-firewall-alert" class="alert" style="display: none;"></div>
<div id="vf-firewall-loader" class="d-flex align-items-center justify-content-center" style="min-height: 60px;">
<div class="spinner-border spinner-border-sm"></div>
</div>
<div id="vf-firewall-content" style="display: none;">
<div class="row mb-3">
<div class="col-12">
<div class="vf-power-buttons">
<button id="vf-firewall-enable" onclick="vfFirewallAction('{$serviceid}','{$systemURL}','firewallEnable')" type="button" class="btn btn-success vf-btn-power">
<span class="vf-btn-spinner spinner-border spinner-border-sm" style="display:none;"></span>
Enable
</button>
<button id="vf-firewall-disable" onclick="vfFirewallAction('{$serviceid}','{$systemURL}','firewallDisable')" type="button" class="btn btn-danger vf-btn-power">
<span class="vf-btn-spinner spinner-border spinner-border-sm" style="display:none;"></span>
Disable
</button>
<button id="vf-firewall-apply" onclick="vfFirewallAction('{$serviceid}','{$systemURL}','firewallApplyRules')" type="button" class="btn btn-primary vf-btn-power">
<span class="vf-btn-spinner spinner-border spinner-border-sm" style="display:none;"></span>
Apply Rules
</button>
</div>
</div>
</div>
<p class="vf-small text-muted mb-0">Manage your server firewall. Use the VirtFusion control panel for advanced rule configuration.</p>
</div>
<script>vfLoadFirewallStatus('{$serviceid}', '{$systemURL}');</script>
</div>
</div>
{* Network Management Panel *}
<div class="panel card panel-default mb-3">
<div class="panel-heading card-header">
<h3 class="panel-title card-title m-0">Network</h3>
</div>
<div class="panel-body card-body p-4">
<div id="vf-network-alert" class="alert" style="display: none;"></div>
<div id="vf-network-loader" class="d-flex align-items-center justify-content-center" style="min-height: 60px;">
<div class="spinner-border spinner-border-sm"></div>
</div>
<div id="vf-network-content" style="display: none;">
<div class="row mb-3">
<div class="col-md-6">
<h5 class="vf-bold">IPv4 Addresses</h5>
<div id="vf-ipv4-list" class="mb-2"></div>
<button id="vf-add-ipv4" onclick="vfAddIP('{$serviceid}','{$systemURL}','addIPv4')" type="button" class="btn btn-sm btn-outline-primary">
<span class="vf-btn-spinner spinner-border spinner-border-sm" style="display:none;"></span>
Add IPv4
</button>
</div>
<div class="col-md-6">
<h5 class="vf-bold">IPv6 Subnets</h5>
<div id="vf-ipv6-list" class="mb-2"></div>
<button id="vf-add-ipv6" onclick="vfAddIP('{$serviceid}','{$systemURL}','addIPv6')" type="button" class="btn btn-sm btn-outline-primary">
<span class="vf-btn-spinner spinner-border spinner-border-sm" style="display:none;"></span>
Add IPv6
</button>
</div>
</div>
</div>
<script>vfLoadServerIPs('{$serviceid}', '{$systemURL}');</script>
</div>
</div>
{* VNC Console Panel *}
<div class="panel card panel-default mb-3">
<div class="panel-heading card-header">
<h3 class="panel-title card-title m-0">VNC Console</h3>
</div>
<div class="panel-body card-body p-4">
<div id="vf-vnc-alert" class="alert" style="display: none;"></div>
<p>Access your server's console directly in your browser. The server must be running for VNC access.</p>
<button id="vf-vnc-button" onclick="vfOpenVnc('{$serviceid}','{$systemURL}')" type="button" class="btn btn-primary text-uppercase d-flex align-items-center">
<span id="vf-vnc-spinner" class="spinner-border spinner-border-sm vf-spinner-margin" style="display:none;"></span>
Open Console
</button>
</div>
</div>
{elseif $serviceStatus eq 'Suspended'}
<div class="panel card panel-default mb-3">