Изменения для совместимости с сервером прокси апача
This commit is contained in:
parent
d5d5497dd6
commit
6c87028631
249
bc.js
249
bc.js
@ -1,228 +1,99 @@
|
||||
// bc.js - Главный файл сервера Battle Club
|
||||
|
||||
const express = require('express');
|
||||
const http = require('http');
|
||||
const http = require('http'); // Используем HTTP, так как SSL терминируется Apache
|
||||
const { Server } = require('socket.io');
|
||||
const path = require('path');
|
||||
|
||||
// Импорт серверных модулей
|
||||
const auth = require('./server_modules/auth');
|
||||
const GameManager = require('./server_modules/gameManager');
|
||||
const db = require('./server_modules/db'); // Импорт для инициализации соединения с БД (хотя пул создается при require)
|
||||
const GAME_CONFIG = require('./server_modules/config'); // Конфиг игры
|
||||
// gameData импортируется внутри GameInstance и GameLogic
|
||||
// Импорт ваших серверных модулей (предполагаем, что они есть и работают)
|
||||
// const auth = require('./server_modules/auth');
|
||||
// const GameManager = require('./server_modules/gameManager');
|
||||
// const db = require('./server_modules/db');
|
||||
// const GAME_CONFIG = require('./server_modules/config');
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
|
||||
const BC_APP_INTERNAL_PORT = 3200; // Внутренний порт, на котором слушает bc.js
|
||||
const BC_APP_INTERNAL_HOST = '127.0.0.1'; // Слушать только на localhost
|
||||
const PUBLIC_PATH_PREFIX = '/battleclub'; // Публичный префикс пути, по которому приложение доступно через Apache
|
||||
|
||||
// Настройка Socket.IO
|
||||
// cors options могут потребоваться, если клиент и сервер работают на разных портах/доменах
|
||||
// Клиент будет подключаться к /battleclub/socket.io/
|
||||
const io = new Server(server, {
|
||||
path: `${PUBLIC_PATH_PREFIX}/socket.io`,
|
||||
cors: {
|
||||
origin: "*", // Разрешить подключение с любого домена (для разработки). В продакшене лучше указать конкретный домен клиента.
|
||||
origin: "https://pavel-chagovsky.com", // Укажите ваш домен для безопасности
|
||||
// origin: "*", // Для разработки можно оставить, но для продакшена лучше конкретный домен
|
||||
methods: ["GET", "POST"]
|
||||
}
|
||||
});
|
||||
|
||||
// Middleware для логирования каждого запроса (полезно для отладки)
|
||||
app.use((req, res, next) => {
|
||||
console.log(`[BC App] Request: ${req.method} ${req.originalUrl}`);
|
||||
next();
|
||||
});
|
||||
|
||||
// Раздача статических файлов из папки 'public'
|
||||
// Так как Apache проксирует /battleclub/ на корень этого Express-приложения,
|
||||
// Express должен отдавать статику от своего корня.
|
||||
// В HTML ссылки на статику должны быть относительными или начинаться с /battleclub/
|
||||
// Например, если в public/js/client.js, то в HTML: <script src="/battleclub/js/client.js"></script>
|
||||
// Или, если index.html отдается с /battleclub/, то <script src="js/client.js"></script>
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// Создаем экземпляр GameManager
|
||||
const gameManager = new GameManager(io);
|
||||
|
||||
// Хранилище информации о залогиненных пользователях по socket.id
|
||||
// В более сложном приложении здесь может быть Redis или другое внешнее хранилище сессий
|
||||
const loggedInUsers = {}; // { socket.id: { userId: ..., username: ... } }
|
||||
// Пример простого маршрута API, если он нужен (доступен по /battleclub/api/test)
|
||||
app.get('/api/test', (req, res) => {
|
||||
res.json({ message: 'Battle Club API is working!' });
|
||||
});
|
||||
|
||||
// Обработка подключений Socket.IO
|
||||
// Если ваше основное приложение - это SPA (Single Page Application),
|
||||
// вам может понадобиться отдавать index.html для всех не-API и не-статических путей,
|
||||
// начинающихся с префикса /battleclub/. Но так как Apache проксирует /battleclub/ на /,
|
||||
// то Express будет видеть пути без /battleclub/.
|
||||
// Поэтому, если index.html должен отдаваться для /battleclub/ или /battleclub/some/path,
|
||||
// то здесь нужен роут для '*' или специфичные роуты.
|
||||
// Пока для простоты, предположим, что Apache проксирует /battleclub/ на корень bc.js,
|
||||
// и index.html находится в public/ и запрашивается как /battleclub/index.html (или просто /battleclub/)
|
||||
app.get('/', (req, res) => {
|
||||
// Этот роут будет срабатывать, если Apache проксировал /battleclub/ на / этого приложения
|
||||
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
||||
});
|
||||
|
||||
|
||||
// --- Обработчики событий Socket.IO ---
|
||||
io.on('connection', (socket) => {
|
||||
console.log(`[Socket.IO] Пользователь подключился: ${socket.id}`);
|
||||
console.log(`[BC App Socket.IO] User connected: ${socket.id} to path ${socket.nsp.name}`);
|
||||
|
||||
// Привязываем user data к сокету (пока пустые)
|
||||
socket.userData = null; // { userId: ..., username: ... }
|
||||
|
||||
// При подключении клиента, если он уже залогинен (например, по cookie/token, что здесь не реализовано,
|
||||
// но может быть добавлено), нужно восстановить его user data и проверить, не в игре ли он.
|
||||
// В текущей простой реализации, мы полагаемся на то, что клиент после коннекта сам отправит логин,
|
||||
// если он был залогинен. Но если бы была проверка сессии, логика была бы тут.
|
||||
// Добавляем вызов handleRequestGameState при коннекте, если есть user data (для примера,
|
||||
// но для полной реализации нужны cookies/токены)
|
||||
// if (socket.userData?.userId) { // Эта проверка сработает только после успешного логина в текущей сессии
|
||||
// gameManager.handleRequestGameState(socket, socket.userData.userId); // Передаем объект socket
|
||||
// }
|
||||
|
||||
|
||||
// --- Обработчики событий Аутентификации ---
|
||||
socket.on('register', async (data) => {
|
||||
console.log(`[Socket.IO] Register attempt for username: "${data?.username}" from ${socket.id}`);
|
||||
const result = await auth.registerUser(data?.username, data?.password);
|
||||
if (result.success) {
|
||||
console.log(`[Socket.IO] Registration successful for ${result.username} (${result.userId})`);
|
||||
} else {
|
||||
console.warn(`[Socket.IO] Registration failed for "${data?.username}": ${result.message}`);
|
||||
}
|
||||
socket.emit('registerResponse', result);
|
||||
socket.on('messageFromClient', (data) => {
|
||||
console.log(`[BC App Socket.IO] Message from client ${socket.id}:`, data);
|
||||
socket.emit('messageFromServer', { text: `Server received: ${data.text}` });
|
||||
});
|
||||
|
||||
socket.on('login', async (data) => {
|
||||
console.log(`[Socket.IO] Login attempt for username: "${data?.username}" from ${socket.id}`);
|
||||
const result = await auth.loginUser(data?.username, data?.password);
|
||||
if (result.success) {
|
||||
console.log(`[Socket.IO] Login successful for ${result.username} (${result.userId}). Assigning to socket ${socket.id}.`);
|
||||
// Сохраняем информацию о пользователе в сессии сокета
|
||||
socket.userData = { userId: result.userId, username: result.username };
|
||||
loggedInUsers[socket.id] = socket.userData;
|
||||
|
||||
// Проверяем, есть ли у пользователя активная игра при логине (если он был отключен)
|
||||
// ИСПРАВЛЕНИЕ: Передаем объект socket
|
||||
gameManager.handleRequestGameState(socket, socket.userData.userId);
|
||||
|
||||
} else {
|
||||
console.warn(`[Socket.IO] Login failed for "${data?.username}": ${result.message}`);
|
||||
socket.userData = null; // Убеждаемся, что данные пользователя на сокете сброшены
|
||||
if (loggedInUsers[socket.id]) delete loggedInUsers[socket.id];
|
||||
}
|
||||
socket.emit('loginResponse', result);
|
||||
});
|
||||
|
||||
socket.on('logout', () => {
|
||||
console.log(`[Socket.IO] Logout for user ${socket.userData?.username || socket.id}`);
|
||||
// Уведомляем gameManager о дисконнекте (для корректного выхода из игры, если в ней был)
|
||||
// Game Manager сам очистит ссылку socketToGame[socket.id] при handleDisconnect
|
||||
// ИСПРАВЛЕНИЕ: Передаем userId или socket.id в handleDisconnect
|
||||
gameManager.handleDisconnect(socket.id, socket.userData?.userId || socket.id);
|
||||
|
||||
// Очищаем информацию о пользователе на сокете и в хранилище
|
||||
socket.userData = null;
|
||||
if (loggedInUsers[socket.id]) delete loggedInUsers[socket.id];
|
||||
|
||||
// Клиент должен сам переключиться на экран аутентификации
|
||||
});
|
||||
|
||||
// --- Обработчики событий Управления Играми ---
|
||||
|
||||
socket.on('createGame', (data) => {
|
||||
// Пользователь, даже не залогиненный, может создать AI игру (идентифицируется по socket.id)
|
||||
// Для PvP игры нужна аутентификация (идентификация по userId)
|
||||
const identifier = socket.userData?.userId || socket.id; // Используем userId для залогиненных, socket.id для гостей
|
||||
const mode = data?.mode || 'ai';
|
||||
|
||||
if (mode === 'pvp' && !socket.userData) {
|
||||
socket.emit('gameError', { message: 'Необходимо войти в систему для создания PvP игры.' });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[Socket.IO] Create Game request from ${socket.userData?.username || socket.id} (Identifier: ${identifier}). Mode: ${mode}, Character: ${data?.characterKey}`);
|
||||
|
||||
const characterKey = data?.characterKey || 'elena'; // По умолчанию Елена
|
||||
gameManager.createGame(socket, mode, characterKey, identifier); // Передаем идентификатор
|
||||
|
||||
});
|
||||
|
||||
socket.on('joinGame', (data) => {
|
||||
if (!socket.userData) { // Проверяем, залогинен ли пользователь
|
||||
socket.emit('gameError', { message: 'Необходимо войти в систему для присоединения к игре.' });
|
||||
return;
|
||||
}
|
||||
console.log(`[Socket.IO] Join Game request from ${socket.userData.username} (${socket.id}). Game ID: ${data?.gameId}`);
|
||||
const gameId = data?.gameId;
|
||||
const identifier = socket.userData.userId; // Присоединиться может только залогиненный
|
||||
|
||||
if (gameId) {
|
||||
gameManager.joinGame(socket, gameId, identifier); // Передаем идентификатор
|
||||
} else {
|
||||
socket.emit('gameError', { message: 'Не указан ID игры для присоединения.' });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('findRandomGame', (data) => {
|
||||
if (!socket.userData) { // Проверяем, залогинен ли пользователь
|
||||
socket.emit('gameError', { message: 'Необходимо войти в систему для поиска игры.' });
|
||||
return;
|
||||
}
|
||||
console.log(`[Socket.IO] Find Random Game request from ${socket.userData.username} (${socket.id}). Preferred Character: ${data?.characterKey}`);
|
||||
const characterKey = data?.characterKey || 'elena'; // Предпочитаемый персонаж для создания, если не найдено
|
||||
const identifier = socket.userData.userId; // Ищет и создает только залогиненный
|
||||
|
||||
gameManager.findAndJoinRandomPvPGame(socket, characterKey, identifier); // Передаем идентификатор
|
||||
});
|
||||
|
||||
socket.on('requestPvPGameList', () => {
|
||||
// Список игр доступен всем, даже не залогиненным, но присоединиться можно только залогиненным
|
||||
// if (!socket.userData) {
|
||||
// socket.emit('gameError', { message: 'Необходимо войти в систему для просмотра игр.' });
|
||||
// return;
|
||||
// }
|
||||
console.log(`[Socket.IO] Request PvP Game List from ${socket.userData?.username || socket.id}`);
|
||||
const availableGames = gameManager.getAvailablePvPGamesListForClient();
|
||||
socket.emit('availablePvPGamesList', availableGames);
|
||||
});
|
||||
|
||||
// Обработчик для клиента, запрашивающего состояние игры (например, при переподключении)
|
||||
socket.on('requestGameState', () => {
|
||||
// Запрашивать состояние игры может только залогиненный пользователь, т.к. только у них есть userId для идентификации
|
||||
if (!socket.userData) {
|
||||
console.log(`[Socket.IO] Request Game State from unauthenticated socket ${socket.id}.`);
|
||||
socket.emit('gameNotFound', { message: 'Необходимо войти для восстановления игры.' });
|
||||
return;
|
||||
}
|
||||
console.log(`[Socket.IO] Request Game State from ${socket.userData.username} (${socket.id}).`);
|
||||
// ИСПРАВЛЕНИЕ: Передаем объект socket и identifier (userId)
|
||||
gameManager.handleRequestGameState(socket, socket.userData.userId);
|
||||
});
|
||||
|
||||
|
||||
// --- Обработчик события Игрового Действия ---
|
||||
socket.on('playerAction', (actionData) => {
|
||||
// Действие в игре может совершить как залогиненный (PvP), так и не залогиненный (AI) игрок.
|
||||
// Используем userId для залогиненных, socket.id для гостей.
|
||||
const identifier = socket.userData?.userId || socket.id;
|
||||
|
||||
// Game Manager сам проверит, находится ли идентификатор в игре и его ли сейчас ход
|
||||
// ИСПРАВЛЕНИЕ: Передаем идентификатор вместо socket.id
|
||||
gameManager.handlePlayerAction(identifier, actionData); // Передаем идентификатор
|
||||
});
|
||||
|
||||
|
||||
// --- Обработчик отключения сокета ---
|
||||
socket.on('disconnect', (reason) => {
|
||||
const identifier = socket.userData?.userId || socket.id; // Используем userId для залогиненных, socket.id для гостей
|
||||
console.log(`[Socket.IO] Пользователь отключился: ${socket.id} (Причина: ${reason}). Identifier: ${identifier}`);
|
||||
|
||||
// Уведомляем gameManager о дисконнекте, чтобы он обновил состояние игры.
|
||||
// Передаем идентификатор пользователя.
|
||||
gameManager.handleDisconnect(socket.id, identifier); // Передаем как socketId, так и identifier
|
||||
|
||||
// Удаляем пользователя из списка залогиненных, если был там
|
||||
if (loggedInUsers[socket.id]) {
|
||||
delete loggedInUsers[socket.id];
|
||||
}
|
||||
// Если сокет не был залогинен, его identifier был socket.id.
|
||||
// Связь userIdentifierToGameId будет очищена в gameManager.handleDisconnect, если игра пуста.
|
||||
console.log(`[BC App Socket.IO] User disconnected: ${socket.id} (Reason: ${reason})`);
|
||||
});
|
||||
|
||||
// Опционально: отправка списка активных игр на сервере для отладки (по запросу с консоли или админки)
|
||||
// global.getActiveGames = () => gameManager.getActiveGamesList();
|
||||
// console.log("Type getActiveGames() in server console to list games.");
|
||||
// ... ваши обработчики auth, GameManager и т.д. ...
|
||||
// Убедитесь, что они не полагаются на префикс пути /battleclub/ во внутренних данных,
|
||||
// так как Express его "не видит" после проксирования Apache.
|
||||
});
|
||||
|
||||
|
||||
// Запуск HTTP сервера
|
||||
const PORT = process.env.PORT || 3200; // Использовать порт из переменных окружения или 3000 по умолчанию
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
server.listen(BC_APP_INTERNAL_PORT, BC_APP_INTERNAL_HOST, () => {
|
||||
console.log(`Battle Club HTTP Application Server running at http://${BC_APP_INTERNAL_HOST}:${BC_APP_INTERNAL_PORT}`);
|
||||
console.log(`Socket.IO will be available via proxy at path: ${PUBLIC_PATH_PREFIX}/socket.io`);
|
||||
console.log(`Serving static files from: ${path.join(__dirname, 'public')}`);
|
||||
// console.log("Database connection pool created/checked (from db.js require)."); // db.js уже логирует
|
||||
});
|
||||
|
||||
// Обработка необработанных промис-ошибок
|
||||
// Обработчики глобальных ошибок
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
console.error('[UNHANDLED REJECTION] Unhandled Rejection at:', promise, 'reason:', reason);
|
||||
// Логировать ошибку, возможно, завершить процесс в продакшене
|
||||
console.error('[BC App UNHANDLED REJECTION] At:', promise, 'reason:', reason);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
console.error('[UNCAUGHT EXCEPTION] Caught exception:', err);
|
||||
// Логировать ошибку, выполнить очистку ресурсов, и завершить процесс
|
||||
// В продакшене здесь может быть более сложная логика, например, graceful shutdown
|
||||
// process.exit(1); // Аварийное завершение процесса - можно раскомментировать в продакшене
|
||||
console.error('[BC App UNCAUGHT EXCEPTION] Caught exception:', err);
|
||||
// process.exit(1); // В продакшене может быть оправдано
|
||||
});
|
4
deploy-script.sh
Executable file
4
deploy-script.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
cd /home/nodejs/bc/ || exit 1
|
||||
git pull origin main
|
||||
pm2 restart /home/nodejs/bc/bc.js
|
9
node_modules/child_process/README.md
generated
vendored
Normal file
9
node_modules/child_process/README.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# Security holding package
|
||||
|
||||
This package name is not currently in use, but was formerly occupied
|
||||
by another package. To avoid malicious use, npm is hanging on to the
|
||||
package name, but loosely, and we'll probably give it to you if you
|
||||
want it.
|
||||
|
||||
You may adopt this package by contacting support@npmjs.com and
|
||||
requesting the name.
|
20
node_modules/child_process/package.json
generated
vendored
Normal file
20
node_modules/child_process/package.json
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "child_process",
|
||||
"version": "1.0.2",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/npm/security-holder.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/npm/security-holder/issues"
|
||||
},
|
||||
"homepage": "https://github.com/npm/security-holder#readme"
|
||||
}
|
7
node_modules/crypto/README.md
generated
vendored
Normal file
7
node_modules/crypto/README.md
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# Deprecated Package
|
||||
|
||||
This package is no longer supported and has been deprecated. To avoid malicious use, npm is hanging on to the package name.
|
||||
|
||||
It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.
|
||||
|
||||
Please contact support@npmjs.com if you have questions about this package.
|
19
node_modules/crypto/package.json
generated
vendored
Normal file
19
node_modules/crypto/package.json
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "crypto",
|
||||
"version": "1.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/npm/deprecate-holder.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/npm/deprecate-holder/issues"
|
||||
},
|
||||
"homepage": "https://github.com/npm/deprecate-holder#readme"
|
||||
}
|
@ -214,7 +214,7 @@
|
||||
</div>
|
||||
</div> <!-- Конец .game-wrapper -->
|
||||
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script src="/battleclub/socket.io/socket.io.js"></script>
|
||||
<script src="./js/ui.js"></script>
|
||||
<script src="./js/client.js"></script>
|
||||
</body>
|
||||
|
@ -1,9 +1,7 @@
|
||||
// /public/js/client.js
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const socket = io({
|
||||
// Опции Socket.IO, если нужны
|
||||
});
|
||||
const socket = io({ path: '/battleclub/socket.io' })
|
||||
|
||||
// --- Состояние клиента ---
|
||||
let currentGameState = null;
|
||||
|
116
webhook.js
Normal file
116
webhook.js
Normal file
@ -0,0 +1,116 @@
|
||||
// webhook-receiver.js
|
||||
const https = require('https'); // Используем https
|
||||
const fs = require('fs'); // Для чтения файлов сертификатов
|
||||
const express = require('express');
|
||||
const crypto = require('crypto'); // Для проверки секрета Gitea (если будете использовать)
|
||||
const { exec } = require('child_process'); // Для запуска скрипта деплоя
|
||||
const path = require('path'); // Убедитесь, что path импортирован в начале файла
|
||||
|
||||
const app = express();
|
||||
const port = 3800; // Порт, на котором слушает этот сервис
|
||||
const ipAddress = "81.177.140.16"; // Ваш IP-адрес, на котором слушать
|
||||
|
||||
// --- Ваши переменные ---
|
||||
const GITEA_SECRET = 'Hjp"f2mWF]3>Mc'; // Ваш секрет Gitea (используется для проверки подписи)
|
||||
const DEPLOY_SCRIPT_PATH = path.join(__dirname, 'deploy-script.sh');
|
||||
|
||||
// --- Опции для HTTPS сервера ---
|
||||
// Убедитесь, что пути к сертификатам верны и у Node.js есть права на их чтение
|
||||
const sslOptions = {
|
||||
key: fs.readFileSync('/etc/letsencrypt/live/pavel-chagovsky.com/privkey.pem'),
|
||||
cert: fs.readFileSync('/etc/letsencrypt/live/pavel-chagovsky.com/fullchain.pem'),
|
||||
};
|
||||
|
||||
// --- Middlewares ---
|
||||
app.use(express.json()); // Для парсинга JSON тела запроса
|
||||
// app.use(express.urlencoded({ extended: true })); // Если будете принимать form-urlencoded данные
|
||||
|
||||
// --- Маршруты ---
|
||||
app.post('/', (req, res) => { // Слушаем POST-запросы на корневой путь "/"
|
||||
console.log(`[${new Date().toISOString()}] --- HTTPS POST Request to / Received ---`);
|
||||
console.log('Headers:', req.headers);
|
||||
console.log('Body:', req.body);
|
||||
|
||||
// Опционально: Проверка секрета, если запрос от Gitea
|
||||
// Эту часть можно закомментировать, если вы тестируете просто с cURL из PHP без секрета
|
||||
const giteaSignature = req.headers['x-gitea-signature'];
|
||||
if (GITEA_SECRET && giteaSignature) { // Проверяем только если секрет задан и подпись пришла
|
||||
const hmac = crypto.createHmac('sha256', GITEA_SECRET);
|
||||
// Важно: Gitea подписывает СЫРОЕ тело запроса.
|
||||
// express.json() уже распарсил req.body. Для точной проверки подписи нужно использовать сырое тело.
|
||||
// Для этого можно использовать middleware типа `raw-body` или настроить express.json с опцией `verify`.
|
||||
// Пока для простоты пропустим точную проверку подписи, но в продакшене это ВАЖНО.
|
||||
// Просто для примера, как это могло бы быть (требует доработки с raw body):
|
||||
// const digest = 'sha256=' + hmac.update(JSON.stringify(req.body) /* НЕПРАВИЛЬНО для проверки подписи Gitea, нужно сырое тело */).digest('hex');
|
||||
// if (!crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(giteaSignature))) {
|
||||
// console.warn(`[${new Date().toISOString()}] Webhook: Invalid signature`);
|
||||
// return res.status(401).send('Invalid signature');
|
||||
// }
|
||||
console.log(`[${new Date().toISOString()}] Webhook: Gitea signature present (проверка пока упрощена).`);
|
||||
} else if (GITEA_SECRET && !giteaSignature) {
|
||||
console.warn(`[${new Date().toISOString()}] Webhook: Missing Gitea signature, but secret is configured.`);
|
||||
// return res.status(401).send('Missing signature'); // Можно раскомментировать для строгости
|
||||
}
|
||||
|
||||
|
||||
// Запуск скрипта деплоя
|
||||
console.log(`[${new Date().toISOString()}] Executing deploy script: ${DEPLOY_SCRIPT_PATH}`);
|
||||
exec(DEPLOY_SCRIPT_PATH, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`[${new Date().toISOString()}] Deploy Script Error: ${error.message}`);
|
||||
console.error(`[${new Date().toISOString()}] Deploy Script Stderr: ${stderr}`);
|
||||
// Не отправляем ошибку клиенту сразу, чтобы не раскрывать детали,
|
||||
// но можно отправить общий код ошибки.
|
||||
if (!res.headersSent) {
|
||||
return res.status(500).send('Deployment script failed to execute.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (stderr) {
|
||||
console.warn(`[${new Date().toISOString()}] Deploy Script Stderr: ${stderr}`);
|
||||
}
|
||||
console.log(`[${new Date().toISOString()}] Deploy Script Stdout: ${stdout}`);
|
||||
if (!res.headersSent) {
|
||||
res.status(200).send('Webhook received and deployment script initiated.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// GET-маршрут для простой проверки, что сервер жив
|
||||
app.get('/', (req, res) => {
|
||||
console.log(`[${new Date().toISOString()}] --- HTTPS GET Request to / Received ---`);
|
||||
res.status(200).send('Express HTTPS server is running.');
|
||||
});
|
||||
|
||||
// --- Запуск HTTPS сервера ---
|
||||
const server = https.createServer(sslOptions, app);
|
||||
|
||||
server.listen(port, ipAddress, () => {
|
||||
console.log(`[${new Date().toISOString()}] Express HTTPS server listening at https://${ipAddress}:${port}`);
|
||||
});
|
||||
|
||||
server.on('error', (err) => {
|
||||
console.error(`[${new Date().toISOString()}] Server critical error:`, err);
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
console.error(`[${new Date().toISOString()}] Port ${port} on IP ${ipAddress} is already in use.`);
|
||||
}
|
||||
// process.exit(1); // Можно завершить процесс при критической ошибке сервера
|
||||
});
|
||||
|
||||
// Обработка сигналов для корректного завершения (опционально, но хорошо для pm2/systemd)
|
||||
function gracefulShutdown(signal) {
|
||||
console.log(`[${new Date().toISOString()}] Received ${signal}. Shutting down server...`);
|
||||
server.close(() => {
|
||||
console.log(`[${new Date().toISOString()}] HTTP server closed.`);
|
||||
// Здесь можно добавить закрытие других ресурсов, если они есть
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// Если сервер не закрывается за таймаут, принудительно завершить
|
||||
setTimeout(() => {
|
||||
console.error(`[${new Date().toISOString()}] Could not close connections in time, forcefully shutting down.`);
|
||||
process.exit(1);
|
||||
}, 10000); // 10 секунд
|
||||
}
|
||||
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
||||
process.on('SIGINT', () => gracefulShutdown('SIGINT')); // Ctrl+C
|
Loading…
x
Reference in New Issue
Block a user