129 lines
4.4 KiB
Bash
129 lines
4.4 KiB
Bash
#!/usr/bin/env bash
|
|||
|
|
# ==========================================================
|
||
|
|
# CPA (CLIProxyAPI) one-click deploy script
|
||
|
|
# Target : Tencent SG server ubuntu@43.156.118.130
|
||
|
|
# Effect : create /opt/cpa + start cli-proxy-api container
|
||
|
|
# existing new-api / nginx / xray are NOT modified
|
||
|
|
# (only a runtime docker network attach for new-api)
|
||
|
|
# Idempotent: safe to run multiple times.
|
||
|
|
# Run as root: sudo bash deploy-cpa.sh
|
||
|
|
# ==========================================================
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
step() { echo ""; echo "========== [$1] $2 =========="; }
|
||
|
|
|
||
|
|
step 1 "Pre-check"
|
||
|
|
[ "$(id -u)" -eq 0 ] || { echo "ERROR: run as root (sudo bash)"; exit 1; }
|
||
|
|
docker ps --format ' {{.Names}} | {{.Status}}'
|
||
|
|
|
||
|
|
step 2 "Create /opt/cpa"
|
||
|
|
mkdir -p /opt/cpa/auths /opt/cpa/logs /opt/cpa/plugins
|
||
|
|
cd /opt/cpa
|
||
|
|
|
||
|
|
step 3 "Generate config.yaml (keep existing if present)"
|
||
|
|
if [ -f config.yaml ]; then
|
||
|
|
echo " config.yaml exists, keep it"
|
||
|
|
else
|
||
|
|
CPA_API_KEY="sk-cpa-$(openssl rand -hex 16)"
|
||
|
|
MGMT_KEY="$(openssl rand -hex 16)"
|
||
|
|
cat > config.yaml <<EOF
|
||
|
|
config-version: 8
|
||
|
|
|
||
|
|
server:
|
||
|
|
host: "" # bind all ifaces INSIDE container; exposure controlled by compose loopback binding
|
||
|
|
port: 8317
|
||
|
|
|
||
|
|
management:
|
||
|
|
allow-remote: false # panel reachable only via localhost / ssh tunnel
|
||
|
|
secret-key: "${MGMT_KEY}"
|
||
|
|
|
||
|
|
access:
|
||
|
|
api-keys:
|
||
|
|
- "${CPA_API_KEY}" # used by New API channel
|
||
|
|
|
||
|
|
oauth:
|
||
|
|
auth-dir: "~/.cli-proxy-api"
|
||
|
|
|
||
|
|
observability:
|
||
|
|
usage:
|
||
|
|
usage-statistics-enabled: true
|
||
|
|
EOF
|
||
|
|
chmod 600 config.yaml
|
||
|
|
cat > KEYS.txt <<EOF
|
||
|
|
# ============================================
|
||
|
|
# CPA secrets (keep private)
|
||
|
|
# Management panel key (MGMT_KEY):
|
||
|
|
${MGMT_KEY}
|
||
|
|
# API key for New API channel (CPA_API_KEY):
|
||
|
|
${CPA_API_KEY}
|
||
|
|
# ============================================
|
||
|
|
EOF
|
||
|
|
chmod 600 KEYS.txt
|
||
|
|
echo " config.yaml + KEYS.txt generated"
|
||
|
|
fi
|
||
|
|
|
||
|
|
step 4 "Write docker-compose.yml"
|
||
|
|
cat > docker-compose.yml <<'EOF'
|
||
|
|
services:
|
||
|
|
cli-proxy-api:
|
||
|
|
image: eceasy/cli-proxy-api:latest
|
||
|
|
container_name: cli-proxy-api
|
||
|
|
restart: unless-stopped
|
||
|
|
ports:
|
||
|
|
# all bound to server loopback only -> NOT reachable from public internet
|
||
|
|
# 8317 = API + management panel, others = OAuth callback ports per provider
|
||
|
|
- "127.0.0.1:8317:8317"
|
||
|
|
- "127.0.0.1:8085:8085"
|
||
|
|
- "127.0.0.1:1455:1455"
|
||
|
|
- "127.0.0.1:54545:54545"
|
||
|
|
- "127.0.0.1:51121:51121" # Antigravity OAuth callback
|
||
|
|
- "127.0.0.1:11451:11451"
|
||
|
|
volumes:
|
||
|
|
- ./config.yaml:/CLIProxyAPI/config.yaml
|
||
|
|
- ./auths:/root/.cli-proxy-api
|
||
|
|
- ./logs:/CLIProxyAPI/logs
|
||
|
|
- ./plugins:/CLIProxyAPI/plugins
|
||
|
|
networks:
|
||
|
|
- ai-net
|
||
|
|
|
||
|
|
networks:
|
||
|
|
ai-net:
|
||
|
|
external: true
|
||
|
|
EOF
|
||
|
|
echo " done"
|
||
|
|
|
||
|
|
step 5 "Docker network ai-net (shared with new-api)"
|
||
|
|
docker network inspect ai-net >/dev/null 2>&1 || docker network create ai-net
|
||
|
|
docker network connect ai-net new-api 2>/dev/null && echo " new-api joined ai-net" || echo " new-api already in ai-net (or not running)"
|
||
|
|
|
||
|
|
step 6 "Start container"
|
||
|
|
docker compose up -d
|
||
|
|
sleep 6
|
||
|
|
docker ps --filter name=cli-proxy-api --format ' {{.Names}} | {{.Status}} | {{.Ports}}'
|
||
|
|
|
||
|
|
step 7 "Verify"
|
||
|
|
CPA_API_KEY=$(grep -oE 'sk-cpa-[0-9a-f]+' config.yaml | head -1 || true)
|
||
|
|
[ -n "$CPA_API_KEY" ] || { echo "ERROR: api key not found in config.yaml"; exit 1; }
|
||
|
|
echo "-- no key (expect 401):"
|
||
|
|
curl -s -o /dev/null -w ' HTTP %{http_code}\n' http://127.0.0.1:8317/v1/models
|
||
|
|
echo "-- with key (expect 200; model list may be empty before OAuth login):"
|
||
|
|
curl -s -w '\n HTTP %{http_code}\n' http://127.0.0.1:8317/v1/models -H "Authorization: Bearer ${CPA_API_KEY}" | head -c 400
|
||
|
|
echo ""
|
||
|
|
echo "-- new-api container -> cli-proxy-api connectivity:"
|
||
|
|
if docker exec new-api sh -c 'command -v curl' >/dev/null 2>&1; then
|
||
|
|
docker exec new-api sh -c "curl -s -o /dev/null -w '%{http_code}' --max-time 5 http://cli-proxy-api:8317/v1/models" | sed 's/^/ HTTP /'
|
||
|
|
echo " (any HTTP code = network OK)"
|
||
|
|
elif docker exec new-api sh -c 'command -v wget' >/dev/null 2>&1; then
|
||
|
|
docker exec new-api sh -c "wget -S -q -O /dev/null http://cli-proxy-api:8317/v1/models 2>&1 | head -1" | sed 's/^/ /'
|
||
|
|
else
|
||
|
|
echo " no curl/wget inside new-api, skip (use New API channel test later)"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "=========================================================="
|
||
|
|
echo " DEPLOY DONE. Secrets (also saved at /opt/cpa/KEYS.txt):"
|
||
|
|
echo "=========================================================="
|
||
|
|
cat KEYS.txt
|
||
|
|
echo ""
|
||
|
|
echo "Next: open tunnel.bat locally, then visit"
|
||
|
|
echo " http://localhost:8317/management.html"
|