feat(steam_workshop): production rewrite with full admin profile page and server settings

- module.php: bump db_version to 2; full v2 schema in install_queries[0]; ALTER TABLE migration in install_queries[2] adding 11 new profile columns plus server_workshop_settings table
- WorkshopRepository: expand saveProfile() with all new fields (steam_app_id, steamcmd_login_mode, steamcmd_path, mod_separator, copy_keys, key paths, pre/post scripts, validation_notes); add nullOrStr helper; add server settings CRUD (getServerSettings, saveServerSettings, recordUpdateResult, setUpdateQueued, listQueuedUpdateHomes); update insertOrUpdateMod with custom_folder; update listAllEnabledMods to select new columns
- WorkshopProfileController: full extractProfileData() with all new fields; validateProfileData with folder template check
- views/admin/profile_form.php: full rewrite – all required fields (steam_app_id, workshop_app_id, login mode, SteamCMD path, OS, cache path, install path, folder naming dropdown, launch params, mod separator, copy method, copy keys with JS show/hide, key paths, pre/per-mod/post bash scripts with DayZ example, validation notes, config file template)
- views/admin/profiles.php: updated list view columns (App IDs, Login, Method)
- WorkshopInstaller.php: full rewrite with %var% template support (+ legacy {var} compat); buildTemplateVars() resolves all 14 variables; pre/post script execution; triggerSteamCmdDownload uses new profile fields; copyKeys helper
- WorkshopModController: add save_settings and queue_update POST actions; handleModsPage loads serverSettings + allProfiles
- views/user_workshop_mods.php: full rewrite – server settings form (enable, profile selector, update mode, restart behavior), update status grid (status/error/time/success time), queue update button, mod table with custom_folder column and toggle/order auto-submit
- lang/en_US.php: ~80 new string keys for all v2 fields
- steam_workshop.css: new v2 styles (3-col grid, script textarea, status grid, server settings card, badges)"

Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/e7f0d80d-f775-4794-adbd-cf48b55bc9c1

Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-05-04 19:49:36 +00:00 committed by GitHub
parent 199b398543
commit 69f415ad86
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 1334 additions and 312 deletions

View file

@ -157,24 +157,58 @@ class WorkshopProfileController
$osValues = array_values(array_intersect($osRaw, $allowedOs));
$supportedOs = implode(',', $osValues !== [] ? $osValues : ['linux']);
$allowedMethods = ['rsync', 'robocopy', 'custom_script'];
$copyMethod = in_array($post['copy_method'] ?? '', $allowedMethods, true)
$allowedCopyMethods = ['copy', 'rsync', 'symlink'];
$copyMethod = in_array($post['copy_method'] ?? '', $allowedCopyMethods, true)
? (string)$post['copy_method']
: 'rsync';
$allowedLoginModes = ['anonymous', 'account'];
$steamcmdLoginMode = in_array($post['steamcmd_login_mode'] ?? '', $allowedLoginModes, true)
? (string)$post['steamcmd_login_mode']
: 'anonymous';
$allowedFolderFormats = ['@%mod_name%', '@%workshop_id%', 'custom'];
$folderNamingFormat = in_array($post['folder_naming_format'] ?? '', $allowedFolderFormats, true)
? (string)$post['folder_naming_format']
: '@%workshop_id%';
$allowedSeparators = ['semicolon', 'comma', 'space'];
$modSeparator = in_array($post['mod_separator'] ?? '', $allowedSeparators, true)
? (string)$post['mod_separator']
: 'semicolon';
// When folder naming is preset (@%mod_name% or @%workshop_id%), derive template from format.
// When 'custom', use the admin-supplied value.
$folderNameTemplate = $folderNamingFormat !== 'custom'
? $folderNamingFormat
: trim((string)($post['folder_name_template'] ?? '@%workshop_id%'));
return [
'game_key' => trim((string)($post['game_key'] ?? '')),
'game_name' => trim((string)($post['game_name'] ?? '')),
'steam_app_id' => preg_replace('/[^0-9]/', '', (string)($post['steam_app_id'] ?? '')) ?? '',
'workshop_app_id' => preg_replace('/[^0-9]/', '', (string)($post['workshop_app_id'] ?? '')) ?? '',
'steam_login_required' => !empty($post['steam_login_required']) ? 1 : 0,
'steamcmd_login_mode' => $steamcmdLoginMode,
'steamcmd_path' => trim((string)($post['steamcmd_path'] ?? '')),
'supported_os' => $supportedOs,
'cache_path_template' => trim((string)($post['cache_path_template'] ?? '')),
'install_path_template' => trim((string)($post['install_path_template'] ?? '')),
'folder_name_template' => trim((string)($post['folder_name_template'] ?? '@{mod_id}')),
'folder_naming_format' => $folderNamingFormat,
'folder_name_template' => $folderNameTemplate,
'mod_launch_param' => trim((string)($post['mod_launch_param'] ?? '')),
'mod_separator' => $modSeparator,
'copy_method' => $copyMethod,
'copy_keys' => !empty($post['copy_keys']) ? 1 : 0,
'key_source_path' => trim((string)($post['key_source_path'] ?? '')),
'key_dest_path' => trim((string)($post['key_dest_path'] ?? '')),
'pre_update_script' => trim((string)($post['pre_update_script'] ?? '')),
'install_script' => trim((string)($post['install_script'] ?? '')),
'post_update_script' => trim((string)($post['post_update_script'] ?? '')),
'config_file_template' => trim((string)($post['config_file_template'] ?? '')),
'launch_param_template' => trim((string)($post['launch_param_template'] ?? '')),
'requires_restart' => !empty($post['requires_restart']) ? 1 : 0,
'validation_notes' => trim((string)($post['validation_notes'] ?? '')),
'enabled' => !empty($post['enabled']) ? 1 : 0,
];
}
@ -198,10 +232,13 @@ class WorkshopProfileController
$errors[] = $this->lang['error_app_id_required'] ?? 'Workshop App ID is required.';
}
if (($data['cache_path_template'] ?? '') === '') {
$errors[] = $this->lang['error_cache_path_required'] ?? 'Cache path template is required.';
$errors[] = $this->lang['error_cache_path_required'] ?? 'SteamCMD cache path template is required.';
}
if (($data['install_path_template'] ?? '') === '') {
$errors[] = $this->lang['error_install_path_required'] ?? 'Install path template is required.';
$errors[] = $this->lang['error_install_path_required'] ?? 'Server install path template is required.';
}
if (($data['folder_naming_format'] ?? '') === 'custom' && ($data['folder_name_template'] ?? '') === '') {
$errors[] = $this->lang['error_folder_template_required'] ?? 'Custom folder name template is required when format is set to custom.';
}
return $errors;
}