#!/usr/bin/env node /** * 暖记开发环境停止脚本 — 清理所有服务进程 * * 用法: * pnpm start:stop */ import { execSync } from "node:child_process"; const IS_WIN = process.platform === "win32"; // ===== 配置 ===== const SERVICES = [ { port: 3000, name: "后端 (Rust/Axum)" }, { port: 5174, name: "管理端前端 (React/Vite)" }, { port: 8080, name: "学生端前端 (Flutter Web)" }, ]; // ===== 颜色输出 ===== const log = { info: (msg) => console.log(`\x1b[34m[INFO]\x1b[0m ${msg}`), ok: (msg) => console.log(`\x1b[32m[OK]\x1b[0m ${msg}`), warn: (msg) => console.log(`\x1b[33m[WARN]\x1b[0m ${msg}`), }; // ===== 工具函数 ===== function findPidsOnPort(port) { try { if (IS_WIN) { const out = execSync( `netstat -ano | findstr :${port} | findstr LISTENING`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] } ); return [ ...new Set( out .trim() .split("\n") .map((l) => l.trim().split(/\s+/).pop()) .filter((p) => p && p !== "0") ), ]; } const out = execSync(`lsof -ti :${port}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); return out.trim().split("\n").filter(Boolean); } catch { return []; } } function killPid(pid) { try { if (IS_WIN) { execSync(`taskkill /F /PID ${pid}`, { stdio: "pipe" }); } else { execSync(`kill -9 ${pid}`, { stdio: "pipe" }); } return true; } catch { return false; } } function killPort(port, name) { const pids = findPidsOnPort(port); if (pids.length === 0) { log.info(`${name} — 端口 ${port} 空闲`); return; } for (const pid of pids) { if (killPid(pid)) { log.ok(`${name} — 已停止 (PID: ${pid}, 端口: ${port})`); } else { log.warn(`${name} — 无法停止 PID: ${pid}`); } } } // ===== 主流程 ===== console.log(); console.log("\x1b[1m\x1b[36m ═══ 暖记 — 停止所有服务 ═══\x1b[0m"); console.log(); log.info("正在停止所有暖记服务..."); for (const { port, name } of SERVICES) { killPort(port, name); } // Windows: 额外清理 erp-server.exe if (IS_WIN) { try { execSync("taskkill /F /IM erp-server.exe", { stdio: "pipe" }); log.ok("已停止 erp-server.exe"); } catch { // 没有运行中的进程,忽略 } } // Windows: 清理可能的残留 node 进程(仅清理 pnpm 启动的) // 注意:不盲目杀所有 node 进程,只清理端口的即可 console.log(); log.ok("所有服务已停止"); console.log();