/** * Prepare local dev: free backend port and ensure MySQL is reachable. * Runs automatically via npm "predev" before `npm run dev`. */ import { execSync } from 'node:child_process'; import net from 'node:net'; import { platform } from 'node:os'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const MYSQL_PORT = 3306; const BACKEND_LISTEN_PORT = 3001; const FRONTEND_PORT = 5173; const __dirname = dirname(fileURLToPath(import.meta.url)); const rootDir = join(__dirname, '..'); const backendDir = join(rootDir, 'backend'); function log(msg) { console.log(`[prepare-dev] ${msg}`); } function isPortOpen(port, host = '127.0.0.1') { return new Promise((resolve) => { const socket = net.createConnection({ port, host }, () => { socket.destroy(); resolve(true); }); socket.on('error', () => resolve(false)); socket.setTimeout(1500, () => { socket.destroy(); resolve(false); }); }); } function freePort(port) { if (platform() === 'win32') { try { const out = execSync(`netstat -ano | findstr :${port}`, { encoding: 'utf8' }); const pids = new Set(); for (const line of out.split('\n')) { if (!line.includes('LISTENING')) continue; const parts = line.trim().split(/\s+/); const pid = parts[parts.length - 1]; if (pid && pid !== '0') pids.add(pid); } for (const pid of pids) { log(`Stopping process ${pid} on port ${port}`); execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' }); } } catch { // nothing listening } return; } try { execSync(`lsof -ti:${port} | xargs kill -9 2>/dev/null`, { stdio: 'ignore', shell: true }); } catch { // nothing listening } } async function ensureMysql() { if (await isPortOpen(MYSQL_PORT)) { log(`MySQL is reachable on port ${MYSQL_PORT}`); return; } log(`MySQL not reachable on port ${MYSQL_PORT}; starting Docker Compose...`); try { execSync('docker compose -f docker-compose.dev.yml up -d', { cwd: backendDir, stdio: 'inherit', }); } catch { console.error( '[prepare-dev] ERROR: MySQL is not running. Start it with:\n' + ' cd backend && docker compose -f docker-compose.dev.yml up -d', ); process.exit(1); } for (let i = 0; i < 30; i += 1) { if (await isPortOpen(MYSQL_PORT)) { log('MySQL is ready'); return; } await new Promise((r) => setTimeout(r, 1000)); } console.error('[prepare-dev] ERROR: MySQL did not become ready in time.'); process.exit(1); } freePort(BACKEND_LISTEN_PORT); freePort(FRONTEND_PORT); await ensureMysql(); log(`Ready — backend will listen on port ${BACKEND_LISTEN_PORT}`);