# Session Server Сессионный сервер для автоматической регистрации на основном API сервере. ## Описание Session Server - это приложение, которое автоматически регистрируется на основном сервере (`stream.graff.tech`) и периодически обновляет свою информацию. Это позволяет основному серверу знать о доступных сессионных серверах для распределения нагрузки. ## Возможности - 🚀 Автоматическая регистрация при запуске - 🔄 Периодическое обновление информации о сервере (по умолчанию каждые 30 секунд после завершения предыдущего запроса) - 🎮 Автоматическое определение и обновление свободной GPU памяти через nvidia-smi (по умолчанию каждую секунду после завершения предыдущего запроса) - 🎯 Автоматическое управление игровыми сессиями - Запуск приложений для сессий со статусом "starting" - Остановка приложений для сессий со статусом "ending" - Отслеживание PID запущенных процессов - Автоматическое обновление статусов сессий на главном сервере - 🌐 Автоматическое определение локального IP адреса - 💻 Автоматическое определение hostname - ⚙️ Гибкая конфигурация через переменные окружения - 🔁 Автоматические повторные попытки при ошибках - 📝 Подробное логирование - ⚡ Защита от наложения запросов для всех операций (рекурсивный вызов через setTimeout) ## Установка ```bash # Установка зависимостей bun install ``` ## Конфигурация Создайте файл `.env` в корне проекта на основе `.env.example`: ```bash # URL основного API сервера API_URL=http://localhost:3000 # Тип сервера: stream или local SERVER_TYPE=stream # Расположение сервера (ОБЯЗАТЕЛЬНО для stream серверов) # Возможные значения: ru1, uae1 SERVER_LOCATION=ru1 # Уровень сервера (только для stream серверов) # Возможные значения: demo, prod SERVER_TIER=demo # ID филиала (ОБЯЗАТЕЛЬНО для local серверов) BRANCH_ID=00000000-0000-0000-0000-000000000000 # Локальный IP адрес (опционально, определяется автоматически) # LOCAL_IP=192.168.1.100 # Hostname сервера (опционально, определяется автоматически) # HOSTNAME=my-session-server # Интервал регистрации в миллисекундах (по умолчанию 30000 = 30 секунд) REGISTER_INTERVAL_MS=30000 # Интервал обновления GPU памяти в миллисекундах (по умолчанию 1000 = 1 секунда) GPU_UPDATE_INTERVAL_MS=1000 # Интервал проверки сессий в миллисекундах (по умолчанию 5000 = 5 секунд) SESSION_CHECK_INTERVAL_MS=5000 ``` ### Переменные окружения | Переменная | Описание | Обязательна | По умолчанию | |------------|----------|-------------|--------------| | `API_URL` | URL основного API сервера | Нет | `http://localhost:3000` | | `SERVER_TYPE` | Тип сервера (`stream` или `local`) | Нет | `stream` | | `SERVER_LOCATION` | Расположение (`ru1`, `uae1`) - **обязательно для `stream`** | Да (для stream) | - | | `SERVER_TIER` | Уровень (`demo`, `prod`) - только для `stream` | Нет | `demo` (для stream) | | `BRANCH_ID` | ID филиала - **обязательно для `local`** | Да (для local) | - | | `LOCAL_IP` | Локальный IP адрес | Нет | Определяется автоматически | | `HOSTNAME` | Hostname сервера | Нет | Определяется автоматически | | `REGISTER_INTERVAL_MS` | Интервал регистрации в мс | Нет | `30000` | | `GPU_UPDATE_INTERVAL_MS` | Интервал обновления GPU памяти в мс | Нет | `1000` | | `SESSION_CHECK_INTERVAL_MS` | Интервал проверки сессий в мс | Нет | `5000` | **Примечание:** Свободная память GPU (`gpuFreeMb`) автоматически определяется через `nvidia-smi` при каждой регистрации и обновляется каждую секунду (или согласно `GPU_UPDATE_INTERVAL_MS`) после завершения предыдущего запроса. Это предотвращает наложение запросов. Если `nvidia-smi` недоступен, сервер завершит работу с ошибкой. ## Управление сессиями Session Server автоматически управляет игровыми сессиями на этом сервере: ### Как это работает 1. **Проверка сессий**: Каждую секунду (или согласно `SESSION_CHECK_INTERVAL_MS`) сервер запрашивает у основного API список сессий для этого сервера 2. **Проверка времени окончания**: Для активных сессий (`started` или `starting`): - ⏰ Проверяется время `endAt` - если оно наступило, сессия автоматически переводится в статус `ending` - **Timezone**: Все время хранится в UTC, сравнение корректно работает независимо от часового пояса сервера - По умолчанию сессии создаются с временем окончания +30 минут от момента создания - Автоматическое завершение гарантирует, что сессии не будут работать бесконечно 3. **Запуск приложений**: Для сессий со статусом `starting`: - ⏰ Проверяется время `startAt` - приложение запускается только если это время уже наступило - **Timezone**: Все время хранится в UTC, сравнение корректно работает независимо от часового пояса сервера - Запланированные сессии (с будущим `startAt`) логируются с информацией о времени до запуска - 🎯 **Централизованное назначение сервера** (для stream-сессий): - **Main server** автоматически назначает серверы каждые 5 секунд - Выбирается сервер с максимальной свободной GPU памятью - Учитываются требования приложения к GPU памяти (`gpuLimitMb`) - Session-server просто проверяет, что сессия назначена ему - Запускается соответствующее приложение - Отслеживается PID процесса - Статус сессии обновляется на `started` на главном сервере 4. **Остановка приложений**: Для сессий со статусом `ending`: - Используется `taskkill /pid {PID} /T /F` для завершения всего дерева процессов - `/T` - завершает указанный процесс и ВСЕ дочерние процессы - `/F` - принудительное завершение - Решает проблему с UE5 и другими приложениями, создающими дочерние процессы - Статус сессии обновляется на `ended` на главном сервере 5. **Автоматическая очистка**: Процессы для неактивных сессий автоматически останавливаются ### API endpoints для управления сессиями Session Server взаимодействует с основным сервером через следующие endpoints: - `GET /servers/:id/sessions` - получить список сессий для сервера - `PATCH /sessions/:id/status` - обновить статус сессии (публичный endpoint) ### Запуск приложений Session Server автоматически запускает `.exe` приложения по стандартному пути: ``` C:\apps\{appName}\{appName}.exe ``` Где `{appName}` - это значение поля `name` из таблицы `apps`. #### Структура директорий Все приложения должны быть размещены в следующей структуре: ``` C:\apps\ ├── minecraft\ │ └── minecraft.exe ├── fortnite\ │ └── fortnite.exe └── cyberpunk\ └── cyberpunk.exe ``` #### Особенности запуска и остановки - ✅ Автоматическая проверка существования exe файла - ✅ Рабочая директория устанавливается в папку приложения (`C:\apps\{appName}\`) - ✅ Окно консоли скрывается (`windowsHide: true`) - ✅ **Запуск без графического окна** - использует флаги `-NOSPLASH` и `-NOWINDOW` для UE приложений - ✅ PID процесса отслеживается и передается на main server - ✅ Автоматическое обновление статуса при завершении процесса - ✅ **Многоступенчатое завершение процессов**: 1. Мягкое завершение (`taskkill /T`) - отправляет WM_CLOSE сообщение окнам 2. Принудительное завершение (`taskkill /T /F`) - если мягкое не сработало 3. PowerShell завершение - закрывает окна и убивает процесс через PowerShell - ✅ Решает проблему с UE5 приложениями, которые создают множественные процессы и имеют графические окна #### Логи запуска ``` [2025-10-06T10:00:00.000Z] 🚀 Запуск приложения "minecraft" для сессии abc-123 [2025-10-06T10:00:00.050Z] 📂 Путь к приложению: C:\apps\minecraft\minecraft.exe [2025-10-06T10:00:01.000Z] ✅ Приложение запущено с PID 12345 [2025-10-06T10:00:01.100Z] ✅ Статус сессии abc-123 обновлен на "started" ``` #### Обработка ошибок Если exe файл не найден: ``` ❌ Файл приложения не найден: C:\apps\minecraft\minecraft.exe. Убедитесь, что приложение установлено. ``` ### Примеры логов Запланированная сессия: ``` [2025-10-06T10:00:00.000Z] ⏰ Сессия 123e4567-e89b-12d3-a456-426614174000 (minecraft) запланирована через 120 сек ``` Запуск сессии: ``` [2025-10-06T10:02:00.000Z] 🚀 Запуск приложения "minecraft" для сессии 123e4567-e89b-12d3-a456-426614174000 [2025-10-06T10:02:01.000Z] ✅ Приложение запущено с PID 12345 [2025-10-06T10:02:01.100Z] ✅ Статус сессии 123e4567-e89b-12d3-a456-426614174000 обновлен на "started" ``` Автоматическое завершение по истечении времени: ``` [2025-10-06T10:32:00.000Z] ⏰ Время сессии 123e4567-e89b-12d3-a456-426614174000 (minecraft) истекло, завершение... [2025-10-06T10:32:00.100Z] ✅ Статус сессии 123e4567-e89b-12d3-a456-426614174000 обновлен на "ending" ``` Остановка сессии: ``` [2025-10-06T10:32:00.000Z] 🛑 Остановка приложения для сессии 123e4567-e89b-12d3-a456-426614174000 (PID: 12345) [2025-10-06T10:32:00.500Z] ✅ Дерево процессов для PID 12345 успешно завершено [2025-10-06T10:32:00.600Z] ✅ Приложение и все дочерние процессы остановлены для сессии 123e4567-e89b-12d3-a456-426614174000 [2025-10-06T10:32:00.700Z] ✅ Статус сессии 123e4567-e89b-12d3-a456-426614174000 обновлен на "ended" ``` ## Отладка проблем (Troubleshooting) Если приложение запускается и сразу завершается с ошибкой, см. подробное руководство по диагностике: - **[TROUBLESHOOTING.md](./TROUBLESHOOTING.md)** ### Новые возможности логирования После обновления вы увидите: - 🔴 **STDERR логи** для диагностики ошибок приложений - 📊 **Счётчик активных процессов** при запуске/завершении - ⚠️ **Детальная информация** о кодах выхода и сигналах Пример логов с ошибкой: ``` [10:51:56.498Z] 🚀 Запуск приложения "ShishimGorka" для сессии abc (активных процессов: 1) [10:51:56.563Z] ✅ Приложение запущено с PID 84112 (всего активных: 2) [10:51:57.500Z] 🔴 STDERR [abc]: Error: Port 8888 is already in use [10:51:57.894Z] 🛑 Приложение для сессии abc завершилось с кодом 1 [10:51:57.894Z] ⚠️ Приложение завершилось с ошибкой! Код выхода: 1 ``` ## Работа с часовыми поясами (Timezone) Session Server корректно работает с разными часовыми поясами: ### Как это работает 1. **База данных**: PostgreSQL хранит все timestamp с timezone в UTC 2. **API**: Основной сервер возвращает время в формате ISO 8601 с timezone (например, `2025-10-06T10:00:00.000Z`) 3. **Session Server**: JavaScript автоматически парсит ISO 8601 строки в UTC 4. **Сравнение**: Все сравнения времени происходят в UTC, независимо от локального часового пояса сервера ### Миграция базы данных ⚠️ **Важно**: Если вы обновляете существующую систему, необходимо выполнить миграцию для добавления timezone в поля `startAt` и `endAt`: ```bash # Из директории server/ psql -d $DATABASE_URL -f timezone_migration.sql ``` См. `server/TIMEZONE_MIGRATION.md` для подробностей. ### Примеры Если сервер в России создаёт сессию на 14:00 по московскому времени (UTC+3): - В БД сохранится: `2025-10-06T11:00:00.000Z` (UTC) - Сервер в ОАЭ (UTC+4) увидит: 15:00 по местному времени - Session Server запустит приложение ровно в 11:00 UTC, независимо от локального времени ## Запуск ### Development режим ```bash bun run dev ``` ### Production режим ```bash # Собрать проект bun run build # Запустить собранное приложение bun run start ``` ## Примеры использования ### Stream сервер в России (демо) ```env API_URL=https://api.stream.graff.tech SERVER_TYPE=stream SERVER_LOCATION=ru1 SERVER_TIER=demo REGISTER_INTERVAL_MS=30000 ``` ### Stream сервер в ОАЭ (продакшн) ```env API_URL=https://api.stream.graff.tech SERVER_TYPE=stream SERVER_LOCATION=uae1 SERVER_TIER=prod REGISTER_INTERVAL_MS=30000 ``` ### Local сервер для филиала ```env API_URL=https://api.stream.graff.tech SERVER_TYPE=local BRANCH_ID=123e4567-e89b-12d3-a456-426614174000 REGISTER_INTERVAL_MS=60000 ``` ## Логирование Сервер выводит подробную информацию о своей работе: ```text ============================================================ 🚀 Запуск сессионного сервера ============================================================ Конфигурация: API URL: http://localhost:3000 Hostname: DESKTOP-ABC123 Local IP: 192.168.1.100 Type: stream GPU Free MB: 8192 (читается из nvidia-smi) Location: ru1 Tier: demo Register Interval: 30000ms GPU Update Interval: 1000ms ============================================================ [2025-10-06T12:00:00.000Z] Регистрация сервера... Данные: { "localIp": "192.168.1.100", "hostname": "DESKTOP-ABC123", "type": "stream", "gpuFreeMb": 8192, "location": "ru1", "tier": "demo" } [2025-10-06T12:00:00.123Z] ✅ Сервер успешно зарегистрирован ID сервера: 123e4567-e89b-12d3-a456-426614174000 [2025-10-06T12:00:01.000Z] 🎮 GPU память обновлена: 8150 MB [2025-10-06T12:00:02.000Z] 🎮 GPU память обновлена: 8120 MB [2025-10-06T12:00:03.000Z] 🎮 GPU память обновлена: 8100 MB ``` ## Обработка ошибок Сервер автоматически обрабатывает ошибки: - **Валидация конфигурации**: - Stream-серверы: обязательно должен быть указан `SERVER_LOCATION` - Local-серверы: обязательно должен быть указан `BRANCH_ID` - При отсутствии обязательных полей сервер завершит работу с критической ошибкой - **Сетевые ошибки**: Автоматические повторные попытки (до 3 раз) - **Таймауты**: 10 секунд на запрос - **Коды ошибок**: Повторная отправка при временных ошибках сервера (408, 429, 500, 502, 503, 504) - **Ошибки GPU**: Если `nvidia-smi` недоступен или возвращает некорректные данные, сервер завершит работу с критической ошибкой - **Критические ошибки**: Подробное логирование с завершением работы ## Архитектура ```text ┌──────────────────────────────────┐ │ Session Server │ │ │ │ Регистрация (рекурсивно): │ │ 1. Определение IP │ │ 2. Определение hostname │ │ 3. Запрос GPU (nvidia-smi) │ │ 4. POST /servers/register │ │ 5. setTimeout → повтор (30s) │ │ │ │ Обновление GPU (рекурсивно): │ │ 1. Запрос GPU (nvidia-smi) │ │ 2. PATCH /servers/:id/gpu │ │ 3. setTimeout → повтор (1s) │ └──────────┬───────────────────────┘ │ │ HTTP Requests │ ▼ ┌──────────────────────────────────┐ │ Main API Server │ │ │ │ 1. Проверка по hostname │ │ 2. Создание/обновление │ │ 3. Обновление GPU памяти │ │ 4. Возврат данных │ └──────────────────────────────────┘ ``` ## Разработка ### Структура проекта ```text session-server/ ├── src/ │ └── index.ts # Основной файл приложения ├── dist/ # Собранное приложение ├── package.json ├── tsconfig.json ├── bun.build.ts # Конфигурация сборки └── README.md ``` ### Требования - Bun >= 1.0 - Node.js >= 18 (опционально) - TypeScript >= 5.0 - **NVIDIA GPU с установленным `nvidia-smi`** (обязательно) ## Лицензия MIT