From c195c0930b89528f6624d618f1b90bd7d33f5865 Mon Sep 17 00:00:00 2001 From: Frank Harris Date: Fri, 5 Jun 2026 14:39:10 -0500 Subject: [PATCH] foxed update and stsrt issues --- Panel/modules/administration/panel_update.php | 461 +++++++++++++++--- Panel/modules/gamemanager/log.php | 27 +- Panel/modules/gamemanager/server_monitor.php | 25 +- docs/features/LOGGING_SYSTEM.md | 11 + docs/features/STATUS_SYSTEM.md | 14 +- docs/modules/GAMEMANAGER.md | 16 +- docs/modules/UPDATE.md | 60 +++ 7 files changed, 544 insertions(+), 70 deletions(-) create mode 100644 docs/modules/UPDATE.md diff --git a/Panel/modules/administration/panel_update.php b/Panel/modules/administration/panel_update.php index 6cded246..75dcd301 100644 --- a/Panel/modules/administration/panel_update.php +++ b/Panel/modules/administration/panel_update.php @@ -27,6 +27,11 @@ defined('GSP_EXPECTED_PANEL') || define('GSP_EXPECTED_PANEL', GSP_EXPECTED_ROOT defined('GSP_EXPECTED_WEBSITE') || define('GSP_EXPECTED_WEBSITE', GSP_EXPECTED_ROOT . '/Website'); defined('GSP_CANONICAL_TIMESTAMP_FILE') || define('GSP_CANONICAL_TIMESTAMP_FILE', GSP_WEBSITE_DIR . '/timestamp.txt'); defined('GSP_BILLING_TIMESTAMP_FILE') || define('GSP_BILLING_TIMESTAMP_FILE', GSP_PANEL_DIR . '/modules/billing/timestamp.txt'); +defined('GSP_LAST_UPDATE_FILE') || define('GSP_LAST_UPDATE_FILE', GSP_ROOT_DIR . '/LAST_UPDATE.txt'); +defined('GSP_DEFAULT_REPO_URL') || define('GSP_DEFAULT_REPO_URL', 'http://forge.runlevelsystems.com/dev/GSP.git'); +defined('GSP_DEFAULT_BRANCH') || define('GSP_DEFAULT_BRANCH', 'Panel-unstable'); +defined('GSP_DEFAULT_REPO_ROOT') || define('GSP_DEFAULT_REPO_ROOT', '/var/www/html/GSP'); +defined('GSP_DEFAULT_PANEL_PATH') || define('GSP_DEFAULT_PANEL_PATH', '/var/www/html/GSP/Panel'); $gspPatchManager = GSP_PANEL_DIR . '/modules/update/patch_manager.php'; if (file_exists($gspPatchManager)) { @@ -129,6 +134,41 @@ $repo_root = gsp_detect_repo_root(); if (!$repo_root || !function_exists('exec')) { return null; } + +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; +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, '/'), +'panel_path' => rtrim($panel_path, '/'), +'backup_before_update' => !isset($settings['gsp_update_backup_before']) ? '1' : (string)$settings['gsp_update_backup_before'], +]; +} + +function gsp_validate_update_settings(array $cfg) +{ +$errors = []; +if (!preg_match('/^https?:\/\/[^ \t\r\n]+\.git$/i', (string)$cfg['repo_url']) +&& !preg_match('/^(?:ssh:\/\/|git@)[^ \t\r\n]+$/i', (string)$cfg['repo_url'])) { +$errors[] = 'Repository URL must be an http(s), ssh, or git@ URL.'; +} +if (!preg_match('/^[A-Za-z0-9._\/-]{1,128}$/', (string)$cfg['branch'])) { +$errors[] = 'Branch/channel contains invalid characters.'; +} +foreach (['repo_root', 'panel_path'] as $key) { +if (trim((string)$cfg[$key]) === '' || strpos((string)$cfg[$key], "\0") !== false || strpos((string)$cfg[$key], '..') !== false) { +$errors[] = ucfirst(str_replace('_', ' ', $key)) . ' is invalid.'; +} +} +if (rtrim((string)$cfg['panel_path'], '/') !== rtrim((string)$cfg['repo_root'], '/') . '/Panel') { +$errors[] = 'Panel Path must point to the Panel folder inside Repository Root.'; +} +return $errors; +} $out = []; $ret = 0; exec('git -C ' . escapeshellarg($repo_root) . ' rev-parse HEAD 2>/dev/null', $out, $ret); @@ -207,58 +247,50 @@ return json_decode($data, true); return false; } -function gsp_preflight_check() +function gsp_preflight_check(array $update_cfg = null) { $errors = []; $warnings = []; +$update_cfg = $update_cfg ?: gsp_update_settings(); $cwd = getcwd(); $cwd_real = $cwd ? (realpath($cwd) ?: $cwd) : ''; -$root_real = realpath(GSP_ROOT_DIR) ?: GSP_ROOT_DIR; -$panel_real = realpath(GSP_PANEL_DIR) ?: GSP_PANEL_DIR; -$website_real = realpath(GSP_WEBSITE_DIR) ?: GSP_WEBSITE_DIR; -$expected_root_real = realpath(GSP_EXPECTED_ROOT) ?: GSP_EXPECTED_ROOT; -$expected_panel_real = realpath(GSP_EXPECTED_PANEL) ?: GSP_EXPECTED_PANEL; -$expected_website_real = realpath(GSP_EXPECTED_WEBSITE) ?: GSP_EXPECTED_WEBSITE; +$root_path = rtrim((string)$update_cfg['repo_root'], '/'); +$panel_path = rtrim((string)$update_cfg['panel_path'], '/'); +$website_path = $root_path . '/Website'; +$root_real = realpath($root_path) ?: $root_path; +$panel_real = realpath($panel_path) ?: $panel_path; +$website_real = realpath($website_path) ?: $website_path; $layout = [ 'cwd' => $cwd, 'cwd_real' => $cwd_real, -'expected_root' => GSP_EXPECTED_ROOT, -'expected_panel' => GSP_EXPECTED_PANEL, -'expected_website' => GSP_EXPECTED_WEBSITE, -'gsp_root' => GSP_ROOT_DIR, +'expected_root' => $root_path, +'expected_panel' => $panel_path, +'expected_website' => $website_path, +'gsp_root' => $root_path, 'gsp_root_real' => $root_real, -'panel_dir' => GSP_PANEL_DIR, +'panel_dir' => $panel_path, 'panel_dir_real' => $panel_real, -'website_dir' => GSP_WEBSITE_DIR, +'website_dir' => $website_path, 'website_dir_real' => $website_real, 'backup_dir' => GSP_BACKUP_BASE, -'config_file' => GSP_PANEL_DIR . '/includes/config.inc.php', -'destination_panel' => GSP_PANEL_DIR, -'destination_website' => GSP_WEBSITE_DIR, +'config_file' => $panel_path . '/includes/config.inc.php', +'destination_panel' => $panel_path, +'destination_website' => $website_path, ]; if (!$layout['cwd']) { $errors[] = 'Unable to read current working directory.'; } elseif (strpos($cwd_real, $panel_real) !== 0) { -$errors[] = 'Current working directory must be under live Panel path: ' . $panel_real; +$warnings[] = 'Current working directory is not under configured Panel path: ' . $panel_real; } -if (!is_dir(GSP_ROOT_DIR)) { +if (!is_dir($root_path)) { $errors[] = 'Detected GSP root path is missing.'; } -if ($root_real !== $expected_root_real) { -$errors[] = 'Detected GSP root does not match expected live root: ' . GSP_EXPECTED_ROOT; -} -if (!is_dir(GSP_PANEL_DIR)) { +if (!is_dir($panel_path)) { $errors[] = 'Panel directory is missing.'; } -if ($panel_real !== $expected_panel_real) { -$errors[] = 'Detected Panel path does not match expected live Panel path: ' . GSP_EXPECTED_PANEL; -} -if (!is_dir(GSP_WEBSITE_DIR)) { -$errors[] = 'Website directory is missing.'; -} -if ($website_real !== $expected_website_real) { -$errors[] = 'Detected Website path does not match expected live Website path: ' . GSP_EXPECTED_WEBSITE; +if (!is_dir($website_path)) { +$warnings[] = 'Website directory is missing. Panel updates can still continue, but Website files will not sync cleanly.'; } if (!file_exists($layout['config_file'])) { $errors[] = 'Panel includes/config.inc.php was not found and cannot be preserved.'; @@ -269,7 +301,7 @@ $errors[] = 'Backups directory is missing and cannot be created.'; } } -foreach ([GSP_ROOT_DIR, GSP_PANEL_DIR, GSP_WEBSITE_DIR, GSP_BACKUP_BASE] as $path) { +foreach ([$root_path, $panel_path, GSP_BACKUP_BASE] as $path) { if (!is_writable($path)) { $errors[] = 'Path is not writable: ' . $path; } @@ -593,8 +625,9 @@ $source_root = $subdirs[0]; return ['success' => true, 'temp_dir' => $temp_dir, 'source_root' => $source_root]; } -function gsp_resolve_source_layout($temp_checkout_path, $source_root) +function gsp_resolve_source_layout($temp_checkout_path, $source_root, array $update_cfg = null) { +$update_cfg = $update_cfg ?: gsp_update_settings(); $source_root_real = realpath($source_root) ?: $source_root; $candidates = [$source_root_real]; if (basename($source_root_real) === 'Panel' || basename($source_root_real) === 'Website') { @@ -611,16 +644,16 @@ break; $layout = [ 'cwd' => getcwd() ?: '', -'live_gsp_root' => GSP_ROOT_DIR, -'live_panel_path' => GSP_PANEL_DIR, -'live_website_path' => GSP_WEBSITE_DIR, +'live_gsp_root' => rtrim((string)$update_cfg['repo_root'], '/'), +'live_panel_path' => rtrim((string)$update_cfg['panel_path'], '/'), +'live_website_path' => rtrim((string)$update_cfg['repo_root'], '/') . '/Website', 'temporary_git_checkout_path' => $temp_checkout_path, 'source_root' => $source_root_real, 'source_repo_root' => $repo_root, 'source_panel_path' => $repo_root ? ($repo_root . '/Panel') : '', 'source_website_path' => $repo_root ? ($repo_root . '/Website') : '', -'destination_panel_path' => GSP_PANEL_DIR, -'destination_website_path' => GSP_WEBSITE_DIR, +'destination_panel_path' => rtrim((string)$update_cfg['panel_path'], '/'), +'destination_website_path' => rtrim((string)$update_cfg['repo_root'], '/') . '/Website', ]; $errors = []; @@ -640,17 +673,8 @@ $errors[] = 'Destination Panel path is nested incorrectly: ' . $layout['destinat if (strpos((string)$layout['destination_website_path'], '/Website/Website') !== false) { $errors[] = 'Destination Website path is nested incorrectly: ' . $layout['destination_website_path']; } -if ((realpath(GSP_ROOT_DIR) ?: GSP_ROOT_DIR) !== (realpath(GSP_EXPECTED_ROOT) ?: GSP_EXPECTED_ROOT)) { -$errors[] = 'Live root mismatch. Expected ' . GSP_EXPECTED_ROOT . ' but detected ' . GSP_ROOT_DIR; -} -if ((realpath(GSP_PANEL_DIR) ?: GSP_PANEL_DIR) !== (realpath(GSP_EXPECTED_PANEL) ?: GSP_EXPECTED_PANEL)) { -$errors[] = 'Live Panel mismatch. Expected ' . GSP_EXPECTED_PANEL . ' but detected ' . GSP_PANEL_DIR; -} -if ((realpath(GSP_WEBSITE_DIR) ?: GSP_WEBSITE_DIR) !== (realpath(GSP_EXPECTED_WEBSITE) ?: GSP_EXPECTED_WEBSITE)) { -$errors[] = 'Live Website mismatch. Expected ' . GSP_EXPECTED_WEBSITE . ' but detected ' . GSP_WEBSITE_DIR; -} -if (strpos((realpath($layout['cwd']) ?: $layout['cwd']), (realpath(GSP_PANEL_DIR) ?: GSP_PANEL_DIR)) !== 0) { -$errors[] = 'Updater must run from a working directory under the live Panel path.'; +if (!is_dir($layout['destination_panel_path'])) { +$errors[] = 'Destination Panel path does not exist: ' . $layout['destination_panel_path']; } gsp_update_log('Deployment layout detection: ' . json_encode($layout)); @@ -836,7 +860,7 @@ if ($entry === 'Panel' || $entry === 'Website' || $entry === 'backups' || $entry continue; } $src = rtrim($source_root, '/') . '/' . $entry; -$dst = GSP_ROOT_DIR . '/' . $entry; +$dst = rtrim($layout['live_gsp_root'], '/') . '/' . $entry; if (is_file($src)) { $rel = gsp_normalize_rel($entry); if (gsp_is_preserved_path($rel)) { @@ -852,7 +876,7 @@ $copied_files[] = $rel; continue; } if (is_dir($src)) { -$part = gsp_copy_tree($src, GSP_ROOT_DIR, $entry); +$part = gsp_copy_tree($src, $layout['live_gsp_root'], $entry); $copied += $part['copied']; $copied_files = array_merge($copied_files, array_slice((array)$part['copied_files'], 0, max(0, 200 - count($copied_files)))); $skipped = array_merge($skipped, $part['skipped']); @@ -890,7 +914,7 @@ $checks = [ ]; foreach ($checks as $rel) { $src = rtrim($layout['source_repo_root'], '/') . '/' . $rel; -$dst = rtrim(GSP_ROOT_DIR, '/') . '/' . $rel; +$dst = rtrim($layout['live_gsp_root'], '/') . '/' . $rel; if (!is_file($src)) { continue; } @@ -919,10 +943,11 @@ return [ ]; } -function gsp_write_last_update_markers() +function gsp_write_last_update_markers($repo_root = null) { $line = 'Last Updated at ' . date('g:ia') . ' on ' . date('Y-m-d'); -$targets = [GSP_CANONICAL_TIMESTAMP_FILE, GSP_BILLING_TIMESTAMP_FILE]; +$last_update_file = rtrim((string)($repo_root ?: GSP_ROOT_DIR), '/') . '/LAST_UPDATE.txt'; +$targets = [GSP_CANONICAL_TIMESTAMP_FILE, GSP_BILLING_TIMESTAMP_FILE, $last_update_file]; foreach ($targets as $target) { $dir = dirname($target); if (!is_dir($dir)) { @@ -953,15 +978,16 @@ return [ return ['success' => true, 'run' => $run]; } -function gsp_apply_update_from_zip($zip_file, $restart_nonce = '') +function gsp_apply_update_from_zip($zip_file, $restart_nonce = '', array $update_cfg = null) { +$update_cfg = $update_cfg ?: gsp_update_settings(); $extract = gsp_extract_update_source($zip_file); if (!$extract['success']) { return $extract; } $temp_dir = $extract['temp_dir']; $source_root = $extract['source_root']; -$resolved_layout = gsp_resolve_source_layout($temp_dir, $source_root); +$resolved_layout = gsp_resolve_source_layout($temp_dir, $source_root, $update_cfg); if (!$resolved_layout['success']) { gsp_rmdir_recursive($temp_dir); return ['success' => false, 'error' => 'Deployment layout validation failed: ' . implode(' | ', $resolved_layout['errors'])]; @@ -985,6 +1011,87 @@ return [ 'drift_files' => $drift_files, ]; } + +function gsp_checkout_update_source(array $update_cfg) +{ +$repo_url = (string)$update_cfg['repo_url']; +$branch = (string)$update_cfg['branch']; +$temp_dir = sys_get_temp_dir() . '/gsp_git_' . time() . '_' . mt_rand(1000, 9999); +if (!@mkdir($temp_dir, 0750, true)) { +return ['success' => false, 'error' => 'Cannot create temporary git checkout directory.']; +} +$out = []; +$ret = 0; +$cmd = 'git clone --depth 1 --branch ' . escapeshellarg($branch) . ' ' . escapeshellarg($repo_url) . ' ' . escapeshellarg($temp_dir) . ' 2>&1'; +exec($cmd, $out, $ret); +if ($ret !== 0) { +gsp_rmdir_recursive($temp_dir); +return ['success' => false, 'error' => 'git clone failed: ' . implode(' | ', array_slice($out, -20))]; +} +return ['success' => true, 'temp_dir' => $temp_dir, 'source_root' => $temp_dir, 'output' => implode("\n", $out)]; +} + +function gsp_apply_update_from_source($source_root, $restart_nonce = '', array $update_cfg = null) +{ +$update_cfg = $update_cfg ?: gsp_update_settings(); +$resolved_layout = gsp_resolve_source_layout($source_root, $source_root, $update_cfg); +if (!$resolved_layout['success']) { +return ['success' => false, 'error' => 'Deployment layout validation failed: ' . implode(' | ', $resolved_layout['errors'])]; +} +$layout = $resolved_layout['layout']; +$_SESSION['gsp_last_update_layout'] = $layout; +$updater_version = substr((string)@hash_file('sha256', $layout['source_panel_path'] . '/modules/administration/panel_update.php'), 0, 12); + +$drift_files = gsp_detect_updater_drift_files($layout['source_repo_root'], $layout['live_gsp_root']); +if (!empty($drift_files) && empty($restart_nonce)) { +$copied = gsp_apply_updater_files_only($layout['source_repo_root'], $layout['live_gsp_root'], $drift_files); +$nonce = gsp_random_token(12); +$_SESSION['gsp_update_restart_nonce'] = $nonce; +gsp_update_log('Updater self-update applied (' . $copied . ' files); restart nonce=' . $nonce); +return [ +'success' => false, +'restart_required' => true, +'restart_nonce' => $nonce, +'updater_files_updated' => $copied, +'drift_files' => $drift_files, +]; +} +if (!empty($restart_nonce)) { +$expected = isset($_SESSION['gsp_update_restart_nonce']) ? $_SESSION['gsp_update_restart_nonce'] : null; +if ($expected === null || !hash_equals($expected, $restart_nonce)) { +return ['success' => false, 'error' => 'Invalid updater restart marker.']; +} +unset($_SESSION['gsp_update_restart_nonce']); +} + +$config_file = rtrim($layout['destination_panel_path'], '/') . '/includes/config.inc.php'; +$config_backup = is_file($config_file) ? @file_get_contents($config_file) : false; +$patches = gsp_run_required_patches($updater_version); +if (!$patches['success']) { +return ['success' => false, 'error' => $patches['error']]; +} +$sync = gsp_apply_layout_sync($layout); +if ($config_backup !== false) { +@file_put_contents($config_file, $config_backup, LOCK_EX); +} +if (!$sync['success']) { +return $sync; +} +$sync_validation = gsp_validate_layout_sync_result($layout, $sync); +if (!$sync_validation['success']) { +return ['success' => false, 'error' => 'Deployed file validation failed: ' . implode(' | ', $sync_validation['errors'])]; +} +gsp_update_log('Layout sync complete: copied=' . $sync['files_copied'] . ', skipped=' . count($sync['skipped'])); +return [ +'success' => true, +'files_copied' => $sync['files_copied'], +'panel_files_copied' => $sync['panel_files_copied'], +'website_files_copied' => $sync['website_files_copied'], +'preserved' => $sync['skipped'], +'copied_files' => $sync['copied_files'], +'patches' => $patches['run'], +]; +} if (!empty($restart_nonce)) { $expected = isset($_SESSION['gsp_update_restart_nonce']) ? $_SESSION['gsp_update_restart_nonce'] : null; if ($expected === null || !hash_equals($expected, $restart_nonce)) { @@ -1068,6 +1175,73 @@ if (!$preflight['success']) { return ['success' => false, 'error' => 'Preflight failed: ' . implode(' | ', $preflight['errors'])]; } +function gsp_do_configured_git_update(array $update_cfg, $restart_nonce = '') +{ +global $db; +$validation = gsp_validate_update_settings($update_cfg); +if (!empty($validation)) { +return ['success' => false, 'error' => implode(' | ', $validation)]; +} +$preflight = gsp_preflight_check($update_cfg); +if (!$preflight['success']) { +return ['success' => false, 'error' => 'Preflight failed: ' . implode(' | ', $preflight['errors'])]; +} + +$backup = ['success' => true, 'backup_dir' => null]; +if (!empty($update_cfg['backup_before_update'])) { +$backup = gsp_create_full_backup('git-update', $update_cfg['branch'], false); +if (!$backup['success']) { +return $backup; +} +gsp_update_log("Backup created before git update to {$update_cfg['branch']}: {$backup['backup_dir']}"); +} + +$checkout = gsp_checkout_update_source($update_cfg); +if (!$checkout['success']) { +return $checkout; +} +$apply = gsp_apply_update_from_source($checkout['source_root'], $restart_nonce, $update_cfg); +gsp_rmdir_recursive($checkout['temp_dir']); +if (!empty($apply['restart_required'])) { +$apply['backup_dir'] = $backup['backup_dir']; +$apply['success'] = false; +return $apply; +} +if (!$apply['success']) { +return $apply; +} + +$commit_after = gsp_get_git_commit(); +gsp_fix_permissions($update_cfg['repo_root']); +gsp_clear_panel_cache($update_cfg['panel_path']); +gsp_write_version_file($update_cfg['branch'], 'git'); +gsp_write_version_json('git', $update_cfg['repo_url'], $commit_after ?: $update_cfg['branch'], $commit_after); +gsp_write_last_update_markers(); +$db->setSettings(['ogp_version' => $update_cfg['branch'], 'version_type' => 'git']); + +if (file_exists($update_cfg['panel_path'] . '/modules/modulemanager/module_handling.php')) { +require_once($update_cfg['panel_path'] . '/modules/modulemanager/module_handling.php'); +} +if (function_exists('updateAllPanelModules')) { +updateAllPanelModules(); +} +if (function_exists('runPostUpdateOperations')) { +runPostUpdateOperations(); +} + +gsp_update_log("Configured git update complete: {$update_cfg['repo_url']} {$update_cfg['branch']}"); +return [ +'success' => true, +'files_copied' => $apply['files_copied'], +'panel_files_copied' => isset($apply['panel_files_copied']) ? $apply['panel_files_copied'] : 0, +'website_files_copied' => isset($apply['website_files_copied']) ? $apply['website_files_copied'] : 0, +'copied_files' => isset($apply['copied_files']) ? $apply['copied_files'] : [], +'backup_dir' => $backup['backup_dir'], +'preserved' => $apply['preserved'], +'patches' => $apply['patches'], +]; +} + $backup = gsp_create_full_backup($update_type, $ref, false); if (!$backup['success']) { return $backup; @@ -1101,7 +1275,7 @@ gsp_write_version_file($ref, $update_type); $vsource = ($update_type === 'release') ? 'GitHub Releases' : $ref; $vversion = ($update_type === 'release') ? $ref : ($commit_after ?: $ref); gsp_write_version_json($update_type, $vsource, $vversion, $commit_after); -gsp_write_last_update_markers(); +gsp_write_last_update_markers($update_cfg['repo_root']); $db->setSettings(['ogp_version' => $ref, 'version_type' => $update_type]); if (file_exists(GSP_PANEL_DIR . '/modules/modulemanager/module_handling.php')) { @@ -1686,10 +1860,7 @@ if ($_SESSION['users_group'] !== 'admin') { return; } -$repo_owner = !empty($settings['gsp_repo_owner']) ? $settings['gsp_repo_owner'] : 'GameServerPanel'; -$repo_name = !empty($settings['gsp_repo_name']) ? $settings['gsp_repo_name'] : 'GSP'; -$stable_branch = !empty($settings['gsp_stable_branch']) ? $settings['gsp_stable_branch'] : 'Panel-stable'; -$unstable_branch = !empty($settings['gsp_unstable_branch']) ? $settings['gsp_unstable_branch'] : 'Panel-unstable'; +$update_cfg = gsp_update_settings(); if (empty($_SESSION['gsp_update_csrf'])) { $_SESSION['gsp_update_csrf'] = gsp_random_token(); @@ -1754,6 +1925,58 @@ print_success('Backup created: ' . htmlspecialchars($result['backup_dir']) } else { print_failure('Backup failed: ' . htmlspecialchars($result['error'])); } +} elseif ($action === 'save_settings') { +$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']), '/') : '', +'backup_before_update' => !empty($_POST['gsp_update_backup_before']) ? '1' : '0', +]; +$errors = gsp_validate_update_settings($new_cfg); +if (!empty($errors)) { +print_failure('Update settings were not saved: ' . htmlspecialchars(implode(' | ', $errors))); +} else { +$db->setSettings([ +'gsp_update_repo_url' => $new_cfg['repo_url'], +'gsp_update_branch' => $new_cfg['branch'], +'gsp_update_repo_root' => $new_cfg['repo_root'], +'gsp_update_panel_path' => $new_cfg['panel_path'], +'gsp_update_backup_before' => $new_cfg['backup_before_update'], +]); +$settings['gsp_update_repo_url'] = $new_cfg['repo_url']; +$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_backup_before'] = $new_cfg['backup_before_update']; +$update_cfg = gsp_update_settings(); +print_success('Update settings saved.'); +} +} elseif ($action === 'update_configured') { +$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'], +'backup_before_update' => !empty($_POST['gsp_update_backup_before']) ? '1' : '0', +]; +$db->setSettings([ +'gsp_update_repo_url' => $update_cfg['repo_url'], +'gsp_update_branch' => $update_cfg['branch'], +'gsp_update_repo_root' => $update_cfg['repo_root'], +'gsp_update_panel_path' => $update_cfg['panel_path'], +'gsp_update_backup_before' => $update_cfg['backup_before_update'], +]); +$result = gsp_do_configured_git_update($update_cfg, $restart_nonce); +if (!empty($result['restart_required'])) { +print_success('Updater files changed and were updated first. Restarting update with refreshed updater...'); +$auto_restart_payload = ['action' => 'update_configured', 'nonce' => $result['restart_nonce']]; +} elseif ($result['success']) { +print_success('Panel updated from configured repository branch ' . htmlspecialchars($update_cfg['branch']) . '. ' +. intval($result['files_copied']) . ' file(s) copied (Panel: ' . intval($result['panel_files_copied']) . ', Website: ' . intval($result['website_files_copied']) . ').'); +} else { +print_failure('Update failed: ' . htmlspecialchars($result['error'])); +} } elseif ($action === 'update_release') { $version = isset($_POST['gsp_release_version']) ? trim($_POST['gsp_release_version']) : ''; if (!preg_match('/^[a-zA-Z0-9._\-]+$/', $version) || strlen($version) > 80) { @@ -1821,17 +2044,129 @@ $last_layout = isset($_SESSION['gsp_last_update_layout']) && is_array($_SESSION[ ? $_SESSION['gsp_last_update_layout'] : null; $vinfo = gsp_read_version_json(); -$releases = gsp_fetch_github_releases($repo_owner, $repo_name); -$latest_release = (is_array($releases) && !empty($releases)) ? htmlspecialchars($releases[0]['tag_name']) : 'N/A'; +$latest_release = 'N/A'; $backups = gsp_get_available_backups(); $patch_overview = gsp_get_patch_overview(); if ($apache_scan_result === null) { $apache_scan_result = gsp_scan_apache_configs(); } if ($preflight_result === null) { -$preflight_result = gsp_preflight_check(); +$preflight_result = gsp_preflight_check($update_cfg); } +$ssl_issue_count = !empty($apache_scan_result['ssl_issues']) ? count($apache_scan_result['ssl_issues']) : 0; +echo "

Panel Updates

\n"; +echo "
\n"; +echo "

Current Installation

\n"; +echo "\n"; +echo "\n"; +echo "\n"; +if ($git_commit) { +echo "\n"; +} +echo "\n"; +echo "\n"; +echo "
Installed Version:" . htmlspecialchars($current_version) . "
Current Branch:" . htmlspecialchars($current_branch) . "
Git Commit:" . htmlspecialchars(substr($git_commit, 0, 12)) . "
Update Trace Log:" . htmlspecialchars(GSP_UPDATE_LOG) . "
Backups Stored:" . intval(count($backups)) . " (retention: 5)
\n"; +if ($ssl_issue_count > 0) { +echo "

SSL warning: " . intval($ssl_issue_count) . " Apache SSL certificate issue(s) detected. Updates are not blocked by this. See Advanced Diagnostics for details.

\n"; +} +if (!empty($preflight_result['errors'])) { +echo "

Update preflight errors:
" . implode('
', array_map('htmlspecialchars', $preflight_result['errors'])) . "

\n"; +} +if (!empty($preflight_result['warnings'])) { +echo "

Update preflight warnings:
" . implode('
', array_map('htmlspecialchars', $preflight_result['warnings'])) . "

\n"; +} + +echo "

Repository Settings

\n"; +echo "
\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "
Repository URL
Branch / Channel
Repository Root
Panel Path
Backup Before Update
\n"; +echo "

\n"; +echo " "; +echo "\n"; +echo "

\n"; +echo "
\n"; + +echo "

Backup

\n"; +echo "
\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "
\n"; + +if (!empty($backups)) { +echo "

Rollback

\n"; +echo "
\n"; +echo "\n"; +echo "\n"; +echo " "; +echo " "; +echo "\n"; +echo "
\n"; +} + +echo "
\n"; +echo "Advanced Diagnostics\n"; +echo "

Preflight

\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "
Status:" . ($preflight_result['success'] ? 'PASS' : 'FAIL') . "
Pending Patches:" . intval(count($patch_overview['pending'])) . "
Patch Directory:" . htmlspecialchars(GSP_PATCH_DIR) . "
\n"; +echo "

Apache

\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "
Config Directory:" . htmlspecialchars($apache_scan_result['base']) . "
Configs Found:" . intval(count($apache_scan_result['files'])) . "
Stale Path Hits:" . intval(count($apache_scan_result['stale_issues'])) . "
SSL Certificate Issues:" . intval($ssl_issue_count) . "
\n"; +if (!empty($apache_scan_result['stale_issues'])) { +echo "

Apache stale path issues:
" . implode('
', array_map('htmlspecialchars', array_unique($apache_scan_result['stale_issues']))) . "

\n"; +} +if (!empty($apache_scan_result['ssl_issues'])) { +echo "

SSL certificate issues:
"; +foreach ((array)$apache_scan_result['ssl_issues'] as $ssl_issue) { +echo htmlspecialchars($ssl_issue['vhost'] . ' ' . $ssl_issue['directive'] . ': ' . $ssl_issue['path'] . ' (' . $ssl_issue['reason'] . ')') . "
"; +} +echo "

\n"; +} +echo "
\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "
\n"; +echo "
\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "
\n"; +echo "
\n"; +echo "\n"; +echo "\n"; +echo "\n"; +echo "
\n"; +echo "
\n"; +echo "
\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'] : ''); +} +return; + echo "

Panel Updates

\n"; echo "
\n"; echo "

Detected Layout

\n"; diff --git a/Panel/modules/gamemanager/log.php b/Panel/modules/gamemanager/log.php index 9f0964c7..d1c995dd 100644 --- a/Panel/modules/gamemanager/log.php +++ b/Panel/modules/gamemanager/log.php @@ -88,7 +88,32 @@ require_once("modules/config_games/server_config_parser.php"); if($log_retval == 1) { $log_url = "home.php?m=gamemanager&p=log&type=cleared&refreshed&home_id-mod_id-ip-port=".rawurlencode($_GET['home_id-mod_id-ip-port']); - echo ''; + echo ''; + echo ''; ?>