232 lines
5.3 KiB
Bash
232 lines
5.3 KiB
Bash
|
|
#!/usr/bin/env bash
|
|||
|
|
set -euo pipefail
|
|||
|
|
|
|||
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||
|
|
RUNTIME_DIR="${RUNTIME_DIR:-${ROOT_DIR}/.local}"
|
|||
|
|
LOG_DIR="${RUNTIME_DIR}/logs"
|
|||
|
|
LOG_FILE="${LOG_FILE:-${LOG_DIR}/frontend.log}"
|
|||
|
|
PID_FILE="${PID_FILE:-${RUNTIME_DIR}/frontend.pid}"
|
|||
|
|
|
|||
|
|
BACKEND_URL="${LOCAL_BACKEND_URL:-http://127.0.0.1:9091}"
|
|||
|
|
FRONTEND_PORT="${PORT:-8000}"
|
|||
|
|
NPM_CMD="${NPM_CMD:-npm}"
|
|||
|
|
MAX_CMD="${ROOT_DIR}/node_modules/.bin/max"
|
|||
|
|
|
|||
|
|
info() {
|
|||
|
|
printf '\033[1;32m%s\033[0m\n' "$*"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
error() {
|
|||
|
|
printf '\033[1;31m%s\033[0m\n' "$*" >&2
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
usage() {
|
|||
|
|
cat <<'EOF'
|
|||
|
|
用法:./local.sh <命令> [参数]
|
|||
|
|
|
|||
|
|
命令:
|
|||
|
|
install 使用 package-lock.json 安装依赖
|
|||
|
|
start [--skip-install] 检查本地后端并在后台启动前端
|
|||
|
|
stop 停止本脚本启动的前端进程
|
|||
|
|
restart [--skip-install] 重启前端
|
|||
|
|
status 查看前端和本地后端状态
|
|||
|
|
logs [行数] 查看并持续跟踪日志,默认显示最后 200 行
|
|||
|
|
help 显示帮助
|
|||
|
|
|
|||
|
|
可选环境变量:
|
|||
|
|
LOCAL_BACKEND_URL 本地后端地址,默认 http://127.0.0.1:9091
|
|||
|
|
PORT 前端端口,默认 8000
|
|||
|
|
NPM_CMD npm 命令,默认 npm
|
|||
|
|
EOF
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
prepare_runtime() {
|
|||
|
|
mkdir -p "${LOG_DIR}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
read_pid() {
|
|||
|
|
if [[ -f "${PID_FILE}" ]]; then
|
|||
|
|
tr -d '[:space:]' < "${PID_FILE}"
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
is_running() {
|
|||
|
|
local pid
|
|||
|
|
pid="$(read_pid)"
|
|||
|
|
[[ -n "${pid}" ]] && kill -0 "${pid}" 2>/dev/null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
install_dependencies() {
|
|||
|
|
command -v "${NPM_CMD}" >/dev/null 2>&1 || {
|
|||
|
|
error "未找到 npm 命令:${NPM_CMD}"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
info "正在根据 package-lock.json 安装依赖..."
|
|||
|
|
(
|
|||
|
|
cd "${ROOT_DIR}"
|
|||
|
|
"${NPM_CMD}" ci
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
check_backend() {
|
|||
|
|
local status
|
|||
|
|
command -v curl >/dev/null 2>&1 || {
|
|||
|
|
error "未找到 curl,无法检查本地后端"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
status="$(curl -s -o /dev/null --max-time 3 -w '%{http_code}' "${BACKEND_URL}/" || true)"
|
|||
|
|
if [[ -z "${status}" || "${status}" == "000" ]]; then
|
|||
|
|
error "本地后端不可访问:${BACKEND_URL}"
|
|||
|
|
error "请先在 shz-backend 中执行:./local.sh start"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
info "本地后端已连接:${BACKEND_URL}(HTTP ${status})"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
start_frontend() {
|
|||
|
|
local skip_install="${1:-}"
|
|||
|
|
|
|||
|
|
prepare_runtime
|
|||
|
|
if is_running; then
|
|||
|
|
info "前端已在运行,PID:$(read_pid)"
|
|||
|
|
return 0
|
|||
|
|
fi
|
|||
|
|
rm -f "${PID_FILE}"
|
|||
|
|
|
|||
|
|
check_backend
|
|||
|
|
|
|||
|
|
if [[ ! -x "${MAX_CMD}" ]]; then
|
|||
|
|
if [[ "${skip_install}" == "--skip-install" ]]; then
|
|||
|
|
error "依赖尚未安装,不能跳过安装:${MAX_CMD} 不存在"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
install_dependencies
|
|||
|
|
elif [[ -n "${skip_install}" && "${skip_install}" != "--skip-install" ]]; then
|
|||
|
|
error "未知参数:${skip_install}"
|
|||
|
|
exit 2
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
export REACT_APP_ENV=dev
|
|||
|
|
export MOCK=none
|
|||
|
|
export UMI_ENV=dev
|
|||
|
|
export LOCAL_BACKEND_URL="${BACKEND_URL}"
|
|||
|
|
export PORT="${FRONTEND_PORT}"
|
|||
|
|
|
|||
|
|
info "正在启动前端:http://localhost:${FRONTEND_PORT}"
|
|||
|
|
info "后端代理目标:${BACKEND_URL}"
|
|||
|
|
(
|
|||
|
|
cd "${ROOT_DIR}"
|
|||
|
|
nohup "${MAX_CMD}" dev >> "${LOG_FILE}" 2>&1 &
|
|||
|
|
printf '%s\n' "$!" > "${PID_FILE}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
local frontend_status="000"
|
|||
|
|
for _ in {1..60}; do
|
|||
|
|
if ! is_running; then
|
|||
|
|
break
|
|||
|
|
fi
|
|||
|
|
frontend_status="$(curl -s -o /dev/null --max-time 1 -w '%{http_code}' \
|
|||
|
|
"http://127.0.0.1:${FRONTEND_PORT}/" || true)"
|
|||
|
|
if [[ -n "${frontend_status}" && "${frontend_status}" != "000" ]]; then
|
|||
|
|
info "前端已启动,PID:$(read_pid),HTTP ${frontend_status}"
|
|||
|
|
info "日志命令:./local.sh logs"
|
|||
|
|
return 0
|
|||
|
|
fi
|
|||
|
|
sleep 1
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
rm -f "${PID_FILE}"
|
|||
|
|
error "前端未能在 60 秒内启动,最近日志如下:"
|
|||
|
|
tail -n 80 "${LOG_FILE}" >&2 || true
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
stop_frontend() {
|
|||
|
|
local pid
|
|||
|
|
pid="$(read_pid)"
|
|||
|
|
|
|||
|
|
if ! is_running; then
|
|||
|
|
rm -f "${PID_FILE}"
|
|||
|
|
info "前端未运行"
|
|||
|
|
return 0
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
info "正在停止前端,PID:${pid}"
|
|||
|
|
kill "${pid}"
|
|||
|
|
for _ in {1..15}; do
|
|||
|
|
if ! kill -0 "${pid}" 2>/dev/null; then
|
|||
|
|
rm -f "${PID_FILE}"
|
|||
|
|
info "前端已停止"
|
|||
|
|
return 0
|
|||
|
|
fi
|
|||
|
|
sleep 1
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
error "前端未在 15 秒内停止,请检查 PID:${pid}"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
show_status() {
|
|||
|
|
local backend_status
|
|||
|
|
if is_running; then
|
|||
|
|
info "前端正在运行,PID:$(read_pid),地址:http://localhost:${FRONTEND_PORT}"
|
|||
|
|
else
|
|||
|
|
rm -f "${PID_FILE}"
|
|||
|
|
info "前端未运行"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
backend_status="$(curl -s -o /dev/null --max-time 2 -w '%{http_code}' "${BACKEND_URL}/" || true)"
|
|||
|
|
if [[ -n "${backend_status}" && "${backend_status}" != "000" ]]; then
|
|||
|
|
info "本地后端可访问:${BACKEND_URL}(HTTP ${backend_status})"
|
|||
|
|
else
|
|||
|
|
error "本地后端不可访问:${BACKEND_URL}"
|
|||
|
|
fi
|
|||
|
|
info "日志文件:${LOG_FILE}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
show_logs() {
|
|||
|
|
local lines="${1:-200}"
|
|||
|
|
if [[ ! "${lines}" =~ ^[0-9]+$ ]]; then
|
|||
|
|
error "日志行数必须是非负整数:${lines}"
|
|||
|
|
exit 2
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
prepare_runtime
|
|||
|
|
touch "${LOG_FILE}"
|
|||
|
|
info "正在跟踪日志:${LOG_FILE}(Ctrl+C 退出)"
|
|||
|
|
tail -n "${lines}" -f "${LOG_FILE}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
command="${1:-help}"
|
|||
|
|
case "${command}" in
|
|||
|
|
install)
|
|||
|
|
install_dependencies
|
|||
|
|
;;
|
|||
|
|
start)
|
|||
|
|
start_frontend "${2:-}"
|
|||
|
|
;;
|
|||
|
|
stop)
|
|||
|
|
stop_frontend
|
|||
|
|
;;
|
|||
|
|
restart)
|
|||
|
|
stop_frontend
|
|||
|
|
start_frontend "${2:-}"
|
|||
|
|
;;
|
|||
|
|
status)
|
|||
|
|
show_status
|
|||
|
|
;;
|
|||
|
|
logs)
|
|||
|
|
show_logs "${2:-200}"
|
|||
|
|
;;
|
|||
|
|
help|-h|--help)
|
|||
|
|
usage
|
|||
|
|
;;
|
|||
|
|
*)
|
|||
|
|
error "未知命令:${command}"
|
|||
|
|
usage >&2
|
|||
|
|
exit 2
|
|||
|
|
;;
|
|||
|
|
esac
|