#!/usr/bin/env bash # tts-like 一键启动脚本 # # 用法: ./run.sh # 或: bash run.sh # # 行为: # 1. 加载 nvm + 启用 pnpm(每台机器首次跑会自动配好) # 2. node_modules 缺失时自动 pnpm install # 3. 后台启动 boardgame.io relay server(:8000) # 4. 前台启动 vite dev(:5173) # 5. Ctrl-C 一起干净退出 # # 启动后打开 http://localhost:5173 set -euo pipefail # ---------- 1. 加载 nvm + pnpm ---------- export NVM_DIR="$HOME/.nvm" if [ -s "$NVM_DIR/nvm.sh" ]; then # shellcheck disable=SC1091 . "$NVM_DIR/nvm.sh" fi # 没有 node 直接报错(提示装 nvm) if ! command -v node >/dev/null 2>&1; then echo "✗ 未找到 node。请先装 nvm + Node 22:" echo " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash" echo " nvm install 22" exit 1 fi NODE_VERSION=$(node -v) echo "✓ node $NODE_VERSION" # 启用 pnpm(corepack 是 Node 自带) if ! command -v pnpm >/dev/null 2>&1; then echo "→ 启用 pnpm (corepack)..." corepack enable >/dev/null 2>&1 || true fi echo "✓ pnpm $(pnpm --version)" # 切到项目根目录(脚本所在目录) cd "$(dirname "$0")" # ---------- 2. 依赖检查 ---------- if [ ! -d node_modules ]; then echo "→ 首次运行,安装依赖(可能要 1-2 分钟)..." pnpm install fi echo "✓ 依赖就绪" # ---------- 3. 清理可能残留的旧进程 ---------- # 端口 8000 / 5173 若被占用会启动失败,这里只做提示 for port in 8000 5173; do if command -v ss >/dev/null 2>&1 && ss -ltn "sport = :$port" 2>/dev/null | grep -q ":$port"; then echo "⚠ 端口 $port 已被占用,可能导致启动失败。如需清理:lsof -ti:$port | xargs kill" fi done # ---------- 4. 后台启动 server ---------- echo "" echo "→ 启动 boardgame.io server (:8000)..." setsid pnpm --filter @tts-like/server dev & SERVER_PID=$! PIDS=("$SERVER_PID") WEB_PID="" # 5. 退出时一起杀掉 server / web cleanup() { echo "" echo "→ 退出,清理子进程..." # 用进程组杀法,确保 tsx watch 派生的 node 子进程也一并退出(否则会残留占用端口) for pid in "${PIDS[@]:-}"; do [ -n "$pid" ] || continue kill -TERM -"$pid" 2>/dev/null || kill "$pid" 2>/dev/null || true done for pid in "${PIDS[@]:-}"; do [ -n "$pid" ] && kill -9 "$pid" 2>/dev/null || true done wait 2>/dev/null || true echo "✓ 已退出" } trap cleanup EXIT INT TERM # 等 server 起来(最多 15 秒,看到 listening 才继续) echo "→ 等待 server 就绪..." for i in $(seq 1 30); do if curl -sf http://localhost:8000/healthz >/dev/null 2>&1; then echo "✓ server 就绪" break fi # server 进程已退出(如端口被占用崩溃)→ 直接失败退出,避免傻等 if ! kill -0 "$SERVER_PID" 2>/dev/null; then echo "✗ server 进程已退出,启动失败。检查上方日志(常见于 :8000 端口被占用)。" exit 1 fi sleep 0.5 done # ---------- 6. 前台启动 web ---------- echo "" echo "════════════════════════════════════════════════════════════════" echo " tts-like 已启动" echo " Web: http://localhost:5173" echo " Server: http://localhost:8000 (REST /api, WebSocket /socket.io)" echo " 健康: http://localhost:8000/healthz" echo "" echo " 局域网其它设备访问: http://<本机内网IP>:5173/" echo " Ctrl-C 一起退出" echo "════════════════════════════════════════════════════════════════" echo "" # 前台跑 vite(Ctrl-C 会同时打到前台进程组,web 退出后 trap 清理 server) setsid pnpm --filter @tts-like/web dev & WEB_PID=$! PIDS+=("$WEB_PID") wait "$WEB_PID" 2>/dev/null || true