| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- #!/usr/bin/env bash
- # ──────────────────────────────────────────────────────────────────────────
- # mode_workflow 查看服务 + Cloudflare 隧道 一键管理脚本
- #
- # ./share.sh start 启动 server.py(后台) + cloudflared 隧道(后台,http2),打印公网链接
- # ./share.sh stop 停掉两者
- # ./share.sh restart 重启两者(换新链接)
- # ./share.sh status 查看运行状态 + 当前链接
- # ./share.sh url 只打印当前公网链接
- #
- # 设计要点:
- # - 两个进程都用 nohup/setsid 脱离终端,关掉终端窗口也不会被 SIGHUP 杀掉。
- # - cloudflared 强制 --protocol http2,绕开公司网/VPN 常封的 UDP(QUIC)。
- # - 幂等:重复 start 会先清掉旧进程,不会起一堆。
- # ──────────────────────────────────────────────────────────────────────────
- set -euo pipefail
- # 项目目录 = 本脚本所在目录(shell/)的上一级
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
- PROJ_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
- PORT="${PORT:-8772}"
- SERVER_LOG="$PROJ_DIR/.server.log"
- CF_LOG="$PROJ_DIR/.cloudflared.log"
- SERVER_PIDF="$PROJ_DIR/.server.pid"
- CF_PIDF="$PROJ_DIR/.cloudflared.pid"
- PY="${PYTHON:-python}"
- # 选一个能后台脱离的方式:优先 setsid,macOS 没有就用 nohup
- _spawn() { # _spawn <logfile> <cmd...>
- local log="$1"; shift
- if command -v setsid >/dev/null 2>&1; then
- setsid "$@" >"$log" 2>&1 < /dev/null &
- else
- nohup "$@" >"$log" 2>&1 < /dev/null &
- fi
- echo $!
- }
- _alive() { [ -n "${1:-}" ] && kill -0 "$1" 2>/dev/null; }
- _server_running() { lsof -nP -iTCP:"$PORT" -sTCP:LISTEN >/dev/null 2>&1; }
- _extract_url() {
- # 优先看脚本自己的日志,兼容回退到手动启动时的 /tmp 日志;没找到不报错
- { grep -hEo "https://[a-z0-9-]+\.trycloudflare\.com" "$CF_LOG" /tmp/cf_tunnel.log 2>/dev/null || true; } | tail -1
- }
- start_server() {
- if _server_running; then
- echo "✅ server.py 已在跑 (:$PORT)"
- return
- fi
- echo "▶️ 启动 server.py …"
- ( cd "$PROJ_DIR" && _spawn "$SERVER_LOG" "$PY" server.py "$PORT" ) > "$SERVER_PIDF"
- # 等监听端口起来
- for _ in $(seq 1 20); do
- _server_running && break
- sleep 0.5
- done
- if _server_running; then
- echo "✅ server.py 已启动 (:$PORT) 日志: $SERVER_LOG"
- else
- echo "❌ server.py 启动失败,看日志: $SERVER_LOG"; tail -15 "$SERVER_LOG" || true; exit 1
- fi
- }
- start_tunnel() {
- echo "▶️ 启动 cloudflared 隧道 (http2) …"
- : > "$CF_LOG"
- _spawn "$CF_LOG" cloudflared tunnel --protocol http2 --url "http://localhost:$PORT" > "$CF_PIDF"
- # 等 URL 出现
- local url=""
- for _ in $(seq 1 40); do
- url="$(_extract_url)"
- [ -n "$url" ] && break
- sleep 0.5
- done
- if [ -n "$url" ]; then
- # 再等连接注册成功
- for _ in $(seq 1 20); do
- grep -q "Registered tunnel connection" "$CF_LOG" && break
- sleep 0.5
- done
- echo
- echo "🌐 公网链接(发给客户即可,无需局域网/改代理):"
- echo " $url"
- echo
- else
- echo "❌ 隧道未拿到地址,看日志: $CF_LOG"; tail -15 "$CF_LOG" || true; exit 1
- fi
- }
- stop_all() {
- echo "⏹ 停止隧道与服务 …"
- pkill -f "cloudflared tunnel --protocol http2 --url http://localhost:$PORT" 2>/dev/null && echo " - cloudflared 已停" || echo " - 无 cloudflared"
- # 杀掉占用端口的 server.py
- local pids; pids="$(lsof -nP -tiTCP:"$PORT" -sTCP:LISTEN 2>/dev/null || true)"
- if [ -n "$pids" ]; then
- echo "$pids" | xargs kill 2>/dev/null && echo " - server.py (:$PORT) 已停"
- else
- echo " - 无 server.py 监听 :$PORT"
- fi
- rm -f "$SERVER_PIDF" "$CF_PIDF"
- }
- status() {
- echo "── 状态 ──────────────────────────────"
- if _server_running; then echo "server.py : ✅ 运行中 (:$PORT)"; else echo "server.py : ❌ 未运行"; fi
- if pgrep -f "cloudflared tunnel .*localhost:$PORT" >/dev/null 2>&1; then
- echo "cloudflared: ✅ 运行中"
- else
- echo "cloudflared: ❌ 未运行"
- fi
- local url; url="$(_extract_url)"
- [ -n "$url" ] && echo "公网链接 : $url" || echo "公网链接 : (无)"
- # 本地健康检查(绕代理)
- local code; code="$(curl -s -m3 --noproxy '*' -o /dev/null -w '%{http_code}' "http://127.0.0.1:$PORT/" 2>/dev/null || echo 000)"
- echo "本地自检 : HTTP $code"
- echo "─────────────────────────────────────"
- }
- case "${1:-start}" in
- start) start_server; start_tunnel ;;
- stop) stop_all ;;
- restart) stop_all; sleep 1; start_server; start_tunnel ;;
- status) status ;;
- url) u="$(_extract_url)"; [ -n "$u" ] && echo "$u" || { echo "(隧道未运行)"; exit 1; } ;;
- *) echo "用法: $0 {start|stop|restart|status|url}"; exit 1 ;;
- esac
|