#!/usr/bin/env bash set -Eeuo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' PROJECT_DIR="/opt/cli-proxy" CONFIG_FILE="${PROJECT_DIR}/config.yaml" COMPOSE_FILE="${PROJECT_DIR}/docker-compose.yml" DOCKER_DISTRO="" DOCKER_CODENAME="" DOCKER_ARCH="$(dpkg --print-architecture)" print_section() { echo "————————————————————————————— $1 —————————————————————————————————" } log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2 } on_error() { local exit_code=$? local line_no=$1 log_error "部署失败,出错行:${line_no},退出码:${exit_code}" exit "${exit_code}" } trap 'on_error ${LINENO}' ERR require_root() { if [[ "${EUID}" -ne 0 ]]; then log_error "请使用 root 用户运行此脚本" exit 1 fi } detect_os() { if [[ ! -r /etc/os-release ]]; then log_error "无法读取 /etc/os-release,无法判断系统版本" exit 1 fi # shellcheck disable=SC1091 source /etc/os-release case "${ID:-}" in debian|ubuntu) DOCKER_DISTRO="${ID}" ;; *) if [[ "${ID_LIKE:-}" == *debian* ]]; then DOCKER_DISTRO="debian" else log_error "当前系统 ${ID:-unknown} 不在脚本支持范围内,仅支持 Debian/Ubuntu" exit 1 fi ;; esac DOCKER_CODENAME="${DOCKER_CODENAME:-${VERSION_CODENAME:-${UBUNTU_CODENAME:-}}}" if [[ -z "${DOCKER_CODENAME}" ]]; then log_error "无法识别系统代号,请手动设置环境变量 DOCKER_CODENAME 后重试" exit 1 fi log_info "检测到系统:${PRETTY_NAME:-${ID:-unknown}},仓库:${DOCKER_DISTRO} ${DOCKER_CODENAME}" } write_project_files() { print_section "写入部署文件" mkdir -p "${PROJECT_DIR}" "${PROJECT_DIR}/auths" "${PROJECT_DIR}/logs" cd "${PROJECT_DIR}" cat > "${CONFIG_FILE}" <<'EOF' host: '0.0.0.0' port: 8311 tls: enable: false remote-management: allow-remote: true secret-key: 'Cici080306' disable-control-panel: false panel-github-repository: 'https://github.com/router-for-me/Cli-Proxy-API-Management-Center' auth-dir: '/root/.cli-proxy-api' api-keys: - sk-HDYr5hQtTtmZfA3QMQv6m2P2hBukt3NUvPhaQtsjNnEfbVet - sk-RcJa6lxsbyehNXtysAonsMMdRvJDRQQtLspamGkBICP8v debug: true pprof: enable: false addr: '127.0.0.1:8316' commercial-mode: true incognito-browser: true logging-to-file: false logs-max-total-size-mb: 72 error-logs-max-files: 10 usage-statistics-enabled: true force-model-prefix: false passthrough-headers: false request-retry: 4 max-retry-interval: 30 quota-exceeded: switch-project: true switch-preview-model: true routing: strategy: 'fill-first' ws-auth: false nonstream-keepalive-interval: 25 streaming: keepalive-seconds: 15 bootstrap-retries: 1 #──────── Oauth 别名 ────────── oauth-model-alias: kiro: - name: kiro-claude-sonnet-4-5 alias: claude-sonnet-4-5-20250929 fork: true - name: kiro-claude-sonnet-4-5 alias: claude-sonnet-4-5 fork: true - name: kiro-claude-sonnet-4 alias: claude-sonnet-4-20250514 fork: true - name: kiro-claude-sonnet-4 alias: claude-sonnet-4 fork: true - name: kiro-claude-opus-4-6 alias: claude-opus-4-6 fork: true - name: kiro-claude-opus-4-5 alias: claude-opus-4-5-20251101 fork: true - name: kiro-claude-opus-4-5 alias: claude-opus-4-5 fork: true - name: kiro-claude-haiku-4-5 alias: claude-haiku-4-5-20251001 fork: true - name: kiro-claude-haiku-4-5 alias: claude-haiku-4-5 fork: true antigravity: - name: claude-opus-4-5-thinking alias: claude-opus-4-5 fork: true - name: claude-opus-4-6-thinking alias: claude-opus-4-6 fork: true - name: claude-sonnet-4-5-thinking alias: claude-sonnet-4-5 fork: true - name: claude-sonnet-4-6-thinking alias: claude-sonnet-4-6 fork: true payload: override: [] EOF cat > "${COMPOSE_FILE}" <<'EOF' services: cli-proxy-api: image: eceasy/cli-proxy-api-plus:latest container_name: cli-proxy-api-plus ports: - "8317:8311" volumes: - ./config.yaml:/CLIProxyAPI/config.yaml - ./auths:/root/.cli-proxy-api - ./logs:/CLIProxyAPI/logs restart: unless-stopped EOF log_info "已写入 ${CONFIG_FILE} 与 ${COMPOSE_FILE}" } remove_conflicting_docker_packages() { local packages=( docker.io docker-doc docker-compose podman-docker containerd runc ) local installed=() local pkg="" for pkg in "${packages[@]}"; do if dpkg -s "${pkg}" >/dev/null 2>&1; then installed+=("${pkg}") fi done if [[ "${#installed[@]}" -gt 0 ]]; then log_warn "检测到冲突软件包:${installed[*]},正在自动移除" apt-get remove -y "${installed[@]}" fi } install_docker() { print_section "部署 Docker" if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then log_info "Docker 与 Docker Compose 已可用,跳过安装" docker --version docker compose version return fi export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y ca-certificates curl remove_conflicting_docker_packages install -m 0755 -d /etc/apt/keyrings curl -fsSL "https://download.docker.com/linux/${DOCKER_DISTRO}/gpg" -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc cat > /etc/apt/sources.list.d/docker.sources </dev/null 2>&1; then log_info "Docker 服务已在运行" return fi if command -v systemctl >/dev/null 2>&1; then systemctl enable --now docker elif command -v service >/dev/null 2>&1; then service docker start else log_error "未找到 systemctl 或 service,无法自动启动 Docker" exit 1 fi docker info >/dev/null log_info "Docker 服务启动成功" } deploy_cliproxy() { print_section "部署 cli-proxy" cd "${PROJECT_DIR}" if [[ "${RUN_COMPOSE_UP:-1}" != "1" ]]; then log_warn "已跳过 docker compose up -d(RUN_COMPOSE_UP=0)" return fi docker compose pull docker compose up -d docker compose ps log_info "cli-proxy 已启动" } main() { require_root detect_os write_project_files install_docker ensure_docker_service deploy_cliproxy } main "$@"