Initial release
This commit is contained in:
ArkHost
2025-07-21 15:25:29 +02:00
committed by GitHub
parent 16f72f786c
commit 394f7e8af8
38 changed files with 6791 additions and 0 deletions
@@ -0,0 +1,756 @@
{*
* WHMCS Server Module - Hetzner VPS
* @package WHMCS
* @copyright ArkHost 2025
* @link https://arkhost.com
* @author ArkHost <support@arkhost.com>
*}
<link rel="stylesheet" type="text/css" href="{$WEB_ROOT}/modules/servers/ArkHostHetznerVPS/template/style.css" />
<script type="text/javascript">
// Initialize template variables for external script
var productURL = '{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}';
var serverInfoInitial = JSON.parse('{$serverInfo|@json_encode}');
var serviceId = '{$serviceid}';
var csrfToken = '{$token}';
var webRoot = '{$WEB_ROOT}';
// Language strings
var lang = {
moduleactionfailed: '{$ADDONLANG.ActionFailed}',
moduleactionsuccess: '{$ADDONLANG.ActionSuccess}',
backups: {
available: '{$ADDONLANG.Backups.Available}',
creating: '{$ADDONLANG.Backups.Creating}',
error: '{$ADDONLANG.Backups.Error}',
automatic: '{$ADDONLANG.Backups.Automatic}',
manual: '{$ADDONLANG.Backups.Manual}'
},
graphs: {
cpuUsage: '{$ADDONLANG.Graphs.CPUUsage}',
networkTraffic: '{$ADDONLANG.Graphs.NetworkTraffic}',
diskIO: '{$ADDONLANG.Graphs.DiskIO}',
cpuUnit: '{$ADDONLANG.Graphs.CPUUnit}',
networkUnit: '{$ADDONLANG.Graphs.NetworkUnit}',
diskUnit: '{$ADDONLANG.Graphs.DiskUnit}',
current: '{$ADDONLANG.Graphs.Current}',
inbound: '{$ADDONLANG.Graphs.Inbound}',
outbound: '{$ADDONLANG.Graphs.Outbound}',
read: '{$ADDONLANG.Graphs.Read}',
write: '{$ADDONLANG.Graphs.Write}'
},
general: {
noBackupsFound: '{$ADDONLANG.General.NoBackupsFound}',
noFirewallRules: '{$ADDONLANG.General.NoFirewallRules}',
noDataAvailable: '{$ADDONLANG.General.NoDataAvailable}',
gb: '{$ADDONLANG.General.GB}',
tb: '{$ADDONLANG.General.TB}',
any: '{$ADDONLANG.General.Any}',
emptyValue: '{$ADDONLANG.General.EmptyValue}',
colon: '{$ADDONLANG.General.Colon}'
},
firewall: {
accept: '{$ADDONLANG.Firewall.Accept}',
drop: '{$ADDONLANG.Firewall.Drop}',
info: '{$ADDONLANG.Firewall.Info}',
any: '{$ADDONLANG.Firewall.Any}'
},
messages: {
portRequired: '{$ADDONLANG.Messages.PortRequired}',
refreshingGraphs: '{$ADDONLANG.Messages.RefreshingGraphs}',
selectOSFirst: '{$ADDONLANG.Messages.SelectOSFirst}'
},
confirm: {
stop: {
title: "{$ADDONLANG.Confirm.Stop.Title|escape:'javascript'|default:'Stop VPS'}",
message: "{$ADDONLANG.Confirm.Stop.Message|escape:'javascript'|default:'Do you want to stop this VPS. This will shutdown the server.'}"
},
restart: {
title: "{$ADDONLANG.Confirm.Restart.Title|escape:'javascript'|default:'Restart VPS'}",
message: "{$ADDONLANG.Confirm.Restart.Message|escape:'javascript'|default:'Do you want to restart this VPS. This will reboot the server.'}"
},
createBackup: {
title: "{$ADDONLANG.Confirm.CreateBackup.Title|escape:'javascript'|default:'Create Backup'}",
message: "{$ADDONLANG.Confirm.CreateBackup.Message|escape:'javascript'|default:'Do you want to create a backup. This may take several minutes.'}"
},
deleteBackup: {
title: "{$ADDONLANG.Confirm.DeleteBackup.Title|escape:'javascript'|default:'Delete Backup'}",
message: "{$ADDONLANG.Confirm.DeleteBackup.Message|escape:'javascript'|default:'Do you want to delete this backup. This action cannot be undone.'}"
},
restoreBackup: {
title: "{$ADDONLANG.Confirm.RestoreBackup.Title|escape:'javascript'|default:'Restore Backup'}",
message: "{$ADDONLANG.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
// Additional language string mappings
lang.confirmShutdownMessage = '{$ADDONLANG.Confirm.Shutdown.Message}';
lang.confirmShutdownTitle = '{$ADDONLANG.Confirm.Shutdown.Title}';
lang.confirmEnableRescueMessage = '{$ADDONLANG.Confirm.EnableRescue.Message}';
lang.confirmEnableRescueTitle = '{$ADDONLANG.Confirm.EnableRescue.Title}';
lang.rescueModeEnabled = '{$ADDONLANG.RescueModeEnabled}';
lang.savePassword = '{$ADDONLANG.SavePassword}';
lang.confirmEnableRescueRebootMessage = '{$ADDONLANG.Confirm.EnableRescueReboot.Message}';
lang.confirmEnableRescueRebootTitle = '{$ADDONLANG.Confirm.EnableRescueReboot.Title}';
lang.confirmDisableRescueMessage = '{$ADDONLANG.Confirm.DisableRescue.Message}';
lang.confirmDisableRescueTitle = '{$ADDONLANG.Confirm.DisableRescue.Title}';
lang.confirmResetPasswordMessage = '{$ADDONLANG.Confirm.ResetPassword.Message}';
lang.confirmResetPasswordTitle = '{$ADDONLANG.Confirm.ResetPassword.Title}';
lang.newRootPassword = '{$ADDONLANG.Settings.Rescue.NewRootPassword}';
lang.passwordPrompt = '{$ADDONLANG.Settings.Rescue.PasswordPrompt}';
lang.copyToClipboard = '{$ADDONLANG.CopyToClipboard}';
lang.passwordSaved = '{$ADDONLANG.PasswordSaved}';
lang.close = '{$ADDONLANG.Close}';
lang.passwordCopied = '{$ADDONLANG.PasswordCopied}';
lang.confirmCancel = '{$ADDONLANG.Confirm.Cancel|escape:'javascript'|default:'Cancel'}';
lang.confirmConfirm = '{$ADDONLANG.Confirm.Confirm|escape:'javascript'|default:'Confirm'}';
lang.networkError = '{$ADDONLANG.NetworkError}';
lang.restore = '{$ADDONLANG.Restore}';
lang.delete = '{$ADDONLANG.Delete}';
lang.confirmDeleteFirewallRuleMessage = '{$ADDONLANG.Confirm.DeleteFirewallRule.Message}';
lang.confirmDeleteFirewallRuleTitle = '{$ADDONLANG.Confirm.DeleteFirewallRule.Title}';
lang.selectISOImage = '{$ADDONLANG.SelectISOImage}';
lang.noISOImages = '{$ADDONLANG.NoISOImages}';
lang.loadingBackups = '{$ADDONLANG.LoadingBackups}';
</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> {$ADDONLANG.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> {$ADDONLANG.Navbar.Graphs}
</a>
</li>
{if $backupsEnabled}
<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> {$ADDONLANG.Navbar.Backups}
</a>
</li>
{/if}
<li class="nav-item">
<a class="nav-link" id="firewall-tab" data-toggle="tab" href="#firewall" onclick="ArkHostHetznerVPS_API('Get Firewall rules', false);return false;" role="tab" aria-controls="firewall" aria-selected="false">
<i class="fa fa-shield-alt"></i> {$ADDONLANG.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> {$ADDONLANG.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>{$ADDONLANG.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%">{$ADDONLANG.Overview.Hostname}:</td>
<td><strong>{$serverInfo['hostname']}</strong></td>
</tr>
<tr>
<td class="text-muted">{$ADDONLANG.IPv4} Address:</td>
<td><code>{$serverInfo['ip']}</code></td>
</tr>
{if $serverInfo['ipv6']}
<tr>
<td class="text-muted">{$ADDONLANG.IPv6} Address:</td>
<td><code style="font-size: 11px;">{$serverInfo['ipv6']}</code></td>
</tr>
{/if}
<tr>
<td class="text-muted">{$ADDONLANG.Overview.OS}:</td>
<td>
<img src="{$serverInfo['operatingSystem']['image']}" width="20" height="20" class="mr-1" style="vertical-align: middle;">
{$serverInfo['operatingSystem']['name']}
</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%">{$ADDONLANG.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">{$ADDONLANG.Overview.Uptime}:</td>
<td><strong>{$serverInfo['uptime_text']}</strong></td>
</tr>
<tr>
<td class="text-muted">{$ADDONLANG.Overview.Location}:</td>
<td><i class="fa fa-map-marker-alt mr-1"></i>{$serverInfo['datacenter']}</td>
</tr>
<tr>
<td class="text-muted">{$ADDONLANG.Overview.ServerType}:</td>
<td><strong>{$serverInfo['server_type']['description']|default:$ADDONLANG.ServerTypes.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>{$ADDONLANG.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']}</h3>
<small class="text-muted">{$ADDONLANG.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']} {$ADDONLANG.General.GB}</h3>
<small class="text-muted">{$ADDONLANG.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']} {$ADDONLANG.General.GB}</h3>
<small class="text-muted">{$ADDONLANG.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">20 {$ADDONLANG.General.TB}</h3>
<small class="text-muted">{$ADDONLANG.Overview.Traffic}</small>
</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>{$ADDONLANG.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}"
style="border-radius: 10px; padding: 8px 20px; font-size: 14px; margin: 5px;"
onclick="{if $serverInfo['status'] !== 'running'}return ArkHostHetznerVPS_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'}{$ADDONLANG.Start}{else}{$ADDONLANG.Stop}{/if}
</button>
<button class="btn btn-primary"
style="border-radius: 10px; padding: 8px 20px; font-size: 14px; margin: 5px;"
onclick="return confirmRestart();">
<i class="fa fa-sync mr-1"></i> {$ADDONLANG.Restart}
</button>
{if $serverInfo['status'] == 'running'}
<button class="btn btn-secondary"
style="border-radius: 10px; padding: 8px 20px; font-size: 14px; margin: 5px;"
onclick="return confirmShutdown();">
<i class="fa fa-power-off mr-1"></i> {$ADDONLANG.Shutdown}
</button>
{/if}
<a href="{$WEB_ROOT}/clientarea.php?action=productdetails&id={$serviceid}&modop=custom&a=VNC"
target="_blank" class="btn btn-info"
style="border-radius: 10px; padding: 8px 20px; font-size: 14px; margin: 5px; text-decoration: none;">
<i class="fa fa-desktop mr-1"></i> {$ADDONLANG.VNC}
</a>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="graphs" role="tabpanel" aria-labelledby="graphs-tab">
<!-- Time Period Selector Card -->
<div class="card mb-4">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="fa fa-chart-line mr-2"></i>{$ADDONLANG.Graphs.Title}</h5>
<button type="button" class="btn btn-sm btn-primary" onclick="refreshAllGraphs()">
<i class="fa fa-sync-alt mr-1"></i> {$ADDONLANG.General.RefreshAll}
</button>
</div>
<div class="card-body text-center">
<p class="text-muted mb-3">{$ADDONLANG.Graphs.SelectPeriod}</p>
<div class="btn-group" role="group">
<button type="button" class="btn btn-sm btn-outline-primary graph-time-btn" onclick="loadGraphs('hour', this)">{$ADDONLANG.Graphs.Hour}</button>
<button type="button" class="btn btn-sm btn-outline-primary graph-time-btn active" onclick="loadGraphs('day', this)">{$ADDONLANG.Graphs.Day}</button>
<button type="button" class="btn btn-sm btn-outline-primary graph-time-btn" onclick="loadGraphs('week', this)">{$ADDONLANG.Graphs.Week}</button>
<button type="button" class="btn btn-sm btn-outline-primary graph-time-btn" onclick="loadGraphs('month', this)">{$ADDONLANG.Graphs.Month}</button>
<button type="button" class="btn btn-sm btn-outline-primary graph-time-btn" onclick="loadGraphs('year', this)">{$ADDONLANG.Graphs.Year}</button>
</div>
</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">{$ADDONLANG.LoadingGraphs}</span>
</div>
<p class="mt-2 text-muted">{$ADDONLANG.LoadingGraphs}</p>
</div>
<!-- Graphs Container -->
<div class="row" id="graphs-container">
<div class="col-lg-12 mb-4">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fa fa-microchip mr-2"></i>{$ADDONLANG.Graphs.CPU}</h5>
</div>
<div class="card-body">
<div id="cpu-graph" class="text-center" style="min-height: 250px;">
<p class="text-muted">{$ADDONLANG.Graphs.Loading}</p>
</div>
</div>
</div>
</div>
<div class="col-lg-12 mb-4">
<div class="card">
<div class="card-header bg-info text-white">
<h5 class="mb-0"><i class="fa fa-network-wired mr-2"></i>{$ADDONLANG.Graphs.Network}</h5>
</div>
<div class="card-body">
<div id="network-graph" class="text-center" style="min-height: 250px;">
<p class="text-muted">{$ADDONLANG.Graphs.Loading}</p>
</div>
</div>
</div>
</div>
<div class="col-lg-12 mb-4">
<div class="card">
<div class="card-header bg-warning text-dark">
<h5 class="mb-0"><i class="fa fa-hdd mr-2"></i>{$ADDONLANG.Graphs.Disk}</h5>
</div>
<div class="card-body">
<div id="disk-graph" class="text-center" style="min-height: 250px;">
<p class="text-muted">{$ADDONLANG.Graphs.Loading}</p>
</div>
</div>
</div>
</div>
</div>
</div>
{if $backupsEnabled}
<div class="tab-pane fade" id="backups" role="tabpanel" aria-labelledby="backups-tab">
<!-- Backup Management Card -->
<div class="card mb-4">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="fa fa-shield-alt mr-2"></i>{$ADDONLANG.Backups.Title}</h5>
</div>
<div class="card-body">
<p class="text-muted mb-4">{$ADDONLANG.Backups.Description}</p>
<div class="text-right mb-3">
<button onclick="createBackup();return false;" class="btn btn-success">
<i class="fa fa-plus-circle mr-2"></i>{$ADDONLANG.Backups.Create}
</button>
</div>
<div class="table-responsive">
<table id="backupTable" class="table table-hover">
<thead class="thead-light">
<tr>
<th><i class="fa fa-calendar mr-1"></i> {$ADDONLANG.Backups.Date}</th>
<th><i class="fa fa-database mr-1"></i> {$ADDONLANG.Backups.Size}</th>
<th><i class="fa fa-tag mr-1"></i> {$ADDONLANG.Backups.Type}</th>
<th><i class="fa fa-info-circle mr-1"></i> {$ADDONLANG.Backups.Status}</th>
<th width="100" class="text-center"><i class="fa fa-cogs mr-1"></i> {$ADDONLANG.Backups.Actions}</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="5" class="text-center text-muted">
<i class="fa fa-spinner fa-spin mr-2"></i>{$ADDONLANG.LoadingBackups}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Backup Information Alert -->
<div class="alert alert-warning">
<h5 class="alert-heading"><i class="fa fa-exclamation-triangle mr-2"></i>Important Information</h5>
{$ADDONLANG.Backups.Warning|unescape:'html'}
</div>
</div>
{/if}
<div class="tab-pane fade" id="settings" role="tabpanel" aria-labelledby="settings-tab">
<div class="row">
<div class="col-lg-3 col-md-4 mb-4">
<div class="card">
<div class="card-header bg-light">
<h6 class="mb-0"><i class="fa fa-sliders-h mr-2"></i>{$ADDONLANG.Settings.Title}</h6>
</div>
<div class="card-body p-0">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<a class="nav-link active" id="hostname-tab" data-toggle="pill" href="#hostname" role="tab" aria-controls="hostname" aria-selected="true">
<i class="fa fa-globe mr-2"></i>{$ADDONLANG.Settings.Hostname.Title}
</a>
<a class="nav-link" id="iso-tab" data-toggle="pill" href="#iso" onclick="ArkHostHetznerVPS_API('ISO Images', false);" role="tab" aria-controls="iso" aria-selected="false">
<i class="fa fa-compact-disc mr-2"></i>{$ADDONLANG.Settings.ISO.Title}
</a>
<a class="nav-link" id="password-tab" data-toggle="pill" href="#password" role="tab" aria-controls="password" aria-selected="false">
<i class="fa fa-key mr-2"></i>{$ADDONLANG.Settings.Password.Title}
</a>
<a class="nav-link" id="reinstall-tab" data-toggle="pill" href="#reinstall" role="tab" aria-controls="reinstall" aria-selected="false">
<i class="fa fa-redo mr-2"></i>{$ADDONLANG.Settings.Reinstall.Title}
</a>
<a class="nav-link" id="rescue-tab" data-toggle="pill" href="#rescue" role="tab" aria-controls="rescue" aria-selected="false">
<i class="fa fa-life-ring mr-2"></i>{$ADDONLANG.Settings.Rescue.Title}
</a>
<a class="nav-link" id="floating-ip-tab" data-toggle="pill" href="#floating-ip" onclick="loadFloatingIPStatus();" role="tab" aria-controls="floating-ip" aria-selected="false">
<i class="fa fa-globe mr-2"></i>{$ADDONLANG.Settings.FloatingIP.Title}
</a>
</div>
</div>
</div>
</div>
<div class="col-lg-9 col-md-8">
<div class="tab-content" id="v-pills-tabContent">
<div class="tab-pane fade show active" id="hostname" role="tabpanel" aria-labelledby="hostname-tab">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="fa fa-globe mr-2"></i>{$ADDONLANG.Settings.Hostname.Title}</h5>
</div>
<div class="card-body">
<div class="alert alert-info">
<i class="fa fa-info-circle mr-2"></i>{$ADDONLANG.Settings.Hostname.Description}
</div>
<div class="form-group">
<label class="font-weight-bold">{$ADDONLANG.Settings.Hostname.Title}:</label>
<input class="form-control" id="hostnameRDNS" type="text" maxlength="128" value="{$serverInfo['hostname']}">
</div>
<div class="text-center">
<button onclick="ArkHostHetznerVPS_API('Hostname rDNS', true, { hostname: document.getElementById('hostnameRDNS').value });return false;" class="btn btn-primary">
<i class="fa fa-save mr-2"></i>{$ADDONLANG.Settings.Hostname.Submit}
</button>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="iso" role="tabpanel" aria-labelledby="iso-tab">
<div class="card">
<div class="card-header bg-info text-white">
<h5 class="mb-0"><i class="fa fa-compact-disc mr-2"></i>{$ADDONLANG.Settings.ISO.Title}</h5>
</div>
<div class="card-body">
<div class="alert alert-info">
<i class="fa fa-info-circle mr-2"></i>{$ADDONLANG.Settings.ISO.Description}
</div>
<div class="form-group">
<label class="font-weight-bold">{$ADDONLANG.Settings.ISO.Image}:</label>
<select class="form-control" id="isoID">
<option value="">Select ISO Image...</option>
</select>
</div>
<div class="text-center">
<button onclick="ArkHostHetznerVPS_API('Load ISO', true, { iso_id: document.getElementById('isoID').value });return false;" class="btn btn-info">
<i class="fa fa-upload mr-2"></i>{$ADDONLANG.Settings.ISO.Submit}
</button>
{if $serverInfo['iso'] !== ''}
<button onclick="ArkHostHetznerVPS_API('Eject ISO', true);return false;" class="btn btn-danger ml-2">
<i class="fa fa-eject mr-2"></i>{$ADDONLANG.Settings.ISO.Remove}
</button>
{/if}
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="password" role="tabpanel" aria-labelledby="password-tab">
<div class="card">
<div class="card-header bg-warning text-dark">
<h5 class="mb-0"><i class="fa fa-key mr-2"></i>{$ADDONLANG.Settings.Password.Title}</h5>
</div>
<div class="card-body">
<div class="alert alert-warning">
<i class="fa fa-exclamation-triangle mr-2"></i>{$ADDONLANG.Settings.Password.Description}
</div>
<div class="form-group">
<label class="font-weight-bold">{$ADDONLANG.Settings.Password.Title}:</label>
<div class="input-group">
<input class="form-control" id="vpsPassword" type="password" disabled value="{if $serverInfo['install_root'] != ''}{$serverInfo['install_root']}{else}Expired{/if}">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" onclick="ArkHostVPS_ShowPassword();return false;">
<i class="fa fa-eye" id="showPasswordIcon"></i>
</button>
</div>
</div>
</div>
<div class="text-center">
<button onclick="ArkHostHetznerVPS_API('Reset root');return false;" class="btn btn-danger text-white">
<i class="fa fa-sync mr-2"></i>{$ADDONLANG.Settings.Password.Submit}
</button>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="reinstall" role="tabpanel" aria-labelledby="reinstall-tab">
<div class="card">
<div class="card-header bg-danger text-white">
<h5 class="mb-0"><i class="fa fa-redo mr-2"></i>{$ADDONLANG.Settings.Reinstall.Title}</h5>
</div>
<div class="card-body">
<div class="alert alert-danger">
<i class="fa fa-exclamation-circle mr-2"></i>{$ADDONLANG.Settings.Reinstall.Description}
</div>
<h6 class="font-weight-bold mb-3">{$ADDONLANG.Settings.Reinstall.OS}:</h6>
<div id="os_list" class="row mb-4">
{foreach from=$operatingSystems key=$group item=$operatingSystemsGroup}
<div class="col-12 col-md-6 col-lg-4 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">{$ADDONLANG.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>
<div class="alert alert-info">
<i class="fa fa-info-circle mr-2"></i><strong>{$ADDONLANG.General.Note}:</strong> {$ADDONLANG.Reinstall.DestroyWarning}
</div>
<input type="hidden" id="newOS" value="0"/>
<div class="text-center">
<button onclick="reinstallWithAdvancedOptions();" class="btn btn-danger">
<i class="fa fa-exclamation-triangle mr-2"></i>{$ADDONLANG.Settings.Reinstall.Submit}
</button>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="rescue" role="tabpanel" aria-labelledby="rescue-tab">
<div class="card">
<div class="card-header bg-warning text-dark">
<h5 class="mb-0"><i class="fa fa-life-ring mr-2"></i>{$ADDONLANG.Settings.Rescue.Title}</h5>
</div>
<div class="card-body">
<div class="alert alert-info mb-4">
<h6 class="alert-heading"><i class="fa fa-info-circle mr-2"></i>{$ADDONLANG.Settings.Rescue.AboutTitle}</h6>
<p>{$ADDONLANG.Settings.Rescue.AboutDescription}</p>
<hr>
<p class="mb-0"><strong>{$ADDONLANG.Settings.Rescue.Important}:</strong> {$ADDONLANG.Settings.Rescue.ImportantNote}</p>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<div class="card h-100">
<div class="card-body text-center">
<i class="fa fa-play-circle fa-3x text-success mb-3"></i>
<h5 class="card-title">{$ADDONLANG.Settings.Rescue.EnableTitle}</h5>
<p class="card-text">{$ADDONLANG.Settings.Rescue.EnableDescription}</p>
<button onclick="enableRescueMode();" class="btn btn-success">
<i class="fa fa-life-ring mr-2"></i>{$ADDONLANG.Settings.Rescue.Enable}
</button>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card h-100">
<div class="card-body text-center">
<i class="fa fa-power-off fa-3x text-danger mb-3"></i>
<h5 class="card-title">{$ADDONLANG.Settings.Rescue.EnableRebootTitle}</h5>
<p class="card-text">{$ADDONLANG.Settings.Rescue.EnableRebootDescription}</p>
<button onclick="enableRescueAndReboot();" class="btn btn-danger">
<i class="fa fa-sync mr-2"></i>{$ADDONLANG.Settings.Rescue.EnableReboot}
</button>
</div>
</div>
</div>
</div>
<div class="card mt-3 border-secondary">
<div class="card-body">
<h6 class="card-title"><i class="fa fa-stop-circle mr-2"></i>{$ADDONLANG.Settings.Rescue.DisableTitle}</h6>
<p class="card-text">{$ADDONLANG.Settings.Rescue.DisableDescription}</p>
<button onclick="disableRescueMode();" class="btn btn-secondary">
<i class="fa fa-times mr-2"></i>{$ADDONLANG.Settings.Rescue.Disable}
</button>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="floating-ip" role="tabpanel" aria-labelledby="floating-ip-tab">
<div class="card">
<div class="card-header bg-info text-white">
<h5 class="mb-0"><i class="fa fa-globe mr-2"></i>{$ADDONLANG.Settings.FloatingIP.Title}</h5>
</div>
<div class="card-body">
<div class="alert alert-info mb-4">
<i class="fa fa-info-circle mr-2"></i>{$ADDONLANG.Settings.FloatingIP.Description}
</div>
<div id="floating-ip-content">
<div class="text-center">
<i class="fas fa-spinner fa-spin fa-2x"></i>
<p class="mt-2">Loading floating IP information...</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</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>{$ADDONLANG.General.Note}:</strong> {$ADDONLANG.Firewall.ResourcesAttached}
</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>{$ADDONLANG.Firewall.AddNewRule}</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-2">
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Action}</label>
<select class="form-control" id="firewallAction">
<option value="ACCEPT" class="text-success">✓ {$ADDONLANG.Settings.Firewall.Accept}</option>
<option value="DROP" class="text-danger">✗ {$ADDONLANG.Settings.Firewall.Drop}</option>
</select>
</div>
<div class="col-md-2">
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Protocol}</label>
<select class="form-control" id="firewallProtocol">
<option value="TCP">{$ADDONLANG.Firewall.TCP}</option>
<option value="UDP">{$ADDONLANG.Firewall.UDP}</option>
<option value="ICMP">{$ADDONLANG.Firewall.ICMP}</option>
<option value="ANY">{$ADDONLANG.Firewall.Any}</option>
</select>
</div>
<div class="col-md-2">
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Port}</label>
<input class="form-control" id="firewallPort" type="number" min="1" max="65535" placeholder="{$ADDONLANG.Firewall.PortPlaceholder}">
</div>
<div class="col-md-3">
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Source}</label>
<input class="form-control" id="firewallSource" type="text" placeholder="{$ADDONLANG.Firewall.SourcePlaceholder}">
</div>
<div class="col-md-3">
<label class="small text-muted">{$ADDONLANG.Settings.Firewall.Note}</label>
<input class="form-control" id="firewallNote" type="text" maxlength="64" placeholder="{$ADDONLANG.Firewall.DescriptionPlaceholder}">
</div>
</div>
<div class="text-right mt-3">
<button class="btn btn-success" onclick="addFirewallRule();return false;">
<i class="fa fa-plus mr-2"></i>{$ADDONLANG.General.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>{$ADDONLANG.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">Action</th>
<th width="100">Protocol</th>
<th width="100">Port</th>
<th>Source</th>
<th>Description</th>
<th width="80" class="text-center">Remove</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>{$ADDONLANG.LoadingFirewallRules}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Commit button not needed - changes are immediate -->
<div class="alert alert-success mt-4 mb-4">
<i class="fa fa-check-circle mr-2"></i><strong>{$ADDONLANG.General.Note}:</strong> {$ADDONLANG.Firewall.ChangesImmediate}
</div>
</div>
</div>
</div>
</div>
</div>
<script src="{$WEB_ROOT}/modules/servers/ArkHostHetznerVPS/template/script.js"></script>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

File diff suppressed because it is too large Load Diff
@@ -0,0 +1,604 @@
/* ArkHost Hetzner VPS Module Styles */
.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;
}