small fixes
This commit is contained in:
parent
bd3875743e
commit
28533be24d
9 changed files with 310 additions and 524 deletions
|
|
@ -222,14 +222,15 @@ function gsp_update_settings()
|
|||
{
|
||||
global $settings;
|
||||
$repo_root = !empty($settings['gsp_update_repo_root']) ? (string)$settings['gsp_update_repo_root'] : GSP_DEFAULT_REPO_ROOT;
|
||||
$panel_path = !empty($settings['gsp_update_panel_path']) ? (string)$settings['gsp_update_panel_path'] : GSP_DEFAULT_PANEL_PATH;
|
||||
$repo_root = rtrim($repo_root, '/');
|
||||
$panel_path = $repo_root . '/Panel';
|
||||
return [
|
||||
'repo_url' => !empty($settings['gsp_update_repo_url']) ? (string)$settings['gsp_update_repo_url'] : GSP_DEFAULT_REPO_URL,
|
||||
'branch' => !empty($settings['gsp_update_branch']) ? (string)$settings['gsp_update_branch'] : GSP_DEFAULT_BRANCH,
|
||||
'repo_root' => rtrim($repo_root, '/'),
|
||||
'repo_root' => $repo_root,
|
||||
'panel_path' => rtrim($panel_path, '/'),
|
||||
'panel_source_path' => !empty($settings['gsp_update_panel_source_path']) ? trim((string)$settings['gsp_update_panel_source_path'], '/') : GSP_DEFAULT_PANEL_SOURCE,
|
||||
'git_path' => !empty($settings['gsp_update_git_path']) ? trim((string)$settings['gsp_update_git_path']) : 'git',
|
||||
'panel_source_path' => GSP_DEFAULT_PANEL_SOURCE,
|
||||
'git_path' => 'git',
|
||||
'backup_path' => !empty($settings['gsp_update_backup_path']) ? rtrim((string)$settings['gsp_update_backup_path'], '/') : GSP_BACKUP_BASE,
|
||||
'backup_retention' => !empty($settings['gsp_update_backup_retention']) ? (string)$settings['gsp_update_backup_retention'] : (string)GSP_DEFAULT_BACKUP_RETENTION,
|
||||
'panel_post_update_command' => !empty($settings['gsp_update_panel_post_update_command']) ? (string)$settings['gsp_update_panel_post_update_command'] : '',
|
||||
|
|
@ -268,15 +269,6 @@ $errors[] = ucfirst(str_replace('_', ' ', $key)) . ' must be a safe absolute pat
|
|||
if (isset($cfg['backup_retention']) && (!preg_match('/^\d+$/', (string)$cfg['backup_retention']) || (int)$cfg['backup_retention'] < 1 || (int)$cfg['backup_retention'] > 200)) {
|
||||
$errors[] = 'Backup retention must be a whole number between 1 and 200.';
|
||||
}
|
||||
foreach (['panel_source_path'] as $key) {
|
||||
$value = isset($cfg[$key]) ? trim((string)$cfg[$key], '/') : '';
|
||||
if ($value === '' || strpos($value, "\0") !== false || strpos($value, '..') !== false || !preg_match('/^[A-Za-z0-9._\/-]+$/', $value)) {
|
||||
$errors[] = ucfirst(str_replace('_', ' ', $key)) . ' is invalid.';
|
||||
}
|
||||
}
|
||||
if (isset($cfg['git_path']) && trim((string)$cfg['git_path']) !== '' && (strpos((string)$cfg['git_path'], "\0") !== false || strpos((string)$cfg['git_path'], "\n") !== false || strpos((string)$cfg['git_path'], "\r") !== false)) {
|
||||
$errors[] = 'Git executable path is invalid.';
|
||||
}
|
||||
foreach (['panel_post_update_command'] as $key) {
|
||||
if (isset($cfg[$key]) && (strpos((string)$cfg[$key], "\0") !== false || strpos((string)$cfg[$key], "\n") !== false || strpos((string)$cfg[$key], "\r") !== false)) {
|
||||
$errors[] = ucfirst(str_replace('_', ' ', $key)) . ' must be a single-line admin command.';
|
||||
|
|
@ -1588,9 +1580,9 @@ $new_cfg = [
|
|||
'repo_url' => isset($_POST['gsp_update_repo_url']) ? trim((string)$_POST['gsp_update_repo_url']) : '',
|
||||
'branch' => isset($_POST['gsp_update_branch']) ? trim((string)$_POST['gsp_update_branch']) : '',
|
||||
'repo_root' => isset($_POST['gsp_update_repo_root']) ? rtrim(trim((string)$_POST['gsp_update_repo_root']), '/') : '',
|
||||
'panel_path' => isset($_POST['gsp_update_panel_path']) ? rtrim(trim((string)$_POST['gsp_update_panel_path']), '/') : '',
|
||||
'panel_source_path' => isset($_POST['gsp_update_panel_source_path']) ? trim((string)$_POST['gsp_update_panel_source_path'], '/') : '',
|
||||
'git_path' => isset($_POST['gsp_update_git_path']) ? trim((string)$_POST['gsp_update_git_path']) : '',
|
||||
'panel_path' => isset($_POST['gsp_update_repo_root']) ? rtrim(trim((string)$_POST['gsp_update_repo_root']), '/') . '/Panel' : GSP_DEFAULT_PANEL_PATH,
|
||||
'panel_source_path' => GSP_DEFAULT_PANEL_SOURCE,
|
||||
'git_path' => 'git',
|
||||
'backup_path' => isset($_POST['gsp_update_backup_path']) ? rtrim(trim((string)$_POST['gsp_update_backup_path']), '/') : '',
|
||||
'backup_retention' => isset($_POST['gsp_update_backup_retention']) ? trim((string)$_POST['gsp_update_backup_retention']) : '',
|
||||
'panel_post_update_command' => isset($_POST['gsp_update_panel_post_update_command']) ? trim((string)$_POST['gsp_update_panel_post_update_command']) : '',
|
||||
|
|
@ -1606,7 +1598,7 @@ $db->setSettings([
|
|||
'gsp_update_repo_root' => $new_cfg['repo_root'],
|
||||
'gsp_update_panel_path' => $new_cfg['panel_path'],
|
||||
'gsp_update_panel_source_path' => $new_cfg['panel_source_path'],
|
||||
'gsp_update_git_path' => $new_cfg['git_path'],
|
||||
'gsp_update_git_path' => 'git',
|
||||
'gsp_update_backup_path' => $new_cfg['backup_path'],
|
||||
'gsp_update_backup_retention' => $new_cfg['backup_retention'],
|
||||
'gsp_update_panel_post_update_command' => $new_cfg['panel_post_update_command'],
|
||||
|
|
@ -1617,7 +1609,7 @@ $settings['gsp_update_branch'] = $new_cfg['branch'];
|
|||
$settings['gsp_update_repo_root'] = $new_cfg['repo_root'];
|
||||
$settings['gsp_update_panel_path'] = $new_cfg['panel_path'];
|
||||
$settings['gsp_update_panel_source_path'] = $new_cfg['panel_source_path'];
|
||||
$settings['gsp_update_git_path'] = $new_cfg['git_path'];
|
||||
$settings['gsp_update_git_path'] = 'git';
|
||||
$settings['gsp_update_backup_path'] = $new_cfg['backup_path'];
|
||||
$settings['gsp_update_backup_retention'] = $new_cfg['backup_retention'];
|
||||
$settings['gsp_update_panel_post_update_command'] = $new_cfg['panel_post_update_command'];
|
||||
|
|
@ -1630,9 +1622,9 @@ $update_cfg = [
|
|||
'repo_url' => isset($_POST['gsp_update_repo_url']) ? trim((string)$_POST['gsp_update_repo_url']) : $update_cfg['repo_url'],
|
||||
'branch' => isset($_POST['gsp_update_branch']) ? trim((string)$_POST['gsp_update_branch']) : $update_cfg['branch'],
|
||||
'repo_root' => isset($_POST['gsp_update_repo_root']) ? rtrim(trim((string)$_POST['gsp_update_repo_root']), '/') : $update_cfg['repo_root'],
|
||||
'panel_path' => isset($_POST['gsp_update_panel_path']) ? rtrim(trim((string)$_POST['gsp_update_panel_path']), '/') : $update_cfg['panel_path'],
|
||||
'panel_source_path' => isset($_POST['gsp_update_panel_source_path']) ? trim((string)$_POST['gsp_update_panel_source_path'], '/') : $update_cfg['panel_source_path'],
|
||||
'git_path' => isset($_POST['gsp_update_git_path']) ? trim((string)$_POST['gsp_update_git_path']) : $update_cfg['git_path'],
|
||||
'panel_path' => (isset($_POST['gsp_update_repo_root']) ? rtrim(trim((string)$_POST['gsp_update_repo_root']), '/') : $update_cfg['repo_root']) . '/Panel',
|
||||
'panel_source_path' => GSP_DEFAULT_PANEL_SOURCE,
|
||||
'git_path' => 'git',
|
||||
'backup_path' => isset($_POST['gsp_update_backup_path']) ? rtrim(trim((string)$_POST['gsp_update_backup_path']), '/') : $update_cfg['backup_path'],
|
||||
'backup_retention' => isset($_POST['gsp_update_backup_retention']) ? trim((string)$_POST['gsp_update_backup_retention']) : $update_cfg['backup_retention'],
|
||||
'panel_post_update_command' => isset($_POST['gsp_update_panel_post_update_command']) ? trim((string)$_POST['gsp_update_panel_post_update_command']) : $update_cfg['panel_post_update_command'],
|
||||
|
|
@ -1644,7 +1636,7 @@ $db->setSettings([
|
|||
'gsp_update_repo_root' => $update_cfg['repo_root'],
|
||||
'gsp_update_panel_path' => $update_cfg['panel_path'],
|
||||
'gsp_update_panel_source_path' => $update_cfg['panel_source_path'],
|
||||
'gsp_update_git_path' => $update_cfg['git_path'],
|
||||
'gsp_update_git_path' => 'git',
|
||||
'gsp_update_backup_path' => $update_cfg['backup_path'],
|
||||
'gsp_update_backup_retention' => $update_cfg['backup_retention'],
|
||||
'gsp_update_panel_post_update_command' => $update_cfg['panel_post_update_command'],
|
||||
|
|
@ -1711,19 +1703,40 @@ $preflight_result = gsp_preflight_check($update_cfg);
|
|||
|
||||
$ssl_issue_count = !empty($apache_scan_result['ssl_issues']) ? count($apache_scan_result['ssl_issues']) : 0;
|
||||
echo "<h2>Panel Updates</h2>\n";
|
||||
echo "<table class='administration-table'><tr><td>\n";
|
||||
echo "<style>
|
||||
.gsp-update-shell{display:grid;gap:18px;margin-top:18px}
|
||||
.gsp-update-card{padding:18px 20px;background:rgba(11,20,36,.92);border:1px solid rgba(77,160,255,.18);border-radius:8px;box-shadow:0 14px 32px rgba(0,0,0,.22)}
|
||||
.gsp-update-card h3{margin-top:0}
|
||||
.gsp-update-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 18px}
|
||||
.gsp-update-grid dt{font-weight:700;color:#e6f0ff}
|
||||
.gsp-update-grid dd{margin:0;color:#d2def4;word-break:break-word}
|
||||
.gsp-update-copy{margin:0 0 12px;color:#9fb5d8}
|
||||
.gsp-update-form-grid{display:grid;grid-template-columns:minmax(180px,240px) minmax(0,1fr);gap:12px 16px;align-items:start}
|
||||
.gsp-update-form-grid label{font-weight:700;color:#e6f0ff}
|
||||
.gsp-update-form-grid input[type=text],.gsp-update-form-grid input[type=number]{width:100%;max-width:100%;box-sizing:border-box}
|
||||
.gsp-update-actions,.gsp-update-inline-actions{display:flex;flex-wrap:wrap;gap:10px}
|
||||
.gsp-update-note{display:block;margin-top:6px;color:#9fb5d8}
|
||||
.gsp-update-path{word-break:break-all}
|
||||
.gsp-update-details{margin-top:12px}
|
||||
.gsp-update-details summary{cursor:pointer}
|
||||
.gsp-update-select{max-width:100%;width:100%}
|
||||
@media (max-width: 720px){.gsp-update-grid,.gsp-update-form-grid{grid-template-columns:1fr}.gsp-update-shell{gap:14px}}
|
||||
</style>\n";
|
||||
echo "<div class='gsp-update-shell'>\n";
|
||||
echo "<section class='gsp-update-card'>\n";
|
||||
echo "<h3>Current Installation</h3>\n";
|
||||
echo "<table class='center'>\n";
|
||||
echo "<tr><td><strong>Installed Version:</strong></td><td>" . htmlspecialchars($current_version) . "</td></tr>\n";
|
||||
echo "<tr><td><strong>Current Branch:</strong></td><td>" . htmlspecialchars($current_branch) . "</td></tr>\n";
|
||||
echo "<p class='gsp-update-copy'>Managed backups are stored update snapshots. Rollback backups are full snapshots currently available for restoration.</p>\n";
|
||||
echo "<dl class='gsp-update-grid'>\n";
|
||||
echo "<dt>Installed Version</dt><dd>" . htmlspecialchars($current_version) . "</dd>\n";
|
||||
echo "<dt>Current Branch</dt><dd>" . htmlspecialchars($current_branch) . "</dd>\n";
|
||||
if ($git_commit) {
|
||||
echo "<tr><td><strong>Git Commit:</strong></td><td>" . htmlspecialchars(substr($git_commit, 0, 12)) . "</td></tr>\n";
|
||||
echo "<dt>Git Commit</dt><dd>" . htmlspecialchars(substr($git_commit, 0, 12)) . "</dd>\n";
|
||||
}
|
||||
echo "<tr><td><strong>Update Trace Log:</strong></td><td><code>" . htmlspecialchars(GSP_UPDATE_LOG) . "</code></td></tr>\n";
|
||||
echo "<tr><td><strong>Backup Path:</strong></td><td><code>" . htmlspecialchars($backup_base) . "</code></td></tr>\n";
|
||||
echo "<tr><td><strong>Managed Backups Stored:</strong></td><td>" . intval(count($managed_backups)) . " (retention: " . intval($backup_retention) . ")</td></tr>\n";
|
||||
echo "<tr><td><strong>Rollback Backups Available:</strong></td><td>" . intval(count($backups)) . "</td></tr>\n";
|
||||
echo "</table>\n";
|
||||
echo "<dt>Update Trace Log</dt><dd class='gsp-update-path'><code>" . htmlspecialchars(GSP_UPDATE_LOG) . "</code></dd>\n";
|
||||
echo "<dt>Backup Path</dt><dd class='gsp-update-path'><code>" . htmlspecialchars($backup_base) . "</code></dd>\n";
|
||||
echo "<dt>Managed Backups Stored</dt><dd>" . intval(count($managed_backups)) . " (retention: " . intval($backup_retention) . ")</dd>\n";
|
||||
echo "<dt>Rollback Backups Available</dt><dd>" . intval(count($backups)) . "</dd>\n";
|
||||
echo "</dl>\n";
|
||||
if ($ssl_issue_count > 0) {
|
||||
echo "<p style='color:#8a6d3b;'><strong>SSL warning:</strong> " . intval($ssl_issue_count) . " Apache SSL certificate issue(s) detected. Updates are not blocked by this. See Advanced Diagnostics for details.</p>\n";
|
||||
}
|
||||
|
|
@ -1733,41 +1746,51 @@ echo "<p style='color:#a94442;'><strong>Update preflight errors:</strong><br>" .
|
|||
if (!empty($preflight_result['warnings'])) {
|
||||
echo "<p style='color:#8a6d3b;'><strong>Update preflight warnings:</strong><br>" . implode('<br>', array_map('htmlspecialchars', $preflight_result['warnings'])) . "</p>\n";
|
||||
}
|
||||
echo "</section>\n";
|
||||
|
||||
echo "<h3>Repository Settings</h3>\n";
|
||||
echo "<section class='gsp-update-card'>\n";
|
||||
echo "<h3>Update Settings</h3>\n";
|
||||
echo "<form method='POST'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
|
||||
echo "<table class='center'>\n";
|
||||
echo "<tr><td><strong>Repository URL / Path</strong></td><td><input type='text' name='gsp_update_repo_url' value='" . htmlspecialchars($update_cfg['repo_url'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
|
||||
echo "<tr><td><strong>Branch / Channel</strong></td><td><input type='text' name='gsp_update_branch' value='" . htmlspecialchars($update_cfg['branch'], ENT_QUOTES, 'UTF-8') . "' size='40'></td></tr>\n";
|
||||
echo "<tr><td><strong>Repository Root</strong></td><td><input type='text' name='gsp_update_repo_root' value='" . htmlspecialchars($update_cfg['repo_root'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
|
||||
echo "<tr><td><strong>Panel Path</strong></td><td><input type='text' name='gsp_update_panel_path' value='" . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
|
||||
echo "<tr><td><strong>Panel Source Folder</strong></td><td><input type='text' name='gsp_update_panel_source_path' value='" . htmlspecialchars($update_cfg['panel_source_path'], ENT_QUOTES, 'UTF-8') . "' size='45'></td></tr>\n";
|
||||
echo "<tr><td><strong>Git Executable</strong></td><td><input type='text' name='gsp_update_git_path' value='" . htmlspecialchars($update_cfg['git_path'], ENT_QUOTES, 'UTF-8') . "' size='45'> <small>Usually <code>git</code>.</small></td></tr>\n";
|
||||
echo "<tr><td><strong>Backup Path</strong></td><td><input type='text' name='gsp_update_backup_path' value='" . htmlspecialchars($update_cfg['backup_path'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
|
||||
echo "<tr><td><strong>Backup Retention</strong></td><td><input type='number' min='1' max='200' name='gsp_update_backup_retention' value='" . htmlspecialchars($update_cfg['backup_retention'], ENT_QUOTES, 'UTF-8') . "' style='width:90px;'> <small>Keep newest backups only.</small></td></tr>\n";
|
||||
echo "<tr><td><strong>Backup Before Update</strong></td><td><label><input type='checkbox' name='gsp_update_backup_before' value='1' " . (!empty($update_cfg['backup_before_update']) ? "checked" : "") . "> create backup before updating</label></td></tr>\n";
|
||||
echo "<tr><td><strong>Panel Post-update Command</strong></td><td><input type='text' name='gsp_update_panel_post_update_command' value='" . htmlspecialchars($update_cfg['panel_post_update_command'], ENT_QUOTES, 'UTF-8') . "' size='85'> <small>Admin-only dangerous command.</small></td></tr>\n";
|
||||
echo "</table>\n";
|
||||
echo "<p>\n";
|
||||
echo "<div class='gsp-update-form-grid'>\n";
|
||||
echo "<label for='gsp_update_repo_url'>Repository URL / Path</label><div><input id='gsp_update_repo_url' type='text' name='gsp_update_repo_url' value='" . htmlspecialchars($update_cfg['repo_url'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>Git URL or safe absolute local repository path.</small></div>\n";
|
||||
echo "<label for='gsp_update_branch'>Branch / Channel</label><div><input id='gsp_update_branch' type='text' name='gsp_update_branch' value='" . htmlspecialchars($update_cfg['branch'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>The updater checks out this branch before copying the Panel folder.</small></div>\n";
|
||||
echo "<label for='gsp_update_repo_root'>Repository Root</label><div><input id='gsp_update_repo_root' type='text' name='gsp_update_repo_root' value='" . htmlspecialchars($update_cfg['repo_root'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>Panel Path is derived automatically as <code>" . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . "</code>.</small></div>\n";
|
||||
echo "<label for='gsp_update_backup_path'>Backup Path</label><div><input id='gsp_update_backup_path' type='text' name='gsp_update_backup_path' value='" . htmlspecialchars($update_cfg['backup_path'], ENT_QUOTES, 'UTF-8') . "'></div>\n";
|
||||
echo "<label for='gsp_update_backup_retention'>Backup Retention</label><div><input id='gsp_update_backup_retention' type='number' min='1' max='200' name='gsp_update_backup_retention' value='" . htmlspecialchars($update_cfg['backup_retention'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>Keep only the newest managed backups.</small></div>\n";
|
||||
echo "<label>Backup Before Update</label><div><label><input type='checkbox' name='gsp_update_backup_before' value='1' " . (!empty($update_cfg['backup_before_update']) ? "checked" : "") . "> create a full backup before updating</label></div>\n";
|
||||
echo "</div>\n";
|
||||
echo "<details class='gsp-update-details'>\n";
|
||||
echo "<summary><strong>Advanced Settings</strong></summary>\n";
|
||||
echo "<div class='gsp-update-form-grid' style='margin-top:12px;'>\n";
|
||||
echo "<label>Panel Source Folder</label><div><code>" . htmlspecialchars($update_cfg['panel_source_path'], ENT_QUOTES, 'UTF-8') . "</code><small class='gsp-update-note'>The updater currently copies the fixed <code>Panel</code> folder from the checked-out repository.</small></div>\n";
|
||||
echo "<label>Panel Path</label><div><code class='gsp-update-path'>" . htmlspecialchars($update_cfg['panel_path'], ENT_QUOTES, 'UTF-8') . "</code><small class='gsp-update-note'>Derived from Repository Root plus <code>/Panel</code>.</small></div>\n";
|
||||
echo "<label for='gsp_update_panel_post_update_command'>Post-update Command</label><div><input id='gsp_update_panel_post_update_command' type='text' name='gsp_update_panel_post_update_command' value='" . htmlspecialchars($update_cfg['panel_post_update_command'], ENT_QUOTES, 'UTF-8') . "'><small class='gsp-update-note'>Optional command to run after a successful update. Leave blank unless required.</small></div>\n";
|
||||
echo "</div>\n";
|
||||
echo "</details>\n";
|
||||
echo "<p class='gsp-update-actions'>\n";
|
||||
echo "<button type='submit' name='gsp_update_action' value='save_settings'>Save Settings</button> ";
|
||||
echo "<button type='submit' name='gsp_update_action' value='update_configured' onclick='return confirm(\"Update Panel from the configured repository and branch?\");'>Update Panel</button>\n";
|
||||
echo "</p>\n";
|
||||
echo "</form>\n";
|
||||
echo "</section>\n";
|
||||
|
||||
echo "<section class='gsp-update-card'>\n";
|
||||
echo "<h3>Backup</h3>\n";
|
||||
echo "<form method='POST'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_action' value='backup_only'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
|
||||
echo "<button type='submit'>Create Backup</button>\n";
|
||||
echo "</form>\n";
|
||||
echo "</section>\n";
|
||||
|
||||
if (!empty($backups)) {
|
||||
echo "<section class='gsp-update-card'>\n";
|
||||
echo "<h3>Rollback</h3>\n";
|
||||
echo "<form method='POST'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_action' value='revert'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
|
||||
echo "<select name='gsp_revert_backup'>\n";
|
||||
echo "<select class='gsp-update-select' name='gsp_revert_backup'>\n";
|
||||
foreach ($backups as $bk) {
|
||||
$label = htmlspecialchars($bk['ts']);
|
||||
if (!empty($bk['meta']['update_target_type']) && !empty($bk['meta']['update_target_version'])) {
|
||||
|
|
@ -1776,26 +1799,29 @@ $label .= ' (before ' . htmlspecialchars($bk['meta']['update_target_type']) . ':
|
|||
echo "<option value='" . htmlspecialchars($bk['ts']) . "'>{$label}</option>\n";
|
||||
}
|
||||
echo "</select> ";
|
||||
echo "<span class='gsp-update-note'>Rollback restores only full timestamped snapshots.</span>";
|
||||
echo "<label><input type='checkbox' name='gsp_restore_apache' value='1'> restore Apache configs if backup contains them</label> ";
|
||||
echo "<button type='submit' onclick='return confirm(\"Restore Panel, version.json, and database from selected backup?\");'>Rollback</button>\n";
|
||||
echo "</form>\n";
|
||||
echo "</section>\n";
|
||||
}
|
||||
|
||||
echo "<details style='margin-top:18px;'>\n";
|
||||
echo "<section class='gsp-update-card'>\n";
|
||||
echo "<details>\n";
|
||||
echo "<summary><strong>Advanced Diagnostics</strong></summary>\n";
|
||||
echo "<h4>Preflight</h4>\n";
|
||||
echo "<table class='center'>\n";
|
||||
echo "<tr><td><strong>Status:</strong></td><td>" . ($preflight_result['success'] ? 'PASS' : 'FAIL') . "</td></tr>\n";
|
||||
echo "<tr><td><strong>Pending Patches:</strong></td><td>" . intval(count($patch_overview['pending'])) . "</td></tr>\n";
|
||||
echo "<tr><td><strong>Patch Directory:</strong></td><td><code>" . htmlspecialchars(GSP_PATCH_DIR) . "</code></td></tr>\n";
|
||||
echo "</table>\n";
|
||||
echo "<dl class='gsp-update-grid'>\n";
|
||||
echo "<dt>Status</dt><dd>" . ($preflight_result['success'] ? 'PASS' : 'FAIL') . "</dd>\n";
|
||||
echo "<dt>Pending Patches</dt><dd>" . intval(count($patch_overview['pending'])) . "</dd>\n";
|
||||
echo "<dt>Patch Directory</dt><dd class='gsp-update-path'><code>" . htmlspecialchars(GSP_PATCH_DIR) . "</code></dd>\n";
|
||||
echo "</dl>\n";
|
||||
echo "<h4>Apache</h4>\n";
|
||||
echo "<table class='center'>\n";
|
||||
echo "<tr><td><strong>Config Directory:</strong></td><td><code>" . htmlspecialchars($apache_scan_result['base']) . "</code></td></tr>\n";
|
||||
echo "<tr><td><strong>Configs Found:</strong></td><td>" . intval(count($apache_scan_result['files'])) . "</td></tr>\n";
|
||||
echo "<tr><td><strong>Stale Path Hits:</strong></td><td>" . intval(count($apache_scan_result['stale_issues'])) . "</td></tr>\n";
|
||||
echo "<tr><td><strong>SSL Certificate Issues:</strong></td><td>" . intval($ssl_issue_count) . "</td></tr>\n";
|
||||
echo "</table>\n";
|
||||
echo "<dl class='gsp-update-grid'>\n";
|
||||
echo "<dt>Config Directory</dt><dd class='gsp-update-path'><code>" . htmlspecialchars($apache_scan_result['base']) . "</code></dd>\n";
|
||||
echo "<dt>Configs Found</dt><dd>" . intval(count($apache_scan_result['files'])) . "</dd>\n";
|
||||
echo "<dt>Stale Path Hits</dt><dd>" . intval(count($apache_scan_result['stale_issues'])) . "</dd>\n";
|
||||
echo "<dt>SSL Certificate Issues</dt><dd>" . intval($ssl_issue_count) . "</dd>\n";
|
||||
echo "</dl>\n";
|
||||
if (!empty($apache_scan_result['stale_issues'])) {
|
||||
echo "<p style='color:#a94442;'><strong>Apache stale path issues:</strong><br>" . implode('<br>', array_map('htmlspecialchars', array_unique($apache_scan_result['stale_issues']))) . "</p>\n";
|
||||
}
|
||||
|
|
@ -1822,7 +1848,8 @@ echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($c
|
|||
echo "<button type='submit' onclick='return confirm(\"Backup Apache configs, run configtest, and apply path fixes?\");'>Fix Apache Paths</button>\n";
|
||||
echo "</form>\n";
|
||||
echo "</details>\n";
|
||||
echo "</td></tr></table>\n";
|
||||
echo "</section>\n";
|
||||
echo "</div>\n";
|
||||
|
||||
if ($auto_restart_payload) {
|
||||
gsp_render_restart_form($auto_restart_payload['action'], $csrf_token, $auto_restart_payload['nonce'], isset($auto_restart_payload['version']) ? $auto_restart_payload['version'] : '');
|
||||
|
|
|
|||
|
|
@ -6,79 +6,53 @@
|
|||
border-radius:0px;
|
||||
-moz-border-radius:0px;
|
||||
}
|
||||
#column2 #ref.online_servers div{
|
||||
float:left;
|
||||
text-align:left;
|
||||
width:35%;
|
||||
height:15px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
-moz-binding: url('assets/xml/ellipsis.xml#ellipsis');
|
||||
}
|
||||
#column2 #ref.online_servers div#gamelink{
|
||||
width:36%;
|
||||
float:right;
|
||||
text-align:right;
|
||||
}
|
||||
#column2 #ref.online_servers div.name{
|
||||
width:29%;
|
||||
height:18px;
|
||||
white-space:nowrap;
|
||||
}
|
||||
#column2 #noref.online_servers div#gamelink{
|
||||
float:right;
|
||||
text-align:right;
|
||||
width:40%;
|
||||
height:18px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#column2 #noref.online_servers div.name{
|
||||
float:left;
|
||||
text-align:left;
|
||||
width:57%;
|
||||
height:18px;
|
||||
overflow: hidden;
|
||||
.dashboard-welcome {
|
||||
margin: 0 0 18px;
|
||||
}
|
||||
|
||||
.dragbox.bloc.rounded {
|
||||
.dashboard-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.dashboard-action-card {
|
||||
padding: 18px 20px;
|
||||
background: linear-gradient(180deg, rgba(10, 18, 32, 0.92) 0%, rgba(14, 25, 45, 0.94) 100%);
|
||||
border: 1px solid rgba(77, 160, 255, 0.18);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 14px 32px rgba(0, 0, 0, 0.22);
|
||||
color: #d2def4;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.dragbox.bloc.rounded > h4,
|
||||
.dashboard-promo-card > h4,
|
||||
.dashboard-secondary-card > h4 {
|
||||
margin: 0 0 12px;
|
||||
.dashboard-card-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.dashboard-card-header > h3 {
|
||||
margin: 0;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(77, 160, 255, 0.18);
|
||||
color: #e6f0ff;
|
||||
letter-spacing: 0.04em;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.dragbox-content {
|
||||
padding: 4px 2px 2px;
|
||||
color: #d2def4;
|
||||
}
|
||||
|
||||
.dashboard-widget-copy {
|
||||
position: relative;
|
||||
padding-right: 56px;
|
||||
}
|
||||
|
||||
.dashboard-widget-copy p {
|
||||
.dashboard-action-card p {
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
.dashboard-widget-icon {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
opacity: 0.9;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.dashboard-link-group {
|
||||
|
|
@ -132,34 +106,13 @@
|
|||
box-shadow: 0 0 0 3px rgba(90, 199, 247, 0.12);
|
||||
}
|
||||
|
||||
.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: 991px) {
|
||||
.dashboard-card-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 430px) {
|
||||
.dashboard-widget-copy {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.dashboard-cta-button,
|
||||
.dashboard-support-link {
|
||||
width: 100%;
|
||||
|
|
@ -167,8 +120,7 @@
|
|||
}
|
||||
|
||||
.dashboard-widget-icon {
|
||||
position: static;
|
||||
display: block;
|
||||
margin: 0 0 12px auto;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
<script type="text/javascript" src="js/jquery/plugins/jquery.json-2.3.min.js"></script>
|
||||
<script type="text/javascript" src="js/modules/dashboard.js"></script>
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
|
|
@ -24,248 +22,92 @@
|
|||
*
|
||||
*/
|
||||
|
||||
require_once('includes/lib_remote.php');
|
||||
|
||||
|
||||
function exec_ogp_module()
|
||||
{
|
||||
global $db, $settings, $loggedInUserInfo;
|
||||
global $settings;
|
||||
|
||||
$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');
|
||||
$theme = isset($settings['theme']) ? $settings['theme'] : 'SimpleBootstrap';
|
||||
$themeBase = 'themes/' . htmlspecialchars($theme, ENT_QUOTES, 'UTF-8') . '/images/icons/';
|
||||
$cards = array(
|
||||
array(
|
||||
'title' => 'Account Overview',
|
||||
'copy' => 'Review your assigned game servers and open the Game Monitor.',
|
||||
'button_label' => 'View My Servers',
|
||||
'button_class' => 'dashboard-cta-button',
|
||||
'url' => 'home.php?m=gamemanager&p=game_monitor',
|
||||
'icon' => $themeBase . 'game_monitor.png',
|
||||
),
|
||||
array(
|
||||
'title' => 'Custom Server Code',
|
||||
'copy' => '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.',
|
||||
'secondary' => 'Tell us what you want to build or improve, and we will review the request with you.',
|
||||
'button_label' => 'Request Custom Development',
|
||||
'button_class' => 'dashboard-cta-button',
|
||||
'url' => $projectRequestUrl,
|
||||
'icon' => $themeBase . 'folder.png',
|
||||
'external' => true,
|
||||
),
|
||||
array(
|
||||
'title' => 'Support',
|
||||
'copy' => 'Need help with an existing service? Create a support ticket or join our Discord support server.',
|
||||
'secondary' => 'Support covers service problems, broken functionality, routine troubleshooting, and help using the features already included with your server.',
|
||||
'button_label' => 'Create Support Ticket',
|
||||
'button_class' => 'dashboard-support-link',
|
||||
'url' => 'home.php?m=tickets&p=submitticket',
|
||||
'icon' => $themeBase . 'support.png',
|
||||
'secondary_button_label' => 'Join Discord',
|
||||
'secondary_button_class' => 'dashboard-support-link dashboard-support-link-secondary',
|
||||
'secondary_url' => $discordInviteUrl,
|
||||
'secondary_external' => true,
|
||||
),
|
||||
array(
|
||||
'title' => 'Server Status',
|
||||
'copy' => 'View the current availability of all configured game-server locations.',
|
||||
'secondary' => 'Status checks run only when you open the status page, so normal dashboard loading stays fast.',
|
||||
'button_label' => 'View Server Status',
|
||||
'button_class' => 'dashboard-cta-button',
|
||||
'url' => $serverStatusUrl,
|
||||
'icon' => $themeBase . 'game_monitor.png',
|
||||
'external' => true,
|
||||
),
|
||||
);
|
||||
|
||||
$isAdmin = $db->isAdmin($_SESSION['user_id']);
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
$page_user = (isset($_GET['page']) && (int)$_GET['page'] > 0) ? (int)$_GET['page'] : 1; // thanks for Adjokip
|
||||
$limit_user = isset($_GET['limit']) ? $_GET['limit'] : 10;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(hasValue($loggedInUserInfo) && is_array($loggedInUserInfo) && $loggedInUserInfo["users_page_limit"] && !(isset($_GET['limit']) and !empty($_GET['limit']))){
|
||||
$limit_user = $loggedInUserInfo["users_page_limit"];
|
||||
if (isset($settings['welcome_title']) && $settings['welcome_title'] == "1" && isset($settings['welcome_title_message']) && !empty($settings['welcome_title_message'])) {
|
||||
echo "<div class='dashboard-welcome'>" . $settings['welcome_title_message'] . "</div>";
|
||||
}
|
||||
|
||||
|
||||
if( isset($settings['welcome_title']) && $settings['welcome_title'] == "1" )
|
||||
{
|
||||
if( isset($settings['welcome_title_message']) && !empty($settings['welcome_title_message'] ))
|
||||
{
|
||||
echo "<div>" . $settings['welcome_title_message'] . "</div>";
|
||||
echo "<div class='dashboard-card-grid'>";
|
||||
foreach ($cards as $card) {
|
||||
echo "<section class='dashboard-action-card'>";
|
||||
echo "<div class='dashboard-card-header'>";
|
||||
echo "<h3>" . htmlspecialchars($card['title'], ENT_QUOTES, 'UTF-8') . "</h3>";
|
||||
if (!empty($card['icon'])) {
|
||||
echo "<img src='" . $card['icon'] . "' class='dashboard-widget-icon' alt=''>";
|
||||
}
|
||||
echo "</div>";
|
||||
echo "<p>" . htmlspecialchars($card['copy'], ENT_QUOTES, 'UTF-8') . "</p>";
|
||||
if (!empty($card['secondary'])) {
|
||||
echo "<p class='dashboard-card-note'>" . htmlspecialchars($card['secondary'], ENT_QUOTES, 'UTF-8') . "</p>";
|
||||
}
|
||||
|
||||
|
||||
require_once("includes/refreshed.php");
|
||||
$refresh = new refreshed();
|
||||
$OnlineServers = "<p>Recent updates and changes</p>";
|
||||
?>
|
||||
<div style="margin-top:20px;">
|
||||
<?php
|
||||
//$title[$id] = "The Title";
|
||||
//$content[$id] = "Content of the Widget";
|
||||
$title = array();
|
||||
$content = array();
|
||||
$href = array();
|
||||
// Account Overview
|
||||
$title[1] = "Account Overview";
|
||||
$content[1] = 'Review your assigned servers and jump straight into the Game Monitor.';
|
||||
$href[1] = 'home.php?m=gamemanager&p=game_monitor';
|
||||
|
||||
// Recent News
|
||||
//$xml=simplexml_load_file("modules/news/data/listings.xml");
|
||||
//$lastnews = count((array)$xml)-1;
|
||||
//$title[2] = "Recent News";
|
||||
//$content[2] = $xml->listing[$lastnews]->title;
|
||||
//$href[2] = 'home.php?m=news&p=news';
|
||||
|
||||
// Notifications
|
||||
$title[3] = "Notifications"; // get_lang('orders');
|
||||
$content[3] = 'View all your notifications. ';
|
||||
$href[3] = 'home.php?m=circular&p=show_circular&list=true';
|
||||
|
||||
// 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] = '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;
|
||||
|
||||
|
||||
|
||||
|
||||
$widgets = $db->resultQuery("SELECT * FROM OGP_DB_PREFIXwidgets_users WHERE user_id='".$_SESSION['user_id']."' ORDER BY sort_no");
|
||||
|
||||
if(!$widgets)
|
||||
{
|
||||
if($db->createUserWidgets($_SESSION['user_id']))
|
||||
$widgets = $db->resultQuery("SELECT * FROM OGP_DB_PREFIXwidgets_users WHERE user_id='".$_SESSION['user_id']."' ORDER BY sort_no");
|
||||
echo "<div class='dashboard-link-group'>";
|
||||
echo "<a class='" . htmlspecialchars($card['button_class'], ENT_QUOTES, 'UTF-8') . "' href='" . htmlspecialchars($card['url'], ENT_QUOTES, 'UTF-8') . "'";
|
||||
if (!empty($card['external'])) {
|
||||
echo " target='_blank' rel='noopener noreferrer'";
|
||||
}
|
||||
|
||||
if($widgets)
|
||||
{
|
||||
$colhtml[1] = '<div class="column one_fourth" id="column1" >';
|
||||
$colhtml[2] = '<div class="column one_two" id="column2" >';
|
||||
$colhtml[3] = '<div class="column one_fourth" id="column3" >';
|
||||
foreach ((array)$widgets as $widget)
|
||||
{
|
||||
if(array_key_exists($widget["widget_id"], (array)$title)){
|
||||
if( (!isset($settings['old_dashboard_behavior']) or $settings['old_dashboard_behavior'] == 0) AND $widget['widget_id'] == "3" )
|
||||
continue;
|
||||
$colhtml[$widget['column_id']] .= '<div class="dragbox bloc rounded" id="item'.$widget['widget_id'].'">'.
|
||||
'<h4><span class="configure"></span>';
|
||||
if(!is_null($title[$widget['widget_id']]))
|
||||
$colhtml[$widget['column_id']] .= $title[$widget['widget_id']];
|
||||
|
||||
$colhtml[$widget['column_id']] .= '</h4><div class="dragbox-content" ';
|
||||
if(!is_null($href[$widget['widget_id']]))
|
||||
{
|
||||
$colhtml[$widget['column_id']] .= "onclick=\"location.href='". $href[$widget['widget_id']] . "'\" style=\"cursor:pointer;";
|
||||
if($widget['collapsed']==1)
|
||||
$colhtml[$widget['column_id']] .= 'display:none;';
|
||||
$colhtml[$widget['column_id']] .= '"';
|
||||
echo ">" . htmlspecialchars($card['button_label'], ENT_QUOTES, 'UTF-8') . "</a>";
|
||||
if (!empty($card['secondary_button_label']) && !empty($card['secondary_url'])) {
|
||||
echo "<a class='" . htmlspecialchars($card['secondary_button_class'], ENT_QUOTES, 'UTF-8') . "' href='" . htmlspecialchars($card['secondary_url'], ENT_QUOTES, 'UTF-8') . "'";
|
||||
if (!empty($card['secondary_external'])) {
|
||||
echo " target='_blank' rel='noopener noreferrer'";
|
||||
}
|
||||
elseif($widget['collapsed']==1)
|
||||
$colhtml[$widget['column_id']] .= 'style="display:none;"';
|
||||
|
||||
$colhtml[$widget['column_id']] .= '>';
|
||||
|
||||
if(!is_null($content[$widget['widget_id']]))
|
||||
$colhtml[$widget['column_id']] .= $content[$widget['widget_id']];
|
||||
|
||||
$colhtml[$widget['column_id']] .= '</div></div>';
|
||||
echo ">" . htmlspecialchars($card['secondary_button_label'], ENT_QUOTES, 'UTF-8') . "</a>";
|
||||
}
|
||||
echo "</div>";
|
||||
echo "</section>";
|
||||
}
|
||||
foreach ((array)$colhtml as $html)
|
||||
echo $html.'</div>';
|
||||
}
|
||||
|
||||
// Server Status Link - Available to all users
|
||||
echo "<div class='dashboard-status-panel'>
|
||||
<div class='bloc rounded'>
|
||||
<h4>Server Status</h4>
|
||||
<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>";
|
||||
|
||||
if( $isAdmin AND $db->isModuleInstalled('status') )
|
||||
{
|
||||
echo "<h0>".get_lang('server_status')."</h0><br>";
|
||||
$servers = $db->getRemoteServers();
|
||||
|
||||
echo "<div id='column4' style='float:left;width:40%;' >
|
||||
<div class='bloc rounded' >
|
||||
<h4>".get_lang('select_remote_server')."</h4>
|
||||
<div>
|
||||
<br>
|
||||
<center>
|
||||
<form action='' method='GET'>
|
||||
<input type='hidden' name='m' value='".$_GET['m']."'/>
|
||||
<input type='hidden' name='p' value='".$_GET['p']."'/>
|
||||
<select name='remote_server_id' onchange=".'"this.form.submit()"'.">\n";
|
||||
|
||||
$agents_ips = array();
|
||||
foreach ((array)$servers as $server_row)
|
||||
{
|
||||
$agents_ips[$server_row['remote_server_id']] = gethostbyname($server_row['agent_ip']);
|
||||
if( !empty( $server_row['remote_server_id'] ) and !isset( $_GET['remote_server_id'] ) OR !empty( $server_row['remote_server_id'] ) and empty( $_GET['remote_server_id'] ) )
|
||||
{
|
||||
$_GET['remote_server_id'] = $server_row['remote_server_id'];
|
||||
}
|
||||
|
||||
if( isset($_GET['remote_server_id']) AND $_GET['remote_server_id'] == $server_row['remote_server_id'] )
|
||||
{
|
||||
$remote = new OGPRemoteLibrary( $server_row['agent_ip'], $server_row['agent_port'],
|
||||
$server_row['encryption_key'], $server_row['timeout'] );
|
||||
$host_stat = $remote->status_chk();
|
||||
if( $host_stat === 1 )
|
||||
{
|
||||
$checked = "selected='selected'";
|
||||
}
|
||||
else
|
||||
{
|
||||
$checked = '';
|
||||
$_GET['remote_server_id'] = 'webhost';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$checked = '';
|
||||
}
|
||||
echo "<option value='".$server_row['remote_server_id']."' $checked >".$server_row['remote_server_name']."</option>\n";
|
||||
}
|
||||
|
||||
if ( function_exists('exec') )
|
||||
{
|
||||
$host_ip = isset($_SERVER['LOCAL_ADDR']) ? $_SERVER['LOCAL_ADDR'] : $_SERVER['SERVER_ADDR'];
|
||||
$remote_server_id = array_search($host_ip,$agents_ips);
|
||||
$show_webhost = true;
|
||||
if($remote_server_id)
|
||||
{
|
||||
$remote_server = $db->getRemoteServer($remote_server_id);
|
||||
$remote = new OGPRemoteLibrary( $remote_server['agent_ip'], $remote_server['agent_port'],
|
||||
$remote_server['encryption_key'], $remote_server['timeout'] );
|
||||
$host_stat = $remote->status_chk();
|
||||
if( $host_stat === 1 )
|
||||
$show_webhost = false;
|
||||
}
|
||||
if($show_webhost)
|
||||
{
|
||||
$checked = ( isset($_GET['remote_server_id']) AND $_GET['remote_server_id'] == 'webhost' ) ? "selected='selected'" : "";
|
||||
echo "<option value='webhost' $checked >Webhost Status</option>";
|
||||
}
|
||||
}
|
||||
|
||||
echo " </select>
|
||||
</form>
|
||||
</center>
|
||||
<br><br>
|
||||
</div>
|
||||
</div>
|
||||
</div>\n";
|
||||
|
||||
if( isset($_GET['remote_server_id']) AND ( $_GET['remote_server_id'] == "webhost" or $_GET['remote_server_id'] == "" ) )
|
||||
unset($_GET['remote_server_id']);
|
||||
|
||||
if( isset($_GET['remote_server_id']) )
|
||||
$remote_server = "&remote_server_id=".$_GET['remote_server_id'];
|
||||
else
|
||||
$remote_server = "";
|
||||
|
||||
if( isset($_GET['remote_server_id']) OR function_exists('exec') )
|
||||
echo $refresh->getdiv($refresh->add("home.php?m=status&type=cleared".$remote_server));
|
||||
}
|
||||
|
||||
?>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
<?php echo $refresh->build(isset($settings['query_cache_life']) ? $settings['query_cache_life'] * 2000 : 60000); ?>
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
|
||||
echo "</div>";
|
||||
}
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -53,33 +53,6 @@ $settings = $db->getSettings();
|
|||
@$GLOBALS['panel_language'] = $settings['panel_language'];
|
||||
ogpLang();
|
||||
|
||||
function panel_ping_host($host, $timeout = 3) {
|
||||
if (!function_exists('exec')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$output = array();
|
||||
$result = 0;
|
||||
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
||||
exec("ping -n 1 -w " . ((int)$timeout * 1000) . " " . escapeshellarg($host), $output, $result);
|
||||
} else {
|
||||
exec("ping -c 1 -W " . (int)$timeout . " " . escapeshellarg($host), $output, $result);
|
||||
}
|
||||
|
||||
if ($result !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ((array)$output as $line) {
|
||||
if (preg_match('/time[=<]?\s*([0-9.]+)\s*ms/i', $line, $matches)) {
|
||||
return (float)$matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
function agent_socket_reachable($host, $port, $timeout = 2.0) {
|
||||
$errno = 0;
|
||||
$errstr = '';
|
||||
|
|
@ -203,27 +176,13 @@ $servers = $db->getRemoteServers();
|
|||
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;
|
||||
.status-summary {
|
||||
margin: 0 0 18px;
|
||||
padding: 12px 14px;
|
||||
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);
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.status-table-wrap {
|
||||
|
|
@ -235,7 +194,7 @@ $servers = $db->getRemoteServers();
|
|||
|
||||
table {
|
||||
width: 100%;
|
||||
min-width: 760px;
|
||||
min-width: 540px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
|
|
@ -297,25 +256,6 @@ $servers = $db->getRemoteServers();
|
|||
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;
|
||||
|
|
@ -364,20 +304,7 @@ $servers = $db->getRemoteServers();
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<p class="status-summary"><strong>Checked:</strong> <?php echo htmlspecialchars($checkedAt, ENT_QUOTES, 'UTF-8'); ?></p>
|
||||
|
||||
<?php if (empty($servers)): ?>
|
||||
<p class="status-empty">No remote servers are currently configured.</p>
|
||||
|
|
@ -389,9 +316,6 @@ $servers = $db->getRemoteServers();
|
|||
<th>Server Name</th>
|
||||
<th>Location / IP</th>
|
||||
<th>Status</th>
|
||||
<th>Agent Status</th>
|
||||
<th>Panel-to-server latency</th>
|
||||
<th>Last Checked</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -404,28 +328,15 @@ $servers = $db->getRemoteServers();
|
|||
$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;
|
||||
|
||||
if ($agentStatus === 1) {
|
||||
$overallStatus = status_badge('Online', 'status-online');
|
||||
$agentBadge = status_badge('Available', 'status-online');
|
||||
} elseif ($agentReachable) {
|
||||
$overallStatus = status_badge('Timed out', 'status-warning');
|
||||
} elseif ($agentIp !== '') {
|
||||
$overallStatus = status_badge('Agent unavailable', 'status-offline');
|
||||
} else {
|
||||
$overallStatus = status_badge('Unknown', 'status-warning');
|
||||
$agentBadge = status_badge('Timed out', 'status-warning');
|
||||
} else {
|
||||
$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>
|
||||
|
|
@ -440,15 +351,6 @@ $servers = $db->getRemoteServers();
|
|||
<?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>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,22 @@ The update page uses these settings:
|
|||
- `gsp_update_panel_post_update_command`
|
||||
- `gsp_update_backup_before`
|
||||
|
||||
The current admin UI shows these directly:
|
||||
|
||||
- Repository URL / Path
|
||||
- Branch / Channel
|
||||
- Repository Root
|
||||
- Backup Path
|
||||
- Backup Retention
|
||||
- Backup Before Update
|
||||
|
||||
The current admin UI keeps these derived or advanced:
|
||||
|
||||
- Panel Path is derived from Repository Root plus `/Panel`
|
||||
- Panel Source Folder is fixed to `Panel`
|
||||
- Git Executable is hidden and defaults to `git`
|
||||
- Post-update Command remains under Advanced Settings
|
||||
|
||||
## Defaults
|
||||
|
||||
- Repository URL / Path: `http://forge.runlevelsystems.com/dev/GSP.git`
|
||||
|
|
@ -58,9 +74,15 @@ The backup directory is created recursively if missing.
|
|||
|
||||
Retention is enforced after each successful backup.
|
||||
|
||||
Current count labels:
|
||||
|
||||
- `Managed Backups Stored` means all managed updater snapshots discovered in the backup base
|
||||
- `Rollback Backups Available` means only full timestamped backups that the restore flow can actually restore
|
||||
|
||||
## Rollback Workflow
|
||||
|
||||
- The rollback selector lists managed backups.
|
||||
- The restore action only accepts full timestamped backups from `gsp_get_available_backups()`.
|
||||
- Restoring a backup restores the Panel tree and metadata.
|
||||
- Missing website archives are ignored because the active backup path is Panel-focused.
|
||||
- Apache config restore is optional.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ The active update page is intentionally narrow:
|
|||
|
||||
- shows the installed Panel version
|
||||
- shows the current git branch and commit when available
|
||||
- exposes editable repository settings for the Panel update only
|
||||
- exposes a reduced set of commonly changed repository settings for the Panel update only
|
||||
- can create a Panel backup
|
||||
- can update the Panel from the configured repository and branch
|
||||
- can roll back to an existing Panel backup
|
||||
|
|
@ -43,6 +43,22 @@ The admin page stores these settings in the Panel settings table:
|
|||
- `gsp_update_panel_post_update_command`
|
||||
- `gsp_update_backup_before`
|
||||
|
||||
The active UI now exposes these commonly changed settings directly:
|
||||
|
||||
- Repository URL / Path
|
||||
- Branch / Channel
|
||||
- Repository Root
|
||||
- Backup Path
|
||||
- Backup Retention
|
||||
- Backup Before Update
|
||||
|
||||
The active UI now treats these as derived or advanced:
|
||||
|
||||
- Panel Path is derived as `Repository Root + /Panel`
|
||||
- Panel Source Folder is fixed to `Panel`
|
||||
- Git Executable is hidden and defaults to `git`
|
||||
- Post-update Command remains available under Advanced Settings
|
||||
|
||||
Defaults:
|
||||
|
||||
- Repository URL / Path: `http://forge.runlevelsystems.com/dev/GSP.git`
|
||||
|
|
@ -66,20 +82,25 @@ Retention is enforced after each successful backup.
|
|||
|
||||
If retention is `5`, the newest five managed backups are kept and older managed backups are pruned automatically.
|
||||
|
||||
Current backup counters on the admin page:
|
||||
|
||||
- `Managed Backups Stored` counts all managed updater snapshots returned by `gsp_get_managed_backup_entries()`, including full and component-style entries when present
|
||||
- `Rollback Backups Available` counts only full timestamped backups returned by `gsp_get_available_backups()` that the restore flow can actually use
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `gsp_update_settings()` and `gsp_validate_update_settings()` remain top-level in `Panel/modules/administration/panel_update.php`.
|
||||
- `gsp_checkout_update_source()` remains a top-level compatibility helper for the configured repository checkout.
|
||||
- `gsp_do_configured_git_update()` now applies only the Panel source folder into the configured Panel path.
|
||||
- `gsp_do_configured_git_update()` now applies only the fixed `Panel` source folder into the derived Panel path.
|
||||
- The page no longer depends on website or agent update settings.
|
||||
|
||||
## Update Flow
|
||||
|
||||
1. Save or submit repository settings.
|
||||
2. Validate repository URL, branch, repo root, Panel path, and backup settings.
|
||||
2. Validate repository URL, branch, repo root, and backup settings.
|
||||
3. Create a backup when enabled.
|
||||
4. Clone the configured repository source and branch into a temporary checkout.
|
||||
5. Copy only the configured Panel source folder into the configured Panel path.
|
||||
5. Copy only the fixed `Panel` source folder into the derived Panel path.
|
||||
6. Preserve `Panel/includes/config.inc.php`.
|
||||
7. Run module updates/post-update hooks.
|
||||
8. Write version metadata and `LAST_UPDATE.txt`.
|
||||
|
|
|
|||
|
|
@ -9,17 +9,16 @@ Main landing dashboard with widgets and quick server overview.
|
|||
## Current Status
|
||||
|
||||
- Functional
|
||||
- Uses the existing collapsible widget layout for customer-facing quick actions
|
||||
- Uses a fixed four-card customer dashboard layout
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Panel dashboard widgets
|
||||
- user permissions
|
||||
- shared Panel URL helpers
|
||||
|
||||
## Database Tables
|
||||
|
||||
- `widgets`
|
||||
- `widgets_users`
|
||||
- none required for the active customer dashboard card layout
|
||||
|
||||
## Agent Interaction
|
||||
|
||||
|
|
@ -40,12 +39,12 @@ Main landing dashboard with widgets and quick server overview.
|
|||
|
||||
## Known Issues
|
||||
|
||||
- some default widget IDs are legacy and limit how far the collapsible layout can be expanded without a schema change
|
||||
- legacy widget infrastructure still exists in the module for historical compatibility, but the active customer dashboard no longer depends on it
|
||||
|
||||
## Missing Functionality
|
||||
|
||||
- richer status and alert surfaces
|
||||
- deeper async server-status embedding without adding remote checks to normal dashboard loads
|
||||
- deeper account summaries if a future design needs per-user metrics beyond the Game Monitor link
|
||||
|
||||
## Suggested Future Improvements
|
||||
|
||||
|
|
@ -55,31 +54,41 @@ Main landing dashboard with widgets and quick server overview.
|
|||
|
||||
- Keep / Improve
|
||||
|
||||
## Runlevel Systems Project Request Integration
|
||||
## Current Dashboard Cards
|
||||
|
||||
- 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`
|
||||
- Theme base: active SimpleBootstrap icon set
|
||||
|
||||
Current project request URL:
|
||||
Rendered cards:
|
||||
|
||||
- `https://runlevelsystems.com/start-project.php`
|
||||
- `Account Overview`
|
||||
- button: `View My Servers`
|
||||
- route: `home.php?m=gamemanager&p=game_monitor`
|
||||
- `Custom Server Code`
|
||||
- button: `Request Custom Development`
|
||||
- URL: `https://runlevelsystems.com/start-project.php`
|
||||
- opens in a new tab
|
||||
- `Support`
|
||||
- buttons:
|
||||
- `Create Support Ticket`
|
||||
- `Join Discord`
|
||||
- support route: `home.php?m=tickets&p=submitticket`
|
||||
- Discord URL: `https://discord.gg/qt9Hnkj6cv`
|
||||
- `Server Status`
|
||||
- button: `View Server Status`
|
||||
- route: `server_status.php`
|
||||
- opens separately so dashboard rendering does not wait on remote checks
|
||||
|
||||
Dashboard behavior:
|
||||
Support versus custom development:
|
||||
|
||||
- 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 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
|
||||
- support is for routine troubleshooting, service issues, and help using included features
|
||||
- custom development is for scripts, integrations, automation, migrations, mods, and new tooling
|
||||
|
||||
Behavior change:
|
||||
|
||||
- collapsible behavior was removed from the active customer dashboard blocks
|
||||
- the old blank-space-prone widget columns are no longer used for the four main customer actions
|
||||
|
|
|
|||
|
|
@ -56,26 +56,34 @@ Admin status page for server/node state.
|
|||
## Display Rules
|
||||
|
||||
- Removed the old `Hostname` column
|
||||
- Removed the repeated per-row `Last Checked` column
|
||||
- Keep one overall `Checked:` timestamp above the table
|
||||
- Removed Panel-to-server latency from the customer-facing page
|
||||
- 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
|
||||
- Status values should remain compact and truthful:
|
||||
- `Online`
|
||||
- `Timed out`
|
||||
- `Agent unavailable`
|
||||
- `Unknown`
|
||||
|
||||
Latency note:
|
||||
|
||||
- Browser ICMP ping is not available from the Panel UI
|
||||
- The previous Panel-to-server latency values were removed because they do not represent the customer's connection to the game-server location
|
||||
- Do not attempt to ping the customer's IP from the Panel or from a game server
|
||||
|
||||
## 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
|
||||
- Keep the page compact and avoid repeated explanatory cards
|
||||
|
||||
## Suggested Future Improvements
|
||||
|
||||
- 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
|
||||
- optional browser-side location latency testing only if each location has a safe public HTTP or HTTPS health endpoint and the implementation remains honest about what is being measured
|
||||
|
||||
## Recommendation
|
||||
|
||||
|
|
|
|||
|
|
@ -46,12 +46,12 @@ Panel update and patch management.
|
|||
## Missing Functionality
|
||||
|
||||
- richer update history and rollback guidance
|
||||
- live progress polling for asynchronous remote agent updates
|
||||
- clearer operator documentation around backup counts and derived settings
|
||||
|
||||
## Suggested Future Improvements
|
||||
|
||||
- keep admin-only and document carefully
|
||||
- add status polling for `component_update` logs after the agent has restarted
|
||||
- add richer update history summaries if the page later needs deeper audit visibility
|
||||
|
||||
## Recommendation
|
||||
|
||||
|
|
@ -63,9 +63,12 @@ The primary update implementation lives in `Panel/modules/administration/panel_u
|
|||
|
||||
Current update targets:
|
||||
|
||||
- Panel files
|
||||
- Website files
|
||||
- Linux agents
|
||||
- Windows/Cygwin agents
|
||||
- Panel files only in the active admin UI
|
||||
|
||||
The updater uses a single configured Git repository with component source folders such as `Panel`, `Website`, `Agent_Linux`, and `Agent-Windows`. Remote agents are updated through the encrypted `component_update` XML-RPC method and preserve hosted game data and agent configuration folders.
|
||||
Active UI behavior:
|
||||
|
||||
- Repository Root is the primary deployment path setting
|
||||
- Panel Path is derived internally as `Repository Root + /Panel`
|
||||
- Panel Source Folder is fixed to `Panel`
|
||||
- Git Executable is hidden and defaults to `git`
|
||||
- Post-update Command remains available only as an advanced field
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue