#!/usr/bin/env bash # ============================================================================= # 07-start-frontend.sh — 启动前端开发服务器 # # 用法: # ./07-start-frontend.sh # 启动全部前端项目 # ./07-start-frontend.sh # 只启动指定项目 # # 项目与端口: # pc → 默认 yarn dev:web :5173(无 Electron);本机要 Electron 时设 PC_ELECTRON=1 使用 yarn dev # 后端地址:在 .env.deploy-test 设 PC_BACKEND_ORIGIN,启动时注入 VITE_*(不改 pc 目录) # meetingh5 → React + Vite :5188 # h5 → Vue + Vite :3003 # cms → UMI Max :8001 # build-cms → UMI Max :8002 # build-down → UMI v3 :8003 # # 日志文件: .local-dev/logs/fe-.log # PID 文件: .local-dev/pids/fe-.pid # ============================================================================= set -euo pipefail source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh" init_dirs # 供文末打印访问地址(set -u 下须已定义;与 01-init-env 中 DEPLOY_TEST_IP 一致) if [[ -f "$ENV_FILE" ]]; then set -a # shellcheck source=/dev/null source "$ENV_FILE" set +a fi DEPLOY_TEST_IP="${DEPLOY_TEST_IP:-127.0.0.1}" normalize_pc_proxy_env init_script_log # ← 脚本执行日志 header "启动前端开发服务器" # ── 前端服务配置 ────────────────────────────────────────────────────────────── # name → (目录, 包管理器, 启动命令, 环境变量, 端口描述) declare -A FE_DIR=( [pc]="pc" [meetingh5]="meetingh5" [h5]="h5" [cms]="cms" [build-cms]="build-cms" [build-down]="build-down" ) declare -A FE_PM=( [pc]="yarn" [meetingh5]="npm" [h5]="npm" [cms]="pnpm" [build-cms]="pnpm" [build-down]="npm" ) declare -A FE_CMD=( [pc]="yarn dev:web" [meetingh5]="npm run dev" [h5]="npm run dev" [cms]="pnpm run dev" [build-cms]="pnpm run dev" [build-down]="npm run dev" ) # cms/build-cms/build-down 不配置端口时默认都是 8000,需手动指定 declare -A FE_ENV=( [pc]="" [meetingh5]="" [h5]="" [cms]="PORT=8001" [build-cms]="PORT=8002" [build-down]="PORT=8003" ) declare -A FE_PORT=( [pc]=":5173 (yarn dev:web)" [meetingh5]=":5188" [h5]=":3003" [cms]=":8001" [build-cms]=":8002" [build-down]=":8003" ) # 本机需 Electron 桌面客户端时:PC_ELECTRON=1 → yarn dev(需 GUI / libatk 等) if [[ "${PC_ELECTRON:-}" == "1" ]] || [[ "${PC_ELECTRON:-}" == "true" ]]; then FE_CMD[pc]="yarn dev" FE_PORT[pc]=":5173 (yarn dev + Electron)" fi # ── 启动单个前端服务 ────────────────────────────────────────────────────────── _start_fe() { local name="$1" local dir="$ROOT_DIR/${FE_DIR[$name]}" local pm="${FE_PM[$name]}" local cmd="${FE_CMD[$name]}" local env_prefix="${FE_ENV[$name]}" local pidfile="$PID_DIR/fe-${name}.pid" local logfile="$LOG_DIR/fe-${name}.log" # 已在运行 if [[ -f "$pidfile" ]] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then warn "$name 已在运行 (PID=$(cat "$pidfile")),跳过" if [[ "$name" == "pc" ]]; then warn " Vite 环境变量只在启动时读取;若仍请求旧地址,请执行: ./deploy-test/stop-frontend.sh pc && ./deploy-test/07-start-frontend.sh pc" pc_export_vite_backend_env pc_print_vite_backend_env fi return 0 fi # 目录检查 if [[ ! -d "$dir" ]]; then warn "$name 目录不存在 ($dir),跳过" return 0 fi # 依赖检查 if [[ ! -d "$dir/node_modules" ]]; then warn "$name node_modules 不存在,请先执行: ./deploy-test/06-install-frontend.sh $name" return 1 fi # 包管理器检查 if ! command -v "$pm" &>/dev/null; then error "$name 需要 $pm,未安装" return 1 fi info "启动 ${BOLD}$name${NC} ..." if [[ "$name" == "pc" ]]; then pc_ensure_wasm_assets pc_export_vite_backend_env pc_print_vite_backend_env pc_check_nginx_gateway fi # 写日志分隔符 { echo "" echo "──── 启动 $(date '+%Y-%m-%d %H:%M:%S') ────" } >> "$logfile" # 后台启动(带环境变量前缀) ( cd "$dir" if [[ "$name" == "pc" ]]; then pc_export_vite_backend_env fi if [[ -n "$env_prefix" ]]; then # shellcheck disable=SC2086 nohup env $env_prefix $cmd >> "$logfile" 2>&1 & else # shellcheck disable=SC2086 nohup $cmd >> "$logfile" 2>&1 & fi echo $! > "$pidfile" ) sleep 2 if [[ -f "$pidfile" ]] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then success " ✓ $name (PID=$(cat "$pidfile")) ${FE_PORT[$name]} → $logfile" if [[ "$name" == "pc" ]]; then pc_check_wasm_assets "${PC_BACKEND_ORIGIN}" info " PC 浏览器入口建议使用: ${PC_BACKEND_ORIGIN%/}/" fi else error " ✗ $name 启动失败,查看日志:" tail -20 "$logfile" 2>/dev/null || true return 1 fi } # ── 入口 ───────────────────────────────────────────────────────────────────── TARGET="${1:-all}" FE_PROJECTS=(pc meetingh5 h5 cms build-cms build-down) _all_valid() { for p in "${FE_PROJECTS[@]}"; do [[ "$p" == "$1" ]] && return 0 done return 1 } if [[ "$TARGET" == "all" ]]; then step "启动全部前端开发服务器" FAILED=() for proj in "${FE_PROJECTS[@]}"; do _start_fe "$proj" || FAILED+=("$proj") done echo "" echo -e "${BOLD}前端服务汇总:${NC}" for proj in "${FE_PROJECTS[@]}"; do local_pidfile="$PID_DIR/fe-${proj}.pid" if [[ -f "$local_pidfile" ]] && kill -0 "$(cat "$local_pidfile")" 2>/dev/null; then printf " ${GREEN}●${NC} %-14s PID=%-7s %s\n" "$proj" "$(cat "$local_pidfile")" "${FE_PORT[$proj]}" else printf " ${RED}○${NC} %-14s 未运行 %s\n" "$proj" "${FE_PORT[$proj]}" fi done echo "" echo -e "${BOLD}访问地址:${NC}" echo " PC: ${PC_BACKEND_ORIGIN%/}/ (外部 HTTPS → 本机 Nginx :80 → pc :5173;默认 yarn dev:web)" echo " PC 直连调试: http://${DEPLOY_TEST_IP}:5173 (绕过 Nginx;Electron 用 PC_ELECTRON=1 或 pc 目录内 yarn dev)" echo " H5: http://${DEPLOY_TEST_IP}:3003" echo " CMS: http://${DEPLOY_TEST_IP}:8001" echo " Build CMS: http://${DEPLOY_TEST_IP}:8002" echo " Build Download: http://${DEPLOY_TEST_IP}:8003" echo "" echo -e "${BOLD}MeetingH5 访问地址(后端 URL 由 .env.local 默认设置,也可通过 URL 参数覆盖):${NC}" echo " 默认: http://${DEPLOY_TEST_IP}:5188" echo " 显式指定后端: http://${DEPLOY_TEST_IP}:5188?ws=ws://${DEPLOY_TEST_IP}:8000&liveApi=http://${DEPLOY_TEST_IP}:8888" echo " 说明: ws → meetingmsg 弹幕 WebSocket (:8000)" echo " liveApi → livestream 直播间 API (:8888)" if [[ ${#FAILED[@]} -gt 0 ]]; then echo "" warn "以下项目启动失败: ${FAILED[*]}" echo " 可能原因: node_modules 未安装,执行: ./deploy-test/06-install-frontend.sh" fi else if ! _all_valid "$TARGET"; then error "未知项目: $TARGET" echo "可用: ${FE_PROJECTS[*]}" exit 1 fi step "启动前端项目: $TARGET" _start_fe "$TARGET" fi