#!/usr/bin/env bash # 生产发布脚本 — 仅当用户明确要求「发布生产」时由人工/Agent 执行 # 本地 H5 测试通过后,一键构建并发布到本机 Cloudflare 隧道(goo.tkmind.cn) # 105 服务器生产环境请用: ./deploy/deploy-h5-105.sh → https://go.tkmind.cn # # 典型流程: # 1. cd ui/h5 && pnpm dev # 开发热更新 (5173) # 2. cd ui/h5 && pnpm run test:local # 本地验证生产构建 (8080) # 3. ./deploy/deploy-h5-prod.sh # 发布到本机隧道 goo.tkmind.cn # # 选项: # --skip-test 跳过 auth 单元测试 # --skip-tunnel 不检查/启动 cloudflared set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" H5_DIR="${ROOT}/ui/h5" H5_PORT="${H5_PORT:-8080}" LOG_DIR="${ROOT}" PROD_URL="${H5_PROD_URL:-https://goo.tkmind.cn}" TUNNEL_NAME="${CLOUDFLARED_TUNNEL:-goose-local}" SKIP_TEST=0 SKIP_TUNNEL=0 for arg in "$@"; do case "$arg" in --skip-test) SKIP_TEST=1 ;; --skip-tunnel) SKIP_TUNNEL=1 ;; -h|--help) sed -n '2,12p' "$0" exit 0 ;; *) echo "未知参数: $arg(可用 --skip-test / --skip-tunnel)" >&2 exit 1 ;; esac done load_env() { if [[ -f "${ROOT}/.env.local" ]]; then set -a # shellcheck disable=SC1091 source "${ROOT}/.env.local" set +a fi if [[ -f "${H5_DIR}/.env" ]]; then set -a # shellcheck disable=SC1091 source "${H5_DIR}/.env" set +a fi } kill_port() { local port="$1" if command -v lsof >/dev/null 2>&1; then local pids pids="$(lsof -ti TCP:"${port}" -s TCP:LISTEN 2>/dev/null || true)" if [[ -n "${pids}" ]]; then echo "==> 释放 :${port} (PID ${pids})" kill ${pids} 2>/dev/null || true sleep 1 kill -9 ${pids} 2>/dev/null || true fi fi } pkg_run() { cd "${H5_DIR}" if command -v pnpm >/dev/null 2>&1; then pnpm "$@" elif command -v npm >/dev/null 2>&1; then npm "$@" else echo "错误: 需要 pnpm 或 npm" >&2 exit 1 fi } load_env export TKMIND_API_TARGET="${TKMIND_API_TARGET:-${GOOSE_API_TARGET:-https://127.0.0.1:${GOOSE_PORT:-18006}}}" export TKMIND_SERVER__SECRET_KEY="${TKMIND_SERVER__SECRET_KEY:-${GOOSE_SERVER__SECRET_KEY:-local-dev-secret}}" export H5_PORT export VITE_TKMIND_WORKING_DIR="${VITE_TKMIND_WORKING_DIR:-${ROOT}}" echo "==> H5 生产发布" echo " 工作目录: ${VITE_TKMIND_WORKING_DIR}" echo " API 代理: ${TKMIND_API_TARGET}" echo " 本地端口: ${H5_PORT}" echo " 公网地址: ${PROD_URL}" if ! curl -sk "${TKMIND_API_TARGET}/status" >/dev/null 2>&1; then echo "⚠️ goosed 未运行,正在启动..." "${ROOT}/local_restart.sh" fi cd "${H5_DIR}" if [[ ! -d node_modules ]]; then echo "==> 安装依赖..." pkg_run install fi if [[ "${SKIP_TEST}" -eq 0 ]]; then echo "==> 运行测试..." pkg_run test fi echo "==> 构建生产包..." pkg_run run build kill_port "${H5_PORT}" if [[ -f "${ROOT}/.h5.pid" ]]; then old_pid="$(<"${ROOT}/.h5.pid")" if kill -0 "${old_pid}" 2>/dev/null; then kill "${old_pid}" 2>/dev/null || true fi rm -f "${ROOT}/.h5.pid" fi echo "==> 重启 H5 服务 @ http://127.0.0.1:${H5_PORT}" nohup node server.mjs >"${LOG_DIR}/h5.log" 2>&1 & echo $! >"${ROOT}/.h5.pid" sleep 1 if [[ "${SKIP_TUNNEL}" -eq 0 ]]; then if pgrep -f "cloudflared tunnel run ${TUNNEL_NAME}" >/dev/null 2>&1; then echo "✅ cloudflared 已在运行" elif command -v cloudflared >/dev/null 2>&1 && [[ -f "${HOME}/.cloudflared/config.yml" ]]; then kill_port 20241 echo "==> 启动 Cloudflare Tunnel (${PROD_URL})" nohup cloudflared tunnel run "${TUNNEL_NAME}" >"${LOG_DIR}/cloudflared.log" 2>&1 & echo $! >"${ROOT}/.cloudflared.pid" sleep 3 else echo "⚠️ 未检测到 cloudflared,仅本地可用" fi fi echo "" echo "=== 健康检查 ===" curl -sk "${TKMIND_API_TARGET}/status" && echo " ← goosed" curl -s "http://127.0.0.1:${H5_PORT}/api/status" && echo " ← h5 本地" if [[ "${SKIP_TUNNEL}" -eq 0 ]] && command -v cloudflared >/dev/null 2>&1; then curl -s --max-time 15 -o /dev/null -w "${PROD_URL} → %{http_code}\n" "${PROD_URL}/" || true fi echo "" echo "✅ H5 已发布" echo " 本地: http://127.0.0.1:${H5_PORT}" echo " 公网: ${PROD_URL}" echo " 日志: ${LOG_DIR}/h5.log"