Комментарии в bc.js

This commit is contained in:
PsiMagistr 2025-05-25 12:09:28 +03:00
parent 6499e8d9ea
commit 79a09d583d

View File

@ -1,5 +1,8 @@
// /server/bc.js - Главный файл сервера Battle Club // /server/bc.js - Главный файл сервера Battle Club
// Загружаем переменные окружения из .env файла
// Убедитесь, что `dotenv` установлен (npm install dotenv)
// и этот вызов находится как можно раньше
require('dotenv').config({ path: require('node:path').resolve(process.cwd(), '.env') }); require('dotenv').config({ path: require('node:path').resolve(process.cwd(), '.env') });
const express = require('express'); const express = require('express');
@ -12,229 +15,295 @@ const cors = require('cors');
// Импорт серверных модулей // Импорт серверных модулей
const authService = require('./auth/authService'); const authService = require('./auth/authService');
const GameManager = require('./game/GameManager'); const GameManager = require('./game/GameManager');
const db = require('./core/db'); const db = require('./core/db'); // Предполагается, что db.js корректно инициализирует соединение
const GAME_CONFIG = require('./core/config'); const GAME_CONFIG = require('./core/config'); // Глобальная конфигурация игры
const app = express(); const app = express();
const server = http.createServer(app); const server = http.createServer(app);
// --- НАСТРОЙКА EXPRESS --- // --- НАСТРОЙКА EXPRESS ---
// Определяем разрешенный источник для HTTP CORS
const clientOrigin = process.env.CORS_ORIGIN_CLIENT || (process.env.NODE_ENV === 'development' ? '*' : undefined); const clientOrigin = process.env.CORS_ORIGIN_CLIENT || (process.env.NODE_ENV === 'development' ? '*' : undefined);
console.log(`[BC Server] NODE_ENV: ${process.env.NODE_ENV}`);
console.log(`[BC Server] process.env.CORS_ORIGIN_CLIENT: ${process.env.CORS_ORIGIN_CLIENT}`);
console.log(`[BC Server] Calculated HTTP CORS Origin (clientOrigin): ${clientOrigin === '*' ? "'*'" : clientOrigin || 'Not explicitly set (will likely fail if not development)'}`);
if (!clientOrigin && process.env.NODE_ENV !== 'development') { if (!clientOrigin && process.env.NODE_ENV !== 'development') {
console.warn("[Server Config] CORS_ORIGIN_CLIENT не установлен для продакшн сборки. HTTP API могут быть недоступны."); console.warn("[BC Server Config] CORS_ORIGIN_CLIENT не установлен для не-development сборки. HTTP API могут быть недоступны.");
} }
app.use(cors({ app.use(cors({
origin: clientOrigin, origin: clientOrigin, // Используем рассчитанное значение
methods: ["GET", "POST"], methods: ["GET", "POST"],
credentials: true // Если вы планируете использовать куки или заголовки авторизации с CORS
})); }));
app.use(express.json()); app.use(express.json());
app.use(express.static(path.join(__dirname, '..', 'public'))); // Раздача статических файлов из папки public уровнем выше (../public)
const publicPath = path.join(__dirname, '..', 'public');
console.log(`[BC Server] Serving static files from: ${publicPath}`);
app.use(express.static(publicPath));
// --- HTTP МАРШРУТЫ АУТЕНТИФИКАЦИИ --- // --- HTTP МАРШРУТЫ АУТЕНТИФИКАЦИИ ---
app.post('/auth/register', async (req, res) => { app.post('/auth/register', async (req, res) => {
const { username, password } = req.body; const { username, password } = req.body;
console.log(`[HTTP /auth/register] Attempt for username: "${username}"`); console.log(`[BC HTTP /auth/register] Attempt for username: "${username}" from IP: ${req.ip}`);
if (!username || !password) { if (!username || !password) {
console.warn('[BC HTTP /auth/register] Bad request: Username or password missing.');
return res.status(400).json({ success: false, message: 'Имя пользователя и пароль обязательны.' }); return res.status(400).json({ success: false, message: 'Имя пользователя и пароль обязательны.' });
} }
const result = await authService.registerUser(username, password); const result = await authService.registerUser(username, password);
if (result.success) { if (result.success) {
console.log(`[BC HTTP /auth/register] Success for "${username}".`);
res.status(201).json(result); res.status(201).json(result);
} else { } else {
console.warn(`[BC HTTP /auth/register] Failed for "${username}": ${result.message}`);
res.status(400).json(result); res.status(400).json(result);
} }
}); });
app.post('/auth/login', async (req, res) => { app.post('/auth/login', async (req, res) => {
const { username, password } = req.body; const { username, password } = req.body;
console.log(`[HTTP /auth/login] Attempt for username: "${username}"`); console.log(`[BC HTTP /auth/login] Attempt for username: "${username}" from IP: ${req.ip}. Origin header: ${req.headers.origin}`);
if (!username || !password) { if (!username || !password) {
console.warn('[BC HTTP /auth/login] Bad request: Username or password missing.');
return res.status(400).json({ success: false, message: 'Имя пользователя и пароль обязательны.' }); return res.status(400).json({ success: false, message: 'Имя пользователя и пароль обязательны.' });
} }
const result = await authService.loginUser(username, password); const result = await authService.loginUser(username, password);
if (result.success) { if (result.success) {
console.log(`[BC HTTP /auth/login] Success for "${username}".`);
res.json(result); res.json(result);
} else { } else {
console.warn(`[BC HTTP /auth/login] Failed for "${username}": ${result.message}`);
res.status(401).json(result); res.status(401).json(result);
} }
}); });
// ------------------------------ // ------------------------------
// Определяем разрешенный источник для Socket.IO CORS
const socketCorsOrigin = process.env.CORS_ORIGIN_SOCKET || (process.env.NODE_ENV === 'development' ? '*' : undefined); const socketCorsOrigin = process.env.CORS_ORIGIN_SOCKET || (process.env.NODE_ENV === 'development' ? '*' : undefined);
console.log(`[BC Server] process.env.CORS_ORIGIN_SOCKET: ${process.env.CORS_ORIGIN_SOCKET}`);
console.log(`[BC Server] Calculated Socket.IO CORS Origin (socketCorsOrigin): ${socketCorsOrigin === '*' ? "'*'" : socketCorsOrigin || 'Not explicitly set (will likely fail if not development)'}`);
if (!socketCorsOrigin && process.env.NODE_ENV !== 'development') { if (!socketCorsOrigin && process.env.NODE_ENV !== 'development') {
console.warn("[Server Config] CORS_ORIGIN_SOCKET не установлен для продакшн сборки. Socket.IO может быть недоступен."); console.warn("[BC Server Config] CORS_ORIGIN_SOCKET не установлен для не-development сборки. Socket.IO может быть недоступен.");
} }
const io = new Server(server, { const io = new Server(server, {
// path: '/socket.io/', // Оставьте эту строку ЗАКОММЕНТИРОВАННОЙ, если клиент подключается к /socket.io/ по умолчанию,
// И ваш прокси НЕ отрезает /socket.io/ (т.е. stripPrefix: false для /socket.io в config.json)
// Если клиент подключается к /, а прокси добавляет /socket.io/, то здесь нужно '/'.
// Если клиент подключается к /socket.io/, а прокси ОТРЕЗАЕТ /socket.io/ (stripPrefix: true), то здесь не нужен path.
// Для вашей конфигурации прокси (stripPrefix: false для /socket.io), и если клиент обращается к /socket.io/,
// то здесь ЛИБО path: '/socket.io/', ЛИБО клиент должен обращаться к wss://domain.com/ (без /socket.io/)
// и тогда прокси должен добавлять /socket.io в target.
// САМЫЙ ПРОСТОЙ ВАРИАНТ: если клиент идет на /socket.io/ (стандарт), и прокси НЕ режет /socket.io/ (stripPrefix: false),
// то здесь path должен быть '/socket.io/'.
path: '/socket.io/', // <--- РАСКОММЕНТИРУЙТЕ ЭТО, если клиент подключается к /socket.io/ и прокси это не отрезает.
cors: { cors: {
origin: socketCorsOrigin, origin: socketCorsOrigin, // Используем рассчитанное значение
methods: ["GET", "POST"] methods: ["GET", "POST"],
credentials: true
}, },
// transports: ['websocket', 'polling'], // Можно явно указать транспорты
}); });
console.log(`[BC Server] Socket.IO server configured with path: ${io.path()}`);
const gameManager = new GameManager(io);
const loggedInUsers = {}; const gameManager = new GameManager(io); // GameManager должен быть инициализирован ПОСЛЕ io
const loggedInUsers = {}; // { socket.id: { userId, username } }
// --- MIDDLEWARE АУТЕНТИФИКАЦИИ SOCKET.IO --- // --- MIDDLEWARE АУТЕНТИФИКАЦИИ SOCKET.IO ---
io.use(async (socket, next) => { io.use(async (socket, next) => {
const token = socket.handshake.auth.token; const token = socket.handshake.auth.token;
console.log(`[Socket.IO Middleware] Attempting to auth socket ${socket.id}. Token ${token ? 'present' : 'absent'}.`); const clientIp = socket.handshake.address;
console.log(`[BC Socket.IO Middleware] Auth attempt for socket ${socket.id} from IP ${clientIp}. Token ${token ? 'present' : 'absent'}. Origin: ${socket.handshake.headers.origin}`);
if (token) { if (token) {
try { try {
const decoded = jwt.verify(token, process.env.JWT_SECRET); const decoded = jwt.verify(token, process.env.JWT_SECRET);
socket.userData = { userId: decoded.userId, username: decoded.username }; socket.userData = { userId: decoded.userId, username: decoded.username };
console.log(`[Socket.IO Middleware] Socket ${socket.id} authenticated for user ${decoded.username} (ID: ${decoded.userId}).`); console.log(`[BC Socket.IO Middleware] Socket ${socket.id} authenticated for user ${decoded.username} (ID: ${decoded.userId}).`);
return next(); return next();
} catch (err) { } catch (err) {
console.warn(`[Socket.IO Middleware] Socket ${socket.id} auth failed: Invalid token. Error: ${err.message}`); console.warn(`[BC Socket.IO Middleware] Socket ${socket.id} auth failed: Invalid token. Error: ${err.message}`);
// не вызываем next(new Error(...)) чтобы разрешить неаутентифицированные подключения,
// но socket.userData не будет установлен. Логика в 'connection' должна это учитывать.
} }
} else { } else {
console.log(`[Socket.IO Middleware] Socket ${socket.id} has no token. Proceeding as unauthenticated.`); console.log(`[BC Socket.IO Middleware] Socket ${socket.id} has no token. Proceeding as unauthenticated.`);
} }
// Позволяем неаутентифицированным сокетам подключаться,
// но они не смогут выполнять действия, требующие userData
next(); next();
}); });
// ------------------------------------ // ------------------------------------
io.on('connection', (socket) => { io.on('connection', (socket) => {
if (socket.userData && socket.userData.userId) { if (socket.userData && socket.userData.userId) {
console.log(`[Socket.IO] Authenticated user ${socket.userData.username} (ID: ${socket.userData.userId}) connected: ${socket.id}`); console.log(`[BC Socket.IO] Authenticated user ${socket.userData.username} (ID: ${socket.userData.userId}) connected with socket: ${socket.id}`);
loggedInUsers[socket.id] = socket.userData; loggedInUsers[socket.id] = socket.userData; // Сохраняем данные пользователя для этого сокета
// Запрашиваем состояние игры для подключившегося пользователя
if (gameManager && typeof gameManager.handleRequestGameState === 'function') { if (gameManager && typeof gameManager.handleRequestGameState === 'function') {
// При подключении (или реконнекте с новым сокетом) пользователя,
// который уже был залогинен (токен валиден), пытаемся восстановить его игру.
gameManager.handleRequestGameState(socket, socket.userData.userId); gameManager.handleRequestGameState(socket, socket.userData.userId);
} else {
console.error("[BC Socket.IO] CRITICAL: gameManager or handleRequestGameState not available on connect for authenticated user!");
} }
} else { } else {
console.log(`[Socket.IO] Unauthenticated user connected: ${socket.id}. No game state will be restored.`); console.log(`[BC Socket.IO] Unauthenticated user connected with socket: ${socket.id}. No game state will be restored.`);
// Можно отправить клиенту сообщение, что он не аутентифицирован, если это необходимо
// socket.emit('authError', { message: 'Вы не аутентифицированы.' });
} }
socket.on('logout', () => { socket.on('logout', () => { // Это событие инициируется клиентом перед фактическим удалением токена
const username = socket.userData?.username || 'UnknownUser'; const username = socket.userData?.username || 'UnknownUser';
const userId = socket.userData?.userId; const userId = socket.userData?.userId;
console.log(`[Socket.IO] Logout request from user ${username} (ID: ${userId}, Socket: ${socket.id})`); console.log(`[BC Socket.IO] 'logout' event from user ${username} (ID: ${userId}, Socket: ${socket.id})`);
// GameManager.handleDisconnect будет вызван автоматически при событии 'disconnect' // GameManager.handleDisconnect будет вызван автоматически при событии 'disconnect',
// которое произойдет после того, как клиент разорвет соединение или обновит токен.
// Если игрок нажал "выход", но еще в игре, клиент пошлет 'playerSurrender' ДО этого. // Если игрок нажал "выход", но еще в игре, клиент пошлет 'playerSurrender' ДО этого.
// Здесь просто очищаем локальные данные сессии для сокета.
if (loggedInUsers[socket.id]) { if (loggedInUsers[socket.id]) {
delete loggedInUsers[socket.id]; delete loggedInUsers[socket.id];
} }
socket.userData = null; socket.userData = null; // Очищаем данные на сокете
console.log(`[Socket.IO] User ${username} (Socket: ${socket.id}) logged out (client-side action).`); // Клиент сам разорвет соединение и переподключится без токена, или с новым токеном.
console.log(`[BC Socket.IO] User ${username} (Socket: ${socket.id}) session data cleared on server due to 'logout' event.`);
}); });
// --- НАЧАЛО ИЗМЕНЕНИЯ ---
socket.on('playerSurrender', () => { socket.on('playerSurrender', () => {
if (!socket.userData?.userId) { if (!socket.userData?.userId) {
console.warn(`[BC Socket.IO 'playerSurrender'] Denied for unauthenticated socket ${socket.id}.`);
socket.emit('gameError', { message: 'Необходимо войти в систему, чтобы сдаться в игре.' }); socket.emit('gameError', { message: 'Необходимо войти в систему, чтобы сдаться в игре.' });
return; return;
} }
const identifier = socket.userData.userId; const identifier = socket.userData.userId;
const username = socket.userData.username; const username = socket.userData.username;
console.log(`[Socket.IO] Player Surrender request from user ${username} (ID: ${identifier}, Socket: ${socket.id})`); console.log(`[BC Socket.IO 'playerSurrender'] Request from user ${username} (ID: ${identifier}, Socket: ${socket.id})`);
if (gameManager && typeof gameManager.handlePlayerSurrender === 'function') { if (gameManager && typeof gameManager.handlePlayerSurrender === 'function') {
gameManager.handlePlayerSurrender(identifier); gameManager.handlePlayerSurrender(identifier);
} else { } else {
console.error("[Socket.IO] CRITICAL: gameManager or handlePlayerSurrender method not found!"); console.error("[BC Socket.IO 'playerSurrender'] CRITICAL: gameManager or handlePlayerSurrender method not found!");
socket.emit('gameError', { message: 'Ошибка сервера при обработке сдачи игры.' }); socket.emit('gameError', { message: 'Ошибка сервера при обработке сдачи игры.' });
} }
// После этого клиент выполнит logout, что вызовет 'disconnect' и GameManager.handleDisconnect
}); });
// --- КОНЕЦ ИЗМЕНЕНИЯ ---
socket.on('createGame', (data) => { socket.on('createGame', (data) => {
if (!socket.userData?.userId) { if (!socket.userData?.userId) {
console.warn(`[BC Socket.IO 'createGame'] Denied for unauthenticated socket ${socket.id}.`);
socket.emit('gameError', { message: 'Необходимо войти в систему для создания игры.' }); socket.emit('gameError', { message: 'Необходимо войти в систему для создания игры.' });
return; return;
} }
const identifier = socket.userData.userId; const identifier = socket.userData.userId;
const mode = data?.mode || 'ai'; const mode = data?.mode || 'ai'; // По умолчанию AI режим
console.log(`[Socket.IO] Create Game from ${socket.userData.username} (ID: ${identifier}). Mode: ${mode}, Char: ${data?.characterKey}`); const charKey = data?.characterKey;
gameManager.createGame(socket, mode, data?.characterKey, identifier); console.log(`[BC Socket.IO 'createGame'] Request from ${socket.userData.username} (ID: ${identifier}). Mode: ${mode}, Char: ${charKey}`);
gameManager.createGame(socket, mode, charKey, identifier);
}); });
socket.on('joinGame', (data) => { socket.on('joinGame', (data) => {
if (!socket.userData?.userId) { if (!socket.userData?.userId) {
console.warn(`[BC Socket.IO 'joinGame'] Denied for unauthenticated socket ${socket.id}.`);
socket.emit('gameError', { message: 'Необходимо войти для присоединения к PvP игре.' }); socket.emit('gameError', { message: 'Необходимо войти для присоединения к PvP игре.' });
return; return;
} }
console.log(`[Socket.IO] Join Game from ${socket.userData.username} (ID: ${socket.userData.userId}). GameID: ${data?.gameId}`); const gameId = data?.gameId;
gameManager.joinGame(socket, data?.gameId, socket.userData.userId); const userId = socket.userData.userId;
console.log(`[BC Socket.IO 'joinGame'] Request from ${socket.userData.username} (ID: ${userId}). GameID: ${gameId}`);
gameManager.joinGame(socket, gameId, userId);
}); });
socket.on('findRandomGame', (data) => { socket.on('findRandomGame', (data) => {
if (!socket.userData?.userId) { if (!socket.userData?.userId) {
console.warn(`[BC Socket.IO 'findRandomGame'] Denied for unauthenticated socket ${socket.id}.`);
socket.emit('gameError', { message: 'Необходимо войти для поиска случайной PvP игры.' }); socket.emit('gameError', { message: 'Необходимо войти для поиска случайной PvP игры.' });
return; return;
} }
console.log(`[Socket.IO] Find Random Game from ${socket.userData.username} (ID: ${socket.userData.userId}). PrefChar: ${data?.characterKey}`); const userId = socket.userData.userId;
gameManager.findAndJoinRandomPvPGame(socket, data?.characterKey, socket.userData.userId); const charKey = data?.characterKey;
console.log(`[BC Socket.IO 'findRandomGame'] Request from ${socket.userData.username} (ID: ${userId}). PrefChar: ${charKey}`);
gameManager.findAndJoinRandomPvPGame(socket, charKey, userId);
}); });
socket.on('requestPvPGameList', () => { socket.on('requestPvPGameList', () => {
// Это событие может запрашивать любой подключенный сокет, аутентификация нестрогая,
// но список будет полезен только залогиненным.
console.log(`[BC Socket.IO 'requestPvPGameList'] Request from socket ${socket.id} (User: ${socket.userData?.username || 'Unauth'}).`);
const availableGames = gameManager.getAvailablePvPGamesListForClient(); const availableGames = gameManager.getAvailablePvPGamesListForClient();
socket.emit('availablePvPGamesList', availableGames); socket.emit('availablePvPGamesList', availableGames);
}); });
socket.on('requestGameState', () => { socket.on('requestGameState', () => {
// Это событие имеет смысл только для аутентифицированного пользователя
if (!socket.userData?.userId) { if (!socket.userData?.userId) {
console.warn(`[BC Socket.IO 'requestGameState'] Denied for unauthenticated socket ${socket.id}.`);
socket.emit('gameNotFound', { message: 'Необходимо войти для восстановления игры.' }); socket.emit('gameNotFound', { message: 'Необходимо войти для восстановления игры.' });
return; return;
} }
gameManager.handleRequestGameState(socket, socket.userData.userId); const userId = socket.userData.userId;
console.log(`[BC Socket.IO 'requestGameState'] Request from ${socket.userData.username} (ID: ${userId}, Socket: ${socket.id})`);
gameManager.handleRequestGameState(socket, userId);
}); });
socket.on('playerAction', (actionData) => { socket.on('playerAction', (actionData) => {
if (!socket.userData?.userId) { if (!socket.userData?.userId) {
console.warn(`[BC Socket.IO 'playerAction'] Denied for unauthenticated socket ${socket.id}. Action: ${actionData?.actionType}`);
socket.emit('gameError', { message: 'Действие не разрешено: пользователь не аутентифицирован.' }); socket.emit('gameError', { message: 'Действие не разрешено: пользователь не аутентифицирован.' });
return; return;
} }
const identifier = socket.userData.userId; const identifier = socket.userData.userId;
console.log(`[BC Socket.IO 'playerAction'] Action from ${socket.userData.username} (ID: ${identifier}). Type: ${actionData?.actionType}, Details: ${JSON.stringify(actionData)}`);
gameManager.handlePlayerAction(identifier, actionData); gameManager.handlePlayerAction(identifier, actionData);
}); });
socket.on('disconnect', (reason) => { socket.on('disconnect', (reason) => {
const identifier = socket.userData?.userId; const identifier = socket.userData?.userId;
const username = socket.userData?.username || 'UnauthenticatedUser'; const username = socket.userData?.username || 'UnauthenticatedUser';
console.log(`[Socket.IO] User ${username} (ID: ${identifier || 'N/A'}, Socket: ${socket.id}) disconnected. Reason: ${reason}.`); console.log(`[BC Socket.IO] User ${username} (ID: ${identifier || 'N/A'}, Socket: ${socket.id}) disconnected. Reason: ${reason}.`);
if (identifier) { if (identifier) {
// Уведомляем GameManager об отключении пользователя, чтобы он мог обработать логику игры
// (например, пометить игрока как временно отключенного, запустить таймер реконнекта)
gameManager.handleDisconnect(socket.id, identifier); gameManager.handleDisconnect(socket.id, identifier);
} }
if (loggedInUsers[socket.id]) { if (loggedInUsers[socket.id]) {
delete loggedInUsers[socket.id]; delete loggedInUsers[socket.id]; // Удаляем из списка активных сокетов
} }
}); });
}); });
const PORT = parseInt(process.env.BC_APP_PORT || '3200', 10); const PORT = parseInt(process.env.BC_APP_PORT || '3200', 10);
const HOSTNAME = process.env.BC_APP_HOSTNAME || '127.0.0.1'; const HOSTNAME = process.env.BC_APP_HOSTNAME || '127.0.0.1'; // '0.0.0.0' для прослушивания на всех интерфейсах
if (isNaN(PORT)) { if (isNaN(PORT)) {
console.error(`[Server FATAL] Некорректное значение для BC_APP_PORT: "${process.env.BC_APP_PORT}". Ожидается число.`); console.error(`[BC Server FATAL] Некорректное значение для BC_APP_PORT: "${process.env.BC_APP_PORT}". Ожидается число.`);
process.exit(1); process.exit(1);
} }
server.listen(PORT, HOSTNAME, () => { server.listen(PORT, HOSTNAME, () => {
console.log(`Battle Club HTTP Application Server running at http://${HOSTNAME}:${PORT}`); console.log(`[BC Server] Battle Club HTTP Application Server running at http://${HOSTNAME}:${PORT}`);
if (HOSTNAME === '127.0.0.1') { if (HOSTNAME === '127.0.0.1') {
console.log(`Server is listening on localhost only. This is suitable if a reverse proxy handles external traffic.`); console.log(`[BC Server] Server is listening on localhost only. This is suitable if a reverse proxy handles external traffic.`);
} else if (HOSTNAME === '0.0.0.0') { } else if (HOSTNAME === '0.0.0.0') {
console.log(`Server is listening on all available network interfaces.`); console.log(`[BC Server] Server is listening on all available network interfaces.`);
} else { } else {
console.log(`Server is listening on a specific interface: ${HOSTNAME}.`); console.log(`[BC Server] Server is listening on a specific interface: ${HOSTNAME}.`);
} }
console.log(`Environment (NODE_ENV): ${process.env.NODE_ENV || 'not set (defaults to development behavior)'}`); console.log(`[BC Server] Static files served from: ${publicPath}`);
console.log(`Serving static files from: ${path.join(__dirname, '..', 'public')}`); console.log(`[BC Server] Socket.IO server using path: ${io.path()} and CORS origin: ${socketCorsOrigin === '*' ? "'*'" : socketCorsOrigin || 'Not set'}`);
console.log(`Client HTTP requests should target: http://${HOSTNAME}:${PORT}`); console.log(`[BC Server] HTTP API CORS origin: ${clientOrigin === '*' ? "'*'" : clientOrigin || 'Not set'}`);
console.log(`Socket.IO CORS origin set to: ${socketCorsOrigin || 'Not explicitly set, defaults may apply'}`);
console.log(`HTTP API CORS origin set to: ${clientOrigin || 'Not explicitly set, defaults may apply'}`);
}); });
// Глобальные обработчики необработанных ошибок
process.on('unhandledRejection', (reason, promise) => { process.on('unhandledRejection', (reason, promise) => {
console.error('[Server FATAL] Unhandled Rejection at:', promise, 'reason:', reason); console.error('[BC Server FATAL] Unhandled Rejection at:', promise, 'reason:', reason);
// В продакшене здесь может быть логика для более корректного завершения или перезапуска
}); });
process.on('uncaughtException', (err) => { process.on('uncaughtException', (err) => {
console.error('[Server FATAL] Uncaught Exception:', err); console.error('[BC Server FATAL] Uncaught Exception:', err);
// Важно: после uncaughtException приложение находится в непредсказуемом состоянии
// и должно быть перезапущено.
process.exit(1); process.exit(1);
}); });