From e85d1ce6689018005b46ab47e66ad79a4a983dc0 Mon Sep 17 00:00:00 2001 From: Web-serfer Date: Sat, 18 Apr 2026 16:19:19 +0500 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0=20scripts/dev.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- scripts/dev.js | 221 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 174 insertions(+), 49 deletions(-) diff --git a/package.json b/package.json index c5f39b8..986ffb5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "dev": "bun run scripts/dev.js", + "dev": "bun ./scripts/dev.js", "dev:backend": "cd backend && ./pocketbase.exe serve", "dev:frontend": "cd frontend && bun dev", "build:frontend": "cd frontend && bun build", diff --git a/scripts/dev.js b/scripts/dev.js index 57f6059..0c6505a 100644 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -3,64 +3,189 @@ import { $ } from "bun"; import { spawn } from "child_process"; import path from "path"; +import { promisify } from "util"; +import net from "net"; -console.log("🚀 Запуск серверов avtourist086...\n"); +const sleep = promisify(setTimeout); -// Запуск Maildev -const maildev = spawn("maildev", ["--web", "1080", "--smtp", "1025"], { - stdio: "inherit", - shell: true, -}); +// Проверка занятости порта +async function isPortInUse(port) { + return new Promise((resolve) => { + const server = net.createServer(); + server.once("error", () => resolve(true)); + server.once("listening", () => { + server.close(); + resolve(false); + }); + server.listen(port); + }); +} -// Запуск PocketBase с SMTP настройками -const backend = spawn("pocketbase.exe", ["serve"], { - cwd: path.join(process.cwd(), "backend"), - stdio: "inherit", - shell: true, - env: { - ...process.env, - PB_SMTP_HOST: "localhost", - PB_SMTP_PORT: "1025", - PB_SMTP_FROM: "noreply@avtourist-surgut.ru" +// Принудительное убийство процесса по PID (Windows) +async function killProcess(pid, force = true) { + try { + if (process.platform === "win32") { + await $`taskkill /PID ${pid} ${force ? "/F" : ""}`.quiet(); + } else { + process.kill(pid, force ? "SIGKILL" : "SIGTERM"); + } + } catch (e) { + // Процесс уже мёртв — ок } -}); +} -// Запуск Astro -const frontend = spawn("bun", ["dev"], { - cwd: "frontend", - stdio: "inherit", - shell: true, -}); +// Поиск и убийство процессов по имени порта +async function killPort(port) { + try { + if (process.platform === "win32") { + // Находим PID по порту + const { stdout } = await $`netstat -ano | findstr :${port}`.quiet().nothrow(); + const lines = stdout.toString().trim().split("\n"); + const pids = new Set(); -// Обработка завершения -const cleanup = () => { + for (const line of lines) { + const parts = line.trim().split(/\s+/); + if (parts.length >= 5) { + const pid = parts[4]; + if (pid && !isNaN(parseInt(pid))) { + pids.add(parseInt(pid)); + } + } + } + + for (const pid of pids) { + await killProcess(pid); + console.log(` 🔪 Убит процесс ${pid} на порту ${port}`); + } + } else { + await $`lsof -ti:${port} | xargs kill -9`.quiet().nothrow(); + } + } catch (e) { + // Нет процессов — ок + } +} + +// Проверка и освобождение портов +async function cleanupPorts() { + console.log("🔍 Проверка портов..."); + const ports = [1025, 1080, 4321, 8090]; + + for (const port of ports) { + if (await isPortInUse(port)) { + console.log(` ⚠️ Порт ${port} занят, освобождаем...`); + await killPort(port); + await sleep(500); // Даём время на освобождение + } + } +} + +let maildev, backend, frontend; + +// Корректное завершение всех процессов +async function cleanup() { console.log("\n🛑 Остановка серверов..."); - maildev.kill(); - backend.kill(); - frontend.kill(); + + // Принудительно убиваем каждый процесс с SIGKILL + const kills = []; + + if (maildev && !maildev.killed) { + maildev.kill("SIGKILL"); + kills.push(new Promise(r => maildev.once("exit", r))); + } + if (backend && !backend.killed) { + backend.kill("SIGKILL"); + kills.push(new Promise(r => backend.once("exit", r))); + } + if (frontend && !frontend.killed) { + frontend.kill("SIGKILL"); + kills.push(new Promise(r => frontend.once("exit", r))); + } + + // Ждём завершения не дольше 3 секунд + await Promise.race([ + Promise.all(kills), + sleep(3000) + ]); + process.exit(0); -}; +} -process.on("SIGINT", cleanup); -process.on("SIGTERM", cleanup); +// Главная функция +async function main() { + await cleanupPorts(); -// Ожидание завершения процессов -maildev.on("exit", (code) => { - console.log(`Maildev остановлен с кодом ${code}`); -}); + console.log("🚀 Запуск серверов avtourist086...\n"); -backend.on("exit", (code) => { - console.log(`Backend остановлен с кодом ${code}`); - cleanup(); -}); + // Запуск Maildev с отдельной группой процессов + maildev = spawn("maildev", ["--web", "1080", "--smtp", "1025"], { + stdio: "inherit", + shell: true, + windowsHide: true, + detached: false + }); -frontend.on("exit", (code) => { - console.log(`Frontend остановлен с кодом ${code}`); - cleanup(); -}); + // Запуск PocketBase + backend = spawn("pocketbase.exe", ["serve"], { + cwd: path.join(process.cwd(), "backend"), + stdio: "inherit", + shell: true, + windowsHide: true, + detached: false, + env: { + ...process.env, + PB_SMTP_HOST: "localhost", + PB_SMTP_PORT: "1025", + PB_SMTP_FROM: "noreply@avtourist-surgut.ru" + } + }); -console.log("✅ Серверы запущены:\n"); -console.log(" Maildev (SMTP): http://localhost:1080"); -console.log(" Backend (PocketBase): http://localhost:8090"); -console.log(" Frontend (Astro): http://localhost:4321\n"); -console.log("Нажмите Ctrl+C для остановки\n"); + // Запуск Astro + frontend = spawn("bun", ["dev"], { + cwd: "frontend", + stdio: "inherit", + shell: true, + windowsHide: true, + detached: false + }); + + // Обработчики сигналов + process.on("SIGINT", cleanup); + process.on("SIGTERM", cleanup); + process.on("SIGBREAK", cleanup); // Windows-specific + + // Обработка закрытия консоли Windows + if (process.platform === "win32") { + const readline = await import("readline"); + readline.createInterface({ + input: process.stdin, + output: process.stdout + }).on("SIGINT", cleanup); + } + + // Мониторинг процессов + maildev.on("exit", (code) => { + console.log(`\n📧 Maildev остановлен с кодом ${code}`); + if (code !== 0 && code !== null) cleanup(); + }); + + backend.on("exit", (code) => { + console.log(`\n💾 Backend остановлен с кодом ${code}`); + if (code !== 0 && code !== null) cleanup(); + }); + + frontend.on("exit", (code) => { + console.log(`\n🌐 Frontend остановлен с кодом ${code}`); + cleanup(); + }); + + console.log("✅ Серверы запущены:\n"); + console.log(" 📧 Maildev (SMTP): http://localhost:1080"); + console.log(" 💾 Backend (PocketBase): http://localhost:8090"); + console.log(" 🌐 Frontend (Astro): http://localhost:4321\n"); + console.log("Нажмите Ctrl+C для остановки\n"); +} + +main().catch(err => { + console.error("❌ Ошибка:", err); + process.exit(1); +}); \ No newline at end of file