foxed update and stsrt issues
This commit is contained in:
parent
c687165132
commit
c195c0930b
7 changed files with 544 additions and 70 deletions
|
|
@ -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: <code>' . 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 <strong>' . htmlspecialchars($update_cfg['branch']) . '</strong>. '
|
||||
. 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 "<h2>Panel Updates</h2>\n";
|
||||
echo "<table class='administration-table'><tr><td>\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";
|
||||
if ($git_commit) {
|
||||
echo "<tr><td><strong>Git Commit:</strong></td><td>" . htmlspecialchars(substr($git_commit, 0, 12)) . "</td></tr>\n";
|
||||
}
|
||||
echo "<tr><td><strong>Update Trace Log:</strong></td><td><code>" . htmlspecialchars(GSP_UPDATE_LOG) . "</code></td></tr>\n";
|
||||
echo "<tr><td><strong>Backups Stored:</strong></td><td>" . intval(count($backups)) . " (retention: 5)</td></tr>\n";
|
||||
echo "</table>\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";
|
||||
}
|
||||
if (!empty($preflight_result['errors'])) {
|
||||
echo "<p style='color:#a94442;'><strong>Update preflight errors:</strong><br>" . implode('<br>', array_map('htmlspecialchars', $preflight_result['errors'])) . "</p>\n";
|
||||
}
|
||||
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 "<h3>Repository 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</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>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 "</table>\n";
|
||||
echo "<p>\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 "<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";
|
||||
|
||||
if (!empty($backups)) {
|
||||
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";
|
||||
foreach ($backups as $bk) {
|
||||
$label = htmlspecialchars($bk['ts']);
|
||||
if (!empty($bk['meta']['update_target_type']) && !empty($bk['meta']['update_target_version'])) {
|
||||
$label .= ' (before ' . htmlspecialchars($bk['meta']['update_target_type']) . ': ' . htmlspecialchars($bk['meta']['update_target_version']) . ')';
|
||||
}
|
||||
echo "<option value='" . htmlspecialchars($bk['ts']) . "'>{$label}</option>\n";
|
||||
}
|
||||
echo "</select> ";
|
||||
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, Website, version.json, and database from selected backup?\");'>Rollback</button>\n";
|
||||
echo "</form>\n";
|
||||
}
|
||||
|
||||
echo "<details style='margin-top:18px;'>\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 "<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";
|
||||
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";
|
||||
}
|
||||
if (!empty($apache_scan_result['ssl_issues'])) {
|
||||
echo "<p style='color:#8a6d3b;'><strong>SSL certificate issues:</strong><br>";
|
||||
foreach ((array)$apache_scan_result['ssl_issues'] as $ssl_issue) {
|
||||
echo htmlspecialchars($ssl_issue['vhost'] . ' ' . $ssl_issue['directive'] . ': ' . $ssl_issue['path'] . ' (' . $ssl_issue['reason'] . ')') . "<br>";
|
||||
}
|
||||
echo "</p>\n";
|
||||
}
|
||||
echo "<form method='POST' style='display:inline-block;margin-right:8px;'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_action' value='preflight'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
|
||||
echo "<button type='submit'>Run Preflight Check</button>\n";
|
||||
echo "</form>\n";
|
||||
echo "<form method='POST' style='display:inline-block;margin-right:8px;'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_action' value='apply_patches'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
|
||||
echo "<button type='submit'>Apply Required Patches</button>\n";
|
||||
echo "</form>\n";
|
||||
echo "<form method='POST' style='display:inline-block;'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_action' value='fix_apache'>\n";
|
||||
echo "<input type='hidden' name='gsp_update_csrf' value='" . htmlspecialchars($csrf_token) . "'>\n";
|
||||
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";
|
||||
|
||||
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 "<h2>Panel Updates</h2>\n";
|
||||
echo "<table class='administration-table'><tr><td>\n";
|
||||
echo "<h3>Detected Layout</h3>\n";
|
||||
|
|
|
|||
|
|
@ -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 '<textarea id="live-server-log" class="log" readonly="readonly" style="height:500px;overflow:auto;max-width:1600px;width:100%;box-sizing:border-box;">'.htmlentities($home_log, ENT_QUOTES, "UTF-8").'</textarea>';
|
||||
echo '<style>
|
||||
#live-server-log {
|
||||
display:block;
|
||||
width:100%;
|
||||
max-width:1600px;
|
||||
min-height:55vh;
|
||||
height:55vh;
|
||||
box-sizing:border-box;
|
||||
font-family:Consolas, Monaco, "Courier New", monospace;
|
||||
font-size:13px;
|
||||
line-height:1.35;
|
||||
white-space:pre-wrap;
|
||||
overflow-y:auto;
|
||||
overflow-x:auto;
|
||||
resize:vertical;
|
||||
tab-size:4;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
#live-server-log {
|
||||
min-height:45vh;
|
||||
height:45vh;
|
||||
font-size:12px;
|
||||
}
|
||||
}
|
||||
</style>';
|
||||
echo '<textarea id="live-server-log" class="log" readonly="readonly" wrap="soft">'.htmlentities($home_log, ENT_QUOTES, "UTF-8").'</textarea>';
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
(function(){
|
||||
|
|
|
|||
|
|
@ -366,6 +366,10 @@ echo "<table id='servermonitor' class='tablesorter' data-sortlist='[[0,0],[3,1]]
|
|||
$pos,
|
||||
$ctrlChkBoxes,
|
||||
$expiration_dates);
|
||||
$address = "";
|
||||
$offlineT = "";
|
||||
$pos = "";
|
||||
$ctrlChkBoxes = "";
|
||||
|
||||
if ( $isAdmin )
|
||||
$server_home['access_rights'] = $db->getFullAccessRightsString();
|
||||
|
|
@ -487,8 +491,8 @@ echo "<table id='servermonitor' class='tablesorter' data-sortlist='[[0,0],[3,1]]
|
|||
if($screen_running)
|
||||
{
|
||||
// Check if the screen running the server is running.
|
||||
$status = "online";
|
||||
$order = 1 + $j;
|
||||
$status = ($agent_state === "ONLINE") ? "online" : "starting";
|
||||
$order = ($agent_state === "ONLINE") ? (1 + $j) : (2 + $j);
|
||||
if($agent_state !== "ONLINE")
|
||||
{
|
||||
$address = "<span class='note'>".htmlentities($agent_state)."</span>";
|
||||
|
|
@ -538,6 +542,15 @@ echo "<table id='servermonitor' class='tablesorter' data-sortlist='[[0,0],[3,1]]
|
|||
}
|
||||
$stats_servers_online++;
|
||||
}
|
||||
elseif($agent_state === "UNKNOWN")
|
||||
{
|
||||
$status = "unknown";
|
||||
$order = 3;
|
||||
$address = "<span class='note'>Unknown - agent status unavailable.</span>";
|
||||
if(isset($agent_status['last_error']) && $agent_status['last_error'] !== "")
|
||||
$address .= " <span class='failure'>".htmlentities($agent_status['last_error'])."</span>";
|
||||
$offlineT = "<span class='note'>Server state is unknown. The Panel did not confirm that this server is offline.</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$status = "offline";
|
||||
|
|
@ -570,16 +583,20 @@ echo "<table id='servermonitor' class='tablesorter' data-sortlist='[[0,0],[3,1]]
|
|||
}
|
||||
}
|
||||
else{
|
||||
$status = "offline";
|
||||
$status = "unknown";
|
||||
$order = 3;
|
||||
$address = "<span style='color:darkred;font-weight:bold;'>Agent Offline</span>";
|
||||
$offlineT = "<span class='note'>Agent is offline. Server state is unknown.</span>";
|
||||
}
|
||||
$user = $db->getUserById($server_home['user_id_main']);
|
||||
|
||||
|
||||
// Template
|
||||
@$first = "<tr class='maintr$trclass'>";
|
||||
$first .= "<td class='collapsible' data-status='$status' data-pos='$pos'><span class='hidden'>$order</span>" . "<img src='" . check_theme_image("images/$status.png") . "' />" . "</td>";
|
||||
$status_color = $status === 'online' ? '#2da44e' : ($status === 'starting' ? '#d29922' : ($status === 'unknown' ? '#8b949e' : '#d1242f'));
|
||||
$status_title = $status === 'starting' ? 'Starting' : ($status === 'unknown' ? 'Unknown' : ucfirst($status));
|
||||
$status_icon = "<span title='" . htmlentities($status_title) . "' style='display:inline-block;width:14px;height:14px;border-radius:50%;background:" . $status_color . ";border:1px solid rgba(255,255,255,.35);vertical-align:middle;'></span>";
|
||||
$first .= "<td class='collapsible' data-status='$status' data-pos='$pos'><span class='hidden'>$order</span>" . $status_icon . "</td>";
|
||||
$first .= "<td class='collapsible'>" . "<span class='hidden'>$mod</span><img src='$icon_path' />" . "</td>";
|
||||
$first .= "<td class='collapsible serverId hide'>" . $server_home["home_id"] . "</td>";
|
||||
$first .= "<td class='collapsible' data-status='$status' data-pos='$pos'><b>" . htmlentities($server_home['home_name']) . "</b>$mod_name</td>";
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ Important references:
|
|||
- live log retrieval exists
|
||||
- logs can be fetched through the Panel
|
||||
- the viewer can update via AJAX
|
||||
- the main game log viewer uses a large monospace output panel
|
||||
|
||||
## What Still Needs Cleanup
|
||||
|
||||
|
|
@ -31,3 +32,13 @@ Important references:
|
|||
- better error highlighting
|
||||
- better downloadable log history
|
||||
|
||||
## Log Viewer Layout
|
||||
|
||||
`Panel/modules/gamemanager/log.php` keeps the AJAX refresh behavior and renders the log in a large textarea:
|
||||
|
||||
- desktop height around `55vh`
|
||||
- mobile height around `45vh`
|
||||
- monospace font
|
||||
- preserved line breaks
|
||||
- vertical scrolling inside the log panel
|
||||
- long-line overflow remains usable without shrinking the panel to one line
|
||||
|
|
|
|||
|
|
@ -16,6 +16,19 @@ Important files:
|
|||
- `Agent_Linux/ogp_agent.pl`
|
||||
- `Agent-Windows/ogp_agent.pl`
|
||||
|
||||
## Panel Display Behavior
|
||||
|
||||
Game Monitor should not convert ambiguous status into false offline.
|
||||
|
||||
Panel display mapping:
|
||||
|
||||
- `ONLINE` displays green.
|
||||
- `STARTING`, `STOPPING`, and `UNRESPONSIVE` display yellow/active.
|
||||
- `OFFLINE` displays red only when the agent confirms offline.
|
||||
- `UNKNOWN` displays gray.
|
||||
|
||||
LGSL/GameQ query failure is not enough to mark a server offline. Query success may add player/map/hostname metadata, but query failure should only show a small unavailable note when the agent says the server is otherwise active.
|
||||
|
||||
## Recommended State Model
|
||||
|
||||
| State | Meaning |
|
||||
|
|
@ -51,4 +64,3 @@ The agent should check, in this order:
|
|||
- use state hints for start/stop transitions
|
||||
- expose clear messages for `STARTING` and `UNRESPONSIVE`
|
||||
- add precise log excerpts when startup fails
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,19 @@ Useful state labels:
|
|||
|
||||
Query checks should remain optional metadata only.
|
||||
|
||||
## Game Monitor Display Rules
|
||||
|
||||
`Panel/modules/gamemanager/server_monitor.php` should display state from the agent status response, not from LGSL/GameQ query success alone.
|
||||
|
||||
Current display mapping:
|
||||
|
||||
- `ONLINE` -> green online indicator
|
||||
- `STARTING`, `STOPPING`, `UNRESPONSIVE` -> yellow active/starting indicator
|
||||
- `OFFLINE` -> red offline indicator
|
||||
- `UNKNOWN` or agent unavailable -> gray unknown indicator
|
||||
|
||||
Query metadata remains optional. A running process/session or listening game port must not be shown as red/offline only because query details are unavailable.
|
||||
|
||||
## Log Viewer
|
||||
|
||||
Relevant files:
|
||||
|
|
@ -72,6 +85,8 @@ Relevant files:
|
|||
|
||||
The log view should be treated as live, AJAX-updated output rather than a full page reload workflow.
|
||||
|
||||
`log.php` now renders the live log as a large monospace textarea with viewport-based height, preserved line breaks, vertical scrolling, and mobile sizing.
|
||||
|
||||
## What This Module Depends On
|
||||
|
||||
- `config_games` for startup parameters and protocol definitions
|
||||
|
|
@ -79,4 +94,3 @@ The log view should be treated as live, AJAX-updated output rather than a full p
|
|||
- `user_games` for server home records
|
||||
- `rcon` for command support where available
|
||||
- `addonsmanager` for content/mod interactions
|
||||
|
||||
|
|
|
|||
60
docs/modules/UPDATE.md
Normal file
60
docs/modules/UPDATE.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Update Module
|
||||
|
||||
## Role
|
||||
|
||||
`Panel/modules/update` exposes the admin Panel update page. The page delegates most update behavior to:
|
||||
|
||||
- `Panel/modules/administration/panel_update.php`
|
||||
|
||||
## Current Behavior
|
||||
|
||||
The update page is intentionally simple:
|
||||
|
||||
- shows the installed Panel version
|
||||
- shows the current git branch and commit when available
|
||||
- exposes editable repository settings
|
||||
- can create a backup
|
||||
- can update from the configured repository and branch
|
||||
- can roll back to an existing backup
|
||||
- keeps Apache diagnostics in a collapsed Advanced Diagnostics section
|
||||
|
||||
## Update Settings
|
||||
|
||||
The admin page stores these settings in the Panel settings table:
|
||||
|
||||
- `gsp_update_repo_url`
|
||||
- `gsp_update_branch`
|
||||
- `gsp_update_repo_root`
|
||||
- `gsp_update_panel_path`
|
||||
- `gsp_update_backup_before`
|
||||
|
||||
Defaults:
|
||||
|
||||
- Repository URL: `http://forge.runlevelsystems.com/dev/GSP.git`
|
||||
- Branch: `Panel-unstable`
|
||||
- Repository Root: `/var/www/html/GSP`
|
||||
- Panel Path: `/var/www/html/GSP/Panel`
|
||||
- Backup Before Update: enabled
|
||||
|
||||
## Update Flow
|
||||
|
||||
1. Save or submit repository settings.
|
||||
2. Validate repository URL, branch, repo root, and Panel path.
|
||||
3. Run preflight against the configured paths.
|
||||
4. Create a backup when enabled.
|
||||
5. Clone the configured repository branch into a temporary checkout.
|
||||
6. Sync files into the configured root/Panel paths.
|
||||
7. Preserve `Panel/includes/config.inc.php`.
|
||||
8. Run module updates/post-update hooks.
|
||||
9. Write version metadata and `LAST_UPDATE.txt`.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Apache and SSL checks are diagnostics only. Missing SSL certificates do not block Panel updates.
|
||||
|
||||
The old repeated SSL vhost disable buttons are not part of the primary update page. Apache path repair remains available under Advanced Diagnostics.
|
||||
|
||||
## Remaining Issues
|
||||
|
||||
- The updater still contains legacy GitHub release helper code that is no longer rendered by the simplified primary UI.
|
||||
- Real production testing should confirm file ownership and web-server permissions on `/var/www/html/GSP`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue