mirror of
https://gitlab.com/ArkHost/WHMCS-ArkhostVPSAG.git
synced 2026-07-24 15:55:54 +02:00
2165 lines
101 KiB
Smarty
2165 lines
101 KiB
Smarty
{*
|
|
* WHMCS Server Module - VPSAG
|
|
* @package WHMCS
|
|
* @copyright ArkHost 2025
|
|
* @link https://arkhost.com
|
|
* @author ArkHost <support@arkhost.com>
|
|
*}
|
|
|
|
<style>
|
|
/* Hide WHMCS default product details except Domain (keep Domain visible to show VPS hostname) */
|
|
/* Hide Username, Server Name, IP Address rows using nth-child selectors */
|
|
#domain > .row:nth-child(2) {
|
|
display: none !important; /* Username */
|
|
}
|
|
#domain > .row:nth-child(3) {
|
|
display: none !important; /* Server Name */
|
|
}
|
|
#domain > .row:nth-child(4) {
|
|
display: none !important; /* IP Address */
|
|
}
|
|
#domain > br {
|
|
display: none !important;
|
|
}
|
|
#domain > p {
|
|
display: none !important; /* Visit Website button */
|
|
}
|
|
|
|
/* Hide original Domain label - will be replaced with Hostname via JavaScript */
|
|
#domain > .row:nth-child(1) .col-sm-5 strong {
|
|
visibility: hidden;
|
|
position: relative;
|
|
}
|
|
#domain > .row:nth-child(1) .col-sm-5 strong::after {
|
|
visibility: visible;
|
|
position: absolute;
|
|
left: 0;
|
|
content: attr(data-hostname-label);
|
|
}
|
|
|
|
|
|
.arkhost-vps-container {
|
|
font-family: 'Arial', sans-serif;
|
|
margin: 15px 0;
|
|
}
|
|
.arkhost-vps-container .title-block {
|
|
color: #575d6f;
|
|
margin-top: 20px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
}
|
|
.arkhost-vps-container .dashboard-value {
|
|
font-size: 18px;
|
|
color: #3c7fb4;
|
|
}
|
|
.arkhost-vps-container .usage-details {
|
|
font-size: 14px;
|
|
color: #888;
|
|
margin-top: 5px;
|
|
}
|
|
.arkhost-vps-container .progress {
|
|
height: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.arkhost-vps-container .nav-tabs {
|
|
border-bottom: 1px solid #dee2e6;
|
|
}
|
|
.arkhost-vps-container .nav-tabs .nav-link {
|
|
border: none;
|
|
color: #6c757d;
|
|
font-weight: 500;
|
|
border-radius: 12px 12px 0 0;
|
|
padding: 12px 20px;
|
|
transition: all 0.2s ease;
|
|
margin-right: 4px;
|
|
}
|
|
.arkhost-vps-container .nav-tabs .nav-link:hover {
|
|
background-color: #f8f9fa;
|
|
color: #495057;
|
|
}
|
|
.arkhost-vps-container .nav-tabs .nav-link.active {
|
|
background-color: #e9ecef;
|
|
color: #495057;
|
|
border-radius: 12px 12px 0 0;
|
|
}
|
|
.arkhost-vps-container .overview-label {
|
|
color: #575d6f;
|
|
font-weight: 600;
|
|
margin-bottom: 5px;
|
|
font-size: 14px;
|
|
}
|
|
.arkhost-vps-container .submit-btn {
|
|
background: linear-gradient(135deg, #3c7fb4 0%, #2d5a87 100%);
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 12px;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.arkhost-vps-container .danger-btn {
|
|
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 12px;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.arkhost-vps-container .head {
|
|
border-bottom: 2px solid #3c7fb4;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 15px;
|
|
margin-top: 10px;
|
|
}
|
|
.arkhost-vps-container .head img {
|
|
width: 32px;
|
|
height: 32px;
|
|
margin-right: 10px;
|
|
margin-top: -8px;
|
|
vertical-align: middle;
|
|
}
|
|
.arkhost-vps-container .v-tabs-container .nav-link {
|
|
padding: 10px 16px;
|
|
margin: 3px 0;
|
|
border-radius: 12px;
|
|
transition: all 0.2s ease;
|
|
color: #6c757d;
|
|
font-weight: 500;
|
|
text-decoration: none;
|
|
display: block;
|
|
}
|
|
.arkhost-vps-container .v-tabs-container .nav-link:hover {
|
|
background-color: #f8f9fa;
|
|
color: #495057;
|
|
text-decoration: none;
|
|
}
|
|
.arkhost-vps-container .v-tabs-container .nav-link.active {
|
|
background-color: #e9ecef;
|
|
color: #495057;
|
|
}
|
|
.arkhost-vps-container .os_badge {
|
|
margin: 8px;
|
|
border-radius: 16px;
|
|
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
min-height: 140px;
|
|
width: 100%;
|
|
max-width: 400px !important;
|
|
background: #ffffff;
|
|
border: none;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge:hover {
|
|
transform: translateY(-6px);
|
|
box-shadow: 0 12px 40px rgba(60, 127, 180, 0.15);
|
|
z-index: 10;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-toggle {
|
|
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 16px;
|
|
padding: 24px 20px;
|
|
transition: all 0.3s ease;
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
min-height: 140px;
|
|
}
|
|
|
|
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-toggle:hover {
|
|
border-color: #3c7fb4;
|
|
background: linear-gradient(135deg, #ffffff 0%, #f0f4f8 100%);
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .media-body {
|
|
min-width: 0;
|
|
flex: 1;
|
|
z-index: 2;
|
|
position: relative;
|
|
margin-top: 6px;
|
|
text-align: center;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .distro_name {
|
|
font-size: 1.1rem;
|
|
line-height: 1.3;
|
|
margin-bottom: 4px;
|
|
word-wrap: break-word;
|
|
overflow-wrap: break-word;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .version {
|
|
font-size: 0.85rem;
|
|
line-height: 1.2;
|
|
color: #6c757d;
|
|
font-weight: 500;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.arkhost-vps-container .distro_img {
|
|
width: 48px;
|
|
height: 48px;
|
|
margin-right: 0;
|
|
margin-top: 8px;
|
|
margin-bottom: 6px;
|
|
border-radius: 50%;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
transition: all 0.3s ease;
|
|
z-index: 2;
|
|
position: relative;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge:hover .distro_img {
|
|
transform: scale(1.1) rotate(5deg);
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
/* Dropdown Menu Enhancements */
|
|
.arkhost-vps-container .os_badge .dropdown-menu {
|
|
border-radius: 12px;
|
|
border: none;
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
|
|
padding: 8px 0;
|
|
margin-top: 8px;
|
|
z-index: 1050;
|
|
min-width: 100%;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-item {
|
|
padding: 12px 20px;
|
|
transition: all 0.2s ease;
|
|
color: #495057;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-item:hover {
|
|
background: linear-gradient(135deg, #3c7fb4 0%, #2d5a87 100%);
|
|
color: white;
|
|
transform: translateX(4px);
|
|
}
|
|
#loading {
|
|
text-align: center;
|
|
padding: 2rem;
|
|
}
|
|
|
|
/* Rounded borders for containers */
|
|
.arkhost-vps-container .border {
|
|
border-radius: 12px !important;
|
|
border: 1px solid #e3e6f0 !important;
|
|
}
|
|
|
|
/* VPS Control Button Styles */
|
|
.arkhost-vps-container .vps-control-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 8px 16px;
|
|
border-radius: 20px;
|
|
margin: 8px 4px;
|
|
transition: all 0.2s ease;
|
|
text-decoration: none;
|
|
color: white;
|
|
font-size: 0.9rem;
|
|
font-weight: 500;
|
|
min-height: 36px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
border: none;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
text-decoration: none;
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn:active {
|
|
transform: translateY(0);
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn.start-btn {
|
|
background: linear-gradient(45deg, #28a745, #34ce57);
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn.start-btn:hover {
|
|
background: linear-gradient(45deg, #218838, #28a745);
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn.stop-btn {
|
|
background: linear-gradient(45deg, #dc3545, #ff4757);
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn.stop-btn:hover {
|
|
background: linear-gradient(45deg, #c82333, #dc3545);
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn.restart-btn {
|
|
background: linear-gradient(45deg, #fd7e14, #ff9f43);
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn.restart-btn:hover {
|
|
background: linear-gradient(45deg, #e65100, #fd7e14);
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn.vnc-btn {
|
|
background: linear-gradient(45deg, #6f42c1, #8854d0);
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn.vnc-btn:hover {
|
|
background: linear-gradient(45deg, #5a32a3, #6f42c1);
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn.vnc-btn img {
|
|
width: 18px;
|
|
height: 18px;
|
|
margin-right: 6px;
|
|
}
|
|
|
|
.arkhost-vps-container .vps-control-btn i {
|
|
margin-right: 6px;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
/* Mobile Responsive - Stack VPS Control Buttons Vertically */
|
|
@media (max-width: 767px) {
|
|
.arkhost-vps-container .vps-control-btn {
|
|
display: block;
|
|
width: 100%;
|
|
margin: 8px 0;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
/* Graph Time Selection Buttons */
|
|
.arkhost-vps-container .graph-time-btn {
|
|
background: #f8f9fa;
|
|
color: #6c757d;
|
|
border: 1px solid #dee2e6;
|
|
padding: 8px 14px;
|
|
border-radius: 12px;
|
|
margin: 0 3px;
|
|
font-weight: 500;
|
|
transition: all 0.2s ease;
|
|
text-decoration: none;
|
|
display: inline-block;
|
|
cursor: pointer;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.arkhost-vps-container .graph-time-btn:hover {
|
|
background: #e9ecef;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.arkhost-vps-container .graph-time-btn.active {
|
|
background: #e9ecef;
|
|
color: #495057;
|
|
border-color: #ced4da;
|
|
}
|
|
|
|
.arkhost-vps-container .graph-time-btn.active:hover {
|
|
background: #dee2e6;
|
|
}
|
|
|
|
/* OS Selection Enhancements */
|
|
.arkhost-vps-container .os_badge {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-toggle::after {
|
|
display: inline-block;
|
|
margin-left: .255em;
|
|
vertical-align: .255em;
|
|
content: "";
|
|
border-top: .3em solid;
|
|
border-right: .3em solid transparent;
|
|
border-bottom: 0;
|
|
border-left: .3em solid transparent;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-toggle:hover::after {
|
|
border-top-color: #495057;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-toggle.btn-success {
|
|
border: 2px solid #28a745 !important;
|
|
background: linear-gradient(135deg, #f8fff9 0%, #e8f5e8 100%);
|
|
box-shadow: 0 6px 25px rgba(40, 167, 69, 0.3);
|
|
color: #155724 !important;
|
|
position: relative;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-toggle.btn-success::before {
|
|
content: '✓';
|
|
position: absolute;
|
|
top: 8px;
|
|
right: 12px;
|
|
font-size: 18px;
|
|
color: #28a745;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-toggle.btn-success .distro_name {
|
|
color: #155724 !important;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-toggle.btn-success .version {
|
|
color: #155724 !important;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-item.active {
|
|
background-color: #007bff !important;
|
|
color: white !important;
|
|
font-weight: bold;
|
|
position: relative;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-item.active::after {
|
|
content: "✓";
|
|
position: absolute;
|
|
right: 15px;
|
|
font-weight: bold;
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-item:hover {
|
|
background-color: #007bff;
|
|
color: white !important;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-item.active:hover {
|
|
background-color: #0056b3 !important;
|
|
color: white !important;
|
|
}
|
|
|
|
/* Fix dropdown menu positioning and styling */
|
|
.arkhost-vps-container .os_badge .dropdown-menu {
|
|
z-index: 1050;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
padding: 8px 0;
|
|
margin-top: 4px;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .dropdown-item {
|
|
padding: 8px 16px;
|
|
font-size: 0.9rem;
|
|
transition: all 0.2s ease;
|
|
border: none;
|
|
background: none;
|
|
}
|
|
|
|
.arkhost-vps-container .os_badge .version .fa-check {
|
|
color: #28a745 !important;
|
|
margin-left: 5px;
|
|
animation: checkmark-appear 0.3s ease-in;
|
|
}
|
|
|
|
@keyframes checkmark-appear {
|
|
0% { opacity: 0; transform: scale(0.5); }
|
|
100% { opacity: 1; transform: scale(1); }
|
|
}
|
|
|
|
/* Custom Notification System */
|
|
.arkhost-notification {
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
min-width: 300px;
|
|
max-width: 400px;
|
|
z-index: 9999;
|
|
padding: 16px 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
color: white;
|
|
font-weight: 500;
|
|
transform: translateX(450px);
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
font-family: 'Arial', sans-serif;
|
|
}
|
|
|
|
.arkhost-notification.show {
|
|
transform: translateX(0);
|
|
}
|
|
|
|
.arkhost-notification.success {
|
|
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
|
|
}
|
|
|
|
.arkhost-notification.error {
|
|
background: linear-gradient(135deg, #dc3545 0%, #fd7e14 100%);
|
|
}
|
|
|
|
.arkhost-notification.warning {
|
|
background: linear-gradient(135deg, #ffc107 0%, #fd7e14 100%);
|
|
color: #212529;
|
|
}
|
|
|
|
.arkhost-notification.info {
|
|
background: linear-gradient(135deg, #17a2b8 0%, #6f42c1 100%);
|
|
}
|
|
|
|
.arkhost-notification i {
|
|
font-size: 20px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.arkhost-notification .close-btn {
|
|
margin-left: auto;
|
|
background: none;
|
|
border: none;
|
|
color: inherit;
|
|
font-size: 18px;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
opacity: 0.8;
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.arkhost-notification .close-btn:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Custom Confirm Dialog */
|
|
.arkhost-confirm-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
z-index: 10000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.arkhost-confirm-overlay.show {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
}
|
|
|
|
.arkhost-confirm-dialog {
|
|
background: white;
|
|
border-radius: 12px;
|
|
padding: 24px;
|
|
max-width: 400px;
|
|
width: 90%;
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
transform: scale(0.8);
|
|
transition: transform 0.3s ease;
|
|
font-family: 'Arial', sans-serif;
|
|
}
|
|
|
|
.arkhost-confirm-overlay.show .arkhost-confirm-dialog {
|
|
transform: scale(1);
|
|
}
|
|
|
|
.arkhost-confirm-title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
margin-bottom: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.arkhost-confirm-message {
|
|
color: #6c757d;
|
|
margin-bottom: 20px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.arkhost-confirm-buttons {
|
|
display: flex;
|
|
gap: 12px;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.arkhost-confirm-btn {
|
|
padding: 8px 16px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
font-family: inherit;
|
|
}
|
|
|
|
.arkhost-confirm-btn.cancel {
|
|
background: #6c757d;
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-confirm-btn.cancel:hover {
|
|
background: #5a6268;
|
|
}
|
|
|
|
.arkhost-confirm-btn.confirm {
|
|
background: #dc3545;
|
|
color: white;
|
|
}
|
|
|
|
.arkhost-confirm-btn.confirm:hover {
|
|
background: #c82333;
|
|
}
|
|
</style>
|
|
|
|
<script type="text/javascript">
|
|
var productURL = '{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}';
|
|
var serverInfoInitial = JSON.parse('{$serverInfo|@json_encode}');
|
|
var lang = {
|
|
moduleactionfailed: 'Action failed!',
|
|
moduleactionsuccess: 'Action completed successfully',
|
|
backups: {
|
|
available: 'Available',
|
|
creating: 'Creating...',
|
|
error: 'Error',
|
|
automatic: 'Automatic',
|
|
manual: 'Manual'
|
|
},
|
|
confirm: {
|
|
stop: {
|
|
title: "{$_LANG['Confirm']['Stop']['Title']|escape:'javascript'|default:'Stop VPS'}",
|
|
message: "{$_LANG['Confirm']['Stop']['Message']|escape:'javascript'|default:'Do you want to stop this VPS. This will shutdown the server.'}"
|
|
},
|
|
restart: {
|
|
title: "{$_LANG['Confirm']['Restart']['Title']|escape:'javascript'|default:'Restart VPS'}",
|
|
message: "{$_LANG['Confirm']['Restart']['Message']|escape:'javascript'|default:'Do you want to restart this VPS. This will reboot the server.'}"
|
|
},
|
|
createBackup: {
|
|
title: "{$_LANG['Confirm']['CreateBackup']['Title']|escape:'javascript'|default:'Create Backup'}",
|
|
message: "{$_LANG['Confirm']['CreateBackup']['Message']|escape:'javascript'|default:'Do you want to create a backup. This may take several minutes.'}"
|
|
},
|
|
deleteBackup: {
|
|
title: "{$_LANG['Confirm']['DeleteBackup']['Title']|escape:'javascript'|default:'Delete Backup'}",
|
|
message: "{$_LANG['Confirm']['DeleteBackup']['Message']|escape:'javascript'|default:'Do you want to delete this backup. This action cannot be undone.'}"
|
|
},
|
|
restoreBackup: {
|
|
title: "{$_LANG['Confirm']['RestoreBackup']['Title']|escape:'javascript'|default:'Restore Backup'}",
|
|
message: "{$_LANG['Confirm']['RestoreBackup']['Message']|escape:'javascript'|default:'Do you want to restore this backup. This will overwrite all current data and cannot be undone.'}"
|
|
}
|
|
}
|
|
};
|
|
|
|
// VPS control functions
|
|
console.log('Defining VPS control functions...');
|
|
|
|
function confirmStop() {
|
|
console.log('confirmStop called');
|
|
showConfirm(lang.confirm.stop.message, lang.confirm.stop.title, function() {
|
|
ArkHostVPS_API('Stop', true);
|
|
});
|
|
return false;
|
|
}
|
|
|
|
function confirmRestart() {
|
|
console.log('confirmRestart called');
|
|
showConfirm(lang.confirm.restart.message, lang.confirm.restart.title, function() {
|
|
ArkHostVPS_API('Reboot', true);
|
|
});
|
|
return false;
|
|
}
|
|
|
|
console.log('VPS control functions defined!', typeof confirmRestart);
|
|
|
|
// Custom notification functions
|
|
function showNotification(message, type, duration) {
|
|
type = type || 'success';
|
|
duration = duration || 4000;
|
|
|
|
// Remove existing notifications
|
|
var existing = document.querySelectorAll('.arkhost-notification');
|
|
for (var i = 0; i < existing.length; i++) {
|
|
existing[i].remove();
|
|
}
|
|
|
|
// Create notification element
|
|
var notification = document.createElement('div');
|
|
notification.className = 'arkhost-notification ' + type;
|
|
|
|
// Set icon based on type
|
|
var icon = 'fa-check-circle';
|
|
if (type === 'error') icon = 'fa-exclamation-circle';
|
|
else if (type === 'warning') icon = 'fa-exclamation-triangle';
|
|
else if (type === 'info') icon = 'fa-info-circle';
|
|
|
|
notification.innerHTML = '<i class="fa ' + icon + '"></i><span>' + message + '</span><button class="close-btn" onclick="this.parentElement.remove()"><i class="fa fa-times"></i></button>';
|
|
|
|
// Add to document
|
|
document.body.appendChild(notification);
|
|
|
|
// Trigger animation
|
|
setTimeout(function() {
|
|
notification.classList.add('show');
|
|
}, 100);
|
|
|
|
// Auto remove
|
|
setTimeout(function() {
|
|
notification.classList.remove('show');
|
|
setTimeout(function() {
|
|
if (notification.parentElement) {
|
|
notification.remove();
|
|
}
|
|
}, 300);
|
|
}, duration);
|
|
}
|
|
|
|
function showConfirm(message, title, onConfirm) {
|
|
title = title || 'Confirm Action';
|
|
|
|
// Remove existing confirms
|
|
var existing = document.querySelectorAll('.arkhost-confirm-overlay');
|
|
for (var i = 0; i < existing.length; i++) {
|
|
existing[i].remove();
|
|
}
|
|
|
|
// Create confirm dialog
|
|
var overlay = document.createElement('div');
|
|
overlay.className = 'arkhost-confirm-overlay';
|
|
|
|
overlay.innerHTML = '<div class="arkhost-confirm-dialog"><div class="arkhost-confirm-title"><i class="fa fa-exclamation-triangle" style="color: #ffc107;"></i>' + title + '</div><div class="arkhost-confirm-message">' + message + '</div><div class="arkhost-confirm-buttons"><button class="arkhost-confirm-btn cancel">{$_LANG['Confirm']['Cancel']|escape:'javascript'|default:'Cancel'}</button><button class="arkhost-confirm-btn confirm">{$_LANG['Confirm']['Confirm']|escape:'javascript'|default:'Confirm'}</button></div></div>';
|
|
|
|
// Add event listeners
|
|
var cancelBtn = overlay.querySelector('.cancel');
|
|
var confirmBtn = overlay.querySelector('.confirm');
|
|
|
|
cancelBtn.onclick = function() {
|
|
overlay.classList.remove('show');
|
|
setTimeout(function() {
|
|
if (overlay.parentElement) {
|
|
overlay.remove();
|
|
}
|
|
}, 300);
|
|
};
|
|
|
|
confirmBtn.onclick = function() {
|
|
overlay.classList.remove('show');
|
|
setTimeout(function() {
|
|
if (overlay.parentElement) {
|
|
overlay.remove();
|
|
}
|
|
}, 300);
|
|
if (onConfirm) {
|
|
onConfirm();
|
|
}
|
|
};
|
|
|
|
// Close on backdrop click
|
|
overlay.onclick = function(e) {
|
|
if (e.target === overlay) {
|
|
overlay.classList.remove('show');
|
|
setTimeout(function() {
|
|
if (overlay.parentElement) {
|
|
overlay.remove();
|
|
}
|
|
}, 300);
|
|
}
|
|
};
|
|
|
|
// Add to document and show
|
|
document.body.appendChild(overlay);
|
|
setTimeout(function() {
|
|
overlay.classList.add('show');
|
|
}, 100);
|
|
}
|
|
|
|
// Helper functions for API calls via WHMCS (use existing jQuery)
|
|
function ArkHostVPS_API(action, showAlert, params) {
|
|
if (showAlert === undefined) showAlert = true;
|
|
if (params === undefined) params = {};
|
|
|
|
var postData = Object.assign({
|
|
action: 'productdetails',
|
|
id: '{$serviceid}',
|
|
modop: 'custom',
|
|
a: 'ClientAreaAPI',
|
|
api: action,
|
|
token: '{$token}' // Add CSRF token to prevent session timeouts
|
|
}, params);
|
|
|
|
// Use existing WHMCS jQuery with proper headers
|
|
if (typeof jQuery !== 'undefined') {
|
|
jQuery.post({
|
|
url: '{$WEB_ROOT}/clientarea.php',
|
|
data: postData,
|
|
dataType: 'json',
|
|
beforeSend: function(xhr) {
|
|
// Add CSRF token header for additional security
|
|
xhr.setRequestHeader('X-CSRF-Token', '{$token}');
|
|
},
|
|
success: function(data) {
|
|
// Check for success - WHMCS might return different success indicators
|
|
var isSuccess = (data.result === 'success') ||
|
|
(data && typeof data === 'object' && !data.error && !data.message) ||
|
|
(Array.isArray(data)) ||
|
|
(data && Object.keys(data).some(key => !isNaN(key))); // Has numeric keys
|
|
|
|
if (isSuccess) {
|
|
if (showAlert) showNotification(lang.moduleactionsuccess, 'success');
|
|
// Handle specific responses
|
|
if (action === 'List backups') {
|
|
updateBackupTable(data);
|
|
}
|
|
if (action === 'Create backup') {
|
|
// Refresh backup list after creation
|
|
setTimeout(function() {
|
|
ArkHostVPS_API('List backups', false);
|
|
}, 1000);
|
|
}
|
|
if (action === 'Delete backup') {
|
|
// Refresh backup list after deletion
|
|
setTimeout(function() {
|
|
ArkHostVPS_API('List backups', false);
|
|
}, 1000);
|
|
}
|
|
if (action === 'Add Firewall rules') {
|
|
// Clear the form
|
|
document.getElementById('firewallAction').value = 'ACCEPT';
|
|
document.getElementById('firewallPort').value = '';
|
|
document.getElementById('firewallProtocol').value = 'ANY';
|
|
document.getElementById('firewallSource').value = '';
|
|
document.getElementById('firewallNote').value = '';
|
|
|
|
// Refresh firewall rules after adding
|
|
setTimeout(function() {
|
|
ArkHostVPS_API('Get Firewall rules', false);
|
|
}, 1000);
|
|
}
|
|
if (action === 'Delete Firewall rule') {
|
|
// Refresh firewall rules after deletion
|
|
setTimeout(function() {
|
|
ArkHostVPS_API('Get Firewall rules', false);
|
|
}, 1000);
|
|
}
|
|
if (action === 'Get Firewall rules') updateFirewallTable(data);
|
|
if (action === 'ISO Images') updateISOSelect(data.iso || data);
|
|
if (action === 'Graphs') updateGraphsDisplay(data);
|
|
|
|
// Refresh page for server state changes
|
|
if (['Start', 'Stop', 'Reboot', 'Reinstall'].indexOf(action) !== -1) {
|
|
setTimeout(function() { location.reload(); }, 2000);
|
|
}
|
|
} else {
|
|
if (showAlert) showNotification(lang.moduleactionfailed + ': ' + (data.message || 'Unknown error'), 'error');
|
|
// Hide loading for graphs
|
|
if (action === 'Graphs') {
|
|
document.getElementById('graphs-loading').style.display = 'none';
|
|
document.getElementById('graphs-container').style.opacity = '1';
|
|
}
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
if (showAlert) showNotification(lang.moduleactionfailed + ': Network error', 'error');
|
|
// Hide loading for graphs
|
|
if (action === 'Graphs') {
|
|
document.getElementById('graphs-loading').style.display = 'none';
|
|
document.getElementById('graphs-container').style.opacity = '1';
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function updateBackupTable(data) {
|
|
var tableBody = document.querySelector('#backupTable tbody');
|
|
tableBody.innerHTML = '';
|
|
|
|
// Handle the VPSAG API response format
|
|
var backupList = [];
|
|
|
|
// Handle different response formats
|
|
if (Array.isArray(data)) {
|
|
// Direct array response
|
|
backupList = data;
|
|
} else if (data && typeof data === 'object') {
|
|
// Check if there are numeric keys (0, 1, 2, etc.) indicating backup array
|
|
var keys = Object.keys(data);
|
|
var numericKeys = keys.filter(function(key) { return !isNaN(key); });
|
|
if (numericKeys.length > 0) {
|
|
backupList = numericKeys.map(function(key) { return data[key]; });
|
|
} else if (data.result === 'success') {
|
|
// WHMCS wrapper with result='success' - look for numeric keys excluding 'result'
|
|
var filteredKeys = keys.filter(function(key) { return !isNaN(key) && key !== 'result'; });
|
|
if (filteredKeys.length > 0) {
|
|
backupList = filteredKeys.map(function(key) { return data[key]; });
|
|
}
|
|
}
|
|
}
|
|
|
|
if (backupList.length > 0) {
|
|
backupList.forEach(function(backup) {
|
|
if (backup && backup.file) {
|
|
var row = document.createElement('tr');
|
|
|
|
// Use the date field if available, otherwise extract from filename
|
|
var backupDate = backup.date || 'N/A';
|
|
if (backupDate !== 'N/A' && backupDate.includes(' ')) {
|
|
// Format the date to show only the date part (YYYY-MM-DD)
|
|
backupDate = backupDate.split(' ')[0];
|
|
}
|
|
|
|
// Determine backup type using language strings
|
|
var backupType = backup.type === 'auto' ? lang.backups.automatic : lang.backups.manual;
|
|
|
|
// Determine status badge using language strings
|
|
var statusBadge = '<span class="badge badge-success">' + lang.backups.available + '</span>';
|
|
if (backup.status === 'running') {
|
|
statusBadge = '<span class="badge badge-warning">' + lang.backups.creating + '</span>';
|
|
} else if (backup.status !== 'ok') {
|
|
statusBadge = '<span class="badge badge-danger">' + lang.backups.error + '</span>';
|
|
}
|
|
|
|
row.innerHTML =
|
|
'<td>' + backupDate + '</td>' +
|
|
'<td>' + (backup.size || 'N/A') + '</td>' +
|
|
'<td>' + backupType + '</td>' +
|
|
'<td>' + statusBadge + '</td>' +
|
|
'<td style="white-space: nowrap;">' +
|
|
'<button class="btn btn-xs btn-primary mr-1" style="padding: 4px 8px; font-size: 12px;" onclick="restoreBackup(\'' + backup.file + '\'); return false;" title="Restore">' +
|
|
'<i class="fa fa-undo"></i>' +
|
|
'</button>' +
|
|
'<button class="btn btn-xs btn-danger" style="padding: 4px 8px; font-size: 12px;" onclick="deleteBackup(\'' + backup.file + '\'); return false;" title="Delete">' +
|
|
'<i class="fa fa-trash"></i>' +
|
|
'</button>' +
|
|
'</td>';
|
|
tableBody.appendChild(row);
|
|
}
|
|
});
|
|
} else {
|
|
tableBody.innerHTML = '<tr><td colspan="5" class="text-center text-muted">No backups found</td></tr>';
|
|
}
|
|
}
|
|
|
|
function createBackup() {
|
|
showConfirm(
|
|
lang.confirm.createBackup.message,
|
|
lang.confirm.createBackup.title,
|
|
function() {
|
|
ArkHostVPS_API('Create backup', true);
|
|
}
|
|
);
|
|
return false;
|
|
}
|
|
|
|
function deleteBackup(filename) {
|
|
showConfirm(
|
|
lang.confirm.deleteBackup.message,
|
|
lang.confirm.deleteBackup.title,
|
|
function() {
|
|
ArkHostVPS_API('Delete backup', true, { file: filename });
|
|
}
|
|
);
|
|
return false;
|
|
}
|
|
|
|
function restoreBackup(filename) {
|
|
showConfirm(
|
|
lang.confirm.restoreBackup.message,
|
|
lang.confirm.restoreBackup.title,
|
|
function() {
|
|
ArkHostVPS_API('Restore backup', true, { file: filename });
|
|
}
|
|
);
|
|
return false;
|
|
}
|
|
|
|
function addFirewallRule() {
|
|
var protocol = document.getElementById('firewallProtocol').value;
|
|
var port = document.getElementById('firewallPort').value;
|
|
|
|
// Validate: Warn if protocol is ANY and port is specified
|
|
if (protocol === 'ANY' && port && port.trim() !== '') {
|
|
showNotification('Warning: Protocol "ANY" does not support specific port numbers. The port will be ignored by the firewall.', 'warning', 5000);
|
|
return false;
|
|
}
|
|
|
|
// Proceed with adding the rule
|
|
ArkHostVPS_API('Add Firewall rules', true, {
|
|
firewallAction: document.getElementById('firewallAction').value,
|
|
protocol: protocol,
|
|
source: document.getElementById('firewallSource').value,
|
|
port: port,
|
|
note: document.getElementById('firewallNote').value
|
|
});
|
|
|
|
return false;
|
|
}
|
|
|
|
function updateFirewallTable(data) {
|
|
// Implement firewall table update
|
|
var tableBody = document.querySelector('#firewallTable tbody');
|
|
if (!tableBody) return;
|
|
|
|
// Clear existing rules (keep the add row)
|
|
var rows = tableBody.querySelectorAll('tr');
|
|
for (var i = rows.length - 1; i >= 0; i--) {
|
|
if (!rows[i].querySelector('#firewallAction')) {
|
|
rows[i].remove();
|
|
}
|
|
}
|
|
|
|
// Handle the API response format
|
|
var rulesList = [];
|
|
|
|
// Handle different response formats
|
|
if (Array.isArray(data)) {
|
|
// Direct array response
|
|
rulesList = data;
|
|
} else if (data && typeof data === 'object') {
|
|
// Check if there are numeric keys (0, 1, 2, etc.) indicating rule array
|
|
var keys = Object.keys(data);
|
|
var numericKeys = keys.filter(function(key) { return !isNaN(key); });
|
|
if (numericKeys.length > 0) {
|
|
rulesList = numericKeys.map(function(key) { return data[key]; });
|
|
} else if (data.rules && Array.isArray(data.rules)) {
|
|
// Rules might be in a 'rules' property
|
|
rulesList = data.rules;
|
|
} else if (data.result === 'success') {
|
|
// WHMCS wrapper with result='success' - look for numeric keys excluding 'result'
|
|
var filteredKeys = keys.filter(function(key) { return !isNaN(key) && key !== 'result'; });
|
|
if (filteredKeys.length > 0) {
|
|
rulesList = filteredKeys.map(function(key) { return data[key]; });
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add firewall rules if any exist
|
|
if (rulesList.length > 0) {
|
|
rulesList.forEach(function(rule) {
|
|
if (rule && rule.id) {
|
|
var row = document.createElement('tr');
|
|
row.innerHTML =
|
|
'<td>' + (rule.action || 'N/A') + '</td>' +
|
|
'<td>' + (rule.port || 'N/A') + '</td>' +
|
|
'<td>' + (rule.protocol || 'N/A') + '</td>' +
|
|
'<td>' + (rule.source || 'N/A') + '</td>' +
|
|
'<td>' + (rule.note || 'N/A') + '</td>' +
|
|
'<td>' +
|
|
'<a href="#" onclick="ArkHostVPS_API(\'Delete Firewall rule\', true, { rule_id: \'' + rule.id + '\' }); return false;" title="Delete">' +
|
|
'<i class="fa fa-trash text-danger"></i>' +
|
|
'</a>' +
|
|
'</td>';
|
|
tableBody.insertBefore(row, tableBody.lastElementChild);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function updateISOSelect(data) {
|
|
var select = document.getElementById('isoID');
|
|
if (!select) return;
|
|
|
|
// Clear existing options except the first one
|
|
select.innerHTML = '<option value="">Select ISO Image...</option>';
|
|
|
|
// Function to check if a value looks like a valid ISO
|
|
function isValidISO(value, name) {
|
|
if (!value || !name) return false;
|
|
|
|
// Convert to strings for comparison
|
|
var valueStr = String(value).toLowerCase();
|
|
var nameStr = String(name).toLowerCase();
|
|
|
|
// Filter out common non-ISO values
|
|
var invalidValues = ['success', 'error', 'status', 'result', 'message', 'data'];
|
|
if (invalidValues.includes(valueStr) || invalidValues.includes(nameStr)) {
|
|
return false;
|
|
}
|
|
|
|
// For VPSAG, if it has an ID and iso_image, it's valid
|
|
return true;
|
|
}
|
|
|
|
// Handle different possible response formats from VPSAG API
|
|
if (data) {
|
|
// Format 1: Array of ISO objects (VPSAG format)
|
|
if (Array.isArray(data)) {
|
|
data.forEach(function(iso) {
|
|
if (iso && iso.id && iso.iso_image) {
|
|
var isoValue = iso.id;
|
|
var isoName = iso.iso_image;
|
|
|
|
if (isValidISO(isoValue, isoName)) {
|
|
var option = document.createElement('option');
|
|
option.value = isoValue;
|
|
option.textContent = isoName;
|
|
select.appendChild(option);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
// Format 2: Object with ISO IDs as keys
|
|
else if (typeof data === 'object') {
|
|
Object.keys(data).forEach(function(key) {
|
|
var iso = data[key];
|
|
if (iso && typeof iso === 'object') {
|
|
var isoValue = iso.id || iso.iso_id || iso.filename || key;
|
|
var isoName = iso.name || iso.filename || iso.label || key;
|
|
|
|
if (isValidISO(isoValue, isoName)) {
|
|
var option = document.createElement('option');
|
|
option.value = isoValue;
|
|
option.textContent = isoName;
|
|
select.appendChild(option);
|
|
}
|
|
} else if (typeof iso === 'string') {
|
|
// Simple key-value format
|
|
if (isValidISO(key, iso)) {
|
|
var option = document.createElement('option');
|
|
option.value = key;
|
|
option.textContent = iso;
|
|
select.appendChild(option);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// If no ISOs were added, show a message
|
|
if (select.options.length === 1) {
|
|
var option = document.createElement('option');
|
|
option.value = '';
|
|
option.textContent = 'No ISO images available';
|
|
option.disabled = true;
|
|
select.appendChild(option);
|
|
}
|
|
}
|
|
|
|
// Helper function to resize graph images
|
|
function resizeGraphImage(htmlContent) {
|
|
if (htmlContent.includes('<img')) {
|
|
// If it's HTML with img tag, modify the img tag to add our styling
|
|
return htmlContent.replace(/<img([^>]*?)>/g, '<img$1 style="max-width: 96%; height: auto;">');
|
|
}
|
|
return htmlContent;
|
|
}
|
|
|
|
function updateGraphsDisplay(data) {
|
|
// Hide loading indicator
|
|
document.getElementById('graphs-loading').style.display = 'none';
|
|
document.getElementById('graphs-container').style.opacity = '1';
|
|
|
|
// Update graphs if data is available
|
|
if (data.graphs) {
|
|
// Update CPU graph
|
|
if (data.graphs.cpu_img) {
|
|
// Check if it's already HTML or just a URL
|
|
if (data.graphs.cpu_img.includes('<img')) {
|
|
document.getElementById('cpu-graph').innerHTML = resizeGraphImage(data.graphs.cpu_img);
|
|
} else {
|
|
document.getElementById('cpu-graph').innerHTML = '<img src="' + data.graphs.cpu_img + '" style="max-width: 96%; height: auto;" alt="CPU Usage">';
|
|
}
|
|
} else {
|
|
document.getElementById('cpu-graph').innerHTML = '<p class="text-muted">CPU graph not available</p>';
|
|
}
|
|
|
|
// Update RAM graph (corrected ID)
|
|
if (data.graphs.mem_img) {
|
|
// Check if it's already HTML or just a URL
|
|
if (data.graphs.mem_img.includes('<img')) {
|
|
document.getElementById('ram-graph').innerHTML = resizeGraphImage(data.graphs.mem_img);
|
|
} else {
|
|
document.getElementById('ram-graph').innerHTML = '<img src="' + data.graphs.mem_img + '" style="max-width: 96%; height: auto;" alt="RAM Usage">';
|
|
}
|
|
} else {
|
|
document.getElementById('ram-graph').innerHTML = '<p class="text-muted">RAM graph not available</p>';
|
|
}
|
|
|
|
// Update Network graph
|
|
if (data.graphs.net_img) {
|
|
// Check if it's already HTML or just a URL
|
|
if (data.graphs.net_img.includes('<img')) {
|
|
document.getElementById('network-graph').innerHTML = resizeGraphImage(data.graphs.net_img);
|
|
} else {
|
|
document.getElementById('network-graph').innerHTML = '<img src="' + data.graphs.net_img + '" style="max-width: 96%; height: auto;" alt="Network Usage">';
|
|
}
|
|
} else {
|
|
document.getElementById('network-graph').innerHTML = '<p class="text-muted">Network graph not available</p>';
|
|
}
|
|
|
|
// Update Disk graph
|
|
if (data.graphs.disk_img) {
|
|
// Check if it's already HTML or just a URL
|
|
if (data.graphs.disk_img.includes('<img')) {
|
|
document.getElementById('disk-graph').innerHTML = resizeGraphImage(data.graphs.disk_img);
|
|
} else {
|
|
document.getElementById('disk-graph').innerHTML = '<img src="' + data.graphs.disk_img + '" style="max-width: 96%; height: auto;" alt="Disk Usage">';
|
|
}
|
|
} else {
|
|
document.getElementById('disk-graph').innerHTML = '<p class="text-muted">Disk graph not available</p>';
|
|
}
|
|
} else {
|
|
document.getElementById('cpu-graph').innerHTML = '<p class="text-muted">No graph data available</p>';
|
|
document.getElementById('ram-graph').innerHTML = '<p class="text-muted">No graph data available</p>';
|
|
document.getElementById('network-graph').innerHTML = '<p class="text-muted">No graph data available</p>';
|
|
document.getElementById('disk-graph').innerHTML = '<p class="text-muted">No graph data available</p>';
|
|
}
|
|
}
|
|
|
|
// Load initial graphs when tab is first opened
|
|
var graphsLoaded = false;
|
|
function loadInitialGraphs() {
|
|
if (!graphsLoaded) {
|
|
setTimeout(function() {
|
|
loadGraphs('day', document.querySelector('.graph-time-btn.active'));
|
|
graphsLoaded = true;
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
// Load backups when tab is first opened
|
|
var backupsLoaded = false;
|
|
function loadBackups() {
|
|
if (!backupsLoaded) {
|
|
// Show loading state
|
|
var tableBody = document.querySelector('#backupTable tbody');
|
|
if (tableBody) {
|
|
tableBody.innerHTML = '<tr><td colspan="5" class="text-center"><i class="fa fa-spinner fa-spin"></i> Loading backups...</td></tr>';
|
|
}
|
|
|
|
setTimeout(function() {
|
|
ArkHostVPS_API('List backups', false);
|
|
backupsLoaded = true;
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
// Function to load graphs for different time periods
|
|
function loadGraphs(timePeriod, buttonElement) {
|
|
// Update active button
|
|
document.querySelectorAll('.graph-time-btn').forEach(btn => btn.classList.remove('active'));
|
|
buttonElement.classList.add('active');
|
|
|
|
// Show loading indicator
|
|
document.getElementById('graphs-loading').style.display = 'block';
|
|
document.getElementById('graphs-container').style.opacity = '0.5';
|
|
|
|
// Use the existing ArkHostVPS_API function which now handles sessions properly
|
|
ArkHostVPS_API('Graphs', false, { time: timePeriod });
|
|
}
|
|
|
|
function ArkHostVPS_ChooseOS(element) {
|
|
if (typeof jQuery !== 'undefined') {
|
|
var osId = jQuery(element).data('os');
|
|
var group = jQuery(element).data('group');
|
|
jQuery('#newOS').val(osId);
|
|
|
|
// FIRST: Reset ALL OS selections (clear all groups)
|
|
jQuery('.os_badge .dropdown-toggle').removeClass('btn-success').addClass('btn-outline-secondary');
|
|
jQuery('.os_badge .dropdown-item').removeClass('active bg-primary text-white');
|
|
jQuery('.os_badge .version .fa-check').remove();
|
|
jQuery('.os_badge .version').each(function() {
|
|
var originalText = jQuery(this).text().replace(' ✓', '').replace(/\s*<i[^>]*><\/i>\s*/g, '');
|
|
jQuery(this).text(originalText);
|
|
});
|
|
|
|
// THEN: Set the selected OS
|
|
// Update the version text with the selected OS
|
|
jQuery('#' + group + '-version').text(jQuery(element).text());
|
|
|
|
// Add active class to the selected item
|
|
jQuery(element).addClass('active bg-primary text-white');
|
|
|
|
// Update the main button to show selected state
|
|
var mainButton = jQuery('#' + group + '-os .dropdown-toggle');
|
|
mainButton.removeClass('btn-outline-secondary').addClass('btn-success');
|
|
|
|
// Add checkmark to show selection
|
|
var versionSpan = jQuery('#' + group + '-version');
|
|
versionSpan.append(' <i class="fa fa-check text-success"></i>');
|
|
|
|
// Show/hide advanced options based on OS type
|
|
var advancedOptions = document.getElementById('advancedReinstallOptions');
|
|
if (advancedOptions) {
|
|
// Hide advanced options for Windows (groups typically include 'windows', 'win', etc.)
|
|
var isWindows = group.toLowerCase().includes('windows') ||
|
|
group.toLowerCase().includes('win') ||
|
|
jQuery(element).text().toLowerCase().includes('windows');
|
|
|
|
if (isWindows) {
|
|
advancedOptions.style.display = 'none';
|
|
// Clear values when hiding
|
|
document.getElementById('reinstallSSHKey').value = '';
|
|
document.getElementById('reinstallScript').value = '';
|
|
document.getElementById('disableSSHPassword').checked = false;
|
|
} else {
|
|
advancedOptions.style.display = 'block';
|
|
|
|
// Update post-installation script placeholder based on OS type
|
|
var scriptTextarea = document.getElementById('reinstallScript');
|
|
if (scriptTextarea) {
|
|
var osName = jQuery(element).text().toLowerCase();
|
|
var groupName = group.toLowerCase();
|
|
|
|
// Determine if it's Debian/Ubuntu based or RHEL/CentOS based
|
|
if (groupName.includes('debian') || groupName.includes('ubuntu') ||
|
|
osName.includes('debian') || osName.includes('ubuntu')) {
|
|
// Debian/Ubuntu based - use apt
|
|
scriptTextarea.placeholder = '#!/bin/bash\n# Example: Install and configure nginx\napt update && apt install -y nginx\nsystemctl enable nginx';
|
|
} else if (groupName.includes('centos') || groupName.includes('rhel') ||
|
|
groupName.includes('rocky') || groupName.includes('alma') ||
|
|
osName.includes('centos') || osName.includes('rhel') ||
|
|
osName.includes('rocky') || osName.includes('alma')) {
|
|
// RHEL based - use yum/dnf
|
|
scriptTextarea.placeholder = '#!/bin/bash\n# Example: Install and configure nginx\nyum update -y && yum install -y nginx\nsystemctl enable nginx';
|
|
} else if (groupName.includes('fedora') || osName.includes('fedora')) {
|
|
// Fedora - use dnf
|
|
scriptTextarea.placeholder = '#!/bin/bash\n# Example: Install and configure nginx\ndnf update -y && dnf install -y nginx\nsystemctl enable nginx';
|
|
} else {
|
|
// Generic example
|
|
scriptTextarea.placeholder = '#!/bin/bash\n# Example: Install packages based on your distribution\n# For Debian/Ubuntu: apt update && apt install -y package\n# For RHEL/CentOS: yum update -y && yum install -y package';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function ArkHostVPS_ShowPassword() {
|
|
if (typeof jQuery !== 'undefined') {
|
|
var input = jQuery('#vpsPassword');
|
|
var icon = jQuery('#showPasswordIcon');
|
|
if (input.attr('type') === 'password') {
|
|
input.attr('type', 'text');
|
|
icon.removeClass('fa-eye').addClass('fa-eye-slash');
|
|
} else {
|
|
input.attr('type', 'password');
|
|
icon.removeClass('fa-eye-slash').addClass('fa-eye');
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function reinstallWithAdvancedOptions() {
|
|
var osId = document.getElementById('newOS').value;
|
|
if (!osId || osId === '0') {
|
|
showNotification('Please select an operating system first.', 'warning');
|
|
return false;
|
|
}
|
|
|
|
// Gather advanced options
|
|
var params = { os: osId };
|
|
|
|
var sshKey = document.getElementById('reinstallSSHKey').value.trim();
|
|
if (sshKey) {
|
|
params.ssh_key = sshKey;
|
|
}
|
|
|
|
var postScript = document.getElementById('reinstallScript').value.trim();
|
|
if (postScript) {
|
|
params.run_on_install = postScript;
|
|
}
|
|
|
|
var disableSSHPassword = document.getElementById('disableSSHPassword').checked;
|
|
if (disableSSHPassword) {
|
|
params.no_ssh_password = true;
|
|
}
|
|
|
|
// Call the API with all parameters
|
|
ArkHostVPS_API('Reinstall', true, params);
|
|
return false;
|
|
}
|
|
|
|
function refreshRAMUsage() {
|
|
// Add spinning animation to the button
|
|
var button = event.target.closest('button');
|
|
var icon = button.querySelector('i');
|
|
icon.classList.add('fa-spin');
|
|
|
|
// Disable button temporarily
|
|
button.disabled = true;
|
|
|
|
// Make API call to get fresh server info
|
|
var productURL = '{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}';
|
|
if (typeof jQuery !== 'undefined') {
|
|
jQuery.get(productURL + '&modop=custom&a=ClientAreaAPI&api=Server Info&token={$token}', function(data) {
|
|
if (data && data.ram_usage && data.ram) {
|
|
// Update RAM usage display
|
|
var ramUsageGB = (data.ram_usage / 1024 / 1024 / 1024);
|
|
var ramPercent = (ramUsageGB / data.ram) * 100;
|
|
|
|
// Update progress bar
|
|
var ramBar = document.getElementById('ramPercentBar');
|
|
ramBar.style.width = Math.round(ramPercent) + '%';
|
|
ramBar.textContent = Math.round(ramPercent) + '%';
|
|
|
|
// Update text display
|
|
var ramText = document.getElementById('ramPercentVal');
|
|
ramText.textContent = ramUsageGB.toFixed(1) + ' / ' + data.ram + ' GB';
|
|
}
|
|
}).always(function() {
|
|
// Re-enable button and stop spinning
|
|
icon.classList.remove('fa-spin');
|
|
button.disabled = false;
|
|
});
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function refreshCPUUsage() {
|
|
// Add spinning animation to the button
|
|
var button = event.target.closest('button');
|
|
var icon = button.querySelector('i');
|
|
icon.classList.add('fa-spin');
|
|
|
|
// Disable button temporarily
|
|
button.disabled = true;
|
|
|
|
// Make API call to get fresh server info
|
|
var productURL = '{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}';
|
|
if (typeof jQuery !== 'undefined') {
|
|
jQuery.get(productURL + '&modop=custom&a=ClientAreaAPI&api=Server Info&token={$token}', function(data) {
|
|
if (data && typeof data.cpu_usage !== 'undefined') {
|
|
// Update CPU usage display
|
|
var cpuPercent;
|
|
if (data.cpu_usage <= 1) {
|
|
cpuPercent = Math.round(data.cpu_usage * 100);
|
|
} else {
|
|
cpuPercent = Math.ceil(data.cpu_usage);
|
|
}
|
|
|
|
// Update progress bar
|
|
var cpuBar = document.getElementById('cpuPercentBar');
|
|
cpuBar.style.width = cpuPercent + '%';
|
|
cpuBar.textContent = cpuPercent + '%';
|
|
|
|
// Update text display
|
|
var cpuText = document.getElementById('cpuPercentVal');
|
|
cpuText.textContent = cpuPercent + '% CPU Usage';
|
|
}
|
|
}).always(function() {
|
|
// Re-enable button and stop spinning
|
|
icon.classList.remove('fa-spin');
|
|
button.disabled = false;
|
|
});
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function refreshBandwidthUsage() {
|
|
// Add spinning animation to the button
|
|
var button = event.target.closest('button');
|
|
var icon = button.querySelector('i');
|
|
icon.classList.add('fa-spin');
|
|
|
|
// Disable button temporarily
|
|
button.disabled = true;
|
|
|
|
// Make API call to get fresh server info
|
|
var productURL = '{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}';
|
|
if (typeof jQuery !== 'undefined') {
|
|
jQuery.get(productURL + '&modop=custom&a=ClientAreaAPI&api=Server Info&token={$token}', function(data) {
|
|
if (data && typeof data.bandwidth_used !== 'undefined' && data.bandwidth) {
|
|
// Update bandwidth usage display
|
|
var bandwidthUsedTB = data.bandwidth_used / 1024;
|
|
var bandwidthPercent = (bandwidthUsedTB / data.bandwidth) * 100;
|
|
|
|
// Update progress bar
|
|
var bandwidthBar = document.getElementById('bandwidthPercentBar');
|
|
bandwidthBar.style.width = Math.round(bandwidthPercent) + '%';
|
|
bandwidthBar.textContent = Math.round(bandwidthPercent) + '%';
|
|
|
|
// Update text display
|
|
var bandwidthText = document.getElementById('bandwidthPercentVal');
|
|
bandwidthText.textContent = bandwidthUsedTB.toFixed(1) + ' / ' + data.bandwidth + ' TB';
|
|
}
|
|
}).always(function() {
|
|
// Re-enable button and stop spinning
|
|
icon.classList.remove('fa-spin');
|
|
button.disabled = false;
|
|
});
|
|
}
|
|
return false;
|
|
}
|
|
</script>
|
|
|
|
<div class="arkhost-vps-container">
|
|
<div id="loading" class="fw-bold" style="display: none;">
|
|
<span class="spinner-border spinner-border-sm" style="width: 3rem; height: 3rem;" id="loading-spinner" role="status" aria-hidden="true"></span>
|
|
</div>
|
|
|
|
<div id="ArkHostVPS">
|
|
<div class="dashboard-tab" id="dashboard">
|
|
<ul class="nav nav-tabs mb-4 dash-tabs" id="pills-tab" role="tablist">
|
|
<li class="nav-item">
|
|
<a class="nav-link active" id="overview-tab" data-toggle="tab" href="#overview" role="tab" aria-controls="overview" aria-selected="true">
|
|
<i class="fa fa-signal"></i> {$_LANG['Navbar']['Overview']}
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="graphs-tab" data-toggle="tab" href="#graphs" role="tab" aria-controls="graphs" aria-selected="false" onclick="loadInitialGraphs()">
|
|
<i class="fa fa-chart-bar"></i> {$_LANG['Navbar']['Graphs']}
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="backups-tab" data-toggle="tab" href="#backups" onclick="loadBackups();return false;" role="tab" aria-controls="backups" aria-selected="false">
|
|
<i class="fa fa-archive"></i> {$_LANG['Navbar']['Backups']}
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="firewall-tab" data-toggle="tab" href="#firewall" onclick="ArkHostVPS_API('Get Firewall rules', false);return false;" role="tab" aria-controls="firewall" aria-selected="false">
|
|
<i class="fa fa-shield-alt"></i> {$_LANG['Settings']['Firewall']['Title']}
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="settings-tab" data-toggle="tab" href="#settings" role="tab" aria-controls="settings" aria-selected="false">
|
|
<i class="fa fa-cog"></i> {$_LANG['Navbar']['Settings']}
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
<div class="tab-content" id="pills-tabContent">
|
|
<div class="tab-pane fade show active" id="overview" role="tabpanel" aria-labelledby="overview-tab">
|
|
<!-- Server Status Card -->
|
|
<div class="card mb-4">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="mb-0"><i class="fa fa-server mr-2"></i>{$_LANG['Overview']['ServerInfo']}</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<table class="table table-sm table-borderless mb-0">
|
|
<tr>
|
|
<td class="text-muted" width="40%">{$_LANG['Overview']['Hostname']}:</td>
|
|
<td><strong>{$serverInfo['hostname']|default:'N/A'}</strong></td>
|
|
</tr>
|
|
<tr>
|
|
<td class="text-muted">{$_LANG['IPv4']} Address:</td>
|
|
<td><code>{$serverInfo['ip']}</code></td>
|
|
</tr>
|
|
{if $serverInfo['ipv6']}
|
|
<tr>
|
|
<td class="text-muted">{$_LANG['IPv6']} Address:</td>
|
|
<td><code style="font-size: 11px;">{$serverInfo['ipv6']}</code></td>
|
|
</tr>
|
|
{/if}
|
|
<tr>
|
|
<td class="text-muted">{$_LANG['Overview']['OS']}:</td>
|
|
<td>
|
|
{if $serverInfo['operatingSystem']['image']}
|
|
<img src="{$serverInfo['operatingSystem']['image']}" width="20" height="20" class="mr-1" style="vertical-align: middle;">
|
|
{/if}
|
|
{$serverInfo['operatingSystem']['name']|default:'N/A'}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<table class="table table-sm table-borderless mb-0">
|
|
<tr>
|
|
<td class="text-muted" width="40%">{$_LANG['Overview']['Status']}:</td>
|
|
<td>
|
|
<img src="{$serverInfo['statusImage']}" height="20" class="mr-1" style="vertical-align: middle;">
|
|
<span class="{if $serverInfo['status'] == 'running'}text-success{else}text-danger{/if}">
|
|
<strong>{$serverInfo['statusDescription']}</strong>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="text-muted">{$_LANG['Overview']['Uptime']}:</td>
|
|
<td><strong>{$serverInfo['uptime_text']|default:'N/A'}</strong></td>
|
|
</tr>
|
|
<tr>
|
|
<td class="text-muted">{$_LANG['Overview']['ServerType']}:</td>
|
|
<td><strong>{$serverInfo['server_type']|default:'Standard'}</strong></td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resource Allocation Card -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="fa fa-microchip mr-2"></i>{$_LANG['Overview']['ResourceAllocation']}</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row text-center">
|
|
<div class="col-6 col-md-3 mb-3">
|
|
<div class="resource-box">
|
|
<i class="fa fa-microchip fa-2x text-primary mb-2"></i>
|
|
<h3 class="mb-0">{$serverInfo['cpu']|default:'N/A'}</h3>
|
|
<small class="text-muted">{$_LANG['Overview']['CPU_Cores']}</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3 mb-3">
|
|
<div class="resource-box">
|
|
<i class="fa fa-memory fa-2x text-success mb-2"></i>
|
|
<h3 class="mb-0">{$serverInfo['ram']|default:'N/A'} {$_LANG['General']['GB']}</h3>
|
|
<small class="text-muted">{$_LANG['Overview']['Memory']}</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3 mb-3">
|
|
<div class="resource-box">
|
|
<i class="fa fa-hdd fa-2x text-warning mb-2"></i>
|
|
<h3 class="mb-0">{$serverInfo['disk']|default:'N/A'} {$_LANG['General']['GB']}</h3>
|
|
<small class="text-muted">{$_LANG['Overview']['Storage']}</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3 mb-3">
|
|
<div class="resource-box">
|
|
<i class="fa fa-network-wired fa-2x text-info mb-2"></i>
|
|
<h3 class="mb-0">{$serverInfo['bandwidth']|default:'20'} {$_LANG['General']['TB']}</h3>
|
|
<small class="text-muted">{$_LANG['Overview']['Traffic']}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resource Usage Card -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="fa fa-chart-bar mr-2"></i>{$_LANG['Overview']['ResourceUsage']}</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-lg-4 col-sm-6 col-md-6 mb-3">
|
|
<div class="usage-details px-3 py-4" style="height: 140px; display: flex; flex-direction: column; justify-content: space-between;">
|
|
<p class="overview-label">
|
|
{$_LANG['Overview']['RAM']}
|
|
<button onclick="refreshRAMUsage();return false;" class="btn btn-sm btn-outline-secondary ml-2" style="padding: 2px 6px; font-size: 10px; border-radius: 50%; width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center;" title="Refresh RAM Usage">
|
|
<i class="fa fa-sync-alt" style="font-size: 8px;"></i>
|
|
</button>
|
|
</p>
|
|
<div class="progress disk-bar">
|
|
<div id="ramPercentBar" aria-valuemin="0" aria-valuemax="100" role="progressbar" class="progress-bar" data-placement="right" style="background: #06d79c; width: {if $serverInfo['ram'] > 0}{($serverInfo['ram_usage']/1024/1024/1024/$serverInfo['ram']*100)|round}{else}0{/if}%">
|
|
{if $serverInfo['ram'] > 0}{($serverInfo['ram_usage']/1024/1024/1024/$serverInfo['ram']*100)|round}{else}0{/if}%
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<span id="ramPercentVal" class="used_disk_percent mr-1">{($serverInfo['ram_usage']/1024/1024/1024)|round:1} / {$serverInfo['ram']} GB</span>
|
|
<span class="overview-label">{$_LANG['Used']}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-4 col-sm-6 col-md-6 mb-3">
|
|
<div class="usage-details px-3 py-4" style="height: 140px; display: flex; flex-direction: column; justify-content: space-between;">
|
|
<p class="overview-label">
|
|
Bandwidth
|
|
<button onclick="refreshBandwidthUsage();return false;" class="btn btn-sm btn-outline-secondary ml-2" style="padding: 2px 6px; font-size: 10px; border-radius: 50%; width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center;" title="Refresh Bandwidth Usage">
|
|
<i class="fa fa-sync-alt" style="font-size: 8px;"></i>
|
|
</button>
|
|
</p>
|
|
<div class="progress disk-bar">
|
|
<div id="bandwidthPercentBar" aria-valuemin="0" aria-valuemax="100" role="progressbar" class="progress-bar" data-placement="right" style="background: #9c06d7; width: {if $serverInfo['bandwidth'] > 0}{($serverInfo['bandwidth_used']/1024/$serverInfo['bandwidth']*100)|round}{else}0{/if}%">
|
|
{if $serverInfo['bandwidth'] > 0}{($serverInfo['bandwidth_used']/1024/$serverInfo['bandwidth']*100)|round}{else}0{/if}%
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<span id="bandwidthPercentVal" class="used_disk_percent mr-1">{($serverInfo['bandwidth_used']/1024)|round:1} / {$serverInfo['bandwidth']} TB</span>
|
|
<span class="overview-label">{$_LANG['Used']}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-4 col-sm-6 col-md-6 mb-3">
|
|
<div class="usage-details px-3 py-4" style="height: 140px; display: flex; flex-direction: column; justify-content: space-between;">
|
|
<p class="overview-label">
|
|
{$_LANG['Overview']['CPU']}
|
|
<button onclick="refreshCPUUsage();return false;" class="btn btn-sm btn-outline-secondary ml-2" style="padding: 2px 6px; font-size: 10px; border-radius: 50%; width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center;" title="Refresh CPU Usage">
|
|
<i class="fa fa-sync-alt" style="font-size: 8px;"></i>
|
|
</button>
|
|
</p>
|
|
<div class="progress disk-bar">
|
|
<div id="cpuPercentBar" aria-valuemin="0" aria-valuemax="100" role="progressbar" class="progress-bar" data-placement="right" style="background: #e74c3c; width: {if $serverInfo['cpu_usage'] <= 1}{($serverInfo['cpu_usage'] * 100)|round}{else}{$serverInfo['cpu_usage']|ceil}{/if}%">
|
|
{if $serverInfo['cpu_usage'] <= 1}{($serverInfo['cpu_usage'] * 100)|round}{else}{$serverInfo['cpu_usage']|ceil}{/if}%
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<span id="cpuPercentVal" class="used_disk_percent mr-1">{if $serverInfo['cpu_usage'] <= 1}{($serverInfo['cpu_usage'] * 100)|round}{else}{$serverInfo['cpu_usage']|ceil}{/if}% {$_LANG['Overview']['CPUUsage']}</span>
|
|
<span class="overview-label"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions Card -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="fa fa-bolt mr-2"></i>{$_LANG['Overview']['QuickActions']}</h5>
|
|
</div>
|
|
<div class="card-body text-center">
|
|
<div class="d-flex justify-content-center flex-wrap" style="gap: 10px;">
|
|
<button class="btn btn-{if $serverInfo['status'] !== 'running'}success{else}danger{/if}"
|
|
onclick="{if $serverInfo['status'] !== 'running'}return ArkHostVPS_API('Start');{else}return confirmStop();{/if}">
|
|
<i class="fa fa-{if $serverInfo['status'] !== 'running'}play{else}stop{/if} mr-1"></i>
|
|
{if $serverInfo['status'] !== 'running'}{$_LANG['Start']}{else}{$_LANG['Stop']}{/if}
|
|
</button>
|
|
<button class="btn btn-primary"
|
|
onclick="return confirmRestart();">
|
|
<i class="fa fa-sync mr-1"></i> {$_LANG['Restart']}
|
|
</button>
|
|
<a href="{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}&modop=custom&a=VNC"
|
|
target="_blank"
|
|
class="btn btn-info">
|
|
<i class="fa fa-desktop mr-1"></i> {$_LANG['VNC']}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="graphs" role="tabpanel" aria-labelledby="graphs-tab">
|
|
<div class="head text-center mb-3">
|
|
<img src="{$images['search']}">
|
|
<span class="h4">{$_LANG['Navbar']['Graphs']}</span>
|
|
</div>
|
|
|
|
<!-- Time Period Selection -->
|
|
<div class="text-center mb-4">
|
|
<div class="graph-time-selection" role="group" aria-label="Time Period">
|
|
<button type="button" class="graph-time-btn" onclick="loadGraphs('hour', this)">{$_LANG['Graphs']['Hour']}</button>
|
|
<button type="button" class="graph-time-btn active" onclick="loadGraphs('day', this)">{$_LANG['Graphs']['Day']}</button>
|
|
<button type="button" class="graph-time-btn" onclick="loadGraphs('week', this)">{$_LANG['Graphs']['Week']}</button>
|
|
<button type="button" class="graph-time-btn" onclick="loadGraphs('month', this)">{$_LANG['Graphs']['Month']}</button>
|
|
<button type="button" class="graph-time-btn" onclick="loadGraphs('year', this)">{$_LANG['Graphs']['Year']}</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Loading indicator -->
|
|
<div id="graphs-loading" class="text-center mb-3" style="display: none;">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="sr-only">Loading graphs...</span>
|
|
</div>
|
|
<p class="mt-2 text-muted">Loading performance graphs...</p>
|
|
</div>
|
|
|
|
<!-- Graphs Display - Stacked Vertically -->
|
|
<div class="row" id="graphs-container">
|
|
<div class="col-12 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">{$_LANG['Graphs']['CPU']}</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="cpu-graph" class="text-center">
|
|
<p class="text-muted">{$_LANG['Graphs']['Loading']}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">{$_LANG['Graphs']['RAM']}</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="ram-graph" class="text-center">
|
|
<p class="text-muted">{$_LANG['Graphs']['Loading']}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">{$_LANG['Graphs']['Network']}</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="network-graph" class="text-center">
|
|
<p class="text-muted">{$_LANG['Graphs']['Loading']}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">{$_LANG['Graphs']['Disk']}</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="disk-graph" class="text-center">
|
|
<p class="text-muted">{$_LANG['Graphs']['Loading']}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="backups" role="tabpanel" aria-labelledby="backups-tab">
|
|
<div class="head text-center mb-3">
|
|
<img src="{$images['cloud']}">
|
|
<span class="h4">{$_LANG['Navbar']['Backups']}</span>
|
|
</div>
|
|
|
|
<div class="alert alert-info mb-3">{$_LANG['Backups']['Description']}</div>
|
|
|
|
<div class="table-responsive">
|
|
<table id="backupTable" cellpadding="0" cellspacing="0" border="0" class="table table-hover" width="100%">
|
|
<thead>
|
|
<tr>
|
|
<th>{$_LANG['Backups']['Date']}</th>
|
|
<th>{$_LANG['Backups']['Size']}</th>
|
|
<th>{$_LANG['Backups']['Type']}</th>
|
|
<th>{$_LANG['Backups']['Status']}</th>
|
|
<th width="50">{$_LANG['Backups']['Actions']}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td colspan="5" class="text-center text-muted">Loading backup data...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<button onclick="createBackup();return false;" class="submit-btn">{$_LANG['Backups']['Create']}</button>
|
|
|
|
<div class="alert alert-warning mt-3">
|
|
{$_LANG['Backups']['Warning']|unescape:'html'}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="firewall" role="tabpanel" aria-labelledby="firewall-tab">
|
|
<div class="alert alert-info mb-4">
|
|
<i class="fa fa-info-circle mr-2"></i><strong>{$_LANG['General']['Note']}:</strong> {$_LANG['Settings']['Firewall']['Description']}
|
|
</div>
|
|
|
|
<!-- Add New Rule Card -->
|
|
<div class="card mb-4 border-primary">
|
|
<div class="card-header bg-light">
|
|
<h6 class="mb-0"><i class="fa fa-plus-circle mr-2"></i>{$_LANG['Settings']['Firewall']['AddNewRule']}</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-2">
|
|
<label class="small text-muted">{$_LANG['Settings']['Firewall']['Action']}</label>
|
|
<select class="form-control" id="firewallAction">
|
|
<option value="ACCEPT" class="text-success">✓ {$_LANG['Settings']['Firewall']['Accept']}</option>
|
|
<option value="DROP" class="text-danger">✗ {$_LANG['Settings']['Firewall']['Drop']}</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="small text-muted">{$_LANG['Settings']['Firewall']['Protocol']}</label>
|
|
<select class="form-control" id="firewallProtocol">
|
|
<option value="ANY">ANY</option>
|
|
<option value="ICMP">ICMP</option>
|
|
<option value="TCP">TCP</option>
|
|
<option value="UDP">UDP</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="small text-muted">{$_LANG['Settings']['Firewall']['Port']}</label>
|
|
<input class="form-control" id="firewallPort" type="number" min="1" max="65535" placeholder="{$_LANG['Settings']['Firewall']['Port']}">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="small text-muted">{$_LANG['Settings']['Firewall']['SourceLabel']}</label>
|
|
<input class="form-control" id="firewallSource" type="text" maxlength="128" placeholder="{$_LANG['Settings']['Firewall']['SourceLabel']}">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="small text-muted">{$_LANG['Settings']['Firewall']['Note']}</label>
|
|
<input class="form-control" id="firewallNote" type="text" maxlength="64" placeholder="{$_LANG['Settings']['Firewall']['Notes']}">
|
|
</div>
|
|
</div>
|
|
<div class="text-right mt-3">
|
|
<button class="btn btn-success" onclick="return addFirewallRule();">
|
|
<i class="fa fa-plus mr-2"></i>{$_LANG['Settings']['Firewall']['AddRule']}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Current Rules -->
|
|
<div class="card">
|
|
<div class="card-header bg-light">
|
|
<h6 class="mb-0"><i class="fa fa-list mr-2"></i>{$_LANG['Settings']['Firewall']['CurrentRules']}</h6>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table id="firewallTable" class="table table-hover mb-0">
|
|
<thead class="thead-light">
|
|
<tr>
|
|
<th width="100">{$_LANG['Settings']['Firewall']['Action']}</th>
|
|
<th width="100">{$_LANG['Settings']['Firewall']['Protocol']}</th>
|
|
<th width="100">{$_LANG['Settings']['Firewall']['Port']}</th>
|
|
<th>{$_LANG['Settings']['Firewall']['Source']}</th>
|
|
<th width="150">{$_LANG['Settings']['Firewall']['Note']}</th>
|
|
<th width="80" class="text-center">{$_LANG['Settings']['Firewall']['Actions']}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td colspan="6" class="text-center text-muted py-4">
|
|
<i class="fa fa-spinner fa-spin mr-2"></i>{$_LANG['LoadingFirewallRules']}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Commit button -->
|
|
<div class="alert alert-warning mt-4 mb-4">
|
|
<i class="fa fa-exclamation-triangle mr-2"></i><strong>{$_LANG['General']['Note']}:</strong> {$_LANG['Settings']['Firewall']['Warning']}
|
|
</div>
|
|
<div class="text-center">
|
|
<button onclick="ArkHostVPS_API('Commit Firewall rules');return false;" class="btn btn-primary btn-lg">
|
|
<i class="fa fa-check mr-2"></i>{$_LANG['Settings']['Firewall']['Submit']}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="settings" role="tabpanel" aria-labelledby="settings-tab">
|
|
<div class="row vertical-tabs">
|
|
<div class="col-12 col-sm-3 col-md-3 col-lg-3 v-tabs-container">
|
|
<div class="nav flex-md-column mx-auto left-side-tabs mb-4 mb-md-0" id="v-pills-tab" role="tablist" aria-orientation="vertical">
|
|
<a class="nav-link mr-2 mr-md-0 active" id="hostname-tab" data-toggle="tab" href="#hostname" role="tab" aria-controls="hostname" aria-selected="true" style="display: block;">{$_LANG['Settings']['Hostname']['Title']}</a>
|
|
<a class="nav-link mr-2 mr-md-0" id="iso-tab" data-toggle="tab" href="#iso" onclick="ArkHostVPS_API('ISO Images', false);" role="tab" aria-controls="iso" aria-selected="false" style="display: block;">{$_LANG['Settings']['ISO']['Title']}</a>
|
|
<a class="nav-link mr-2 mr-md-0" id="password-tab" data-toggle="tab" href="#password" role="tab" aria-controls="password" aria-selected="false" style="display: block;">{$_LANG['Settings']['Password']['Title']}</a>
|
|
<a class="nav-link mr-2 mr-md-0" id="reinstall-tab" data-toggle="tab" href="#reinstall" role="tab" aria-controls="reinstall" aria-selected="false" style="display: block;">{$_LANG['Settings']['Reinstall']['Title']}</a>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-sm-9 col-md-9 col-lg-9">
|
|
<div class="tab-content vertical-tab-content">
|
|
<div class="tab-pane fade active show" id="hostname" role="tabpanel" aria-labelledby="hostname-tab">
|
|
<div class="head">
|
|
<img src="{$images['search']}">
|
|
<span class="h4">{$_LANG['Settings']['Hostname']['Title']}</span>
|
|
</div>
|
|
|
|
<div class="alert alert-info mb-3">
|
|
{$_LANG['Settings']['Hostname']['Description']}
|
|
</div>
|
|
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">{$_LANG['Settings']['Hostname']['Title']}:</label>
|
|
<input class="form-control" id="hostnameRDNS" type="text" size="30" maxlength="128" value="{$serverInfo['hostname']}">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button onclick="ArkHostVPS_API('Hostname rDNS', true, { hostname: document.getElementById('hostnameRDNS').value });return false;" class="submit-btn">{$_LANG['Settings']['Hostname']['Submit']}</button>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="iso" role="tabpanel" aria-labelledby="iso-tab">
|
|
<div class="head">
|
|
<img src="{$images['edit']}" style="width: 32px; height: 32px;">
|
|
<span class="h4">{$_LANG['Settings']['ISO']['Title']}</span>
|
|
</div>
|
|
|
|
<div class="alert alert-info mb-3">
|
|
{$_LANG['Settings']['ISO']['Description']}
|
|
</div>
|
|
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">{$_LANG['Settings']['ISO']['Image']}:</label>
|
|
<select class="form-control" id="isoID">
|
|
<option value="">Select ISO Image...</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button onclick="ArkHostVPS_API('Load ISO', true, { iso_id: document.getElementById('isoID').value });return false;" class="submit-btn">{$_LANG['Settings']['ISO']['Submit']}</button>
|
|
|
|
{if $serverInfo['iso'] !== ''}
|
|
<button onclick="ArkHostVPS_API('Eject ISO', true);return false;" class="danger-btn">{$_LANG['Settings']['ISO']['Remove']}</button>
|
|
{/if}
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="password" role="tabpanel" aria-labelledby="password-tab">
|
|
<div class="head">
|
|
<img src="{$images['lock']}" style="width: 32px; height: 32px;">
|
|
<span class="h4">{$_LANG['Settings']['Password']['Title']}</span>
|
|
</div>
|
|
|
|
<div class="alert alert-warning mb-3">
|
|
{$_LANG['Settings']['Password']['Description']}
|
|
</div>
|
|
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">{$_LANG['Settings']['Password']['Title']}:</label>
|
|
|
|
<div class="input-group mb-3">
|
|
<input class="form-control" id="vpsPassword" type="password" size="30" maxlength="128" disabled value="{if $serverInfo['install_root'] != ''}{$serverInfo['install_root']}{else}Expired{/if}">
|
|
<button class="btn btn-outline-secondary" type="button" onclick="ArkHostVPS_ShowPassword();return false;">
|
|
<i class="fa fa-eye" id="showPasswordIcon" aria-hidden="true"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button onclick="ArkHostVPS_API('Reset root');return false;" class="submit-btn">{$_LANG['Settings']['Password']['Submit']}</button>
|
|
</div>
|
|
|
|
<div class="tab-pane fade" id="reinstall" role="tabpanel" aria-labelledby="reinstall-tab">
|
|
<div class="head">
|
|
<img src="{$images['installing']}" style="width: 32px; height: 32px;">
|
|
<span class="h4">{$_LANG['Settings']['Reinstall']['Title']}</span>
|
|
</div>
|
|
|
|
<div class="alert alert-danger mb-3">
|
|
{$_LANG['Settings']['Reinstall']['Description']}
|
|
</div>
|
|
|
|
<div id="reinstallIntructions" class="col-lg-10 col-12 mx-auto">
|
|
<label class="form-label">{$_LANG['Settings']['Reinstall']['OS']}:</label>
|
|
|
|
<div id="os_list" class="row mb-4 justify-content-center">
|
|
{foreach from=$operatingSystems key=$group item=$operatingSystemsGroup}
|
|
<div class="col-12 col-md-8 col-lg-6 mb-3">
|
|
<div id="{$group}-os" class="os_badge media dropdown">
|
|
<button class="btn dropdown-toggle btn-outline-secondary w-100 p-0" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
<div class="media-left p-1 float-left">
|
|
<img class="distro_img media-object" src="{$operatingSystemsGroup['image']}">
|
|
</div>
|
|
|
|
<div class="media-body float-left text-left p-2">
|
|
<h6 class="distro_name media-heading">{$operatingSystemsGroup['name']}</h6>
|
|
<span id="{$group}-version" class="version small">{$_LANG['Settings']['Reinstall']['Version']}</span>
|
|
</div>
|
|
</button>
|
|
|
|
<div class="os_badge_list dropdown-menu w-100">
|
|
{foreach from=$operatingSystemsGroup['versions'] item=$operatingSystem}
|
|
<a class="dropdown-item" href="#" data-os="{$operatingSystem['id']}" data-group="{$group}" onclick="ArkHostVPS_ChooseOS(this);return false;">{$operatingSystem['name']}</a>
|
|
{/foreach}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/foreach}
|
|
</div>
|
|
|
|
<!-- Advanced Reinstall Options (Linux only) -->
|
|
<div id="advancedReinstallOptions" class="row mb-3" style="display: none;">
|
|
<div class="col-12">
|
|
<h6 class="text-muted mb-3">
|
|
<i class="fa fa-cogs"></i> Advanced Options (Linux only)
|
|
</h6>
|
|
</div>
|
|
|
|
<div class="col-12 mb-4">
|
|
<label class="form-label font-weight-bold">{$_LANG['Settings']['Reinstall']['SSHKey']}</label>
|
|
<textarea class="form-control" id="reinstallSSHKey" rows="2" placeholder="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... your-email@example.com"></textarea>
|
|
<small class="text-muted">{$_LANG['Settings']['Reinstall']['SSHKeyDesc']}</small>
|
|
</div>
|
|
|
|
<div class="col-12 mb-4">
|
|
<label class="form-label font-weight-bold">{$_LANG['Settings']['Reinstall']['PostScript']}</label>
|
|
<textarea class="form-control" id="reinstallScript" rows="4" placeholder="#!/bin/bash # Example: Install and configure nginx apt update && apt install -y nginx systemctl enable nginx"></textarea>
|
|
<small class="text-muted">{$_LANG['Settings']['Reinstall']['PostScriptDesc']}</small>
|
|
</div>
|
|
|
|
<div class="col-12 mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="disableSSHPassword">
|
|
<label class="form-check-label font-weight-bold" for="disableSSHPassword">
|
|
{$_LANG['Settings']['Reinstall']['DisableSSHPassword']}
|
|
</label>
|
|
<br><small class="text-muted">{$_LANG['Settings']['Reinstall']['DisableSSHPasswordDesc']}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<input type="hidden" id="newOS" value="0"/>
|
|
<button onclick="reinstallWithAdvancedOptions();" class="submit-btn">{$_LANG['Settings']['Reinstall']['Submit']}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Wait for WHMCS jQuery to be ready and use it
|
|
jQuery(document).ready(function($) {
|
|
// WHMCS already loads Bootstrap and Font Awesome - no need to load additional libraries
|
|
|
|
// Replace "Domain" label with translated "Hostname" label
|
|
var hostnameLabel = '{$_LANG.Overview.Hostname|escape:"javascript"}';
|
|
$('#domain > .row:nth-child(1) .col-sm-5 strong').attr('data-hostname-label', hostnameLabel);
|
|
|
|
// Intercept sidebar VPS action button clicks to show confirmation popups
|
|
// Use capture phase to intercept before other handlers
|
|
$('.list-group-item').each(function() {
|
|
var $link = $(this);
|
|
var href = $link.attr('href');
|
|
|
|
if (!href) return;
|
|
|
|
// Remove any existing click handlers and add our own
|
|
if (href.indexOf('modop=custom&a=Start') !== -1) {
|
|
$link.off('click').on('click', function(e) {
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
ArkHostVPS_API('Start');
|
|
return false;
|
|
});
|
|
} else if (href.indexOf('modop=custom&a=Stop') !== -1) {
|
|
$link.off('click').on('click', function(e) {
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
confirmStop();
|
|
return false;
|
|
});
|
|
} else if (href.indexOf('modop=custom&a=Reboot') !== -1) {
|
|
$link.off('click').on('click', function(e) {
|
|
e.preventDefault();
|
|
e.stopImmediatePropagation();
|
|
confirmRestart();
|
|
return false;
|
|
});
|
|
}
|
|
});
|
|
|
|
});
|
|
</script> |