astro_avtourist/scripts/dev.js

191 lines
5.4 KiB
JavaScript
Raw Permalink Normal View History

2026-03-31 22:53:39 +05:00
#!/usr/bin/env bun
import { $ } from "bun";
import { spawn } from "child_process";
import path from "path";
import { promisify } from "util";
import net from "net";
2026-03-31 22:53:39 +05:00
const sleep = promisify(setTimeout);
// Проверка занятости порта
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);
});
}
// Принудительное убийство процесса по 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) {
// Процесс уже мёртв — ок
}
}
// Поиск и убийство процессов по имени порта
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();
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) {
// Нет процессов — ок
}
}
2026-03-31 22:53:39 +05:00
// Проверка и освобождение портов
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); // Даём время на освобождение
}
}
}
2026-03-31 22:53:39 +05:00
let maildev, backend, frontend;
// Корректное завершение всех процессов
async function cleanup() {
2026-03-31 22:53:39 +05:00
console.log("\n🛑 Остановка серверов...");
// Принудительно убиваем каждый процесс с 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)
]);
2026-03-31 22:53:39 +05:00
process.exit(0);
}
// Главная функция
async function main() {
await cleanupPorts();
console.log("🚀 Запуск серверов avtourist086...\n");
// Запуск Maildev с отдельной группой процессов
maildev = spawn("maildev", ["--web", "1080", "--smtp", "1025"], {
stdio: "inherit",
shell: true,
windowsHide: true,
detached: false
});
// Запуск 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"
}
});
// Запуск 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);
});