server status fix
This commit is contained in:
parent
bb02be7daa
commit
bd3875743e
8 changed files with 555 additions and 306 deletions
|
|
@ -66,6 +66,14 @@ function gsp_project_request_url(): string {
|
|||
return 'https://runlevelsystems.com/start-project.php';
|
||||
}
|
||||
|
||||
function gsp_discord_invite_url(): string {
|
||||
return 'https://discord.gg/qt9Hnkj6cv';
|
||||
}
|
||||
|
||||
function gsp_server_status_url(): string {
|
||||
return 'server_status.php';
|
||||
}
|
||||
|
||||
function gsp_panel_footer_copyright(): string {
|
||||
return "\u{00A9} 2026 " . gsp_company_name();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,30 +63,29 @@
|
|||
color: #d2def4;
|
||||
}
|
||||
|
||||
.dashboard-service-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 16px;
|
||||
margin-top: 20px;
|
||||
.dashboard-widget-copy {
|
||||
position: relative;
|
||||
padding-right: 56px;
|
||||
}
|
||||
|
||||
.dashboard-promo-card,
|
||||
.dashboard-secondary-card {
|
||||
padding: 18px 20px;
|
||||
background: linear-gradient(180deg, rgba(12, 22, 40, 0.94) 0%, rgba(8, 16, 29, 0.96) 100%);
|
||||
border: 1px solid rgba(77, 160, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.dashboard-promo-card {
|
||||
border-left: 3px solid #48c4f5;
|
||||
}
|
||||
|
||||
.dashboard-promo-card p,
|
||||
.dashboard-secondary-card p {
|
||||
.dashboard-widget-copy p {
|
||||
margin: 0 0 12px;
|
||||
color: #d2def4;
|
||||
}
|
||||
|
||||
.dashboard-widget-icon {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.dashboard-link-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.dashboard-card-note {
|
||||
|
|
@ -121,6 +120,11 @@
|
|||
color: #f0f5ff;
|
||||
}
|
||||
|
||||
.dashboard-support-link-secondary {
|
||||
background: rgba(72, 196, 245, 0.08);
|
||||
border-color: rgba(72, 196, 245, 0.24);
|
||||
}
|
||||
|
||||
.dashboard-support-link:hover,
|
||||
.dashboard-support-link:focus {
|
||||
color: #ffffff;
|
||||
|
|
@ -128,16 +132,32 @@
|
|||
box-shadow: 0 0 0 3px rgba(90, 199, 247, 0.12);
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.dashboard-service-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.dashboard-status-panel {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.dashboard-status-content {
|
||||
padding: 18px 20px;
|
||||
text-align: center;
|
||||
color: #d2def4;
|
||||
}
|
||||
|
||||
.dashboard-status-content p {
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
|
||||
.dashboard-status-button {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.dashboard-status-note {
|
||||
display: block;
|
||||
color: #9fb5d8;
|
||||
}
|
||||
|
||||
@media (max-width: 430px) {
|
||||
.dashboard-promo-card,
|
||||
.dashboard-secondary-card {
|
||||
padding: 16px;
|
||||
.dashboard-widget-copy {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.dashboard-cta-button,
|
||||
|
|
@ -145,4 +165,10 @@
|
|||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dashboard-widget-icon {
|
||||
position: static;
|
||||
display: block;
|
||||
margin: 0 0 12px auto;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ function exec_ogp_module()
|
|||
global $db, $settings, $loggedInUserInfo;
|
||||
|
||||
$projectRequestUrl = htmlspecialchars(gsp_project_request_url(), ENT_QUOTES, 'UTF-8');
|
||||
$discordInviteUrl = htmlspecialchars(gsp_discord_invite_url(), ENT_QUOTES, 'UTF-8');
|
||||
$serverStatusUrl = htmlspecialchars(gsp_server_status_url(), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
$isAdmin = $db->isAdmin($_SESSION['user_id']);
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
|
@ -85,16 +87,27 @@ function exec_ogp_module()
|
|||
$content[3] = 'View all your notifications. ';
|
||||
$href[3] = 'home.php?m=circular&p=show_circular&list=true';
|
||||
|
||||
// Support Resources quick link
|
||||
$title[4] = 'Support Resources';
|
||||
$content[4] ='Need routine help? Use the support section for tickets, service problems, documentation, and troubleshooting with existing features.';
|
||||
$href[4] = 'home.php?m=tickets';
|
||||
// Custom Server Code
|
||||
$title[4] = 'Custom Server Code';
|
||||
$content[4] = '<div class="dashboard-widget-copy">'
|
||||
. '<p>Need something beyond the standard server options? Our developers can customize, repair, automate, or extend your game server with mods, scripts, integrations, migrations, and server-specific tools.</p>'
|
||||
. '<p class="dashboard-card-note">Tell us what you want to build or improve, and we will review the request with you.</p>'
|
||||
. '<a class="dashboard-cta-button" href="' . $projectRequestUrl . '" target="_blank" rel="noopener noreferrer">Request Custom Development</a>'
|
||||
. '</div>';
|
||||
$href[4] = null;
|
||||
|
||||
// Support
|
||||
$title[5] = (isset($settings['support_widget_title']) && $settings['support_widget_title'] != "") ?
|
||||
$settings['support_widget_title'] : get_lang('support');
|
||||
$content[5] = '<img src="themes/' . $settings['theme'] . '/images/icons/support.png" style="width:48px;float:right;margin:0 0 0 8px" /> Submit a SUPPORT TICKET or use our Discord Chat at the bottom right. Click this box to JOIN our Discord';
|
||||
$href[5] = 'https://discord.gg/cWHAbav';
|
||||
$title[5] = 'Support';
|
||||
$content[5] = '<div class="dashboard-widget-copy">'
|
||||
. '<img src="themes/' . $settings['theme'] . '/images/icons/support.png" class="dashboard-widget-icon" alt="" />'
|
||||
. '<p>Need help with an existing service? Submit a support ticket or join our Discord support server.</p>'
|
||||
. '<p class="dashboard-card-note">Support is for service problems, broken functionality, routine troubleshooting, and help using the features already included with your server.</p>'
|
||||
. '<div class="dashboard-link-group">'
|
||||
. '<a class="dashboard-support-link" href="home.php?m=tickets">Open Support Tickets</a>'
|
||||
. '<a class="dashboard-support-link dashboard-support-link-secondary" href="' . $discordInviteUrl . '" target="_blank" rel="noopener noreferrer">Join Discord Support</a>'
|
||||
. '</div>'
|
||||
. '</div>';
|
||||
$href[5] = null;
|
||||
|
||||
|
||||
|
||||
|
|
@ -145,30 +158,14 @@ function exec_ogp_module()
|
|||
echo $html.'</div>';
|
||||
}
|
||||
|
||||
echo "<div class='dashboard-service-grid'>
|
||||
<div class='dashboard-promo-card'>
|
||||
<h4>CUSTOM SERVER DEVELOPMENT</h4>
|
||||
<p>Need something beyond the standard server options? Our developers can customize, repair, automate, or extend your game server with mods, scripts, integrations, migrations, and server-specific tools.</p>
|
||||
<p class='dashboard-card-note'>Tell us what you want to build or improve, and we will review the request with you.</p>
|
||||
<a class='dashboard-cta-button' href='{$projectRequestUrl}'>Request Custom Development</a>
|
||||
</div>
|
||||
<div class='dashboard-secondary-card'>
|
||||
<h4>Support and Troubleshooting</h4>
|
||||
<p>Use support when something is broken, you need routine troubleshooting, or you need help with the features already included with your service.</p>
|
||||
<a class='dashboard-support-link' href='home.php?m=tickets'>Open Support Resources</a>
|
||||
</div>
|
||||
</div>";
|
||||
|
||||
// Server Status Link - Available to all users
|
||||
echo "<div style='margin-top:20px;'>
|
||||
<div class='bloc rounded' >
|
||||
echo "<div class='dashboard-status-panel'>
|
||||
<div class='bloc rounded'>
|
||||
<h4>Server Status</h4>
|
||||
<div style='text-align: center; padding: 20px;'>
|
||||
<p>View the status of all game servers</p>
|
||||
<a href='server_status.php' target='_blank' style='background-color: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; display: inline-block; margin: 10px;'>
|
||||
🖥️ View Server Status
|
||||
</a>
|
||||
<br><small style='color: #666;'>Opens in a new window</small>
|
||||
<div class='dashboard-status-content'>
|
||||
<p>Check the current state of configured remote servers without delaying the main dashboard.</p>
|
||||
<a class='dashboard-cta-button dashboard-status-button' href='{$serverStatusUrl}' target='_blank' rel='noopener noreferrer'>View Server Status</a>
|
||||
<small class='dashboard-status-note'>Opens in a new tab and runs remote checks only when requested.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>";
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<link>https://worlddomination.software/projects/gsp</link>
|
||||
<description>Quick answers for GameServer Panel (GSP) operators</description>
|
||||
<dc:language>en</dc:language>
|
||||
<generator>World Domination Software FAQ Generator</generator>
|
||||
<generator>Runlevel Systems FAQ Generator</generator>
|
||||
<pubDate>Tue, 11 Feb 2025 12:00:00 GMT</pubDate>
|
||||
|
||||
<image>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
<category>General</category>
|
||||
<content:encoded>
|
||||
(1) submit a ticket on our Panel menu
|
||||
(2) join our <a href=https://discord.gg/cWHAbav target=_blank > Discord Server</a>
|
||||
(2) join our <a href=https://discord.gg/qt9Hnkj6cv target=_blank rel="noopener noreferrer"> Discord Server</a>
|
||||
(3) Look at the LOWER RIGHT of every page: Click the Discord icon
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,269 +22,444 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// Standalone Server Status Page - Available to all users
|
||||
require_once("includes/functions.php");
|
||||
require_once("includes/helpers.php");
|
||||
require_once("includes/html_functions.php");
|
||||
require_once "includes/functions.php";
|
||||
require_once "includes/helpers.php";
|
||||
require_once "includes/html_functions.php";
|
||||
startSession();
|
||||
|
||||
// Report all PHP errors
|
||||
error_reporting(E_ERROR);
|
||||
|
||||
// Path definitions
|
||||
define("IMAGES", "images/");
|
||||
define("INCLUDES", "includes/");
|
||||
define("MODULES", "modules/");
|
||||
define("CONFIG_FILE","includes/config.inc.php");
|
||||
define("CONFIG_FILE", "includes/config.inc.php");
|
||||
|
||||
require_once CONFIG_FILE;
|
||||
require_once('includes/lib_remote.php');
|
||||
require_once "includes/lib_remote.php";
|
||||
|
||||
// Connect to the database server and select database.
|
||||
$db = createDatabaseConnection($db_type, $db_host, $db_user, $db_pass, $db_name, $table_prefix, isset($db_port) ? $db_port : NULL);
|
||||
$db = createDatabaseConnection($db_type, $db_host, $db_user, $db_pass, $db_name, $table_prefix, isset($db_port) ? $db_port : null);
|
||||
|
||||
// Load languages.
|
||||
include_once("includes/lang.php");
|
||||
include_once "includes/lang.php";
|
||||
|
||||
if (!$db instanceof OGPDatabase) {
|
||||
ogpLang();
|
||||
die(get_lang('no_db_connection'));
|
||||
}
|
||||
|
||||
// Check if user is logged in
|
||||
if (!isset($_SESSION['users_login'])) {
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get user info
|
||||
$loggedInUserInfo = $db->getUserById($_SESSION['user_id']);
|
||||
|
||||
// Get settings
|
||||
$settings = $db->getSettings();
|
||||
@$GLOBALS['panel_language'] = $settings['panel_language'];
|
||||
ogpLang();
|
||||
|
||||
function ping_host($host, $timeout = 5) {
|
||||
if (function_exists('exec')) {
|
||||
function panel_ping_host($host, $timeout = 3) {
|
||||
if (!function_exists('exec')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$output = array();
|
||||
$result = 0;
|
||||
|
||||
// Use ping command based on OS
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
||||
exec("ping -n 1 -w " . ($timeout * 1000) . " " . escapeshellarg($host), $output, $result);
|
||||
exec("ping -n 1 -w " . ((int)$timeout * 1000) . " " . escapeshellarg($host), $output, $result);
|
||||
} else {
|
||||
exec("ping -c 1 -W " . $timeout . " " . escapeshellarg($host), $output, $result);
|
||||
exec("ping -c 1 -W " . (int)$timeout . " " . escapeshellarg($host), $output, $result);
|
||||
}
|
||||
|
||||
if ($result !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($result === 0) {
|
||||
// Extract ping time from output
|
||||
foreach ((array)$output as $line) {
|
||||
if (preg_match('/time[<=]([0-9.]+)\s*ms/i', $line, $matches)) {
|
||||
return floatval($matches[1]);
|
||||
if (preg_match('/time[=<]?\s*([0-9.]+)\s*ms/i', $line, $matches)) {
|
||||
return (float)$matches[1];
|
||||
}
|
||||
}
|
||||
return 0; // Host is up but couldn't extract time
|
||||
}
|
||||
}
|
||||
return false; // Host is down or ping unavailable
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
function get_hostname($ip) {
|
||||
$hostname = gethostbyaddr($ip);
|
||||
return ($hostname && $hostname !== $ip) ? $hostname : false;
|
||||
function agent_socket_reachable($host, $port, $timeout = 2.0) {
|
||||
$errno = 0;
|
||||
$errstr = '';
|
||||
$socket = @fsockopen($host, (int)$port, $errno, $errstr, $timeout);
|
||||
if ($socket === false) {
|
||||
return false;
|
||||
}
|
||||
fclose($socket);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get all remote servers
|
||||
function status_badge($label, $class) {
|
||||
return '<span class="status-badge ' . htmlspecialchars($class, ENT_QUOTES, 'UTF-8') . '">' .
|
||||
htmlspecialchars($label, ENT_QUOTES, 'UTF-8') .
|
||||
'</span>';
|
||||
}
|
||||
|
||||
$checkedAt = gmdate('Y-m-d H:i:s') . ' UTC';
|
||||
$servers = $db->getRemoteServers();
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Server Status - OGP</title>
|
||||
<title>Server Status | <?php echo htmlspecialchars(gsp_company_name(), ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
--bg: #07111f;
|
||||
--panel: rgba(11, 20, 36, 0.94);
|
||||
--panel-strong: rgba(15, 26, 46, 0.98);
|
||||
--border: rgba(77, 160, 255, 0.18);
|
||||
--accent: #48c4f5;
|
||||
--accent-strong: #2f9fde;
|
||||
--text: #e6f0ff;
|
||||
--muted: #9fb5d8;
|
||||
--success: #42d392;
|
||||
--warning: #f2c94c;
|
||||
--danger: #f46d75;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 24px 16px 32px;
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
background-color: #f5f5f5;
|
||||
background: radial-gradient(circle at top, rgba(35, 74, 129, 0.28), transparent 36%), var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.status-shell {
|
||||
max-width: 1180px;
|
||||
margin: 0 auto;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.status-card {
|
||||
background: linear-gradient(180deg, var(--panel) 0%, var(--panel-strong) 100%);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.26);
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.status-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 2px solid #4CAF50;
|
||||
padding-bottom: 10px;
|
||||
margin: 0 0 8px;
|
||||
font-size: 28px;
|
||||
line-height: 1.1;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.status-table {
|
||||
|
||||
.status-subtitle,
|
||||
.status-note,
|
||||
.status-updated,
|
||||
.status-empty {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.status-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.status-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 42px;
|
||||
padding: 10px 16px;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(107, 214, 255, 0.55);
|
||||
background: linear-gradient(180deg, #5ac7f7 0%, #2f9fde 100%);
|
||||
color: #04111f;
|
||||
}
|
||||
|
||||
.status-button-secondary {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.status-meta {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 12px;
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.status-meta-item {
|
||||
padding: 14px 16px;
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.status-meta-item strong {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.status-table-wrap {
|
||||
overflow-x: auto;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
min-width: 760px;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.status-table th {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 12px;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
text-align: left;
|
||||
border: 1px solid #ddd;
|
||||
vertical-align: top;
|
||||
}
|
||||
.status-table td {
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: center;
|
||||
|
||||
th {
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--muted);
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
.status-table tr:nth-child(even) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.status-table tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.status-up {
|
||||
color: #4CAF50;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
.status-down {
|
||||
color: #f44336;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
.ping-good {
|
||||
color: #4CAF50;
|
||||
font-weight: bold;
|
||||
}
|
||||
.ping-medium {
|
||||
color: #ff9800;
|
||||
font-weight: bold;
|
||||
}
|
||||
.ping-bad {
|
||||
color: #f44336;
|
||||
font-weight: bold;
|
||||
}
|
||||
.refresh-btn {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.refresh-btn:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
.last-updated {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
margin-top: 20px;
|
||||
font-style: italic;
|
||||
|
||||
tbody tr:hover {
|
||||
background: rgba(72, 196, 245, 0.05);
|
||||
}
|
||||
|
||||
.server-name {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.server-detail {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.status-online {
|
||||
background: rgba(66, 211, 146, 0.14);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.status-warning {
|
||||
background: rgba(242, 201, 76, 0.14);
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.status-offline {
|
||||
background: rgba(244, 109, 117, 0.14);
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.latency-good {
|
||||
color: var(--success);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.latency-medium {
|
||||
color: var(--warning);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.latency-bad {
|
||||
color: var(--danger);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.latency-muted {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.status-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-top: 18px;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
body {
|
||||
padding: 16px 12px 24px;
|
||||
}
|
||||
|
||||
.status-card {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.status-actions {
|
||||
width: 100%;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.status-button {
|
||||
width: 100%;
|
||||
}
|
||||
.no-servers {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
padding: 40px;
|
||||
font-size: 18px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🖥️ Server Status Dashboard</h1>
|
||||
<div class="status-shell">
|
||||
<div class="status-card">
|
||||
<div class="status-header">
|
||||
<div>
|
||||
<h1>Server Status</h1>
|
||||
<p class="status-subtitle">Configured remote servers are checked in real time when this page loads. The main dashboard stays fast because these checks do not run until you open this page.</p>
|
||||
</div>
|
||||
<div class="status-actions">
|
||||
<a class="status-button" href="<?php echo htmlspecialchars(gsp_server_status_url(), ENT_QUOTES, 'UTF-8'); ?>">Refresh Status</a>
|
||||
<a class="status-button status-button-secondary" href="home.php?m=dashboard&p=dashboard">Back to Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="refresh-btn" onclick="window.location.reload();">🔄 Refresh Status</button>
|
||||
<div class="status-meta">
|
||||
<div class="status-meta-item">
|
||||
<strong>Latency label</strong>
|
||||
<span>Panel-to-server latency. This is a server-side connectivity check from the Panel host, not latency from the customer browser.</span>
|
||||
</div>
|
||||
<div class="status-meta-item">
|
||||
<strong>Status source</strong>
|
||||
<span>Remote servers from the active Panel configuration with live agent status checks.</span>
|
||||
</div>
|
||||
<div class="status-meta-item">
|
||||
<strong>Checked</strong>
|
||||
<span><?php echo htmlspecialchars($checkedAt, ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (empty($servers)): ?>
|
||||
<div class="no-servers">
|
||||
No servers configured in the system.
|
||||
</div>
|
||||
<p class="status-empty">No remote servers are currently configured.</p>
|
||||
<?php else: ?>
|
||||
<table class="status-table">
|
||||
<div class="status-table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Server Name</th>
|
||||
<th>Location/IP</th>
|
||||
<th>Hostname</th>
|
||||
<th>Location / IP</th>
|
||||
<th>Status</th>
|
||||
<th>Ping (ms)</th>
|
||||
<th>Agent Status</th>
|
||||
<th>Panel-to-server latency</th>
|
||||
<th>Last Checked</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ((array)$servers as $server):
|
||||
$server_ip = gethostbyname($server['agent_ip']);
|
||||
$hostname = get_hostname($server_ip);
|
||||
<?php foreach ((array)$servers as $server): ?>
|
||||
<?php
|
||||
$agentIp = isset($server['agent_ip']) ? $server['agent_ip'] : '';
|
||||
$resolvedIp = $agentIp !== '' ? gethostbyname($agentIp) : '';
|
||||
$displayIp = $resolvedIp !== '' ? $resolvedIp : $agentIp;
|
||||
$agentPort = isset($server['agent_port']) ? (int)$server['agent_port'] : 0;
|
||||
$agentReachable = ($agentIp !== '' && $agentPort > 0) ? agent_socket_reachable($agentIp, $agentPort, 2.0) : false;
|
||||
$remote = new OGPRemoteLibrary($agentIp, $agentPort, $server['encryption_key'], $server['timeout']);
|
||||
$agentStatus = $remote->status_chk();
|
||||
$latency = $displayIp !== '' ? panel_ping_host($displayIp, 3) : false;
|
||||
|
||||
// Check server status
|
||||
$remote = new OGPRemoteLibrary($server['agent_ip'], $server['agent_port'],
|
||||
$server['encryption_key'], $server['timeout']);
|
||||
$status = $remote->status_chk();
|
||||
$is_online = ($status === 1);
|
||||
|
||||
// Get ping time
|
||||
$ping_time = ping_host($server_ip, 3);
|
||||
|
||||
// Determine ping color class
|
||||
$ping_class = '';
|
||||
if ($ping_time !== false) {
|
||||
if ($ping_time <= 50) {
|
||||
$ping_class = 'ping-good';
|
||||
} elseif ($ping_time <= 150) {
|
||||
$ping_class = 'ping-medium';
|
||||
if ($agentStatus === 1) {
|
||||
$overallStatus = status_badge('Online', 'status-online');
|
||||
$agentBadge = status_badge('Available', 'status-online');
|
||||
} elseif ($agentReachable) {
|
||||
$overallStatus = status_badge('Unknown', 'status-warning');
|
||||
$agentBadge = status_badge('Timed out', 'status-warning');
|
||||
} else {
|
||||
$ping_class = 'ping-bad';
|
||||
$overallStatus = status_badge('Offline', 'status-offline');
|
||||
$agentBadge = status_badge('Unavailable', 'status-offline');
|
||||
}
|
||||
|
||||
$latencyClass = 'latency-muted';
|
||||
if ($latency !== false) {
|
||||
if ($latency <= 50) {
|
||||
$latencyClass = 'latency-good';
|
||||
} elseif ($latency <= 150) {
|
||||
$latencyClass = 'latency-medium';
|
||||
} else {
|
||||
$latencyClass = 'latency-bad';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td class="server-name"><?php echo htmlspecialchars($server['remote_server_name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($server['agent_ip']); ?>
|
||||
<?php if ($server_ip !== $server['agent_ip']): ?>
|
||||
<br><small>(<?php echo htmlspecialchars($server_ip); ?>)</small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo $hostname ? htmlspecialchars($hostname) : '<em>N/A</em>'; ?></td>
|
||||
<td>
|
||||
<?php if ($is_online): ?>
|
||||
<span class="status-up">🟢 UP</span>
|
||||
<?php else: ?>
|
||||
<span class="status-down">🔴 DOWN</span>
|
||||
<?php endif; ?>
|
||||
<span class="server-name"><?php echo htmlspecialchars($server['remote_server_name'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
<span class="server-detail">Agent port <?php echo htmlspecialchars((string)$agentPort, ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($ping_time !== false): ?>
|
||||
<span class="<?php echo $ping_class; ?>">
|
||||
<?php echo number_format($ping_time, 1); ?> ms
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span style="color: #999;">N/A</span>
|
||||
<?php echo htmlspecialchars($agentIp, ENT_QUOTES, 'UTF-8'); ?>
|
||||
<?php if ($displayIp !== '' && $displayIp !== $agentIp): ?>
|
||||
<span class="server-detail">Resolved IP: <?php echo htmlspecialchars($displayIp, ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo $overallStatus; ?></td>
|
||||
<td><?php echo $agentBadge; ?></td>
|
||||
<td>
|
||||
<?php if ($latency !== false): ?>
|
||||
<span class="<?php echo htmlspecialchars($latencyClass, ENT_QUOTES, 'UTF-8'); ?>"><?php echo htmlspecialchars(number_format($latency, 1), ENT_QUOTES, 'UTF-8'); ?> ms</span>
|
||||
<?php else: ?>
|
||||
<span class="latency-muted">Unavailable</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($checkedAt, ENT_QUOTES, 'UTF-8'); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="last-updated">
|
||||
Last updated: <?php echo date('Y-m-d H:i:s'); ?>
|
||||
<div class="status-footer">
|
||||
<span class="status-note">One unavailable remote can still report cleanly without changing the dashboard page flow.</span>
|
||||
<span><?php echo htmlspecialchars(gsp_panel_footer_copyright(), ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<a href="home.php" style="color: #4CAF50; text-decoration: none; font-weight: bold;">← Back to Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ Main landing dashboard with widgets and quick server overview.
|
|||
## Current Status
|
||||
|
||||
- Functional
|
||||
- Uses the existing collapsible widget layout for customer-facing quick actions
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
|
@ -39,12 +40,12 @@ Main landing dashboard with widgets and quick server overview.
|
|||
|
||||
## Known Issues
|
||||
|
||||
- some default widgets are legacy
|
||||
- some default widget IDs are legacy and limit how far the collapsible layout can be expanded without a schema change
|
||||
|
||||
## Missing Functionality
|
||||
|
||||
- richer status and alert surfaces
|
||||
- clear separation between support requests and custom project work
|
||||
- deeper async server-status embedding without adding remote checks to normal dashboard loads
|
||||
|
||||
## Suggested Future Improvements
|
||||
|
||||
|
|
@ -59,6 +60,8 @@ Main landing dashboard with widgets and quick server overview.
|
|||
- Dashboard render file: `Panel/modules/dashboard/dashboard.php`
|
||||
- Dashboard styles: `Panel/modules/dashboard/dashboard.css`
|
||||
- Shared Panel project URL helper: `Panel/includes/functions.php`
|
||||
- Shared Discord invite helper: `Panel/includes/functions.php`
|
||||
- Shared server-status route helper: `Panel/includes/functions.php`
|
||||
|
||||
Current project request URL:
|
||||
|
||||
|
|
@ -66,5 +69,17 @@ Current project request URL:
|
|||
|
||||
Dashboard behavior:
|
||||
|
||||
- retained collapsible sections:
|
||||
- `Account Overview`
|
||||
- `Custom Server Code`
|
||||
- `Support`
|
||||
- removed duplicate standalone sections:
|
||||
- `Custom Server Development`
|
||||
- `Support and Troubleshooting`
|
||||
- support remains the path for routine troubleshooting, service issues, and existing features
|
||||
- the custom-development CTA is separate and points users at Runlevel Systems for project work such as custom scripts, mods, integrations, automation, migrations, dashboards, and advanced server tooling
|
||||
- the custom-development CTA points users at Runlevel Systems for project work such as custom scripts, mods, integrations, automation, migrations, dashboards, and advanced server tooling
|
||||
- the Discord support invite should use:
|
||||
- `https://discord.gg/qt9Hnkj6cv`
|
||||
- `Custom Server Code` should open the Runlevel project request in a new tab
|
||||
- `Support` should keep ticketing and Discord support separate from paid project work
|
||||
- `Server Status` stays available from the dashboard, but remote checks are intentionally deferred to the separate status page so normal dashboard loads do not block on agent/network timeouts
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ Admin status page for server/node state.
|
|||
|
||||
## Current Status
|
||||
|
||||
- Experimental
|
||||
- Alpha
|
||||
- Functional standalone page
|
||||
- Kept separate from the dashboard request path on purpose
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
|
@ -22,11 +22,13 @@ Admin status page for server/node state.
|
|||
|
||||
## Agent Interaction
|
||||
|
||||
- may read status summaries
|
||||
- reads configured remote servers from the active Panel database
|
||||
- performs live agent availability checks when the status page is opened
|
||||
|
||||
## User Workflow
|
||||
|
||||
- not a primary customer workflow
|
||||
- open `Panel/server_status.php` from the dashboard when status information is needed
|
||||
- refresh manually when a new check is required
|
||||
|
||||
## Admin Workflow
|
||||
|
||||
|
|
@ -38,17 +40,43 @@ Admin status page for server/node state.
|
|||
|
||||
## Known Issues
|
||||
|
||||
- alpha-grade module
|
||||
- live remote checks may wait on agent/network timeouts, so the page must not be loaded as part of the normal dashboard request
|
||||
|
||||
## Missing Functionality
|
||||
|
||||
- stable dashboard integration
|
||||
- async dashboard embedding if a future implementation can safely avoid blocking dashboard render
|
||||
|
||||
## Current Architecture
|
||||
|
||||
- Render file: `Panel/server_status.php`
|
||||
- Dashboard entry point: `Panel/modules/dashboard/dashboard.php`
|
||||
- Data source: `$db->getRemoteServers()` plus `OGPRemoteLibrary::status_chk()`
|
||||
- Dashboard architecture decision: keep status checks on the separate page so the main dashboard remains responsive even when one or more remote servers are slow or unavailable
|
||||
|
||||
## Display Rules
|
||||
|
||||
- Removed the old `Hostname` column
|
||||
- Keep:
|
||||
- `Server Name`
|
||||
- `Location / IP`
|
||||
- `Status`
|
||||
- `Agent Status`
|
||||
- `Panel-to-server latency`
|
||||
- `Last Checked`
|
||||
- Latency wording must remain truthful:
|
||||
- current label: `Panel-to-server latency`
|
||||
- this is a server-side connectivity check from the Panel host
|
||||
- it is not customer-browser latency and must not be presented as the player's ping
|
||||
|
||||
## Theme Notes
|
||||
|
||||
- The status page should visually match the active dark Panel theme
|
||||
- Use responsive table wrapping for mobile widths instead of forcing a wide desktop table into the viewport
|
||||
|
||||
## Suggested Future Improvements
|
||||
|
||||
- replace with a proper node health/status dashboard
|
||||
- optional browser-side latency testing only if each location has a safe public health endpoint and the implementation can remain honest about what is being measured
|
||||
|
||||
## Recommendation
|
||||
|
||||
- Rewrite / Deprecate
|
||||
|
||||
- Keep / Improve
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue