From 2b499bbd4758f9a9440a95b9d5c4c14fc55f112e Mon Sep 17 00:00:00 2001 From: Divarion-D Date: Mon, 20 Apr 2026 20:34:19 +0300 Subject: [PATCH] refactor(bootstrap): extract StreamingRequestBootstrap and WebApiBootstrap - StreamingRequestBootstrap: unified entry for streaming endpoints - WebApiBootstrap: unified entry for web API requests - index.php: use new bootstrap classes instead of direct requires - www/init.php, www/stream/init.php: mark as deprecated shims - StatusCommand: use StreamingRequestBootstrap::init() - autoload.php, bootstrap.php: register new classes --- src/autoload.php | 1 + src/bootstrap.php | 6 +- src/cli/Commands/StatusCommand.php | 4 +- .../bootstrap/StreamingRequestBootstrap.php | 106 ++++++++++++++++++ .../bootstrap/WebApiBootstrap.php | 71 ++++++++++++ src/public/index.php | 5 +- src/www/init.php | 5 + src/www/stream/init.php | 5 + 8 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 src/infrastructure/bootstrap/StreamingRequestBootstrap.php create mode 100644 src/infrastructure/bootstrap/WebApiBootstrap.php diff --git a/src/autoload.php b/src/autoload.php index 82502b06..9ad5b44a 100644 --- a/src/autoload.php +++ b/src/autoload.php @@ -288,6 +288,7 @@ class XC_Autoloader { * @return void */ public static function warmCache() { + self::$resolved = []; self::$notFound = []; foreach (self::$directories as $dir) { self::scanDirectoryForClasses($dir); diff --git a/src/bootstrap.php b/src/bootstrap.php index ab841f86..983a7c6e 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -441,8 +441,7 @@ class XC_Bootstrap { /** * Initialize Admin API + Reseller API. * - * Loads reseller_api.php, - * initializes ResellerAPI class and admin user info. + * Initializes ResellerAPI class and admin user info. */ private static function initAdminAPI(): void { if (self::$adminReady) { @@ -451,7 +450,6 @@ class XC_Bootstrap { global $db; - require_once MAIN_HOME . 'infrastructure/legacy/reseller_api.php'; // Admin user info if (isset($_SESSION['hash'])) { @@ -612,7 +610,7 @@ class XC_Bootstrap { $language = Translator::class; $allowedLangs = $language::available(); - // Sort servers by order (moved from infrastructure/legacy/admin.php) + // Sort servers by order if (is_array($rServers)) { uasort( $rServers, diff --git a/src/cli/Commands/StatusCommand.php b/src/cli/Commands/StatusCommand.php index 255210e7..b0f03606 100644 --- a/src/cli/Commands/StatusCommand.php +++ b/src/cli/Commands/StatusCommand.php @@ -37,7 +37,7 @@ class StatusCommand implements CommandInterface { $rFirstRun = isset($rArgs[0]); $rReload = false; - require_once MAIN_HOME . 'www/stream/init.php'; + StreamingRequestBootstrap::init('status'); ini_set('display_startup_errors', 1); ini_set('display_errors', 1); error_reporting(E_ALL); @@ -135,8 +135,6 @@ class StatusCommand implements CommandInterface { return $rServers; } - - private function fixPermissions(): void { shell_exec('sudo chmod 0660 ' . MAIN_HOME . 'bin/php/sockets/*'); shell_exec('sudo chmod 0771 ' . MAIN_HOME . 'bin/daemons.sh'); diff --git a/src/infrastructure/bootstrap/StreamingRequestBootstrap.php b/src/infrastructure/bootstrap/StreamingRequestBootstrap.php new file mode 100644 index 00000000..3a30f135 --- /dev/null +++ b/src/infrastructure/bootstrap/StreamingRequestBootstrap.php @@ -0,0 +1,106 @@ + + * @copyright 2025-2026 Vateron Media + * @link https://github.com/Vateron-Media/XC_VM + * @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html + */ + +class StreamingRequestBootstrap { + + /** + * Инициализирует streaming контекст. + * + * @param string $rFilename Имя endpoint'а (player_api, live, vod, status, …) + */ + public static function init(string $rFilename): void { + // ── 1. Базовые модули ──────────────────────────────────── + require_once MAIN_HOME . 'core/Error/ErrorCodes.php'; + require_once MAIN_HOME . 'core/Error/ErrorHandler.php'; + require_once MAIN_HOME . 'core/Config/Paths.php'; + require_once MAIN_HOME . 'core/Config/AppConfig.php'; + require_once MAIN_HOME . 'core/Config/Binaries.php'; + + @ini_set('user_agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'); + @ini_set('default_socket_timeout', 5); + + $rIsCli = (PHP_SAPI === 'cli'); + + // ── 2. Flood-protection (HTTP only) ────────────────────── + if (!$rIsCli) { + $rIP = $_SERVER['REMOTE_ADDR'] ?? ''; + if ($rIP !== '' && file_exists(FLOOD_TMP_PATH . 'block_' . $rIP)) { + http_response_code(403); + exit(); + } + } + + // ── 3. Настройки из файлового кэша ─────────────────────── + if (file_exists(CACHE_TMP_PATH . 'settings')) { + $rSettings = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'settings')); + } else { + // Fail-closed: блокировать всё кроме status если кэш отсутствует + $rSettings = ['verify_host' => false, 'debug_show_errors' => false, 'enable_cache' => false, 'exit' => true]; + } + + $rShowErrors = false; + + // ── 4. Host verification (HTTP only) ───────────────────── + if (!$rIsCli) { + if (!defined('HOST')) { + define('HOST', trim(explode(':', $_SERVER['HTTP_HOST'] ?? '')[0])); + } + + if (is_array($rSettings) && !empty($rSettings['verify_host'])) { + $rAllowedDomains = (igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'allowed_domains')) ?: []); + + if (is_array($rAllowedDomains) + && count($rAllowedDomains) > 0 + && !in_array(HOST, $rAllowedDomains, true) + && HOST !== 'xc_vm' + && !filter_var(HOST, FILTER_VALIDATE_IP) + ) { + generateError('INVALID_HOST'); + } + + unset($rAllowedDomains); + } + + $rShowErrors = isset($rSettings['debug_show_errors']) ? (bool)$rSettings['debug_show_errors'] : false; + } + + // ── 5. Logger ──────────────────────────────────────────── + if (!defined('PHP_ERRORS')) { + define('PHP_ERRORS', $rShowErrors); + } + + require_once MAIN_HOME . 'core/Logging/Logger.php'; + Logger::init(PHP_ERRORS, LOGS_TMP_PATH . 'error_log.log'); + + // ── 6. Fail-closed gate (настройки недоступны) ─────────── + if (isset($rSettings['exit']) && $rFilename !== 'status') { + generate404(); + } + + // ── 7. Передача в StreamingBootstrap ──────────────────── + $rStreamingEndpoints = ['probe', 'player_api', 'live', 'thumb', 'subtitle', 'timeshift', 'vod', 'status', 'rtmp', 'portal']; + if (in_array($rFilename, $rStreamingEndpoints, true)) { + StreamingBootstrap::bootstrap($rFilename, $rSettings); + } + } +} diff --git a/src/infrastructure/bootstrap/WebApiBootstrap.php b/src/infrastructure/bootstrap/WebApiBootstrap.php new file mode 100644 index 00000000..2b6ab1ee --- /dev/null +++ b/src/infrastructure/bootstrap/WebApiBootstrap.php @@ -0,0 +1,71 @@ + + * @copyright 2025-2026 Vateron Media + * @link https://github.com/Vateron-Media/XC_VM + * @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html + */ + +class WebApiBootstrap { + + /** + * Инициализирует web API контекст. + * + * @param string $rFilename Имя endpoint'а (enigma2, epg, playlist, xplugin, api, …) + */ + public static function init(string $rFilename): void { + // ── 1. Ошибки ──────────────────────────────────────────── + require_once MAIN_HOME . 'core/Error/ErrorCodes.php'; + require_once MAIN_HOME . 'core/Error/ErrorHandler.php'; + + // ── 2. PHP defaults ────────────────────────────────────── + @ini_set('user_agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'); + @ini_set('default_socket_timeout', 5); + + // ── 3. Константы и конфигурация ────────────────────────── + require_once MAIN_HOME . 'core/Config/Paths.php'; + require_once MAIN_HOME . 'core/Config/AppConfig.php'; + require_once MAIN_HOME . 'core/Config/Binaries.php'; + require_once MAIN_HOME . 'core/Config/ConfigLoader.php'; + + // ── 4. Flood / host / Logger ───────────────────────────── + require_once MAIN_HOME . 'core/Http/RequestGuard.php'; + + // ── 5. DB + LegacyInitializer ──────────────────────────── + require_once MAIN_HOME . 'core/Init/LegacyInitializer.php'; + require_once MAIN_HOME . 'core/Database/DatabaseHandler.php'; + require_once MAIN_HOME . 'core/Updates/GithubReleases.php'; + + global $_INFO, $db, $gitRelease; + + $rCachedEndpoints = ['enigma2', 'epg', 'playlist', 'api', 'xplugin', 'live', 'proxy_api', 'thumb', 'timeshift', 'vod']; + $rUseCache = in_array($rFilename, $rCachedEndpoints, true); + + $db = new DatabaseHandler($_INFO['username'], $_INFO['password'], $_INFO['database'], $_INFO['hostname'], $_INFO['port']); + DatabaseFactory::set($db); + LegacyInitializer::initCore($rUseCache); + + if ($rUseCache && !SettingsManager::getAll()['enable_cache']) { + $db = new DatabaseHandler($_INFO['username'], $_INFO['password'], $_INFO['database'], $_INFO['hostname'], $_INFO['port']); + DatabaseFactory::set($db); + } + + // ── 6. GithubReleases ──────────────────────────────────── + $gitRelease = new GitHubReleases(GIT_OWNER, GIT_REPO_MAIN, SettingsManager::getAll()['update_channel']); + } +} diff --git a/src/public/index.php b/src/public/index.php index a1b31597..be00404e 100644 --- a/src/public/index.php +++ b/src/public/index.php @@ -136,11 +136,10 @@ if (isset($rawScope) && $rawScope === 'api' && !empty($_SERVER['XC_API'])) { $rFilename = ($rApiName === 'internal') ? 'api' : $rApiName; - chdir(MAIN_HOME . 'www/'); if ($rApiName === 'player_api') { - require MAIN_HOME . 'www/stream/init.php'; + StreamingRequestBootstrap::init($rFilename); } else { - require MAIN_HOME . 'www/init.php'; + WebApiBootstrap::init($rFilename); } $rControllerClass = $rApiControllerMap[$rApiName]; diff --git a/src/www/init.php b/src/www/init.php index 5337ed2b..fc6ade08 100644 --- a/src/www/init.php +++ b/src/www/init.php @@ -3,6 +3,11 @@ /** * Web request initialization * + * @deprecated Используй WebApiBootstrap::init() напрямую. + * Этот файл оставлен как shim на один релиз для обратной совместимости + * с legacy call sites (TableController, Reseller/TableController, admin/table.php). + * Будет удалён в рамках L-3. + * * @package XC_VM_Web * @author Divarion_D * @copyright 2025-2026 Vateron Media diff --git a/src/www/stream/init.php b/src/www/stream/init.php index 3df52215..a9200d82 100644 --- a/src/www/stream/init.php +++ b/src/www/stream/init.php @@ -3,6 +3,11 @@ /** * XC_VM - Stream initialization (stream/init.php) * + * @deprecated Используй StreamingRequestBootstrap::init() напрямую. + * Этот файл оставлен как shim на один релиз для обратной совместимости + * с legacy call sites (ministra/portal.php). + * Будет удалён в рамках L-5/L-6. + * * Bootstrap for streaming endpoints (live, vod, timeshift, thumb, subtitle, rtmp, portal). * High-load path - does not load config.ini, does not connect to DB directly. *