#!/bin/bash # --- 颜色定义 --- GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' PID_FILE="app.pid" LOG_FILE="app.log" is_port_listening() { if command -v lsof >/dev/null 2>&1; then lsof -iTCP:2333 -sTCP:LISTEN >/dev/null 2>&1 elif command -v ss >/dev/null 2>&1; then ss -ltn 2>/dev/null | awk '{print $4}' | grep -Eq '(^|[[:space:]])([^[:space:]]*:)?2333$' elif command -v netstat >/dev/null 2>&1; then netstat -ltn 2>/dev/null | awk '{print $4}' | grep -Eq '(^|[[:space:]])([^[:space:]]*:)?2333$' elif command -v fuser >/dev/null 2>&1; then fuser -n tcp 2333 >/dev/null 2>&1 else return 1 fi } get_port_pids() { if command -v lsof >/dev/null 2>&1; then lsof -t -i:2333 2>/dev/null elif command -v ss >/dev/null 2>&1; then ss -ltnp 2>/dev/null | grep -E '[:.]2333[[:space:]]' | sed -n 's/.*pid=\([0-9]\+\).*/\1/p' | sort -u elif command -v netstat >/dev/null 2>&1; then netstat -ltnp 2>/dev/null | grep -E '[:.]2333[[:space:]]' | sed -n 's/.* \([0-9]\+\)\/.*/\1/p' | sort -u elif command -v fuser >/dev/null 2>&1; then fuser -n tcp 2333 2>/dev/null | tr ' ' '\n' | sort -u fi } prepare_standalone() { if [ -f ".next/standalone/server.js" ]; then rm -rf .next/standalone/.next .next/standalone/public mkdir -p .next/standalone/.next # 复制 .next 目录内容,排除 standalone 目录本身 for item in .next/*; do item_name=$(basename "$item") if [ "$item_name" != "standalone" ]; then cp -r "$item" .next/standalone/.next/ fi done # 复制 public 目录 [ -d public ] && cp -r public .next/standalone/ fi } start_server() { if [ -f ".next/standalone/server.js" ]; then # Standalone 模式:从 standalone 目录启动服务器 cd .next/standalone NODE_ENV=production PORT=2333 nohup node server.js > "../../$LOG_FILE" 2>&1 & cd ../.. else # 非 standalone 模式:使用 pnpm start NODE_ENV=production nohup $PM run start > "$LOG_FILE" 2>&1 & fi } # --- 逻辑封装 --- function do_stop() { printf "${YELLOW}正在停止 STARK Todo List...${NC}\n" # 1. 首先尝试通过 PID 文件停止 if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE") pkill -P $PID >/dev/null 2>&1 kill $PID >/dev/null 2>&1 rm "$PID_FILE" fi # 2. 强力兜底:查找并杀死占?2333 端口的所?Node 进程 PORT_PIDS=$(get_port_pids) if [ -n "$PORT_PIDS" ]; then for P in $PORT_PIDS; do kill -9 $P >/dev/null 2>&1 done fi printf "${YELLOW}已停止所有相关进程并释放端口?{NC}\n" } function do_start() { if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then printf "${YELLOW}App already running (PID: $(cat $PID_FILE))${NC}\n" return 0 fi if is_port_listening; then printf "${YELLOW}Port 2333 is in use, trying to stop old process...${NC}\n" do_stop sleep 1 if is_port_listening; then printf "${RED}Error: port 2333 still in use after auto-stop${NC}\n" return 1 fi fi printf "${GREEN}Starting STARK Todo List (production) with $PM...${NC}\n" NEED_BUILD=0 if [ ! -f ".next/BUILD_ID" ] && [ ! -f ".next/standalone/server.js" ]; then NEED_BUILD=1 fi if [ "$FORCE_BUILD" = "1" ] || [ "$2" = "build" ]; then NEED_BUILD=1 fi if [ "$NEED_BUILD" -eq 1 ]; then $PM install > /dev/null 2>&1 $PM run build > /dev/null 2>&1 prepare_standalone else printf "${BLUE}Build artifacts found, skipping build. Force rebuild: FORCE_BUILD=1 bash run.sh start or bash run.sh start build${NC}\n" fi start_server echo $! > "$PID_FILE" sleep 2 if kill -0 $(cat "$PID_FILE") 2>/dev/null; then printf "${GREEN}Started: http://localhost:2333${NC}\n" else printf "${RED}Start failed, check $LOG_FILE${NC}\n" rm -f "$PID_FILE" fi } # --- 初始?--- if [ ! -f "todos.json" ]; then echo "[]" > todos.json printf "${YELLOW}已创建初始数据文?todos.json${NC}\n" fi # 检查包管理? if command -v pnpm >/dev/null 2>&1; then PM="pnpm" else PM="npm" fi case "$1" in start) do_start ;; stop) do_stop ;; restart) printf "${BLUE}Restarting...${NC}\n" do_stop sleep 1 rm -rf .next do_start ;; logs) [ -f "$LOG_FILE" ] && tail -f "$LOG_FILE" || printf "${RED}Log file not found${NC}\n" ;; status) if is_port_listening; then printf "${GREEN}App is running${NC}\n" (command -v lsof >/dev/null 2>&1 && lsof -i:2333) || (command -v ss >/dev/null 2>&1 && ss -ltnp 'sport = :2333') || (command -v netstat >/dev/null 2>&1 && netstat -ltnp) || true else printf "${RED}App is not running${NC}\n" fi ;; clean) printf "${BLUE}Cleaning cache and logs...${NC}\n" rm -rf .next "$LOG_FILE" "$PID_FILE" printf "${GREEN}Clean complete${NC}\n" ;; *) printf "${BLUE}STARK Todo List management script${NC}\n" echo "Usage: $0 [start|stop|restart|logs|status|clean]" exit 1 ;; esac