|
|
|
|
@ -32,6 +32,11 @@ defined('GSP_DEFAULT_REPO_URL') || define('GSP_DEFAULT_REPO_URL', 'http://forge.
|
|
|
|
|
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');
|
|
|
|
|
defined('GSP_DEFAULT_WEBSITE_PATH') || define('GSP_DEFAULT_WEBSITE_PATH', '/var/www/html/GSP/Website');
|
|
|
|
|
defined('GSP_DEFAULT_PANEL_SOURCE') || define('GSP_DEFAULT_PANEL_SOURCE', 'Panel');
|
|
|
|
|
defined('GSP_DEFAULT_LINUX_AGENT_SOURCE') || define('GSP_DEFAULT_LINUX_AGENT_SOURCE', 'Agent_Linux');
|
|
|
|
|
defined('GSP_DEFAULT_WINDOWS_AGENT_SOURCE') || define('GSP_DEFAULT_WINDOWS_AGENT_SOURCE', 'Agent-Windows');
|
|
|
|
|
defined('GSP_DEFAULT_WEBSITE_SOURCE') || define('GSP_DEFAULT_WEBSITE_SOURCE', 'Website');
|
|
|
|
|
|
|
|
|
|
$gspPatchManager = GSP_PANEL_DIR . '/modules/update/patch_manager.php';
|
|
|
|
|
if (file_exists($gspPatchManager)) {
|
|
|
|
|
@ -151,11 +156,23 @@ 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;
|
|
|
|
|
$website_path = !empty($settings['gsp_update_website_path']) ? (string)$settings['gsp_update_website_path'] : GSP_DEFAULT_WEBSITE_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, '/'),
|
|
|
|
|
'website_path' => rtrim($website_path, '/'),
|
|
|
|
|
'panel_source_path' => !empty($settings['gsp_update_panel_source_path']) ? trim((string)$settings['gsp_update_panel_source_path'], '/') : GSP_DEFAULT_PANEL_SOURCE,
|
|
|
|
|
'linux_agent_source_path' => !empty($settings['gsp_update_linux_agent_source_path']) ? trim((string)$settings['gsp_update_linux_agent_source_path'], '/') : GSP_DEFAULT_LINUX_AGENT_SOURCE,
|
|
|
|
|
'windows_agent_source_path' => !empty($settings['gsp_update_windows_agent_source_path']) ? trim((string)$settings['gsp_update_windows_agent_source_path'], '/') : GSP_DEFAULT_WINDOWS_AGENT_SOURCE,
|
|
|
|
|
'website_source_path' => !empty($settings['gsp_update_website_source_path']) ? trim((string)$settings['gsp_update_website_source_path'], '/') : GSP_DEFAULT_WEBSITE_SOURCE,
|
|
|
|
|
'git_path' => !empty($settings['gsp_update_git_path']) ? trim((string)$settings['gsp_update_git_path']) : 'git',
|
|
|
|
|
'backup_path' => !empty($settings['gsp_update_backup_path']) ? rtrim((string)$settings['gsp_update_backup_path'], '/') : GSP_BACKUP_BASE,
|
|
|
|
|
'panel_post_update_command' => !empty($settings['gsp_update_panel_post_update_command']) ? (string)$settings['gsp_update_panel_post_update_command'] : '',
|
|
|
|
|
'website_post_update_command' => !empty($settings['gsp_update_website_post_update_command']) ? (string)$settings['gsp_update_website_post_update_command'] : '',
|
|
|
|
|
'linux_agent_post_update_command' => !empty($settings['gsp_update_linux_agent_post_update_command']) ? (string)$settings['gsp_update_linux_agent_post_update_command'] : '',
|
|
|
|
|
'windows_agent_post_update_command' => !empty($settings['gsp_update_windows_agent_post_update_command']) ? (string)$settings['gsp_update_windows_agent_post_update_command'] : '',
|
|
|
|
|
'backup_before_update' => !isset($settings['gsp_update_backup_before']) ? '1' : (string)$settings['gsp_update_backup_before'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
@ -175,7 +192,7 @@ $errors[] = 'Repository source must be an http(s), ssh, git@ URL, or a safe abso
|
|
|
|
|
if ($is_local_path && !is_dir($repo_source)) {
|
|
|
|
|
$errors[] = 'Repository local path does not exist or is not a directory: ' . $repo_source;
|
|
|
|
|
}
|
|
|
|
|
if (!preg_match('/^[A-Za-z0-9._\/-]{1,128}$/', (string)$cfg['branch'])) {
|
|
|
|
|
if (!preg_match('/^[A-Za-z0-9._\/-]{1,128}$/', (string)$cfg['branch']) || strpos((string)$cfg['branch'], '..') !== false) {
|
|
|
|
|
$errors[] = 'Branch/channel contains invalid characters.';
|
|
|
|
|
}
|
|
|
|
|
foreach (['repo_root', 'panel_path'] as $key) {
|
|
|
|
|
@ -183,6 +200,25 @@ if (trim((string)$cfg[$key]) === '' || strpos((string)$cfg[$key], "\0") !== fals
|
|
|
|
|
$errors[] = ucfirst(str_replace('_', ' ', $key)) . ' is invalid.';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
foreach (['website_path', 'backup_path'] as $key) {
|
|
|
|
|
if (isset($cfg[$key]) && (trim((string)$cfg[$key]) === '' || strpos((string)$cfg[$key], "\0") !== false || strpos((string)$cfg[$key], '..') !== false || strpos((string)$cfg[$key], '/') !== 0)) {
|
|
|
|
|
$errors[] = ucfirst(str_replace('_', ' ', $key)) . ' must be a safe absolute path.';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
foreach (['panel_source_path', 'linux_agent_source_path', 'windows_agent_source_path', 'website_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', 'website_post_update_command', 'linux_agent_post_update_command', 'windows_agent_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.';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (rtrim((string)$cfg['panel_path'], '/') !== rtrim((string)$cfg['repo_root'], '/') . '/Panel') {
|
|
|
|
|
$errors[] = 'Panel Path must point to the Panel folder inside Repository Root.';
|
|
|
|
|
}
|
|
|
|
|
@ -264,7 +300,7 @@ $cwd = getcwd();
|
|
|
|
|
$cwd_real = $cwd ? (realpath($cwd) ?: $cwd) : '';
|
|
|
|
|
$root_path = rtrim((string)$update_cfg['repo_root'], '/');
|
|
|
|
|
$panel_path = rtrim((string)$update_cfg['panel_path'], '/');
|
|
|
|
|
$website_path = $root_path . '/Website';
|
|
|
|
|
$website_path = !empty($update_cfg['website_path']) ? rtrim((string)$update_cfg['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;
|
|
|
|
|
@ -654,14 +690,14 @@ $layout = [
|
|
|
|
|
'cwd' => getcwd() ?: '',
|
|
|
|
|
'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',
|
|
|
|
|
'live_website_path' => !empty($update_cfg['website_path']) ? rtrim((string)$update_cfg['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') : '',
|
|
|
|
|
'source_panel_path' => $repo_root ? ($repo_root . '/' . trim((string)$update_cfg['panel_source_path'], '/')) : '',
|
|
|
|
|
'source_website_path' => $repo_root ? ($repo_root . '/' . trim((string)$update_cfg['website_source_path'], '/')) : '',
|
|
|
|
|
'destination_panel_path' => rtrim((string)$update_cfg['panel_path'], '/'),
|
|
|
|
|
'destination_website_path' => rtrim((string)$update_cfg['repo_root'], '/') . '/Website',
|
|
|
|
|
'destination_website_path' => !empty($update_cfg['website_path']) ? rtrim((string)$update_cfg['website_path'], '/') : (rtrim((string)$update_cfg['repo_root'], '/') . '/Website'),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$errors = [];
|
|
|
|
|
@ -1024,6 +1060,7 @@ function gsp_checkout_update_source(array $update_cfg)
|
|
|
|
|
{
|
|
|
|
|
$repo_url = (string)$update_cfg['repo_url'];
|
|
|
|
|
$branch = (string)$update_cfg['branch'];
|
|
|
|
|
$git_bin = !empty($update_cfg['git_path']) ? (string)$update_cfg['git_path'] : 'git';
|
|
|
|
|
$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.'];
|
|
|
|
|
@ -1034,7 +1071,7 @@ return ['success' => false, 'error' => 'PHP exec() is disabled, so the updater c
|
|
|
|
|
}
|
|
|
|
|
$out = [];
|
|
|
|
|
$ret = 0;
|
|
|
|
|
$cmd = 'git clone --depth 1 --branch ' . escapeshellarg($branch) . ' ' . escapeshellarg($repo_url) . ' ' . escapeshellarg($temp_dir) . ' 2>&1';
|
|
|
|
|
$cmd = escapeshellarg($git_bin) . ' clone --depth 1 --branch ' . escapeshellarg($branch) . ' ' . escapeshellarg($repo_url) . ' ' . escapeshellarg($temp_dir) . ' 2>&1';
|
|
|
|
|
gsp_update_log('Starting configured git checkout from ' . $repo_url . ' branch ' . $branch);
|
|
|
|
|
exec($cmd, $out, $ret);
|
|
|
|
|
if ($ret !== 0) {
|
|
|
|
|
@ -1191,6 +1228,65 @@ if (!$preflight['success']) {
|
|
|
|
|
return ['success' => false, 'error' => 'Preflight failed: ' . implode(' | ', $preflight['errors'])];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$backup = gsp_create_full_backup($update_type, $ref, false);
|
|
|
|
|
if (!$backup['success']) {
|
|
|
|
|
return $backup;
|
|
|
|
|
}
|
|
|
|
|
gsp_update_log("Backup created at {$backup['backup_ts']} before {$update_type} update to {$ref}");
|
|
|
|
|
|
|
|
|
|
$temp_dir = sys_get_temp_dir() . '/gsp_dl_' . time() . '_' . mt_rand(1000, 9999);
|
|
|
|
|
@mkdir($temp_dir, 0750, true);
|
|
|
|
|
$zip_file = gsp_download_zip($repo_owner, $repo_name, $ref, $temp_dir);
|
|
|
|
|
if (!$zip_file) {
|
|
|
|
|
gsp_rmdir_recursive($temp_dir);
|
|
|
|
|
return ['success' => false, 'error' => 'Failed to download update ZIP from GitHub.'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$apply = gsp_apply_update_from_zip($zip_file, $restart_nonce);
|
|
|
|
|
@unlink($zip_file);
|
|
|
|
|
gsp_rmdir_recursive($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(GSP_ROOT_DIR);
|
|
|
|
|
gsp_clear_panel_cache(GSP_PANEL_DIR);
|
|
|
|
|
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_ROOT_DIR);
|
|
|
|
|
$db->setSettings(['ogp_version' => $ref, 'version_type' => $update_type]);
|
|
|
|
|
|
|
|
|
|
if (file_exists(GSP_PANEL_DIR . '/modules/modulemanager/module_handling.php')) {
|
|
|
|
|
require_once(GSP_PANEL_DIR . '/modules/modulemanager/module_handling.php');
|
|
|
|
|
}
|
|
|
|
|
if (function_exists('updateAllPanelModules')) {
|
|
|
|
|
updateAllPanelModules();
|
|
|
|
|
}
|
|
|
|
|
if (function_exists('runPostUpdateOperations')) {
|
|
|
|
|
runPostUpdateOperations();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gsp_update_log("Update to {$ref} (type={$update_type}) complete");
|
|
|
|
|
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'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_do_configured_git_update(array $update_cfg, $restart_nonce = '')
|
|
|
|
|
{
|
|
|
|
|
global $db;
|
|
|
|
|
@ -1258,65 +1354,332 @@ return [
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$backup = gsp_create_full_backup($update_type, $ref, false);
|
|
|
|
|
function gsp_safe_component_backup($component, $source_path, $backup_base)
|
|
|
|
|
{
|
|
|
|
|
$component = preg_replace('/[^A-Za-z0-9._-]/', '_', (string)$component);
|
|
|
|
|
$backup_base = rtrim((string)$backup_base, '/');
|
|
|
|
|
if ($backup_base === '' || strpos($backup_base, '/') !== 0) {
|
|
|
|
|
$backup_base = GSP_BACKUP_BASE;
|
|
|
|
|
}
|
|
|
|
|
if (!is_dir($backup_base) && !@mkdir($backup_base, 0755, true)) {
|
|
|
|
|
return ['success' => false, 'error' => 'Cannot create backup path: ' . $backup_base];
|
|
|
|
|
}
|
|
|
|
|
$ts = date('Y-m-d_H-i-s');
|
|
|
|
|
$backup_dir = $backup_base . '/component_' . $component . '_' . $ts;
|
|
|
|
|
if (!@mkdir($backup_dir, 0755, true)) {
|
|
|
|
|
return ['success' => false, 'error' => 'Cannot create component backup directory: ' . $backup_dir];
|
|
|
|
|
}
|
|
|
|
|
if (!is_dir($source_path)) {
|
|
|
|
|
return ['success' => true, 'backup_dir' => $backup_dir, 'skipped' => true];
|
|
|
|
|
}
|
|
|
|
|
$archive = $backup_dir . '/' . $component . '.tar.gz';
|
|
|
|
|
$archive_result = gsp_create_archive($source_path, $archive, ['./logs', './cache', './tmp', './backups', './uploads', './upload', './Cfg', './ServerFiles', './Schedule', './steamcmd', './startups']);
|
|
|
|
|
if (!$archive_result['success']) {
|
|
|
|
|
return $archive_result;
|
|
|
|
|
}
|
|
|
|
|
@file_put_contents($backup_dir . '/backup.json', json_encode([
|
|
|
|
|
'component' => $component,
|
|
|
|
|
'source_path' => $source_path,
|
|
|
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
'archive' => basename($archive),
|
|
|
|
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
|
|
|
|
return ['success' => true, 'backup_dir' => $backup_dir, 'archive' => $archive];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_component_source_path($source_root, $relative)
|
|
|
|
|
{
|
|
|
|
|
$relative = trim((string)$relative, '/');
|
|
|
|
|
if ($relative === '' || strpos($relative, '..') !== false || strpos($relative, "\0") !== false) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$path = rtrim($source_root, '/') . '/' . $relative;
|
|
|
|
|
$real = realpath($path);
|
|
|
|
|
if ($real === false || strpos($real, realpath($source_root) ?: $source_root) !== 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return $real;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_component_copy_tree($src_root, $dst_root, $component_label)
|
|
|
|
|
{
|
|
|
|
|
$copied = 0;
|
|
|
|
|
$skipped = [];
|
|
|
|
|
$copied_files = [];
|
|
|
|
|
if (!is_dir($src_root)) {
|
|
|
|
|
return ['success' => false, 'error' => 'Source component path not found: ' . $src_root];
|
|
|
|
|
}
|
|
|
|
|
if (!is_dir($dst_root) && !@mkdir($dst_root, 0755, true)) {
|
|
|
|
|
return ['success' => false, 'error' => 'Destination path could not be created: ' . $dst_root];
|
|
|
|
|
}
|
|
|
|
|
$iter = new RecursiveIteratorIterator(
|
|
|
|
|
new RecursiveDirectoryIterator($src_root, RecursiveDirectoryIterator::SKIP_DOTS),
|
|
|
|
|
RecursiveIteratorIterator::SELF_FIRST
|
|
|
|
|
);
|
|
|
|
|
$protected = [
|
|
|
|
|
'includes/config.inc.php',
|
|
|
|
|
'config.inc.php',
|
|
|
|
|
'Cfg/',
|
|
|
|
|
'ServerFiles/',
|
|
|
|
|
'Schedule/',
|
|
|
|
|
'logs/',
|
|
|
|
|
'log/',
|
|
|
|
|
'backups/',
|
|
|
|
|
'backup/',
|
|
|
|
|
'cache/',
|
|
|
|
|
'tmp/',
|
|
|
|
|
'temp/',
|
|
|
|
|
'uploads/',
|
|
|
|
|
'upload/',
|
|
|
|
|
'home/',
|
|
|
|
|
'homes/',
|
|
|
|
|
'servers/',
|
|
|
|
|
'game_servers/',
|
|
|
|
|
'steamcmd/',
|
|
|
|
|
'startups/',
|
|
|
|
|
];
|
|
|
|
|
foreach ($iter as $item) {
|
|
|
|
|
$rel = gsp_normalize_rel(substr($item->getPathname(), strlen(rtrim($src_root, '/')) + 1));
|
|
|
|
|
$is_protected = false;
|
|
|
|
|
foreach ($protected as $prefix) {
|
|
|
|
|
if ($rel === rtrim($prefix, '/') || strpos($rel, $prefix) === 0) {
|
|
|
|
|
$is_protected = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($is_protected) {
|
|
|
|
|
$skipped[] = $component_label . '/' . $rel;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$dst = rtrim($dst_root, '/') . '/' . $rel;
|
|
|
|
|
if ($item->isDir()) {
|
|
|
|
|
if (!is_dir($dst)) {
|
|
|
|
|
@mkdir($dst, 0755, true);
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (gsp_copy_file($item->getPathname(), $dst)) {
|
|
|
|
|
$copied++;
|
|
|
|
|
if (count($copied_files) < 200) {
|
|
|
|
|
$copied_files[] = $component_label . '/' . $rel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ['success' => true, 'files_copied' => $copied, 'skipped' => array_values(array_unique($skipped)), 'copied_files' => $copied_files];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_run_admin_post_update_command($command, $cwd)
|
|
|
|
|
{
|
|
|
|
|
$command = trim((string)$command);
|
|
|
|
|
if ($command === '') {
|
|
|
|
|
return ['success' => true, 'skipped' => true, 'output' => ''];
|
|
|
|
|
}
|
|
|
|
|
if (strpos($command, "\0") !== false || strpos($command, "\n") !== false || strpos($command, "\r") !== false) {
|
|
|
|
|
return ['success' => false, 'error' => 'Post-update command must be a single line.'];
|
|
|
|
|
}
|
|
|
|
|
$out = [];
|
|
|
|
|
$ret = 0;
|
|
|
|
|
exec('cd ' . escapeshellarg($cwd) . ' && ' . $command . ' 2>&1', $out, $ret);
|
|
|
|
|
return ['success' => $ret === 0, 'exit_code' => $ret, 'output' => implode("\n", $out), 'error' => $ret === 0 ? '' : implode("\n", $out)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_do_local_component_update(array $update_cfg, array $components)
|
|
|
|
|
{
|
|
|
|
|
$allowed = array('panel', 'website');
|
|
|
|
|
$components = array_values(array_intersect($allowed, array_unique($components)));
|
|
|
|
|
if (empty($components)) {
|
|
|
|
|
return ['success' => true, 'files_copied' => 0, 'results' => []];
|
|
|
|
|
}
|
|
|
|
|
$validation = gsp_validate_update_settings($update_cfg);
|
|
|
|
|
if (!empty($validation)) {
|
|
|
|
|
return ['success' => false, 'error' => implode(' | ', $validation)];
|
|
|
|
|
}
|
|
|
|
|
$checkout = gsp_checkout_update_source($update_cfg);
|
|
|
|
|
if (!$checkout['success']) {
|
|
|
|
|
return $checkout;
|
|
|
|
|
}
|
|
|
|
|
$results = [];
|
|
|
|
|
$total = 0;
|
|
|
|
|
$all_skipped = [];
|
|
|
|
|
$all_copied = [];
|
|
|
|
|
$map = [
|
|
|
|
|
'panel' => ['source' => $update_cfg['panel_source_path'], 'dest' => $update_cfg['panel_path'], 'post' => $update_cfg['panel_post_update_command']],
|
|
|
|
|
'website' => ['source' => $update_cfg['website_source_path'], 'dest' => $update_cfg['website_path'], 'post' => $update_cfg['website_post_update_command']],
|
|
|
|
|
];
|
|
|
|
|
foreach ($components as $component) {
|
|
|
|
|
$source = gsp_component_source_path($checkout['source_root'], $map[$component]['source']);
|
|
|
|
|
if ($source === false) {
|
|
|
|
|
gsp_rmdir_recursive($checkout['temp_dir']);
|
|
|
|
|
return ['success' => false, 'error' => 'Source folder for ' . $component . ' is invalid or missing.'];
|
|
|
|
|
}
|
|
|
|
|
$dest = rtrim((string)$map[$component]['dest'], '/');
|
|
|
|
|
if (strpos($dest, '/') !== 0 || strpos($dest, '..') !== false || strpos($dest, "\0") !== false) {
|
|
|
|
|
gsp_rmdir_recursive($checkout['temp_dir']);
|
|
|
|
|
return ['success' => false, 'error' => 'Destination path for ' . $component . ' is invalid.'];
|
|
|
|
|
}
|
|
|
|
|
$backup = ['success' => true, 'backup_dir' => null];
|
|
|
|
|
if (!empty($update_cfg['backup_before_update'])) {
|
|
|
|
|
$backup = gsp_safe_component_backup($component, $dest, $update_cfg['backup_path']);
|
|
|
|
|
if (!$backup['success']) {
|
|
|
|
|
gsp_rmdir_recursive($checkout['temp_dir']);
|
|
|
|
|
return $backup;
|
|
|
|
|
}
|
|
|
|
|
gsp_update_log("Backup created at {$backup['backup_ts']} before {$update_type} update to {$ref}");
|
|
|
|
|
|
|
|
|
|
$temp_dir = sys_get_temp_dir() . '/gsp_dl_' . time() . '_' . mt_rand(1000, 9999);
|
|
|
|
|
@mkdir($temp_dir, 0750, true);
|
|
|
|
|
$zip_file = gsp_download_zip($repo_owner, $repo_name, $ref, $temp_dir);
|
|
|
|
|
if (!$zip_file) {
|
|
|
|
|
gsp_rmdir_recursive($temp_dir);
|
|
|
|
|
return ['success' => false, 'error' => 'Failed to download update ZIP from GitHub.'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$apply = gsp_apply_update_from_zip($zip_file, $restart_nonce);
|
|
|
|
|
@unlink($zip_file);
|
|
|
|
|
gsp_rmdir_recursive($temp_dir);
|
|
|
|
|
if (!empty($apply['restart_required'])) {
|
|
|
|
|
$apply['backup_dir'] = $backup['backup_dir'];
|
|
|
|
|
$apply['success'] = false;
|
|
|
|
|
return $apply;
|
|
|
|
|
$sync = gsp_component_copy_tree($source, $dest, ucfirst($component));
|
|
|
|
|
if (!$sync['success']) {
|
|
|
|
|
gsp_rmdir_recursive($checkout['temp_dir']);
|
|
|
|
|
return $sync;
|
|
|
|
|
}
|
|
|
|
|
if (!$apply['success']) {
|
|
|
|
|
return $apply;
|
|
|
|
|
$post = gsp_run_admin_post_update_command($map[$component]['post'], $dest);
|
|
|
|
|
if (!$post['success']) {
|
|
|
|
|
gsp_rmdir_recursive($checkout['temp_dir']);
|
|
|
|
|
return ['success' => false, 'error' => ucfirst($component) . ' post-update command failed: ' . $post['error'], 'results' => $results];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$commit_after = gsp_get_git_commit();
|
|
|
|
|
gsp_fix_permissions(GSP_ROOT_DIR);
|
|
|
|
|
gsp_clear_panel_cache(GSP_PANEL_DIR);
|
|
|
|
|
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);
|
|
|
|
|
$results[$component] = [
|
|
|
|
|
'source' => $source,
|
|
|
|
|
'destination' => $dest,
|
|
|
|
|
'backup_dir' => $backup['backup_dir'],
|
|
|
|
|
'files_copied' => $sync['files_copied'],
|
|
|
|
|
'skipped' => $sync['skipped'],
|
|
|
|
|
'post_update' => $post,
|
|
|
|
|
];
|
|
|
|
|
$total += (int)$sync['files_copied'];
|
|
|
|
|
$all_skipped = array_merge($all_skipped, $sync['skipped']);
|
|
|
|
|
$all_copied = array_merge($all_copied, $sync['copied_files']);
|
|
|
|
|
gsp_update_log('Local component update complete: ' . $component . ' copied=' . intval($sync['files_copied']) . ' source=' . $source . ' dest=' . $dest);
|
|
|
|
|
}
|
|
|
|
|
gsp_rmdir_recursive($checkout['temp_dir']);
|
|
|
|
|
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')) {
|
|
|
|
|
require_once(GSP_PANEL_DIR . '/modules/modulemanager/module_handling.php');
|
|
|
|
|
if (in_array('panel', $components, true)) {
|
|
|
|
|
gsp_clear_panel_cache($update_cfg['panel_path']);
|
|
|
|
|
gsp_fix_permissions($update_cfg['panel_path']);
|
|
|
|
|
}
|
|
|
|
|
if (function_exists('updateAllPanelModules')) {
|
|
|
|
|
updateAllPanelModules();
|
|
|
|
|
}
|
|
|
|
|
if (function_exists('runPostUpdateOperations')) {
|
|
|
|
|
runPostUpdateOperations();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gsp_update_log("Update to {$ref} (type={$update_type}) complete");
|
|
|
|
|
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'],
|
|
|
|
|
'files_copied' => $total,
|
|
|
|
|
'results' => $results,
|
|
|
|
|
'preserved' => array_values(array_unique($all_skipped)),
|
|
|
|
|
'copied_files' => array_slice(array_values(array_unique($all_copied)), 0, 200),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_update_payload_encode(array $payload)
|
|
|
|
|
{
|
|
|
|
|
$lines = [];
|
|
|
|
|
foreach ($payload as $key => $value) {
|
|
|
|
|
if (is_array($value)) {
|
|
|
|
|
$value = json_encode($value, JSON_UNESCAPED_SLASHES);
|
|
|
|
|
}
|
|
|
|
|
$lines[] = $key . '=' . base64_encode((string)$value);
|
|
|
|
|
}
|
|
|
|
|
return implode("\n", $lines);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_infer_agent_os(array $remote_server, $reported_os = '')
|
|
|
|
|
{
|
|
|
|
|
$reported = strtolower((string)$reported_os);
|
|
|
|
|
$haystack = strtolower((string)$remote_server['remote_server_name'] . ' ' . (string)$remote_server['agent_ip'] . ' ' . $reported);
|
|
|
|
|
if (strpos($haystack, 'windows') !== false || strpos($haystack, 'cygwin') !== false || strpos($haystack, 'mingw') !== false) {
|
|
|
|
|
return 'windows';
|
|
|
|
|
}
|
|
|
|
|
return 'linux';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_get_remote_agent_rows()
|
|
|
|
|
{
|
|
|
|
|
global $db;
|
|
|
|
|
$rows = $db->getRemoteServers();
|
|
|
|
|
return is_array($rows) ? $rows : [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_dispatch_remote_agent_update(array $remote_server, array $update_cfg, $component)
|
|
|
|
|
{
|
|
|
|
|
if (!function_exists('xmlrpc_encode_request') || !function_exists('xmlrpc_decode')) {
|
|
|
|
|
return ['success' => false, 'status' => 'missing_xmlrpc', 'message' => 'PHP xmlrpc extension is required for remote agent updates.'];
|
|
|
|
|
}
|
|
|
|
|
require_once(GSP_PANEL_DIR . '/includes/lib_remote.php');
|
|
|
|
|
$component = ($component === 'windows_agent') ? 'windows_agent' : 'linux_agent';
|
|
|
|
|
$source_path = ($component === 'windows_agent') ? $update_cfg['windows_agent_source_path'] : $update_cfg['linux_agent_source_path'];
|
|
|
|
|
$post_cmd = ($component === 'windows_agent') ? $update_cfg['windows_agent_post_update_command'] : $update_cfg['linux_agent_post_update_command'];
|
|
|
|
|
$payload = [
|
|
|
|
|
'component' => $component,
|
|
|
|
|
'repo_url' => $update_cfg['repo_url'],
|
|
|
|
|
'branch' => $update_cfg['branch'],
|
|
|
|
|
'source_path' => $source_path,
|
|
|
|
|
'install_path' => '',
|
|
|
|
|
'git_path' => $update_cfg['git_path'],
|
|
|
|
|
'backup_path' => '',
|
|
|
|
|
'post_update_command' => $post_cmd,
|
|
|
|
|
'requested_at' => date('Y-m-d H:i:s'),
|
|
|
|
|
];
|
|
|
|
|
$remote = new OGPRemoteLibrary($remote_server['agent_ip'], $remote_server['agent_port'], $remote_server['encryption_key'], $remote_server['timeout']);
|
|
|
|
|
if ((int)$remote->status_chk() !== 1) {
|
|
|
|
|
return ['success' => false, 'status' => 'agent_unreachable', 'message' => 'Agent unreachable or encryption mismatch.'];
|
|
|
|
|
}
|
|
|
|
|
if (!method_exists($remote, 'component_update')) {
|
|
|
|
|
return ['success' => false, 'status' => 'panel_missing_rpc_wrapper', 'message' => 'Panel remote library does not expose component_update().'];
|
|
|
|
|
}
|
|
|
|
|
$response = $remote->component_update(gsp_update_payload_encode($payload));
|
|
|
|
|
if (is_array($response)) {
|
|
|
|
|
return $response;
|
|
|
|
|
}
|
|
|
|
|
return ['success' => false, 'status' => 'unexpected_response', 'message' => 'Unexpected agent response: ' . substr((string)$response, 0, 300)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_do_remote_agents_update(array $update_cfg, array $remote_ids, array $component_filters)
|
|
|
|
|
{
|
|
|
|
|
global $db;
|
|
|
|
|
if (!function_exists('xmlrpc_encode_request') || !function_exists('xmlrpc_decode')) {
|
|
|
|
|
return ['success' => false, 'results' => [], 'success_count' => 0, 'message' => 'PHP xmlrpc extension is required for remote agent updates.'];
|
|
|
|
|
}
|
|
|
|
|
require_once(GSP_PANEL_DIR . '/includes/lib_remote.php');
|
|
|
|
|
$remote_ids = array_map('intval', $remote_ids);
|
|
|
|
|
$remote_ids = array_values(array_filter(array_unique($remote_ids)));
|
|
|
|
|
if (empty($remote_ids)) {
|
|
|
|
|
return ['success' => true, 'results' => [], 'message' => 'No remote agents selected.'];
|
|
|
|
|
}
|
|
|
|
|
$all = gsp_get_remote_agent_rows();
|
|
|
|
|
$by_id = [];
|
|
|
|
|
foreach ($all as $row) {
|
|
|
|
|
$by_id[(int)$row['remote_server_id']] = $row;
|
|
|
|
|
}
|
|
|
|
|
$results = [];
|
|
|
|
|
$success_count = 0;
|
|
|
|
|
foreach ($remote_ids as $remote_id) {
|
|
|
|
|
if (empty($by_id[$remote_id])) {
|
|
|
|
|
$results[$remote_id] = ['success' => false, 'status' => 'not_found', 'message' => 'Remote host not found.'];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$row = $by_id[$remote_id];
|
|
|
|
|
$reported_os = '';
|
|
|
|
|
try {
|
|
|
|
|
$probe = new OGPRemoteLibrary($row['agent_ip'], $row['agent_port'], $row['encryption_key'], $row['timeout']);
|
|
|
|
|
$reported_os = (string)$probe->what_os();
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
$reported_os = '';
|
|
|
|
|
}
|
|
|
|
|
$agent_os = gsp_infer_agent_os($row, $reported_os);
|
|
|
|
|
if ($agent_os === 'linux' && !in_array('linux_agent', $component_filters, true)) {
|
|
|
|
|
$results[$remote_id] = ['success' => true, 'status' => 'skipped', 'message' => 'Skipped Linux agent by filter.', 'agent_os' => $agent_os];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ($agent_os === 'windows' && !in_array('windows_agent', $component_filters, true)) {
|
|
|
|
|
$results[$remote_id] = ['success' => true, 'status' => 'skipped', 'message' => 'Skipped Windows agent by filter.', 'agent_os' => $agent_os];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$component = ($agent_os === 'windows') ? 'windows_agent' : 'linux_agent';
|
|
|
|
|
$result = gsp_dispatch_remote_agent_update($row, $update_cfg, $component);
|
|
|
|
|
$result['agent_os'] = $agent_os;
|
|
|
|
|
$result['remote_server_name'] = $row['remote_server_name'];
|
|
|
|
|
$result['agent_ip'] = $row['agent_ip'];
|
|
|
|
|
$results[$remote_id] = $result;
|
|
|
|
|
if (!empty($result['success'])) {
|
|
|
|
|
$success_count++;
|
|
|
|
|
}
|
|
|
|
|
gsp_update_log('Remote agent update requested: id=' . $remote_id . ' component=' . $component . ' result=' . json_encode($result));
|
|
|
|
|
}
|
|
|
|
|
return ['success' => true, 'results' => $results, 'success_count' => $success_count];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gsp_get_available_backups()
|
|
|
|
|
{
|
|
|
|
|
$backups = [];
|
|
|
|
|
@ -1947,6 +2310,17 @@ $new_cfg = [
|
|
|
|
|
'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']), '/') : '',
|
|
|
|
|
'website_path' => isset($_POST['gsp_update_website_path']) ? rtrim(trim((string)$_POST['gsp_update_website_path']), '/') : '',
|
|
|
|
|
'panel_source_path' => isset($_POST['gsp_update_panel_source_path']) ? trim((string)$_POST['gsp_update_panel_source_path'], '/') : '',
|
|
|
|
|
'linux_agent_source_path' => isset($_POST['gsp_update_linux_agent_source_path']) ? trim((string)$_POST['gsp_update_linux_agent_source_path'], '/') : '',
|
|
|
|
|
'windows_agent_source_path' => isset($_POST['gsp_update_windows_agent_source_path']) ? trim((string)$_POST['gsp_update_windows_agent_source_path'], '/') : '',
|
|
|
|
|
'website_source_path' => isset($_POST['gsp_update_website_source_path']) ? trim((string)$_POST['gsp_update_website_source_path'], '/') : '',
|
|
|
|
|
'git_path' => isset($_POST['gsp_update_git_path']) ? trim((string)$_POST['gsp_update_git_path']) : '',
|
|
|
|
|
'backup_path' => isset($_POST['gsp_update_backup_path']) ? rtrim(trim((string)$_POST['gsp_update_backup_path']), '/') : '',
|
|
|
|
|
'panel_post_update_command' => isset($_POST['gsp_update_panel_post_update_command']) ? trim((string)$_POST['gsp_update_panel_post_update_command']) : '',
|
|
|
|
|
'website_post_update_command' => isset($_POST['gsp_update_website_post_update_command']) ? trim((string)$_POST['gsp_update_website_post_update_command']) : '',
|
|
|
|
|
'linux_agent_post_update_command' => isset($_POST['gsp_update_linux_agent_post_update_command']) ? trim((string)$_POST['gsp_update_linux_agent_post_update_command']) : '',
|
|
|
|
|
'windows_agent_post_update_command' => isset($_POST['gsp_update_windows_agent_post_update_command']) ? trim((string)$_POST['gsp_update_windows_agent_post_update_command']) : '',
|
|
|
|
|
'backup_before_update' => !empty($_POST['gsp_update_backup_before']) ? '1' : '0',
|
|
|
|
|
];
|
|
|
|
|
$errors = gsp_validate_update_settings($new_cfg);
|
|
|
|
|
@ -1958,22 +2332,122 @@ $db->setSettings([
|
|
|
|
|
'gsp_update_branch' => $new_cfg['branch'],
|
|
|
|
|
'gsp_update_repo_root' => $new_cfg['repo_root'],
|
|
|
|
|
'gsp_update_panel_path' => $new_cfg['panel_path'],
|
|
|
|
|
'gsp_update_website_path' => $new_cfg['website_path'],
|
|
|
|
|
'gsp_update_panel_source_path' => $new_cfg['panel_source_path'],
|
|
|
|
|
'gsp_update_linux_agent_source_path' => $new_cfg['linux_agent_source_path'],
|
|
|
|
|
'gsp_update_windows_agent_source_path' => $new_cfg['windows_agent_source_path'],
|
|
|
|
|
'gsp_update_website_source_path' => $new_cfg['website_source_path'],
|
|
|
|
|
'gsp_update_git_path' => $new_cfg['git_path'],
|
|
|
|
|
'gsp_update_backup_path' => $new_cfg['backup_path'],
|
|
|
|
|
'gsp_update_panel_post_update_command' => $new_cfg['panel_post_update_command'],
|
|
|
|
|
'gsp_update_website_post_update_command' => $new_cfg['website_post_update_command'],
|
|
|
|
|
'gsp_update_linux_agent_post_update_command' => $new_cfg['linux_agent_post_update_command'],
|
|
|
|
|
'gsp_update_windows_agent_post_update_command' => $new_cfg['windows_agent_post_update_command'],
|
|
|
|
|
'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_website_path'] = $new_cfg['website_path'];
|
|
|
|
|
$settings['gsp_update_panel_source_path'] = $new_cfg['panel_source_path'];
|
|
|
|
|
$settings['gsp_update_linux_agent_source_path'] = $new_cfg['linux_agent_source_path'];
|
|
|
|
|
$settings['gsp_update_windows_agent_source_path'] = $new_cfg['windows_agent_source_path'];
|
|
|
|
|
$settings['gsp_update_website_source_path'] = $new_cfg['website_source_path'];
|
|
|
|
|
$settings['gsp_update_git_path'] = $new_cfg['git_path'];
|
|
|
|
|
$settings['gsp_update_backup_path'] = $new_cfg['backup_path'];
|
|
|
|
|
$settings['gsp_update_panel_post_update_command'] = $new_cfg['panel_post_update_command'];
|
|
|
|
|
$settings['gsp_update_website_post_update_command'] = $new_cfg['website_post_update_command'];
|
|
|
|
|
$settings['gsp_update_linux_agent_post_update_command'] = $new_cfg['linux_agent_post_update_command'];
|
|
|
|
|
$settings['gsp_update_windows_agent_post_update_command'] = $new_cfg['windows_agent_post_update_command'];
|
|
|
|
|
$settings['gsp_update_backup_before'] = $new_cfg['backup_before_update'];
|
|
|
|
|
$update_cfg = gsp_update_settings();
|
|
|
|
|
print_success('Update settings saved.');
|
|
|
|
|
}
|
|
|
|
|
} elseif ($action === 'update_components') {
|
|
|
|
|
$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'],
|
|
|
|
|
'website_path' => isset($_POST['gsp_update_website_path']) ? rtrim(trim((string)$_POST['gsp_update_website_path']), '/') : $update_cfg['website_path'],
|
|
|
|
|
'panel_source_path' => isset($_POST['gsp_update_panel_source_path']) ? trim((string)$_POST['gsp_update_panel_source_path'], '/') : $update_cfg['panel_source_path'],
|
|
|
|
|
'linux_agent_source_path' => isset($_POST['gsp_update_linux_agent_source_path']) ? trim((string)$_POST['gsp_update_linux_agent_source_path'], '/') : $update_cfg['linux_agent_source_path'],
|
|
|
|
|
'windows_agent_source_path' => isset($_POST['gsp_update_windows_agent_source_path']) ? trim((string)$_POST['gsp_update_windows_agent_source_path'], '/') : $update_cfg['windows_agent_source_path'],
|
|
|
|
|
'website_source_path' => isset($_POST['gsp_update_website_source_path']) ? trim((string)$_POST['gsp_update_website_source_path'], '/') : $update_cfg['website_source_path'],
|
|
|
|
|
'git_path' => isset($_POST['gsp_update_git_path']) ? trim((string)$_POST['gsp_update_git_path']) : $update_cfg['git_path'],
|
|
|
|
|
'backup_path' => isset($_POST['gsp_update_backup_path']) ? rtrim(trim((string)$_POST['gsp_update_backup_path']), '/') : $update_cfg['backup_path'],
|
|
|
|
|
'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'],
|
|
|
|
|
'website_post_update_command' => isset($_POST['gsp_update_website_post_update_command']) ? trim((string)$_POST['gsp_update_website_post_update_command']) : $update_cfg['website_post_update_command'],
|
|
|
|
|
'linux_agent_post_update_command' => isset($_POST['gsp_update_linux_agent_post_update_command']) ? trim((string)$_POST['gsp_update_linux_agent_post_update_command']) : $update_cfg['linux_agent_post_update_command'],
|
|
|
|
|
'windows_agent_post_update_command' => isset($_POST['gsp_update_windows_agent_post_update_command']) ? trim((string)$_POST['gsp_update_windows_agent_post_update_command']) : $update_cfg['windows_agent_post_update_command'],
|
|
|
|
|
'backup_before_update' => !empty($_POST['gsp_update_backup_before']) ? '1' : '0',
|
|
|
|
|
];
|
|
|
|
|
$errors = gsp_validate_update_settings($update_cfg);
|
|
|
|
|
if (!empty($errors)) {
|
|
|
|
|
print_failure('Update settings are invalid: ' . htmlspecialchars(implode(' | ', $errors)));
|
|
|
|
|
} else {
|
|
|
|
|
$requested_components = isset($_POST['gsp_update_components']) && is_array($_POST['gsp_update_components']) ? $_POST['gsp_update_components'] : [];
|
|
|
|
|
$requested_components = array_values(array_intersect(array('panel', 'website', 'linux_agent', 'windows_agent'), array_map('strval', $requested_components)));
|
|
|
|
|
$local_components = array_values(array_intersect(array('panel', 'website'), $requested_components));
|
|
|
|
|
$agent_components = array_values(array_intersect(array('linux_agent', 'windows_agent'), $requested_components));
|
|
|
|
|
$remote_ids = isset($_POST['gsp_update_remote_ids']) && is_array($_POST['gsp_update_remote_ids']) ? array_map('intval', $_POST['gsp_update_remote_ids']) : [];
|
|
|
|
|
if (!empty($_POST['gsp_update_all_agents'])) {
|
|
|
|
|
$remote_ids = array();
|
|
|
|
|
foreach (gsp_get_remote_agent_rows() as $row) {
|
|
|
|
|
$remote_ids[] = (int)$row['remote_server_id'];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (empty($requested_components)) {
|
|
|
|
|
print_failure('Select at least one component to update.');
|
|
|
|
|
} else {
|
|
|
|
|
$messages = [];
|
|
|
|
|
if (!empty($local_components)) {
|
|
|
|
|
$local_result = gsp_do_local_component_update($update_cfg, $local_components);
|
|
|
|
|
if (!$local_result['success']) {
|
|
|
|
|
print_failure('Local component update failed: ' . htmlspecialchars($local_result['error']));
|
|
|
|
|
} else {
|
|
|
|
|
$messages[] = 'Local components updated, files copied: ' . intval($local_result['files_copied']);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!empty($agent_components)) {
|
|
|
|
|
$remote_result = gsp_do_remote_agents_update($update_cfg, $remote_ids, $agent_components);
|
|
|
|
|
if (empty($remote_result['success']) && empty($remote_result['results'])) {
|
|
|
|
|
print_failure('Remote agent update failed: ' . htmlspecialchars(isset($remote_result['message']) ? $remote_result['message'] : 'Unknown error.'));
|
|
|
|
|
}
|
|
|
|
|
if (!empty($remote_result['results'])) {
|
|
|
|
|
echo "<h4>Remote Agent Update Results</h4><table class='center'><tr><th>Remote ID</th><th>Status</th><th>Message</th></tr>";
|
|
|
|
|
foreach ($remote_result['results'] as $rid => $result_row) {
|
|
|
|
|
$status = !empty($result_row['success']) ? (isset($result_row['status']) ? $result_row['status'] : 'queued') : (isset($result_row['status']) ? $result_row['status'] : 'failed');
|
|
|
|
|
$msg = isset($result_row['message']) ? $result_row['message'] : (isset($result_row['log_path']) ? $result_row['log_path'] : '');
|
|
|
|
|
echo "<tr><td>" . intval($rid) . "</td><td>" . htmlspecialchars($status) . "</td><td>" . htmlspecialchars($msg) . "</td></tr>";
|
|
|
|
|
}
|
|
|
|
|
echo "</table>";
|
|
|
|
|
}
|
|
|
|
|
$messages[] = 'Remote agent updates requested: ' . intval(isset($remote_result['success_count']) ? $remote_result['success_count'] : 0);
|
|
|
|
|
}
|
|
|
|
|
if (!empty($messages)) {
|
|
|
|
|
print_success(implode(' | ', array_map('htmlspecialchars', $messages)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} 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'],
|
|
|
|
|
'website_path' => isset($_POST['gsp_update_website_path']) ? rtrim(trim((string)$_POST['gsp_update_website_path']), '/') : $update_cfg['website_path'],
|
|
|
|
|
'panel_source_path' => isset($_POST['gsp_update_panel_source_path']) ? trim((string)$_POST['gsp_update_panel_source_path'], '/') : $update_cfg['panel_source_path'],
|
|
|
|
|
'linux_agent_source_path' => isset($_POST['gsp_update_linux_agent_source_path']) ? trim((string)$_POST['gsp_update_linux_agent_source_path'], '/') : $update_cfg['linux_agent_source_path'],
|
|
|
|
|
'windows_agent_source_path' => isset($_POST['gsp_update_windows_agent_source_path']) ? trim((string)$_POST['gsp_update_windows_agent_source_path'], '/') : $update_cfg['windows_agent_source_path'],
|
|
|
|
|
'website_source_path' => isset($_POST['gsp_update_website_source_path']) ? trim((string)$_POST['gsp_update_website_source_path'], '/') : $update_cfg['website_source_path'],
|
|
|
|
|
'git_path' => isset($_POST['gsp_update_git_path']) ? trim((string)$_POST['gsp_update_git_path']) : $update_cfg['git_path'],
|
|
|
|
|
'backup_path' => isset($_POST['gsp_update_backup_path']) ? rtrim(trim((string)$_POST['gsp_update_backup_path']), '/') : $update_cfg['backup_path'],
|
|
|
|
|
'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'],
|
|
|
|
|
'website_post_update_command' => isset($_POST['gsp_update_website_post_update_command']) ? trim((string)$_POST['gsp_update_website_post_update_command']) : $update_cfg['website_post_update_command'],
|
|
|
|
|
'linux_agent_post_update_command' => isset($_POST['gsp_update_linux_agent_post_update_command']) ? trim((string)$_POST['gsp_update_linux_agent_post_update_command']) : $update_cfg['linux_agent_post_update_command'],
|
|
|
|
|
'windows_agent_post_update_command' => isset($_POST['gsp_update_windows_agent_post_update_command']) ? trim((string)$_POST['gsp_update_windows_agent_post_update_command']) : $update_cfg['windows_agent_post_update_command'],
|
|
|
|
|
'backup_before_update' => !empty($_POST['gsp_update_backup_before']) ? '1' : '0',
|
|
|
|
|
];
|
|
|
|
|
$db->setSettings([
|
|
|
|
|
@ -1981,9 +2455,24 @@ $db->setSettings([
|
|
|
|
|
'gsp_update_branch' => $update_cfg['branch'],
|
|
|
|
|
'gsp_update_repo_root' => $update_cfg['repo_root'],
|
|
|
|
|
'gsp_update_panel_path' => $update_cfg['panel_path'],
|
|
|
|
|
'gsp_update_website_path' => $update_cfg['website_path'],
|
|
|
|
|
'gsp_update_panel_source_path' => $update_cfg['panel_source_path'],
|
|
|
|
|
'gsp_update_linux_agent_source_path' => $update_cfg['linux_agent_source_path'],
|
|
|
|
|
'gsp_update_windows_agent_source_path' => $update_cfg['windows_agent_source_path'],
|
|
|
|
|
'gsp_update_website_source_path' => $update_cfg['website_source_path'],
|
|
|
|
|
'gsp_update_git_path' => $update_cfg['git_path'],
|
|
|
|
|
'gsp_update_backup_path' => $update_cfg['backup_path'],
|
|
|
|
|
'gsp_update_panel_post_update_command' => $update_cfg['panel_post_update_command'],
|
|
|
|
|
'gsp_update_website_post_update_command' => $update_cfg['website_post_update_command'],
|
|
|
|
|
'gsp_update_linux_agent_post_update_command' => $update_cfg['linux_agent_post_update_command'],
|
|
|
|
|
'gsp_update_windows_agent_post_update_command' => $update_cfg['windows_agent_post_update_command'],
|
|
|
|
|
'gsp_update_backup_before' => $update_cfg['backup_before_update'],
|
|
|
|
|
]);
|
|
|
|
|
if (!function_exists('gsp_do_configured_git_update')) {
|
|
|
|
|
$result = ['success' => false, 'error' => 'Configured Git updater helper is unavailable. Deploy the current Panel/modules/administration/panel_update.php and reload.'];
|
|
|
|
|
} else {
|
|
|
|
|
$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']];
|
|
|
|
|
@ -2101,12 +2590,65 @@ echo "<tr><td><strong>Repository URL / Path</strong></td><td><input type='text'
|
|
|
|
|
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>Website Path</strong></td><td><input type='text' name='gsp_update_website_path' value='" . htmlspecialchars($update_cfg['website_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>Linux Agent Source Folder</strong></td><td><input type='text' name='gsp_update_linux_agent_source_path' value='" . htmlspecialchars($update_cfg['linux_agent_source_path'], ENT_QUOTES, 'UTF-8') . "' size='45'></td></tr>\n";
|
|
|
|
|
echo "<tr><td><strong>Windows Agent Source Folder</strong></td><td><input type='text' name='gsp_update_windows_agent_source_path' value='" . htmlspecialchars($update_cfg['windows_agent_source_path'], ENT_QUOTES, 'UTF-8') . "' size='45'></td></tr>\n";
|
|
|
|
|
echo "<tr><td><strong>Website Source Folder</strong></td><td><input type='text' name='gsp_update_website_source_path' value='" . htmlspecialchars($update_cfg['website_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 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 "<tr><td><strong>Website Post-update Command</strong></td><td><input type='text' name='gsp_update_website_post_update_command' value='" . htmlspecialchars($update_cfg['website_post_update_command'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
|
|
|
|
|
echo "<tr><td><strong>Linux Agent Post-update Command</strong></td><td><input type='text' name='gsp_update_linux_agent_post_update_command' value='" . htmlspecialchars($update_cfg['linux_agent_post_update_command'], ENT_QUOTES, 'UTF-8') . "' size='85'></td></tr>\n";
|
|
|
|
|
echo "<tr><td><strong>Windows Agent Post-update Command</strong></td><td><input type='text' name='gsp_update_windows_agent_post_update_command' value='" . htmlspecialchars($update_cfg['windows_agent_post_update_command'], ENT_QUOTES, 'UTF-8') . "' size='85'></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 "<h3>Update Components</h3>\n";
|
|
|
|
|
echo "<p>Select the application components to update from the configured repository. Server homes, hosted game data, uploads, logs, cache, and agent configuration folders are protected.</p>\n";
|
|
|
|
|
echo "<p>";
|
|
|
|
|
echo "<label><input type='checkbox' name='gsp_update_components[]' value='panel'> Panel files</label> ";
|
|
|
|
|
echo "<label><input type='checkbox' name='gsp_update_components[]' value='website'> Website files</label> ";
|
|
|
|
|
echo "<label><input type='checkbox' name='gsp_update_components[]' value='linux_agent'> Linux agents</label> ";
|
|
|
|
|
echo "<label><input type='checkbox' name='gsp_update_components[]' value='windows_agent'> Windows agents</label> ";
|
|
|
|
|
echo "<label><input type='checkbox' name='gsp_update_all_agents' value='1'> All remote agents</label>";
|
|
|
|
|
echo "</p>\n";
|
|
|
|
|
$remote_rows = gsp_get_remote_agent_rows();
|
|
|
|
|
if (!empty($remote_rows)) {
|
|
|
|
|
if (!class_exists('OGPRemoteLibrary') && function_exists('xmlrpc_encode_request') && function_exists('xmlrpc_decode') && file_exists(GSP_PANEL_DIR . '/includes/lib_remote.php')) {
|
|
|
|
|
require_once(GSP_PANEL_DIR . '/includes/lib_remote.php');
|
|
|
|
|
}
|
|
|
|
|
echo "<table class='center'><tr><th>Select</th><th>Name</th><th>Host</th><th>Status</th><th>Reported OS</th></tr>\n";
|
|
|
|
|
foreach ($remote_rows as $remote_row) {
|
|
|
|
|
$status_text = 'not checked';
|
|
|
|
|
$os_text = '';
|
|
|
|
|
if (class_exists('OGPRemoteLibrary')) {
|
|
|
|
|
try {
|
|
|
|
|
$probe = new OGPRemoteLibrary($remote_row['agent_ip'], $remote_row['agent_port'], $remote_row['encryption_key'], 2);
|
|
|
|
|
$status_text = ((int)$probe->status_chk() === 1) ? 'online' : 'offline/unknown';
|
|
|
|
|
if ($status_text === 'online') {
|
|
|
|
|
$os_text = (string)$probe->what_os();
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
$status_text = 'unknown';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
echo "<tr>";
|
|
|
|
|
echo "<td><input type='checkbox' name='gsp_update_remote_ids[]' value='" . intval($remote_row['remote_server_id']) . "'></td>";
|
|
|
|
|
echo "<td>" . htmlspecialchars($remote_row['remote_server_name']) . "</td>";
|
|
|
|
|
echo "<td>" . htmlspecialchars($remote_row['agent_ip'] . ':' . $remote_row['agent_port']) . "</td>";
|
|
|
|
|
echo "<td>" . htmlspecialchars($status_text) . "</td>";
|
|
|
|
|
echo "<td>" . htmlspecialchars($os_text !== '' ? $os_text : gsp_infer_agent_os($remote_row, '')) . "</td>";
|
|
|
|
|
echo "</tr>\n";
|
|
|
|
|
}
|
|
|
|
|
echo "</table>\n";
|
|
|
|
|
} else {
|
|
|
|
|
echo "<p>No remote agents are configured.</p>\n";
|
|
|
|
|
}
|
|
|
|
|
echo "<p><button type='submit' name='gsp_update_action' value='update_components' onclick='return confirm(\"Update selected components from the configured repository? Remote agents may restart.\");'>Update Selected Components</button></p>\n";
|
|
|
|
|
echo "</form>\n";
|
|
|
|
|
|
|
|
|
|
echo "<h3>Backup</h3>\n";
|
|
|
|
|
|