mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-19 12:02:59 +02:00
- Remove DEVELOPMENT constant from AppConfig.php; introduce DB_ACCESS_ENABLED (controls phpMiniAdmin access in admin panel only, not core DB connections) - Detach bootstrap.php from www/constants.php: load core/Config/* and core/Logging/Logger directly; define PHP_ERRORS fallback - Switch Logger::init() to PHP_ERRORS in bootstrap.php, stream/init.php, RequestGuard.php; remove leftover TODO comment - Logger: always set error_reporting(E_ALL); UI visibility controlled separately via display_errors; rename $development -> $showErrors - MigrationRunner: track applied/failed counts separately; failed migrations are not marked as applied - Replace DEVELOPMENT with DB_ACCESS_ENABLED in admin settings.php and database.php (phpMiniAdmin gate) - Replace DEVELOPMENT with PHP_ERRORS in CertbotCronJob - Docs (en/ru): add DB_ACCESS_ENABLED flag description; update feature-flags, updates_checklist, http-request-handling; remove DEVELOPMENT references - MIGRATION.md: mark L-1 as done, remove from backlog and wave A; unblock L-2; renumber steps
225 lines
7.8 KiB
Markdown
225 lines
7.8 KiB
Markdown
# Обработка HTTP-запросов и middleware
|
||
|
||
Слой обработки HTTP-запросов в XC_VM состоит из трёх компонентов: **RequestGuard** (безопасность входящих запросов), **Request** (обёртка над суперглобальными переменными) и **Router** (маршрутизация к обработчикам).
|
||
|
||
---
|
||
|
||
## Обзор потока запроса
|
||
|
||
```
|
||
Входящий HTTP-запрос
|
||
│
|
||
▼
|
||
[RequestGuard]
|
||
├── Flood-protection (блокировка по IP-файлу)
|
||
├── Host verification (проверка домена)
|
||
├── Загрузка $rSettings из кеша
|
||
└── Инициализация Logger
|
||
│
|
||
▼
|
||
[Request::capture()]
|
||
├── Санитизация $_GET, $_POST, $_COOKIE
|
||
└── Создание объекта запроса
|
||
│
|
||
▼
|
||
[Router::dispatch()]
|
||
├── Определение маршрута по URL
|
||
├── Проверка метода (GET / POST)
|
||
└── Вызов обработчика [Controller::method]
|
||
```
|
||
|
||
---
|
||
|
||
## RequestGuard
|
||
|
||
`src/core/Http/RequestGuard.php`
|
||
|
||
Выполняется автоматически при загрузке `www/constants.php` в контекстах HTTP (STREAM, ADMIN).
|
||
|
||
### Flood-protection
|
||
|
||
Блокирует IP, если существует файл `FLOOD_TMP_PATH/block_{IP}`.
|
||
|
||
```
|
||
/tmp/xc_vm/flood/block_1.2.3.4 → HTTP 403 + exit()
|
||
```
|
||
|
||
Файлы блокировки создаются отдельной защитной логикой при превышении лимита запросов.
|
||
|
||
### Host verification
|
||
|
||
Если в настройках включена проверка хоста (`verify_host = true`) и загружен кеш разрешённых доменов (`allowed_domains`):
|
||
|
||
- Запрос с не зарегистрированного домена → `generateError('INVALID_HOST')`
|
||
- IP-адреса как хост всегда разрешены
|
||
- Служебный хост `xc_vm` всегда разрешён
|
||
|
||
### Загрузка настроек
|
||
|
||
`$rSettings` загружается из файлового кеша (`CACHE_TMP_PATH/settings`) через `igbinary_unserialize()`. Это позволяет избежать обращения к БД на каждом запросе.
|
||
|
||
### Инициализация Logger
|
||
|
||
```php
|
||
Logger::init(PHP_ERRORS, LOGS_TMP_PATH . 'error_log.log');
|
||
```
|
||
|
||
После RequestGuard Logger доступен во всех последующих компонентах.
|
||
|
||
---
|
||
|
||
## Request
|
||
|
||
`src/core/Http/Request.php`
|
||
|
||
Обёртка над `$_GET`, `$_POST`, `$_SERVER`, `$_COOKIE` с санитизацией входных данных.
|
||
|
||
### Создание объекта
|
||
|
||
```php
|
||
// Захватить текущий HTTP-запрос
|
||
$request = Request::capture();
|
||
|
||
// Или создать вручную
|
||
$request = new Request($_GET, $_POST, $_SERVER, $_COOKIE);
|
||
```
|
||
|
||
При создании все данные из `$_GET`, `$_POST`, `$_COOKIE` проходят санитизацию через `cleanGlobals()` и `parseIncomingRecursively()`.
|
||
|
||
### Получение данных
|
||
|
||
```php
|
||
$request->get('key'); // из $_GET
|
||
$request->post('key'); // из $_POST
|
||
$request->input('key'); // из POST или GET (POST имеет приоритет)
|
||
$request->cookie('key'); // из $_COOKIE
|
||
$request->server('key'); // из $_SERVER (без санитизации)
|
||
|
||
// С дефолтным значением
|
||
$request->get('page', 1);
|
||
$request->input('action', '');
|
||
```
|
||
|
||
### Вспомогательные методы
|
||
|
||
```php
|
||
$request->method(); // 'GET' или 'POST'
|
||
$request->isPost(): bool
|
||
$request->isGet(): bool
|
||
$request->ip(): string // IP клиента
|
||
$request->all(): array // Весь merged input
|
||
$request->has('key'): bool
|
||
$request->only(['a', 'b']): array
|
||
$request->except(['x']): array
|
||
```
|
||
|
||
### Санитизация
|
||
|
||
- Ключи: очищаются через `parseCleanKey()` — удаляются все символы кроме `a-z`, `A-Z`, `0-9`, `_`, `-`
|
||
- Значения: очищаются через `parseCleanValue()` — `strip_tags()` + обрезка пробелов
|
||
- `$_SERVER` намеренно не санитизируется (содержит пути и бинарные данные)
|
||
|
||
---
|
||
|
||
## Router
|
||
|
||
`src/core/Http/Router.php`
|
||
|
||
Маршрутизатор для администраторской панели. Заменяет паттерн `switch($rAction)` и прямые `include` в admin-страницах.
|
||
|
||
### Регистрация маршрутов
|
||
|
||
```php
|
||
$router = new Router();
|
||
|
||
// GET-маршруты (страницы)
|
||
$router->get('streams', [StreamController::class, 'index']);
|
||
$router->get('stream/add', [StreamController::class, 'add']);
|
||
|
||
// POST-маршруты (формы)
|
||
$router->post('stream/save', [StreamController::class, 'save']);
|
||
|
||
// API-маршруты (JSON-ответ)
|
||
$router->api('getStreams', [StreamController::class, 'apiList']);
|
||
$router->api('deleteStream', [StreamController::class, 'apiDelete']);
|
||
```
|
||
|
||
### Группировка маршрутов
|
||
|
||
```php
|
||
$router->group('plex', function(Router $r) {
|
||
$r->get('', [PlexController::class, 'index']);
|
||
$r->get('add', [PlexController::class, 'add']);
|
||
$r->post('save', [PlexController::class, 'save']);
|
||
$r->api('enable', [PlexController::class, 'apiEnable']);
|
||
});
|
||
// Результат: 'plex', 'plex/add', 'plex/save', 'plex/enable'
|
||
```
|
||
|
||
### Диспетчеризация
|
||
|
||
```php
|
||
// Страница по URL
|
||
$router->dispatch($page, $method);
|
||
|
||
// API-вызов
|
||
$router->dispatchApi($action);
|
||
```
|
||
|
||
### Модульная регистрация маршрутов
|
||
|
||
Каждый модуль регистрирует свои маршруты через `ModuleInterface::registerRoutes()`:
|
||
|
||
```php
|
||
class WatchModule implements ModuleInterface {
|
||
public function registerRoutes(Router $router): void {
|
||
$router->group('watch', function(Router $r) {
|
||
$r->get('', [WatchController::class, 'index']);
|
||
$r->get('add', [WatchController::class, 'add']);
|
||
$r->post('settings', [WatchController::class, 'saveSettings']);
|
||
$r->api('enable', [WatchController::class, 'apiEnable']);
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
### Обратная совместимость
|
||
|
||
Пока `admin/api.php` использует `switch($rAction)`, Router используется как fallback в конце switch-цепочки:
|
||
|
||
```php
|
||
// В конце switch($rAction):
|
||
default:
|
||
$router->dispatchApi($rAction);
|
||
```
|
||
|
||
---
|
||
|
||
## RequestManager
|
||
|
||
`src/core/Http/RequestManager.php`
|
||
|
||
Статический фасад для доступа к параметрам запроса без создания объекта `Request`.
|
||
|
||
```php
|
||
RequestManager::get('key');
|
||
RequestManager::post('key');
|
||
RequestManager::getAll(): array
|
||
RequestManager::postAll(): array
|
||
```
|
||
|
||
Используется в компонентах, которые не получают `Request` через инъекцию.
|
||
|
||
---
|
||
|
||
## Связанные файлы
|
||
|
||
| Файл | Назначение |
|
||
| --------------------------------- | ----------------------------------- |
|
||
| `src/core/Http/RequestGuard.php` | Flood-protection, host check, settings |
|
||
| `src/core/Http/Request.php` | Обёртка над суперглобальными |
|
||
| `src/core/Http/Router.php` | Маршрутизация GET/POST/API |
|
||
| `src/core/Http/RequestManager.php`| Статический фасад для запросов |
|
||
| `src/core/Http/Response.php` | Формирование HTTP-ответов |
|
||
| `src/www/constants.php` | Загружает RequestGuard |
|