#!/usr/bin/env bash # ============================================================================ # tunnel_api.sh — 为 Spark API (默认8080) 开 aitun.cc 公网隧道 # 位置: /content/drive/MyDrive/spark-t4/tunnel_api.sh # # 原理: 直接运行 aitun-client 二进制,并用独立的 HOME (= /root/.aitun-api) # 注册,完全不触碰现有 Flask 桥接隧道的 /root/.aitun 配置, # 两隧道互不影响。免费模式自动分配隧道码和公网 URL。 # # 用法: # bash tunnel_api.sh start # 开隧道并打印公网 URL(约10~20秒) # bash tunnel_api.sh status # bash tunnel_api.sh stop # 自定义: PORT=8080 bash tunnel_api.sh start # ============================================================================ set -euo pipefail PORT="${PORT:-8080}" CLIENT_BIN="/usr/local/lib/python3.13/dist-packages/aitun/bin/aitun-client-linux-amd64" API_HOME="/root/.aitun-api" # 独立配置目录,保护桥接隧道 LOG_FILE="/content/aitun-api-tunnel.log" PID_FILE="/content/aitun-api-tunnel.pid" GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m' info() { echo -e "${GREEN}[隧道]${NC} $*"; } fail() { echo -e "${RED}[隧道]${NC} $*"; exit 1; } [[ -x "$CLIENT_BIN" ]] || fail "未找到 aitun 客户端: $CLIENT_BIN (本机需已安装 aitun)" extract_url() { grep -oE "https://aitun\.cc/[A-Za-z0-9]+" "$LOG_FILE" 2>/dev/null | tail -1 } do_start() { do_stop 2>/dev/null || true mkdir -p "$API_HOME" info "启动隧道 (本地 :$PORT → 公网)..." HOME="$API_HOME" nohup "$CLIENT_BIN" -s aitun.cc:6639 -p "$PORT" \ > "$LOG_FILE" 2>&1 & echo $! > "$PID_FILE" for i in $(seq 1 12); do sleep 5 URL="$(extract_url)" if [[ -n "$URL" ]]; then kill -0 "$(cat "$PID_FILE")" 2>/dev/null || fail "隧道进程退出,日志: $(tail -10 "$LOG_FILE")" info "隧道已建立!" echo -e "${CYAN}──────────────────────────────────────────────────${NC}" echo -e " 公网 API: $URL/v1/chat/completions" echo -e " 健康检查: $URL/health" echo -e " 模型列表: $URL/v1/models" if [[ -s "/content/drive/MyDrive/spark-t4/.api_key" ]]; then echo -e " API Key: $(cat /content/drive/MyDrive/spark-t4/.api_key)" echo -e " 调用时带: Authorization: Bearer " fi echo -e "${CYAN}──────────────────────────────────────────────────${NC}" echo -e " curl 示例:" echo -e " curl -N $URL/v1/chat/completions \\" echo -e " -H 'Content-Type: application/json' \\" if [[ -s "/content/drive/MyDrive/spark-t4/.api_key" ]]; then echo -e " -H \"Authorization: Bearer \$(cat /content/drive/MyDrive/spark-t4/.api_key)\" \\" fi echo -e " -d '{\"messages\":[{\"role\":\"user\",\"content\":\"你好\"}],\"stream\":true}'" return 0 fi done fail "60 秒未取得公网 URL,日志: $(tail -15 "$LOG_FILE")" } do_stop() { if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then kill "$(cat "$PID_FILE")" && info "隧道已停止 (PID $(cat "$PID_FILE"))" fi rm -f "$PID_FILE" } do_status() { URL="$(extract_url)" if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then info "隧道运行中 (PID $(cat "$PID_FILE")) URL: ${URL:-未知}" else echo "隧道未运行" fi } case "${1:-help}" in start) do_start ;; stop) do_stop ;; status) do_status ;; *) grep '^# ' "$0" | sed 's/^# //' ;; esac