Files
paper-burner-x/deploy.sh
T
wing bbb88e8f76 feat(agents): 添加 agents 运行时系统支持 PPT 生成工作流
- 新增 orchestrator 运行时,支持阶段注册与事件驱动
- 添加 textprep、design、eval 等 stage 模块
- 添加 storage 模块管理 artifact 和 run 数据
- 重构 ppt_generator_workflow 使用新运行时
- 添加单元测试和开发依赖
2025-12-13 09:10:11 +08:00

472 lines
14 KiB
Bash

#!/usr/bin/env bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script version
VERSION="1.0.0"
# Global variables
SUFFIX=""
ADMIN_TOKEN=""
DB_PASSWORD=""
DEPLOY_DIR=""
OS_TYPE=""
IMAGE_TAG="latest"
BRANCH_NAME="main"
print_header() {
echo -e "${BLUE}"
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ Claude Code Hub - One-Click Deployment ║"
echo "║ Version ${VERSION} ║"
echo "║ ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
}
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
detect_os() {
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
OS_TYPE="linux"
DEPLOY_DIR="/www/compose/claude-code-hub"
elif [[ "$OSTYPE" == "darwin"* ]]; then
OS_TYPE="macos"
DEPLOY_DIR="$HOME/Applications/claude-code-hub"
else
log_error "Unsupported operating system: $OSTYPE"
exit 1
fi
log_info "Detected OS: $OS_TYPE"
}
select_branch() {
echo ""
echo -e "${BLUE}Please select the branch to deploy:${NC}"
echo -e " ${GREEN}1)${NC} main (Stable release - recommended for production)"
echo -e " ${YELLOW}2)${NC} dev (Latest features - for testing)"
echo ""
local choice
while true; do
read -p "Enter your choice [1]: " choice
choice=${choice:-1}
case $choice in
1)
IMAGE_TAG="latest"
BRANCH_NAME="main"
log_success "Selected branch: main (image tag: latest)"
break
;;
2)
IMAGE_TAG="dev"
BRANCH_NAME="dev"
log_success "Selected branch: dev (image tag: dev)"
break
;;
*)
log_error "Invalid choice. Please enter 1 or 2."
;;
esac
done
}
check_docker() {
log_info "Checking Docker installation..."
if ! command -v docker &> /dev/null; then
log_warning "Docker is not installed"
return 1
fi
if ! docker compose version &> /dev/null && ! docker-compose --version &> /dev/null; then
log_warning "Docker Compose is not installed"
return 1
fi
log_success "Docker and Docker Compose are installed"
docker --version
docker compose version 2>/dev/null || docker-compose --version
return 0
}
install_docker() {
log_info "Installing Docker..."
if [[ "$OS_TYPE" == "linux" ]]; then
if [[ $EUID -ne 0 ]]; then
log_error "Docker installation requires root privileges on Linux"
log_info "Please run: sudo $0"
exit 1
fi
fi
log_info "Downloading Docker installation script from get.docker.com..."
if curl -fsSL https://get.docker.com -o /tmp/get-docker.sh; then
log_info "Running Docker installation script..."
sh /tmp/get-docker.sh
rm /tmp/get-docker.sh
if [[ "$OS_TYPE" == "linux" ]]; then
log_info "Starting Docker service..."
systemctl start docker
systemctl enable docker
if [[ -n "$SUDO_USER" ]]; then
log_info "Adding user $SUDO_USER to docker group..."
usermod -aG docker "$SUDO_USER"
log_warning "Please log out and log back in for group changes to take effect"
fi
fi
log_success "Docker installed successfully"
else
log_error "Failed to download Docker installation script"
exit 1
fi
}
generate_random_suffix() {
SUFFIX=$(tr -dc 'a-z0-9' < /dev/urandom | head -c 4)
log_info "Generated random suffix: $SUFFIX"
}
generate_admin_token() {
if command -v openssl &> /dev/null; then
ADMIN_TOKEN=$(openssl rand -base64 32 | tr -d '/+=' | head -c 32)
else
ADMIN_TOKEN=$(cat /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 32)
fi
log_info "Generated secure admin token"
}
generate_db_password() {
if command -v openssl &> /dev/null; then
DB_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | head -c 24)
else
DB_PASSWORD=$(cat /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 24)
fi
log_info "Generated secure database password"
}
create_deployment_dir() {
log_info "Creating deployment directory: $DEPLOY_DIR"
if [[ "$OS_TYPE" == "linux" ]] && [[ ! -d "/www" ]]; then
if [[ $EUID -ne 0 ]]; then
log_error "Creating /www directory requires root privileges"
log_info "Please run: sudo $0"
exit 1
fi
mkdir -p "$DEPLOY_DIR"
if [[ -n "$SUDO_USER" ]]; then
chown -R "$SUDO_USER:$SUDO_USER" /www
fi
else
mkdir -p "$DEPLOY_DIR"
fi
mkdir -p "$DEPLOY_DIR/data/postgres"
mkdir -p "$DEPLOY_DIR/data/redis"
log_success "Deployment directory created"
}
write_compose_file() {
log_info "Writing docker-compose.yaml..."
cat > "$DEPLOY_DIR/docker-compose.yaml" << EOF
services:
postgres:
image: postgres:18
container_name: claude-code-hub-db-${SUFFIX}
restart: unless-stopped
ports:
- "35432:5432"
env_file:
- ./.env
environment:
POSTGRES_USER: \${DB_USER:-postgres}
POSTGRES_PASSWORD: \${DB_PASSWORD:-postgres}
POSTGRES_DB: \${DB_NAME:-claude_code_hub}
PGDATA: /data/pgdata
TZ: Asia/Shanghai
PGTZ: Asia/Shanghai
volumes:
- ./data/postgres:/data
networks:
- claude-code-hub-net-${SUFFIX}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \${DB_USER:-postgres} -d \${DB_NAME:-claude_code_hub}"]
interval: 5s
timeout: 5s
retries: 10
start_period: 10s
redis:
image: redis:7-alpine
container_name: claude-code-hub-redis-${SUFFIX}
restart: unless-stopped
volumes:
- ./data/redis:/data
command: redis-server --appendonly yes
networks:
- claude-code-hub-net-${SUFFIX}
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
start_period: 5s
app:
image: ghcr.io/ding113/claude-code-hub:${IMAGE_TAG}
container_name: claude-code-hub-app-${SUFFIX}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
env_file:
- ./.env
environment:
NODE_ENV: production
PORT: \${APP_PORT:-23000}
DSN: postgresql://\${DB_USER:-postgres}:\${DB_PASSWORD:-postgres}@claude-code-hub-db-${SUFFIX}:5432/\${DB_NAME:-claude_code_hub}
REDIS_URL: redis://claude-code-hub-redis-${SUFFIX}:6379
AUTO_MIGRATE: \${AUTO_MIGRATE:-true}
ENABLE_RATE_LIMIT: \${ENABLE_RATE_LIMIT:-true}
SESSION_TTL: \${SESSION_TTL:-300}
TZ: Asia/Shanghai
ports:
- "\${APP_PORT:-23000}:\${APP_PORT:-23000}"
restart: unless-stopped
networks:
- claude-code-hub-net-${SUFFIX}
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:\${APP_PORT:-23000}/api/actions/health || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
networks:
claude-code-hub-net-${SUFFIX}:
driver: bridge
name: claude-code-hub-net-${SUFFIX}
EOF
log_success "docker-compose.yaml created"
}
write_env_file() {
log_info "Writing .env file..."
cat > "$DEPLOY_DIR/.env" << EOF
# Admin Token (KEEP THIS SECRET!)
ADMIN_TOKEN=${ADMIN_TOKEN}
# Database Configuration
DB_USER=postgres
DB_PASSWORD=${DB_PASSWORD}
DB_NAME=claude_code_hub
# Application Configuration
APP_PORT=23000
APP_URL=
# Auto Migration (enabled for first-time setup)
AUTO_MIGRATE=true
# Redis Configuration
ENABLE_RATE_LIMIT=true
# Session Configuration
SESSION_TTL=300
STORE_SESSION_MESSAGES=false
# Cookie Security
ENABLE_SECURE_COOKIES=true
# Circuit Breaker Configuration
ENABLE_CIRCUIT_BREAKER_ON_NETWORK_ERRORS=false
# Environment
NODE_ENV=production
TZ=Asia/Shanghai
LOG_LEVEL=info
EOF
log_success ".env file created"
}
start_services() {
log_info "Starting Docker services..."
cd "$DEPLOY_DIR"
if docker compose version &> /dev/null; then
docker compose pull
docker compose up -d
else
docker-compose pull
docker-compose up -d
fi
log_success "Docker services started"
}
wait_for_health() {
log_info "Waiting for services to become healthy (max 60 seconds)..."
cd "$DEPLOY_DIR"
local max_attempts=12
local attempt=0
while [ $attempt -lt $max_attempts ]; do
attempt=$((attempt + 1))
local postgres_health=$(docker inspect --format='{{.State.Health.Status}}' "claude-code-hub-db-${SUFFIX}" 2>/dev/null || echo "unknown")
local redis_health=$(docker inspect --format='{{.State.Health.Status}}' "claude-code-hub-redis-${SUFFIX}" 2>/dev/null || echo "unknown")
local app_health=$(docker inspect --format='{{.State.Health.Status}}' "claude-code-hub-app-${SUFFIX}" 2>/dev/null || echo "unknown")
log_info "Health status - Postgres: $postgres_health, Redis: $redis_health, App: $app_health"
if [[ "$postgres_health" == "healthy" ]] && [[ "$redis_health" == "healthy" ]] && [[ "$app_health" == "healthy" ]]; then
log_success "All services are healthy!"
return 0
fi
if [ $attempt -lt $max_attempts ]; then
sleep 5
fi
done
log_warning "Services did not become healthy within 60 seconds"
log_info "You can check the logs with: cd $DEPLOY_DIR && docker compose logs -f"
return 1
}
get_network_addresses() {
local addresses=()
if [[ "$OS_TYPE" == "linux" ]]; then
if command -v ip &> /dev/null; then
while IFS= read -r line; do
addresses+=("$line")
done < <(ip addr show 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '^127\.' | grep -v '^172\.17\.' | grep -v '^169\.254\.')
elif command -v ifconfig &> /dev/null; then
while IFS= read -r line; do
addresses+=("$line")
done < <(ifconfig 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '^127\.' | grep -v '^172\.17\.' | grep -v '^169\.254\.')
fi
elif [[ "$OS_TYPE" == "macos" ]]; then
while IFS= read -r line; do
addresses+=("$line")
done < <(ifconfig 2>/dev/null | grep 'inet ' | awk '{print $2}' | grep -v '^127\.' | grep -v '^169\.254\.')
fi
addresses+=("localhost")
printf '%s\n' "${addresses[@]}"
}
print_success_message() {
local addresses=($(get_network_addresses))
echo ""
echo -e "${GREEN}╔════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}║ 🎉 Claude Code Hub Deployed Successfully! 🎉 ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}╚════════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "${BLUE}📍 Deployment Directory:${NC}"
echo -e " $DEPLOY_DIR"
echo ""
echo -e "${BLUE}🌐 Access URLs:${NC}"
for addr in "${addresses[@]}"; do
echo -e " ${GREEN}http://${addr}:23000${NC}"
done
echo ""
echo -e "${BLUE}🔑 Admin Token (KEEP THIS SECRET!):${NC}"
echo -e " ${YELLOW}${ADMIN_TOKEN}${NC}"
echo ""
echo -e "${BLUE}📚 Usage Documentation:${NC}"
for addr in "${addresses[@]}"; do
echo -e " Chinese: ${GREEN}http://${addr}:23000/zh-CN/usage-doc${NC}"
echo -e " English: ${GREEN}http://${addr}:23000/en-US/usage-doc${NC}"
break
done
echo ""
echo -e "${BLUE}🔧 Useful Commands:${NC}"
echo -e " View logs: ${YELLOW}cd $DEPLOY_DIR && docker compose logs -f${NC}"
echo -e " Stop services: ${YELLOW}cd $DEPLOY_DIR && docker compose down${NC}"
echo -e " Restart: ${YELLOW}cd $DEPLOY_DIR && docker compose restart${NC}"
echo ""
echo -e "${RED}⚠️ IMPORTANT: Please save the admin token in a secure location!${NC}"
echo ""
}
main() {
print_header
detect_os
if ! check_docker; then
log_warning "Docker is not installed. Attempting to install..."
install_docker
if ! check_docker; then
log_error "Docker installation failed. Please install Docker manually."
exit 1
fi
fi
select_branch
generate_random_suffix
generate_admin_token
generate_db_password
create_deployment_dir
write_compose_file
write_env_file
start_services
if wait_for_health; then
print_success_message
else
log_warning "Deployment completed but some services may not be fully healthy yet"
log_info "Please check the logs: cd $DEPLOY_DIR && docker compose logs -f"
print_success_message
fi
}
main "$@"