#!/bin/bash # Shared by deploy_test.sh / deploy_prod.sh; compatible with macOS Bash 3.2. fail() { printf '错误:%s\n' "$*" >&2; exit 1; } step() { printf '\n▸ %s\n' "$*"; } need() { command -v "$1" >/dev/null || fail "缺少命令 $1"; } valid_host() { [[ "$1" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.@-]*$ ]] || fail "$2 格式无效或未配置"; } valid_path() { [[ "$1" =~ ^/[a-zA-Z0-9_-]+(/[a-zA-Z0-9_.-]+)*/?$ ]] && [[ "$1/" != *'/../'* ]] && [[ "$1/" != *'/./'* ]] || fail "$2 必须是普通绝对路径"; } valid_port() { [[ "$1" =~ ^[0-9]+$ ]] && [ "$1" -ge 1 ] && [ "$1" -le 65535 ] || fail "$2 必须在 1–65535 内"; } check_local() { need node; need npm node -e 'const [a,b]=process.versions.node.split(".").map(Number);if(a<20||a===20&&b<9)process.exit(1)' || fail '需要 Node.js >=20.9,推荐 22.17.1' [ -d node_modules/next ] && [ -d node_modules/dotenv ] || fail '请先执行 npm ci 安装本地依赖' [ -s "config/$1.env" ] && [ -s package-lock.json ] || fail "缺少 config/$1.env 或 package-lock.json" } public_profile() { node --input-type=module -e 'import{readFileSync}from"node:fs";import{parse}from"dotenv";const c=parse(readFileSync(`config/${process.argv[1]}.env`));console.log(` API=${c.UPSTREAM_API_URL||c.API_BASE_URL||c.API_PROXY_TARGET} PORT=${c.PORT}`)' "$1" } lock_build() { [ ! -L dist ] || fail 'dist 不允许为符号链接' mkdir -p dist DEPLOY_LOCK="$SCRIPT_DIR/dist/.deploy.lock" mkdir "$DEPLOY_LOCK" 2>/dev/null || fail "已有构建/发布进程或残留锁:$DEPLOY_LOCK" trap 'rmdir "$DEPLOY_LOCK" 2>/dev/null || true' EXIT trap 'exit 130' INT trap 'exit 143' TERM }