264 lines
7.8 KiB
Bash
Executable File
264 lines
7.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# =============================================================================
|
||
# 07-start-frontend.sh — 启动前端开发服务器(开发态)
|
||
#
|
||
# 用法:
|
||
# ./07-start-frontend.sh # 启动全部前端项目
|
||
# ./07-start-frontend.sh <project> # 只启动指定项目
|
||
#
|
||
# 项目与端口:
|
||
# meetingh5 → Vue + Vite :5188
|
||
# h5 → Vue + Vite :3003
|
||
#
|
||
#
|
||
# 注意:
|
||
# pc / cms / build-cms / build-down 已改为静态构建 + Nginx 域名访问:
|
||
# ./deploy-test/08-build-static-frontend.sh
|
||
#
|
||
# 日志文件: .local-dev/logs/fe-<project>.log
|
||
# PID 文件: .local-dev/pids/fe-<project>.pid
|
||
# =============================================================================
|
||
set -euo pipefail
|
||
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||
init_dirs
|
||
SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
|
||
|
||
# 供文末打印访问地址(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=(
|
||
[meetingh5]="meetingh5"
|
||
[h5]="h5"
|
||
)
|
||
|
||
declare -A FE_PM=(
|
||
[meetingh5]="npm"
|
||
[h5]="npm"
|
||
)
|
||
|
||
declare -A FE_CMD=(
|
||
[meetingh5]="npm run dev"
|
||
[h5]="npm run dev"
|
||
)
|
||
|
||
declare -A FE_ENV=(
|
||
[meetingh5]=""
|
||
[h5]=""
|
||
)
|
||
|
||
declare -A FE_PORT=(
|
||
[meetingh5]=":5188"
|
||
[h5]=":3003"
|
||
)
|
||
|
||
declare -A FE_PORT_NUM=(
|
||
[meetingh5]="5188"
|
||
[h5]="3003"
|
||
)
|
||
|
||
_check_fe_port_free() {
|
||
local name="$1"
|
||
local port="${FE_PORT_NUM[$name]}"
|
||
local pids
|
||
|
||
info "检查 ${BOLD}$name${NC} 端口 $port ..."
|
||
pids="$(lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)"
|
||
if [[ -n "$pids" ]]; then
|
||
error "$name 启动失败: 端口 $port 已被占用"
|
||
echo "占用进程:"
|
||
lsof -nP -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || true
|
||
echo ""
|
||
echo "请先停止占用进程,例如:"
|
||
echo " ./deploy-test/stop-frontend.sh $name"
|
||
echo " lsof -ti :$port | xargs -r kill"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
_wait_fe_port_listen() {
|
||
local name="$1"
|
||
local port="${FE_PORT_NUM[$name]}"
|
||
local i
|
||
|
||
for i in {1..10}; do
|
||
if lsof -tiTCP:"$port" -sTCP:LISTEN &>/dev/null; then
|
||
return 0
|
||
fi
|
||
sleep 1
|
||
done
|
||
|
||
error "$name 启动后未监听预期端口 $port"
|
||
return 1
|
||
}
|
||
|
||
_kill_tree() {
|
||
local pid="$1" signal="${2:-TERM}"
|
||
local children child
|
||
|
||
children=$(pgrep -P "$pid" 2>/dev/null || true)
|
||
for child in $children; do
|
||
_kill_tree "$child" "$signal"
|
||
done
|
||
|
||
kill "-$signal" "$pid" 2>/dev/null || true
|
||
}
|
||
|
||
# ── 启动单个前端服务 ──────────────────────────────────────────────────────────
|
||
_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"
|
||
local port="${FE_PORT_NUM[$name]}"
|
||
|
||
_check_fe_port_free "$name" || return 1
|
||
|
||
# 已在运行
|
||
if [[ -f "$pidfile" ]] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
|
||
warn "$name 已在运行 (端口=$port, PID=$(cat "$pidfile")),跳过"
|
||
return 0
|
||
fi
|
||
|
||
# 目录检查
|
||
if [[ ! -d "$dir" ]]; then
|
||
warn "$name 目录不存在 (端口=$port, 目录=$dir),跳过"
|
||
return 0
|
||
fi
|
||
|
||
# 依赖检查
|
||
if [[ ! -d "$dir/node_modules" ]]; then
|
||
warn "$name node_modules 不存在 (端口=$port),请先执行: ./deploy-test/06-install-frontend.sh $name"
|
||
return 1
|
||
fi
|
||
|
||
# 包管理器检查
|
||
if ! command -v "$pm" &>/dev/null; then
|
||
error "$name 需要 $pm,未安装 (端口=$port)"
|
||
return 1
|
||
fi
|
||
|
||
info "启动 ${BOLD}$name${NC} (端口=$port) ..."
|
||
# 写日志分隔符
|
||
{
|
||
echo ""
|
||
echo "──── 启动 $(date '+%Y-%m-%d %H:%M:%S') ────"
|
||
} >> "$logfile"
|
||
|
||
# 后台启动(带环境变量前缀)
|
||
(
|
||
cd "$dir"
|
||
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
|
||
if ! _wait_fe_port_listen "$name"; then
|
||
local started_pid
|
||
started_pid="$(cat "$pidfile")"
|
||
warn "$name 未监听预期端口 $port,清理本次启动进程 (PID=$started_pid)"
|
||
_kill_tree "$started_pid" TERM
|
||
sleep 1
|
||
if kill -0 "$started_pid" 2>/dev/null; then
|
||
_kill_tree "$started_pid" KILL
|
||
fi
|
||
rm -f "$pidfile"
|
||
return 1
|
||
fi
|
||
success " ✓ $name (端口=$port, PID=$(cat "$pidfile")) ${FE_PORT[$name]} → $logfile"
|
||
else
|
||
error " ✗ $name 启动失败 (端口=$port),查看日志:"
|
||
tail -20 "$logfile" 2>/dev/null || true
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# ── 入口 ─────────────────────────────────────────────────────────────────────
|
||
TARGET="${1:-all}"
|
||
FE_PROJECTS=(meetingh5 h5)
|
||
STATIC_FRONTENDS=(pc 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
|
||
if ! bash "$SCRIPT_PATH" "$proj"; then
|
||
FAILED+=("$proj")
|
||
fi
|
||
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 " H5: http://${DEPLOY_TEST_IP}:3003"
|
||
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
|
||
for static_fe in "${STATIC_FRONTENDS[@]}"; do
|
||
if [[ "$TARGET" == "$static_fe" ]]; then
|
||
error "$TARGET 已改为静态构建,不再通过 07-start-frontend.sh 启动"
|
||
echo "请执行: ./deploy-test/08-build-static-frontend.sh $TARGET"
|
||
echo "然后执行: sudo ./deploy-test/00-init-tools.sh nginx"
|
||
exit 1
|
||
fi
|
||
done
|
||
if ! _all_valid "$TARGET"; then
|
||
error "未知项目: $TARGET"
|
||
echo "开发态前端可用: ${FE_PROJECTS[*]}"
|
||
echo "静态前端请用: ./deploy-test/08-build-static-frontend.sh [pc|cms|build-cms|build-down]"
|
||
exit 1
|
||
fi
|
||
step "启动前端项目: $TARGET"
|
||
_start_fe "$TARGET"
|
||
fi
|