#!/bin/bash # ================= 颜色定义 ================= RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;36m' PLAIN='\033[0m' # ================= 工具函数 ================= log_info() { echo -e "${GREEN}[INFO]${PLAIN} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${PLAIN} $1"; } log_error() { echo -e "${RED}[ERROR]${PLAIN} $1"; } log_step() { echo -e "\n${BLUE}[${1}]${PLAIN} ${2}"; } # 交互式读取,有默认值可选 read_or_default() { local prompt_msg="$1" local default_value="$2" if [ -t 0 ]; then read -p "$prompt_msg" input_val echo "${input_val:-$default_value}" else echo "$default_value" fi } # 交互式确认(管道模式下默认返回 true) confirm_or_default() { local prompt_msg="$1" local default="$2" if [ -t 0 ]; then read -p "$prompt_msg" answer answer="${answer:-$default}" [[ "$answer" == "y" || "$answer" == "Y" ]] else [[ "$default" == "y" || "$default" == "Y" ]] fi } # 检查是否有 systemd(pid=1 是 systemd 才算真正有 systemd) has_systemd() { [ -f /proc/1/comm ] && grep -qx "systemd" /proc/1/comm 2>/dev/null } # 安全的进程启动(支持 systemd 和 nohup 两种模式) # 返回 0 表示启动成功 safe_start_service() { local name=$1 local cmd=$2 if has_systemd; then systemctl daemon-reload 2>/dev/null if systemctl enable "$name" --now 2>/dev/null; then log_info "$name 已通过 systemd 启动" return 0 fi # systemd 启动失败,回退到 nohup log_warn "systemd 启动失败,回退到 nohup 模式" fi # 容器/无 systemd 环境:使用 nohup + 简单的 PID 文件管理 local pid_file="/var/run/${name}.pid" local log_file="/var/log/${name}.log" if [ -f "$pid_file" ] && kill -0 "$(cat "$pid_file")" 2>/dev/null; then log_warn "$name 已在运行 (PID: $(cat "$pid_file"))" return 0 fi nohup $cmd > "$log_file" 2>&1 & echo $! > "$pid_file" sleep 1 if kill -0 "$!" 2>/dev/null; then log_info "$name 已通过 nohup 启动 (PID: $!)" return 0 else log_error "$name 启动失败,查看日志: $log_file" return 1 fi } # 检查服务状态 get_service_status() { local name=$1 if has_systemd; then systemctl is-active "$name" 2>/dev/null || echo "inactive" else local pid_file="/var/run/${name}.pid" if [ -f "$pid_file" ] && kill -0 "$(cat "$pid_file")" 2>/dev/null; then echo "active (nohup, PID: $(cat "$pid_file"))" else echo "inactive" fi fi } # 重启服务 restart_service() { local name=$1 if has_systemd; then systemctl restart "$name" 2>/dev/null else local pid_file="/var/run/${name}.pid" if [ -f "$pid_file" ]; then kill "$(cat "$pid_file")" 2>/dev/null sleep 1 fi fi } echo -e "${BLUE}==================================================${PLAIN}" echo -e "${GREEN} 华为云 Token 动态网关 (6小时高能效缓存版) 部署 ${PLAIN}" echo -e "${BLUE}==================================================${PLAIN}" # 1. 检查权限 if [ "$EUID" -ne 0 ]; then log_error "请使用 root 用户运行此脚本!" exit 1 fi # 检测环境类型 IS_CONTAINER=false if [ -f /.dockerenv ] || grep -qE 'docker|containerd|kubepods' /proc/1/cgroup 2>/dev/null; then IS_CONTAINER=true log_warn "检测到容器环境,将使用 nohup 模式替代 systemd" fi # ================= 检测并安装 frpc ================= log_step "1/5" "正在检测 frpc 环境..." if ! command -v frpc &>/dev/null && [ ! -f "/usr/local/bin/frpc" ] && [ ! -f "/usr/bin/frpc" ]; then log_warn "未检测到 frpc!" if confirm_or_default "是否需要为您自动下载并安装最新版 frpc (使用国内加速镜像)? (y/n, 默认 y): " "y"; then ARCH=$(uname -m) case "$ARCH" in x86_64) FRP_ARCH="amd64" ;; aarch64) FRP_ARCH="arm64" ;; armv7l) FRP_ARCH="arm" ;; i386|i686) FRP_ARCH="386" ;; *) log_error "不支持的系统架构 $ARCH,请手动安装 frpc。" exit 1 ;; esac FRP_VERSION="0.61.0" log_info "开始通过国内节点下载 frpc v${FRP_VERSION} (${FRP_ARCH})..." # 使用多个镜像源,自动回退 MIRRORS=( "https://ghfast.top/https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_${FRP_ARCH}.tar.gz" "https://mirror.ghproxy.com/https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_${FRP_ARCH}.tar.gz" "https://github.moeyy.xyz/https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_${FRP_ARCH}.tar.gz" ) DOWNLOADED=false for url in "${MIRRORS[@]}"; do log_info "尝试下载: $url" if wget --timeout=30 --tries=2 --no-check-certificate -O frp.tar.gz "$url" 2>/dev/null; then DOWNLOADED=true break fi done if [ "$DOWNLOADED" != "true" ]; then log_error "所有镜像源下载失败!请检查网络或手动安装 frpc。" exit 1 fi tar -zxf frp.tar.gz > /dev/null 2>&1 || { log_error "解压失败"; exit 1; } cd frp_${FRP_VERSION}_linux_${FRP_ARCH} || { log_error "无法进入解压目录"; exit 1; } cp frpc /usr/local/bin/ && chmod +x /usr/local/bin/frpc mkdir -p /etc/frp if [ ! -f "/etc/frp/frpc.toml" ]; then cat << 'FRPCFG' > /etc/frp/frpc.toml serverAddr = "x.x.x.x" serverPort = 7000 auth.method = "token" auth.token = "your_token_here" FRPCFG log_warn "已生成 frpc 配置模板,请编辑 /etc/frp/frpc.toml 填入正确的服务器信息" fi # 生成 systemd 服务文件(即使当前无 systemd,也保留以便后续使用) cat << 'FRPSVC' > /etc/systemd/system/frpc.service [Unit] Description=Frp Client Service After=network-online.target Wants=network-online.target [Service] Type=simple User=root Restart=on-failure RestartSec=5s StartLimitInterval=60s StartLimitBurst=3 ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml ExecReload=/usr/local/bin/frpc reload -c /etc/frp/frpc.toml [Install] WantedBy=multi-user.target FRPSVC if has_systemd; then systemctl daemon-reload && systemctl enable frpc --now fi cd .. && rm -rf frp_${FRP_VERSION}_linux_${FRP_ARCH} frp.tar.gz log_info "frpc 安装完成!配置文件位于 /etc/frp/frpc.toml" fi else log_info "系统已安装 frpc。" fi # ================= 配置网关与 frpc 代理参数 ================= log_step "2/5" "配置 frpc 连接与网关端口参数:" # frps 服务器连接信息(必填 —— 管道模式下必须通过环境变量提供) if [ -t 0 ]; then # 交互模式 while [ -z "$FRPS_ADDR" ]; do read -p "请输入 frps 服务器地址 (例: 1.2.3.4): " FRPS_ADDR [ -z "$FRPS_ADDR" ] && log_error "此项为必填,请输入!" done read -p "请输入 frps 服务器端口 (默认 7000): " FRPS_PORT FRPS_PORT=${FRPS_PORT:-7000} read -p "请输入 frps 认证 token (默认留空): " FRPS_TOKEN else # 管道模式:必须通过环境变量提供关键参数 if [ -z "$FRPS_ADDR" ]; then log_error "管道模式下必须通过环境变量提供参数!" log_error "用法: FRPS_ADDR=1.2.3.4 FRPS_PORT=7000 FRPS_TOKEN=xxx curl -sSL ... | bash" log_error "或下载后交互式: curl -sSL -o hwaishell.sh && bash hwaishell.sh" exit 1 fi FRPS_PORT=${FRPS_PORT:-7000} FRPS_TOKEN=${FRPS_TOKEN:-} fi PROXY_NAME="${PROXY_NAME:-$(read_or_default "请输入代理名称 (默认 huawei-dynamic-gateway): " "huawei-dynamic-gateway")}" GATEWAY_PORT="${GATEWAY_PORT:-$(read_or_default "请输入 Python 动态网关的本地监听端口 (默认 8080): " "8080")}" REMOTE_PORT="${REMOTE_PORT:-$(read_or_default "请输入在 frps 公网端暴露的端口 (默认 18000): " "18000")}" # 端口有效性检查 if ! [[ "$GATEWAY_PORT" =~ ^[0-9]+$ ]] || [ "$GATEWAY_PORT" -lt 1 ] || [ "$GATEWAY_PORT" -gt 65535 ]; then log_error "无效的本地端口: $GATEWAY_PORT" exit 1 fi if ! [[ "$REMOTE_PORT" =~ ^[0-9]+$ ]] || [ "$REMOTE_PORT" -lt 1 ] || [ "$REMOTE_PORT" -gt 65535 ]; then log_error "无效的远程端口: $REMOTE_PORT" exit 1 fi # ================= 生成/追加 frpc 配置 ================= log_step "3/5" "检测并生成 frpc 配置文件..." FRPC_CONFIG="" FRPC_TYPE="toml" # 按优先级查找现有配置文件 for path in "/etc/frp/frpc.toml" "/etc/frp/frpc.ini" "/usr/local/etc/frp/frpc.toml" "/usr/local/etc/frp/frpc.ini"; do if [ -f "$path" ]; then FRPC_CONFIG="$path" [[ "$path" == *.ini ]] && FRPC_TYPE="ini" break fi done # 如果没找到,搜索 if [ -z "$FRPC_CONFIG" ]; then FRPC_PATH_SEARCH=$(find /etc /usr/local/etc /opt /home -maxdepth 3 \( -name "frpc.toml" -o -name "frpc.ini" \) 2>/dev/null | head -n 1) if [ -n "$FRPC_PATH_SEARCH" ]; then FRPC_CONFIG="$FRPC_PATH_SEARCH" [[ "$FRPC_PATH_SEARCH" == *.ini ]] && FRPC_TYPE="ini" fi fi # 构建代理规则块 build_proxy_block() { local type=$1 # toml | ini if [ "$type" = "toml" ]; then cat << BLK [[proxies]] name = "${PROXY_NAME}" type = "tcp" localIP = "127.0.0.1" localPort = ${GATEWAY_PORT} remotePort = ${REMOTE_PORT} BLK else cat << BLK [${PROXY_NAME}] type = tcp local_ip = 127.0.0.1 local_port = ${GATEWAY_PORT} remote_port = ${REMOTE_PORT} BLK fi } if [ -n "$FRPC_CONFIG" ]; then # --- 已有 frpc 配置:追加代理规则 --- log_info "识别到 frpc 配置: $FRPC_CONFIG (${FRPC_TYPE})" if confirm_or_default "是否自动将华为云转发规则追加到该文件? (y/n, 默认 y): " "y"; then if grep -q "${PROXY_NAME}" "$FRPC_CONFIG" 2>/dev/null; then log_warn "frpc 配置中已存在 ${PROXY_NAME} 规则,跳过追加" else cp "$FRPC_CONFIG" "${FRPC_CONFIG}.backup.$(date +%Y%m%d%H%M%S)" build_proxy_block "$FRPC_TYPE" >> "$FRPC_CONFIG" log_info "代理规则已追加到 $FRPC_CONFIG (原配置已备份)" fi fi else # --- 没有 frpc 配置:自动创建 --- log_warn "未找到 frpc 配置文件" if confirm_or_default "是否自动创建 frpc 配置文件并添加网关代理规则? (y/n, 默认 y): " "y"; then FRPC_CONFIG="/etc/frp/frpc.toml" mkdir -p /etc/frp # 生成主配置 cat << BLK > "$FRPC_CONFIG" serverAddr = "${FRPS_ADDR}" serverPort = ${FRPS_PORT} auth.method = "token" auth.token = "${FRPS_TOKEN}" BLK # 追加代理规则 build_proxy_block "toml" >> "$FRPC_CONFIG" log_info "已创建 frpc 配置文件: $FRPC_CONFIG" fi fi # ================= 安装 Python 依赖 ================= log_step "4/5" "部署 Python 运行环境..." # 检查 python3 PYTHON_CMD="" for cmd in python3 python3.11 python3.10 python3.9 python3.8; do if command -v "$cmd" &>/dev/null; then PYTHON_CMD="$cmd" break fi done if [ -z "$PYTHON_CMD" ]; then log_warn "未找到 Python3,尝试自动安装..." if command -v apt &>/dev/null; then apt-get update -qq && apt-get install -y -qq python3 python3-pip elif command -v yum &>/dev/null; then yum install -y -q python3 python3-pip elif command -v apk &>/dev/null; then apk add --no-cache python3 py3-pip else log_error "无法自动安装 Python,请手动安装 python3 和 pip" exit 1 fi PYTHON_CMD="python3" fi # 检查 pip 并安装依赖 if ! $PYTHON_CMD -m pip --version &>/dev/null 2>&1; then log_warn "pip 未安装,尝试安装..." $PYTHON_CMD -m ensurepip --upgrade 2>/dev/null || { if command -v apt &>/dev/null; then apt-get install -y -qq python3-pip; fi } fi # 使用虚拟环境避免权限问题(可选) USE_VENV=false VENV_PATH="/opt/huawei-gateway/venv" if [ "$IS_CONTAINER" = true ] || [ "$EUID" -eq 0 ]; then if confirm_or_default "是否使用虚拟环境安装 Python 依赖? (推荐, y/n, 默认 y): " "y"; then USE_VENV=true fi fi if [ "$USE_VENV" = true ]; then mkdir -p /opt/huawei-gateway $PYTHON_CMD -m venv "$VENV_PATH" 2>/dev/null || { log_warn "无法创建虚拟环境,将使用系统 Python" USE_VENV=false } fi PIP_CMD="$PYTHON_CMD -m pip" if [ "$USE_VENV" = true ]; then PIP_CMD="$VENV_PATH/bin/pip" PYTHON_CMD="$VENV_PATH/bin/python" fi # 安装依赖(使用国内镜像) log_info "正在安装 Flask 和 requests..." $PIP_CMD install --upgrade pip --quiet -i https://pypi.tuna.tsinghua.edu.cn/simple 2>/dev/null $PIP_CMD install flask requests --quiet -i https://pypi.tuna.tsinghua.edu.cn/simple # ================= 创建网关核心代码 ================= log_step "5/5" "生成网关代码并启动服务..." mkdir -p /usr/local/bin /var/log /var/run cat << 'PYEOF' > /usr/local/bin/huawei_gateway.py #!/usr/bin/env python3 """ 华为云 Token 动态网关 - 6小时缓存机制 - 支持内存扫描自动刷新 - 401 自动重试 - 兼容生产环境 (Waitress/Gunicorn) """ import os import re import sys import time import logging import threading import traceback from concurrent.futures import ThreadPoolExecutor, as_completed from flask import Flask, request, Response # 尝试导入 requests,失败则给出明确提示 try: import requests except ImportError: print("错误:缺少 requests 模块。请运行: pip install requests") sys.exit(1) # ================= 配置 ================= CACHE_TTL = 19800 # 5.5 小时(安全线) MAX_WORKERS = 8 # 内存扫描线程数 MAX_MEM_SEGMENT = 200 * 1024 * 1024 # 单段最大扫描 200MB TOKEN_PATTERN = re.compile(b'Bearer ([A-Za-z0-9+/=_-]{100,})') TARGET_HOST = 'tokenhub.developer.huaweicloud.com' # ================= 日志 ================= logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s', handlers=[ logging.StreamHandler(sys.stdout) ] ) logger = logging.getLogger('huawei-gateway') # ================= 缓存 ================= class TokenCache: def __init__(self): self._token = None self._expires_at = 0 self._lock = threading.RLock() self._last_scan = 0 self._scan_interval = 60 # 扫描间隔最小 60 秒 def get(self): with self._lock: now = time.time() if self._token and now < self._expires_at: return self._token return None def set(self, token, ttl=CACHE_TTL): with self._lock: self._token = token self._expires_at = time.time() + ttl self._last_scan = time.time() def is_scan_cooldown(self): with self._lock: return (time.time() - self._last_scan) < self._scan_interval def clear(self): with self._lock: self._token = None self._expires_at = 0 cache = TokenCache() # ================= 内存扫描 ================= def scan_pid_mem(pid): """扫描单个进程的内存寻找 Token""" maps_path = f'/proc/{pid}/maps' mem_path = f'/proc/{pid}/mem' if not os.path.exists(maps_path) or not os.path.exists(mem_path): return None try: with open(maps_path, 'r') as f: for line in f: parts = line.split() if len(parts) < 2: continue perms = parts[1] if 'r' not in perms or 'w' not in perms: # 只扫描可读可写段(更精确) continue addrs = parts[0].split('-') if len(addrs) != 2: continue start = int(addrs[0], 16) end = int(addrs[1], 16) size = end - start # 跳过过大或过小的段 if size > MAX_MEM_SEGMENT or size < 1024: continue try: with open(mem_path, 'rb') as mem: mem.seek(start) # 分段读取避免大内存占用 chunk_size = 64 * 1024 remaining = size while remaining > 0: to_read = min(chunk_size, remaining) data = mem.read(to_read) if not data: break for match in TOKEN_PATTERN.finditer(data): token = match.group(1).decode('ascii', errors='replace') # 验证 token 格式(华为云 token 通常是 JWT 格式) if len(token) > 200 and token.count('.') >= 2: return token remaining -= len(data) except (PermissionError, OSError, ValueError): continue except (PermissionError, OSError, ProcessLookupError): pass return None def find_token_in_memory(): """在所有进程中扫描 Token""" if cache.is_scan_cooldown() and cache.get(): return cache.get() try: pids = [pid for pid in os.listdir('/proc') if pid.isdigit()] except OSError: logger.error("无法访问 /proc 目录") return None # 优先扫描常见进程(python, node, java 等可能持有 token 的进程) priority_pids = [] other_pids = [] for pid in pids: try: exe_path = os.readlink(f'/proc/{pid}/exe') if any(x in exe_path for x in ['python', 'node', 'java', 'chrome', 'electron']): priority_pids.append(pid) else: other_pids.append(pid) except (OSError, PermissionError): other_pids.append(pid) # 先扫描优先级进程 all_pids = priority_pids + other_pids with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: futures = {executor.submit(scan_pid_mem, pid): pid for pid in all_pids} for future in as_completed(futures): try: token = future.result(timeout=5) if token: cache.set(token) logger.info(f"Token 已刷新 (来源 PID: {futures[future]})") return token except Exception: continue return cache.get() # 返回可能过期的缓存作为兜底 # ================= Flask 应用 ================= app = Flask(__name__) @app.route('/health') def health(): """健康检查端点""" token = cache.get() return { "status": "healthy", "token_cached": token is not None, "token_expires_in": max(0, cache._expires_at - time.time()) if hasattr(cache, '_expires_at') else 0 }, 200 @app.route('/v2/', methods=['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE']) def proxy(subpath): if request.method == 'OPTIONS': return Response(status=200, headers={ 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization' }) real_token = find_token_in_memory() if not real_token: logger.error("未在内存中找到华为云 Token") return {"error": "未在内存中找到华为云Token,请确保华为云相关应用正在运行"}, 500 # 构建请求头 headers = {} for k, v in request.headers: kl = k.lower() if kl not in ('host', 'content-length', 'connection', 'accept-encoding'): headers[k] = v headers['Authorization'] = f'Bearer {real_token}' headers['Host'] = TARGET_HOST target_url = f'https://{TARGET_HOST}/v2/{subpath}' try: resp = requests.request( method=request.method, url=target_url, headers=headers, data=request.get_data(), cookies=request.cookies, allow_redirects=False, timeout=60, stream=False ) # 401 兜底:Token 可能提前过期,强制刷新重试一次 if resp.status_code == 401: logger.warning("收到 401,尝试强制刷新 Token 并重试...") cache.clear() new_token = find_token_in_memory() if new_token and new_token != real_token: headers['Authorization'] = f'Bearer {new_token}' resp = requests.request( method=request.method, url=target_url, headers=headers, data=request.get_data(), cookies=request.cookies, allow_redirects=False, timeout=60 ) # 构建响应 response_headers = [] for k, v in resp.headers.items(): if k.lower() not in ('transfer-encoding', 'content-encoding', 'content-length'): response_headers.append((k, v)) return Response( resp.content, status=resp.status_code, headers=response_headers ) except requests.exceptions.Timeout: logger.error("请求华为云 API 超时") return {"error": "网关超时,请稍后重试"}, 504 except requests.exceptions.ConnectionError: logger.error("无法连接到华为云 API") return {"error": "无法连接到华为云服务"}, 502 except Exception as e: logger.error(f"网关转发失败: {traceback.format_exc()}") return {"error": f"网关转发失败: {str(e)}"}, 500 def main(): port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080 host = sys.argv[2] if len(sys.argv) > 2 else '127.0.0.1' # 尝试使用生产级 WSGI 服务器 try: import waitress logger.info(f"使用 Waitress 启动网关 ({host}:{port})") waitress.serve(app, host=host, port=port, threads=16) except ImportError: try: import gunicorn.app.wsgiapp logger.info(f"使用 Gunicorn 启动网关 ({host}:{port})") os.execlp('gunicorn', 'gunicorn', '-w', '4', '-b', f'{host}:{port}', '--access-logfile', '-', 'huawei_gateway:app') except (ImportError, OSError): logger.warning("未安装 Waitress/Gunicorn,使用 Flask 开发服务器(建议生产环境安装 waitress)") logger.info(f"启动网关 ({host}:{port})") app.run(host=host, port=port, debug=False, threaded=True) if __name__ == '__main__': main() PYEOF chmod +x /usr/local/bin/huawei_gateway.py # ================= 注册网关服务 ================= if has_systemd; then cat << EOF > /etc/systemd/system/huawei-gateway.service [Unit] Description=Huawei Dynamic Gateway (6H Cache) After=network-online.target Wants=network-online.target [Service] Type=simple User=root ExecStart=$PYTHON_CMD /usr/local/bin/huawei_gateway.py $GATEWAY_PORT Restart=always RestartSec=3 StartLimitInterval=60s StartLimitBurst=3 StandardOutput=append:/var/log/huawei-gateway.log StandardError=append:/var/log/huawei-gateway.log [Install] WantedBy=multi-user.target EOF fi # 启动服务 safe_start_service "huawei-gateway" "$PYTHON_CMD /usr/local/bin/huawei_gateway.py $GATEWAY_PORT" # ================= 启动 frpc ================= if [ -n "$FRPC_CONFIG" ] && [ -f "$FRPC_CONFIG" ]; then if command -v frpc &>/dev/null; then # 校验配置基本完整性 if ! grep -q "serverAddr" "$FRPC_CONFIG" 2>/dev/null || ! grep -q "remotePort\|remote_port" "$FRPC_CONFIG" 2>/dev/null; then log_error "frpc 配置文件不完整: $FRPC_CONFIG" log_error "请检查配置后手动启动: frpc -c $FRPC_CONFIG" else # 重启已运行的 frpc,或首次启动 if has_systemd; then if systemctl list-units --type=service 2>/dev/null | grep -q "frpc"; then systemctl restart frpc 2>/dev/null && log_info "frpc 服务已重启" else systemctl enable frpc --now 2>/dev/null && log_info "frpc 服务已通过 systemd 启动" fi else restart_service "frpc" nohup frpc -c "$FRPC_CONFIG" > /var/log/frpc.log 2>&1 & echo $! > /var/run/frpc.pid sleep 2 if kill -0 "$(cat /var/run/frpc.pid)" 2>/dev/null; then log_info "frpc 已通过 nohup 启动 (PID: $(cat /var/run/frpc.pid))" else log_error "frpc 启动失败!最后几行日志:" tail -5 /var/log/frpc.log | while IFS= read -r line; do echo -e " ${RED}|${PLAIN} $line" done fi fi fi else log_warn "frpc 未安装,跳过启动。配置文件已生成: $FRPC_CONFIG" fi fi # ================= 部署总结 ================= echo -e "\n${BLUE}==================================================${PLAIN}" echo -e "${GREEN} 🎉 6小时长效缓存网关部署完成 🎉 ${PLAIN}" echo -e "${BLUE}==================================================${PLAIN}" echo -e "1. 动态网关状态:$(get_service_status huawei-gateway)" echo -e "2. frpc 配置: $FRPC_CONFIG" echo -e "3. 健康检查: http://127.0.0.1:${GATEWAY_PORT}/health" echo -e "4. 您的本地一劳永逸调用地址:" echo -e " - ${GREEN}API Base URL:${PLAIN} http://${FRPS_ADDR}:${REMOTE_PORT}/v2" echo -e " - ${GREEN}API Key:${PLAIN} sk-anything" echo -e " - ${GREEN}Model:${PLAIN} glm-5.1" if [ "$IS_CONTAINER" = true ]; then echo -e "\n${YELLOW}[容器环境提示]${PLAIN}" echo -e " - 服务通过 nohup 运行,日志位于 /var/log/huawei-gateway.log" echo -e " - 如需停止服务: kill \$(cat /var/run/huawei-gateway.pid)" echo -e " - 建议安装 waitress 提升性能: pip install waitress" fi echo -e "${BLUE}==================================================${PLAIN}\n"