diff --git a/backend/lib/portFinder.js b/backend/lib/portFinder.js new file mode 100644 index 0000000..da06584 --- /dev/null +++ b/backend/lib/portFinder.js @@ -0,0 +1,77 @@ +// Утилита для поиска свободного порта + +const net = require('net'); + +/** + * Проверяет, свободен ли порт + * @param {number} port - Порт для проверки + * @returns {Promise} - true если порт свободен, false если занят + */ +function isPortAvailable(port) { + return new Promise((resolve) => { + const server = net.createServer(); + + server.listen(port, () => { + server.once('close', () => { + resolve(true); + }); + server.close(); + }); + + server.on('error', (err) => { + if (err.code === 'EADDRINUSE') { + resolve(false); + } else { + resolve(false); + } + }); + }); +} + +/** + * Находит свободный порт начиная с заданного + * @param {number} startPort - Начальный порт для поиска + * @param {number} maxAttempts - Максимальное количество попыток (по умолчанию 10) + * @returns {Promise} - Свободный порт + */ +async function findFreePort(startPort, maxAttempts = 10) { + for (let i = 0; i < maxAttempts; i++) { + const port = startPort + i; + const available = await isPortAvailable(port); + if (available) { + return port; + } + } + throw new Error( + `Не удалось найти свободный порт в диапазоне ${startPort}-${startPort + maxAttempts - 1}` + ); +} + +/** + * Получить порт для сервера (проверяет занятость и находит свободный) + * @param {number} defaultPort - Порт по умолчанию + * @param {boolean} autoFind - Автоматически искать свободный если занят + * @returns {Promise} - Порт для использования + */ +async function getServerPort(defaultPort, autoFind = true) { + const available = await isPortAvailable(defaultPort); + if (available) { + return defaultPort; + } + + if (!autoFind) { + throw new Error(`Порт ${defaultPort} занят`); + } + + console.log(`⚠️ Порт ${defaultPort} занят, ищем свободный...`); + const freePort = await findFreePort(defaultPort + 1); + console.log(`✅ Найден свободный порт: ${freePort}`); + return freePort; +} + +module.exports = { + isPortAvailable, + findFreePort, + getServerPort, +}; + diff --git a/backend/server.js b/backend/server.js index 8de5bcf..4cc24fd 100644 --- a/backend/server.js +++ b/backend/server.js @@ -9,7 +9,11 @@ const errorHandler = require('./middleware/errorHandler'); const { generalLimiter } = require('./middleware/rateLimiter'); const app = express(); -const PORT = process.env.PORT || 3001; +const { getServerPort } = require('./lib/portFinder'); + +// Получаем порт (с автоматическим поиском свободного если нужно) +const DEFAULT_PORT = process.env.PORT || 3001; +let PORT = DEFAULT_PORT; // Middleware app.use(simpleLogger); // Простое логирование запросов @@ -63,10 +67,23 @@ app.use((req, res) => { // Обработка ошибок (должен быть последним middleware) app.use(errorHandler); -// Запуск сервера -app.listen(PORT, () => { - winstonLogger.info(`🚀 Server is running on http://localhost:${PORT}`); - winstonLogger.info(`📡 Health check: http://localhost:${PORT}/api/health`); - winstonLogger.info(`📝 Environment: ${process.env.NODE_ENV || 'development'}`); -}); +// Запуск сервера с автоматическим поиском свободного порта +(async () => { + try { + PORT = await getServerPort(DEFAULT_PORT, true); + app.listen(PORT, () => { + winstonLogger.info(`🚀 Server is running on http://localhost:${PORT}`); + winstonLogger.info(`📡 Health check: http://localhost:${PORT}/api/health`); + winstonLogger.info(`📝 Environment: ${process.env.NODE_ENV || 'development'}`); + if (PORT !== DEFAULT_PORT) { + winstonLogger.info( + `ℹ️ Используется порт ${PORT} вместо ${DEFAULT_PORT} (занят)` + ); + } + }); + } catch (error) { + winstonLogger.error(`❌ Ошибка запуска сервера: ${error.message}`); + process.exit(1); + } +})(); diff --git a/frontend/package.json b/frontend/package.json index 7aa682e..fa8c73b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --turbopack", + "dev": "node scripts/devWithPort.js", "build": "next build", "start": "next start", "lint": "next lint", diff --git a/frontend/scripts/devWithPort.js b/frontend/scripts/devWithPort.js new file mode 100644 index 0000000..13ed9d9 --- /dev/null +++ b/frontend/scripts/devWithPort.js @@ -0,0 +1,42 @@ +// Скрипт для запуска Next.js dev с автоматическим поиском свободного порта + +const { spawn } = require('child_process'); +const { findFreePort } = require('./findPort'); + +const DEFAULT_PORT = 3000; + +(async () => { + try { + const port = await findFreePort(DEFAULT_PORT); + + if (port !== DEFAULT_PORT) { + console.log(`⚠️ Порт ${DEFAULT_PORT} занят, используем порт ${port}`); + } else { + console.log(`✅ Порт ${port} свободен`); + } + + // Запускаем Next.js dev с найденным портом + const nextDev = spawn('npx', ['next', 'dev', '--turbopack', '-p', String(port)], { + stdio: 'inherit', + shell: true, + }); + + nextDev.on('error', (error) => { + console.error('Ошибка запуска Next.js:', error); + process.exit(1); + }); + + nextDev.on('exit', (code) => { + process.exit(code || 0); + }); + + // Обработка Ctrl+C + process.on('SIGINT', () => { + nextDev.kill('SIGINT'); + }); + } catch (error) { + console.error('Ошибка поиска порта:', error.message); + process.exit(1); + } +})(); + diff --git a/frontend/scripts/findPort.js b/frontend/scripts/findPort.js new file mode 100644 index 0000000..af650ef --- /dev/null +++ b/frontend/scripts/findPort.js @@ -0,0 +1,56 @@ +// Скрипт для поиска свободного порта для Next.js + +const net = require('net'); + +/** + * Проверяет, свободен ли порт + */ +function isPortAvailable(port) { + return new Promise((resolve) => { + const server = net.createServer(); + + server.listen(port, () => { + server.once('close', () => { + resolve(true); + }); + server.close(); + }); + + server.on('error', () => { + resolve(false); + }); + }); +} + +/** + * Находит свободный порт начиная с заданного + */ +async function findFreePort(startPort, maxAttempts = 10) { + for (let i = 0; i < maxAttempts; i++) { + const port = startPort + i; + const available = await isPortAvailable(port); + if (available) { + return port; + } + } + throw new Error( + `Не удалось найти свободный порт в диапазоне ${startPort}-${startPort + maxAttempts - 1}` + ); +} + +// Если запущен как скрипт +if (require.main === module) { + const startPort = parseInt(process.argv[2]) || 3000; + findFreePort(startPort) + .then((port) => { + console.log(port); + process.exit(0); + }) + .catch((error) => { + console.error(error.message); + process.exit(1); + }); +} + +module.exports = { isPortAvailable, findFreePort }; +