#!/usr/bin/env bash set -euo pipefail # PMFlow 公网穿透启动器(保留为唯一 tunnel 入口) # 优先命名隧道;未配置时自动退回 quick tunnel。 SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd -- "${SCRIPT_DIR}/../.." && pwd)" CONFIG_FILE="${SCRIPT_DIR}/config.yml" SETUP_SCRIPT="${SCRIPT_DIR}/setup.sh" LOG_DIR="${ROOT_DIR}/logs" FRONTEND_PORT="${PMFLOW_TUNNEL_FRONTEND_PORT:-${FRONTEND_PORT:-5174}}" BACKEND_PORT="${PMFLOW_TUNNEL_BACKEND_PORT:-${BACKEND_PORT:-8000}}" TUNNEL_PROTOCOL="${PMFLOW_TUNNEL_PROTOCOL:-quic}" HEALTH_RETRIES="${PMFLOW_TUNNEL_HEALTH_RETRIES:-3}" FOREGROUND="${PMFLOW_TUNNEL_FOREGROUND:-1}" FRONT_LOG="${LOG_DIR}/tunnel-frontend.log" BACK_LOG="${LOG_DIR}/tunnel-backend.log" extract_url() { local logfile="$1" grep -aEo 'https://[-a-z0-9]+\.trycloudflare\.com' "$logfile" 2>/dev/null | tail -1 } wait_for_url() { local logfile="$1" local retries="${2:-30}" local i=1 local url="" while [ "$i" -le "$retries" ]; do url="$(extract_url "$logfile")" if [ -n "$url" ]; then echo "$url" return 0 fi sleep 1 i=$((i + 1)) done return 1 } check_url() { local url="$1" local max_retry="$2" local retry=1 while [ "$retry" -le "$max_retry" ]; do if curl -kfsS --max-time 8 "$url" >/dev/null 2>&1; then return 0 fi sleep 2 retry=$((retry + 1)) done return 1 } run_quick_tunnels() { mkdir -p "$LOG_DIR" pkill -f "cloudflared tunnel .*127\\.0\\.0\\.1:${FRONTEND_PORT}" >/dev/null 2>&1 || true pkill -f "cloudflared tunnel .*127\\.0\\.0\\.1:${BACKEND_PORT}" >/dev/null 2>&1 || true echo "🚀 启动临时隧道..." echo "📂 日志目录: ${LOG_DIR}" echo "🌐 前端隧道: 127.0.0.1:${FRONTEND_PORT}" cloudflared tunnel --protocol "${TUNNEL_PROTOCOL}" --url "http://127.0.0.1:${FRONTEND_PORT}" >"$FRONT_LOG" 2>&1 & local front_pid=$! echo "🌐 后端隧道: 127.0.0.1:${BACKEND_PORT}" cloudflared tunnel --protocol "${TUNNEL_PROTOCOL}" --url "http://127.0.0.1:${BACKEND_PORT}" >"$BACK_LOG" 2>&1 & local back_pid=$! echo "⏳ 等待隧道地址..." local front_url="" local back_url="" front_url="$(wait_for_url "$FRONT_LOG" 45 || true)" back_url="$(wait_for_url "$BACK_LOG" 45 || true)" if [ -z "$front_url" ] || [ -z "$back_url" ]; then echo "❌ 未获取到公网地址。" echo " 前端日志: $FRONT_LOG" echo " 后端日志: $BACK_LOG" tail -n 20 "$FRONT_LOG" || true tail -n 20 "$BACK_LOG" || true kill "$front_pid" "$back_pid" >/dev/null 2>&1 || true return 1 fi local front_ok=false local back_ok=false if check_url "${front_url}/login" "$HEALTH_RETRIES" || check_url "${front_url}" "$HEALTH_RETRIES"; then front_ok=true fi if check_url "${back_url}/healthz" "$HEALTH_RETRIES"; then back_ok=true fi if [ "$front_ok" = true ] && [ "$back_ok" = true ]; then echo "✅ 隧道连通成功" else echo "⚠️ 隧道已启动,但健康检查未全部通过(可稍后重试)" fi echo "----------------------------------------" echo "前端: ${front_url}" echo "后端: ${back_url}" echo "----------------------------------------" echo "ℹ️ 停止命令: pkill -f \"cloudflared tunnel\"" if [ "$FOREGROUND" != "1" ]; then echo "✅ 已切到后台运行" echo " 前端 PID: ${front_pid}" echo " 后端 PID: ${back_pid}" return 0 fi cleanup() { echo "" echo "🔴 tunnel 已关闭" kill "$front_pid" "$back_pid" >/dev/null 2>&1 || true } trap cleanup EXIT INT TERM wait "$front_pid" "$back_pid" } echo "🚀 启动公网穿透..." if [ -f "$CONFIG_FILE" ]; then echo "✅ 检测到命名隧道配置: ${CONFIG_FILE}" echo "🌐 使用命名隧道运行(前台)" exec cloudflared tunnel run --config "$CONFIG_FILE" fi echo "⚠️ 未检测到命名隧道配置,自动切换临时隧道。" echo " 若需固定域名,请先执行: ${SETUP_SCRIPT}" run_quick_tunnels