264 lines
8.1 KiB
Bash
Executable File
264 lines
8.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
cd "$PROJECT_DIR"
|
|
|
|
MODE="${1:-dev}"
|
|
|
|
stop_legacy_conflicts() {
|
|
local pid cmd
|
|
if [[ "${PMFLOW_KEEP_LEGACY:-0}" == "1" ]]; then
|
|
echo "[PMFlow] skip legacy conflict cleanup (PMFLOW_KEEP_LEGACY=1)"
|
|
return
|
|
fi
|
|
|
|
while read -r pid; do
|
|
[[ -z "${pid:-}" ]] && continue
|
|
cmd="$(ps -p "$pid" -o command= 2>/dev/null || true)"
|
|
if [[ "$cmd" == *"uvicorn app.main:app"* ]]; then
|
|
kill "$pid" 2>/dev/null || true
|
|
echo "[PMFlow] stopped legacy backend pid=$pid"
|
|
fi
|
|
done < <(lsof -tiTCP:8000 -sTCP:LISTEN 2>/dev/null || true)
|
|
|
|
while read -r pid; do
|
|
[[ -z "${pid:-}" ]] && continue
|
|
cmd="$(ps -p "$pid" -o command= 2>/dev/null || true)"
|
|
if [[ "$cmd" == *"npm run dev -- --host 0.0.0.0 --port 5174"* ]] || [[ "$cmd" == *"vite --host 0.0.0.0 --port 5174"* ]]; then
|
|
kill "$pid" 2>/dev/null || true
|
|
echo "[PMFlow] stopped legacy frontend pid=$pid"
|
|
fi
|
|
done < <(lsof -tiTCP:5174 -sTCP:LISTEN 2>/dev/null || true)
|
|
}
|
|
|
|
extract_tunnel_url() {
|
|
local logfile="$1"
|
|
rg -o "https://[a-z0-9-]+\\.trycloudflare\\.com" "$logfile" 2>/dev/null \
|
|
| rg -v "^https://api\\.trycloudflare\\.com$" \
|
|
| tail -n 1
|
|
}
|
|
|
|
probe_tunnel_url() {
|
|
local url="$1"
|
|
local health_path="$2"
|
|
local code=""
|
|
code="$(curl -s -m 8 -o /dev/null -w "%{http_code}" "${url}${health_path}" || true)"
|
|
[[ "$code" =~ ^[0-9]{3}$ ]] || return 1
|
|
[[ "$code" -ge 200 && "$code" -lt 500 ]]
|
|
}
|
|
|
|
probe_tunnel_url_stable() {
|
|
local url="$1"
|
|
local health_path="$2"
|
|
local checks="${3:-2}"
|
|
local current=1
|
|
|
|
while [[ "$current" -le "$checks" ]]; do
|
|
probe_tunnel_url "$url" "$health_path" || return 1
|
|
if [[ "$current" -lt "$checks" ]]; then
|
|
sleep 1
|
|
fi
|
|
current=$((current + 1))
|
|
done
|
|
}
|
|
|
|
probe_local_origin() {
|
|
local port="$1"
|
|
local path="${2:-/}"
|
|
local code=""
|
|
code="$(curl -s -m 3 -o /dev/null -w "%{http_code}" "http://127.0.0.1:${port}${path}" || true)"
|
|
[[ "$code" =~ ^[0-9]{3}$ ]] || return 1
|
|
[[ "$code" -ge 200 && "$code" -lt 500 ]]
|
|
}
|
|
|
|
resolve_frontend_tunnel_port() {
|
|
local preferred="${PMFLOW_FRONTEND_TUNNEL_PORT:-${FRONTEND_PORT:-5174}}"
|
|
local candidates=("$preferred" 5174 5175 5176 5177 5178)
|
|
local seen=" "
|
|
local port=""
|
|
|
|
for port in "${candidates[@]}"; do
|
|
[[ "$seen" == *" ${port} "* ]] && continue
|
|
seen="${seen}${port} "
|
|
if probe_local_origin "$port" "/"; then
|
|
echo "$port"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
echo "$preferred"
|
|
}
|
|
|
|
start_or_reuse_tunnel() {
|
|
local name="$1"
|
|
local port="$2"
|
|
local health_path="${3:-/}"
|
|
local pidfile="logs/tunnel-${name}.pid"
|
|
local logfile="logs/tunnel-${name}.log"
|
|
local protocol="${PMFLOW_TUNNEL_PROTOCOL:-http2}"
|
|
# default: reuse existing tunnel URL; set PMFLOW_FORCE_NEW_TUNNEL=1 to force rotate
|
|
local force_new="${PMFLOW_FORCE_NEW_TUNNEL:-0}"
|
|
# default: probe existing URL before reuse; set 0 only if you explicitly want fast/stale reuse
|
|
local strict_check="${PMFLOW_STRICT_TUNNEL_CHECK:-1}"
|
|
local url=""
|
|
local max_attempts=2
|
|
local attempt=1
|
|
local new_pid=""
|
|
|
|
if [[ "$force_new" != "1" ]]; then
|
|
if [[ -f "$pidfile" ]]; then
|
|
local old_pid
|
|
old_pid="$(cat "$pidfile" 2>/dev/null || true)"
|
|
if [[ -n "${old_pid:-}" ]] && ps -p "$old_pid" >/dev/null 2>&1; then
|
|
url="$(extract_tunnel_url "$logfile")"
|
|
if [[ -n "$url" ]]; then
|
|
if [[ "$strict_check" != "1" ]] || probe_tunnel_url_stable "$url" "$health_path"; then
|
|
echo "$url"
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
while [[ "$attempt" -le "$max_attempts" ]]; do
|
|
if [[ -f "$pidfile" ]]; then
|
|
local old_pid
|
|
old_pid="$(cat "$pidfile" 2>/dev/null || true)"
|
|
if [[ -n "${old_pid:-}" ]] && ps -p "$old_pid" >/dev/null 2>&1; then
|
|
kill "$old_pid" >/dev/null 2>&1 || true
|
|
fi
|
|
fi
|
|
pkill -f "cloudflared tunnel .*http://127.0.0.1:${port}" >/dev/null 2>&1 || true
|
|
rm -f "$pidfile" "$logfile"
|
|
nohup env -u HTTPS_PROXY -u HTTP_PROXY -u ALL_PROXY -u https_proxy -u http_proxy -u all_proxy \
|
|
NO_PROXY="*" \
|
|
cloudflared tunnel --protocol "$protocol" --url "http://127.0.0.1:${port}" >"$logfile" 2>&1 &
|
|
new_pid="$!"
|
|
echo "$new_pid" >"$pidfile"
|
|
|
|
for _ in {1..35}; do
|
|
url="$(extract_tunnel_url "$logfile")"
|
|
if [[ -n "$url" ]]; then
|
|
if probe_tunnel_url_stable "$url" "$health_path"; then
|
|
echo "$url"
|
|
return 0
|
|
fi
|
|
fi
|
|
if [[ -n "$new_pid" ]] && ! ps -p "$new_pid" >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
attempt=$((attempt + 1))
|
|
done
|
|
|
|
echo ""
|
|
}
|
|
|
|
print_temp_public_urls() {
|
|
if [[ "${PMFLOW_NO_TUNNEL:-0}" == "1" ]]; then
|
|
echo "[PMFlow] skip temporary tunnel (PMFLOW_NO_TUNNEL=1)"
|
|
return
|
|
fi
|
|
if ! command -v cloudflared >/dev/null 2>&1; then
|
|
echo "[PMFlow] cloudflared not found, skip temporary public urls"
|
|
return
|
|
fi
|
|
|
|
mkdir -p logs
|
|
local front_url back_url frontend_port
|
|
local expose_backend="${PMFLOW_EXPOSE_BACKEND_TUNNEL:-0}"
|
|
local protocol="${PMFLOW_TUNNEL_PROTOCOL:-http2}"
|
|
local frontend_watch="${PMFLOW_FRONTEND_TUNNEL_WATCH:-1}"
|
|
echo "[PMFlow] tunnel protocol: ${protocol}"
|
|
frontend_port="$(resolve_frontend_tunnel_port)"
|
|
echo "[PMFlow] tunnel frontend origin: http://127.0.0.1:${frontend_port}"
|
|
front_url=""
|
|
if [[ "$frontend_watch" == "1" ]]; then
|
|
bash scripts/tunnel_frontend_watch.sh start >/dev/null 2>&1 || true
|
|
for _ in {1..25}; do
|
|
front_url="$(rg '^frontend=' logs/public_url.txt | tail -n 1 | sed 's/^frontend=//' || true)"
|
|
if [[ -z "$front_url" ]]; then
|
|
front_url="$(sed -n '1p' logs/public_url.txt | rg '^https?://' -N || true)"
|
|
fi
|
|
if [[ -n "$front_url" ]]; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
else
|
|
front_url="$(start_or_reuse_tunnel "frontend" "$frontend_port" "/")"
|
|
fi
|
|
back_url=""
|
|
if [[ "$expose_backend" == "1" ]]; then
|
|
back_url="$(start_or_reuse_tunnel "backend" "8000" "/healthz")"
|
|
fi
|
|
|
|
: > logs/public_url.txt
|
|
if [[ -n "$front_url" ]]; then
|
|
echo "frontend=$front_url" >> logs/public_url.txt
|
|
echo "[PMFlow] temp frontend: $front_url"
|
|
else
|
|
echo "[PMFlow] temp frontend: (pending, check logs/tunnel-frontend.log)"
|
|
fi
|
|
if [[ "$expose_backend" != "1" ]]; then
|
|
echo "[PMFlow] temp backend: (skip, PMFLOW_EXPOSE_BACKEND_TUNNEL=0)"
|
|
else
|
|
if [[ -n "$back_url" ]]; then
|
|
echo "backend=$back_url" >> logs/public_url.txt
|
|
echo "[PMFlow] temp backend: $back_url"
|
|
else
|
|
echo "[PMFlow] temp backend: (pending, check logs/tunnel-backend.log)"
|
|
fi
|
|
fi
|
|
echo "[PMFlow] public urls saved: logs/public_url.txt"
|
|
}
|
|
|
|
case "$MODE" in
|
|
dev|"")
|
|
echo "[PMFlow] start mode: docker-dev hot reload"
|
|
stop_legacy_conflicts
|
|
bash scripts/docker_stack.sh dev-start
|
|
echo "[PMFlow] frontend: http://127.0.0.1:5174"
|
|
echo "[PMFlow] backend: http://127.0.0.1:8000"
|
|
print_temp_public_urls
|
|
;;
|
|
prod)
|
|
echo "[PMFlow] start mode: docker prod compose"
|
|
stop_legacy_conflicts
|
|
bash scripts/docker_stack.sh start
|
|
echo "[PMFlow] frontend: http://127.0.0.1:5174"
|
|
echo "[PMFlow] backend: http://127.0.0.1:8000"
|
|
print_temp_public_urls
|
|
;;
|
|
legacy)
|
|
echo "[PMFlow] start mode: legacy local (venv + npm + tunnel)"
|
|
bash scripts/start_legacy_local.sh
|
|
;;
|
|
urls)
|
|
if [[ -s logs/public_url.txt ]]; then
|
|
local_front_url="$(rg '^frontend=' logs/public_url.txt | tail -n 1 | sed 's/^frontend=//' || true)"
|
|
local_back_url="$(rg '^backend=' logs/public_url.txt | tail -n 1 | sed 's/^backend=//' || true)"
|
|
if [[ -z "$local_front_url" ]]; then
|
|
local_front_url="$(sed -n '1p' logs/public_url.txt | rg '^https?://' -N || true)"
|
|
fi
|
|
if [[ -z "$local_back_url" ]]; then
|
|
local_back_url="$(sed -n '2p' logs/public_url.txt | rg '^https?://' -N || true)"
|
|
fi
|
|
echo "[PMFlow] 当前公网地址:"
|
|
echo "前端: ${local_front_url:-未就绪}"
|
|
echo "后端: ${local_back_url:-未暴露}"
|
|
else
|
|
echo "[PMFlow] no public url record found (logs/public_url.txt)"
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
echo "Usage: $0 [dev|prod|legacy|urls]"
|
|
exit 1
|
|
;;
|
|
esac
|