Bläddra i källkod

feat: add one-click deployment scripts for Linux/macOS/Windows

- Add deploy.sh for Linux/macOS with auto Docker installation
- Add deploy.ps1 for Windows PowerShell
- Support branch selection (main/dev) during deployment
- Auto-generate secure admin token and database password
- Create unique container names with 4-char random suffix
- Create dedicated Docker network for each deployment
- Wait for health checks and display all access URLs
- Update Chinese and English READMEs with deployment guide
- Point documentation links to /zh-CN/usage-doc and /en-US/usage-doc

Deployment directories:
- Linux: /www/compose/claude-code-hub
- macOS: ~/Applications/claude-code-hub
- Windows: C:\ProgramData\claude-code-hub
ding113 2 månader sedan
förälder
incheckning
23cc94ea91
4 ändrade filer med 1052 tillägg och 0 borttagningar
  1. 54 0
      README.en.md
  2. 54 0
      README.md
  3. 471 0
      scripts/deploy.ps1
  4. 473 0
      scripts/deploy.sh

+ 54 - 0
README.en.md

@@ -53,6 +53,60 @@ Cubence offers special discount coupons for users of CCH: when purchasing with t
 - Docker and Docker Compose (latest version recommended)
 - Optional (for local development): Node.js ≥ 20, pnpm ≥ 9.15
 
+### 🚀 One-Click Deployment Script (✨ Recommended - Fully Automated)
+
+The one-click deployment script **automatically handles** all of the following:
+
+- ✅ Check and install Docker and Docker Compose (Linux/macOS support auto-install)
+- ✅ Create deployment directory and configuration files
+- ✅ Generate secure admin token and database password
+- ✅ Start all services and wait for health checks
+- ✅ Display access URLs and admin token
+
+**Linux / macOS:**
+
+```bash
+# Download and run the deployment script
+curl -fsSL https://raw.githubusercontent.com/ding113/claude-code-hub/main/scripts/deploy.sh -o deploy.sh
+chmod +x deploy.sh
+./deploy.sh
+```
+
+Or using wget:
+
+```bash
+wget https://raw.githubusercontent.com/ding113/claude-code-hub/main/scripts/deploy.sh
+chmod +x deploy.sh
+./deploy.sh
+```
+
+**Windows (PowerShell as Administrator):**
+
+```powershell
+# Download and run the deployment script
+Invoke-WebRequest -Uri "https://raw.githubusercontent.com/ding113/claude-code-hub/main/scripts/deploy.ps1" -OutFile "deploy.ps1"
+Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
+.\deploy.ps1
+```
+
+**Deployment Directories:**
+
+- Linux: `/www/compose/claude-code-hub`
+- macOS: `~/Applications/claude-code-hub`
+- Windows: `C:\ProgramData\claude-code-hub`
+
+**Branch Selection:**
+
+The script will prompt you to select a deployment branch:
+
+- `main` (default): Stable release, recommended for production
+- `dev`: Development version with latest features, for testing
+
+**Important Notes:**
+
+- ⚠️ Please save the **Admin Token** displayed by the script - it's the only credential to access the admin dashboard!
+- ⚠️ Windows users: If Docker Desktop is not installed, the script will automatically open the download page
+
 ### Three-Step Launch (Docker Compose)
 
 1. **Clone and configure**

+ 54 - 0
README.md

@@ -53,6 +53,60 @@ Cubence 为 CCH 的使用用户提供了特别的优惠折扣:在购买时使
 - Docker 与 Docker Compose(推荐使用最新版本)
 - 可选(本地开发):Node.js ≥ 20,pnpm ≥ 9.15
 
+### 🚀 一键部署脚本(✨ 推荐方式,全自动安装)
+
+一键部署脚本会**自动完成**以下所有步骤:
+
+- ✅ 检查并安装 Docker 和 Docker Compose(Linux/macOS 支持自动安装)
+- ✅ 创建部署目录并配置文件
+- ✅ 生成安全的管理员令牌和数据库密码
+- ✅ 启动所有服务并等待健康检查
+- ✅ 显示访问地址和管理员令牌
+
+**Linux / macOS:**
+
+```bash
+# 下载并运行部署脚本
+curl -fsSL https://raw.githubusercontent.com/ding113/claude-code-hub/main/scripts/deploy.sh -o deploy.sh
+chmod +x deploy.sh
+./deploy.sh
+```
+
+或者使用 wget:
+
+```bash
+wget https://raw.githubusercontent.com/ding113/claude-code-hub/main/scripts/deploy.sh
+chmod +x deploy.sh
+./deploy.sh
+```
+
+**Windows (PowerShell 管理员模式):**
+
+```powershell
+# 下载并运行部署脚本
+Invoke-WebRequest -Uri "https://raw.githubusercontent.com/ding113/claude-code-hub/main/scripts/deploy.ps1" -OutFile "deploy.ps1"
+Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
+.\deploy.ps1
+```
+
+**部署目录:**
+
+- Linux: `/www/compose/claude-code-hub`
+- macOS: `~/Applications/claude-code-hub`
+- Windows: `C:\ProgramData\claude-code-hub`
+
+**分支选择:**
+
+脚本会提示选择部署分支:
+
+- `main`(默认):稳定版本,推荐生产环境使用
+- `dev`:开发版本,包含最新功能,用于测试
+
+**重要提示:**
+
+- ⚠️ 请妥善保存脚本输出的**管理员令牌**(Admin Token),这是登录后台的唯一凭证!
+- ⚠️ Windows 用户:如果未安装 Docker Desktop,脚本会自动打开下载页面
+
 ### 三步启动(Docker Compose)
 
 1. **克隆项目并配置环境**

+ 471 - 0
scripts/deploy.ps1

@@ -0,0 +1,471 @@
+# Claude Code Hub - One-Click Deployment Script for Windows
+# PowerShell 5.1+ required
+
+#Requires -Version 5.1
+
+# Script version
+$VERSION = "1.0.0"
+
+# Global variables
+$SUFFIX = ""
+$ADMIN_TOKEN = ""
+$DB_PASSWORD = ""
+$DEPLOY_DIR = "C:\ProgramData\claude-code-hub"
+$IMAGE_TAG = "latest"
+$BRANCH_NAME = "main"
+
+function Write-ColorOutput {
+    param(
+        [string]$Message,
+        [string]$Type = "Info"
+    )
+    
+    switch ($Type) {
+        "Header" { Write-Host $Message -ForegroundColor Cyan }
+        "Info" { Write-Host "[INFO] $Message" -ForegroundColor Blue }
+        "Success" { Write-Host "[SUCCESS] $Message" -ForegroundColor Green }
+        "Warning" { Write-Host "[WARNING] $Message" -ForegroundColor Yellow }
+        "Error" { Write-Host "[ERROR] $Message" -ForegroundColor Red }
+        default { Write-Host $Message }
+    }
+}
+
+function Show-Header {
+    Write-ColorOutput "╔════════════════════════════════════════════════════════════════╗" -Type Header
+    Write-ColorOutput "║                                                                ║" -Type Header
+    Write-ColorOutput "║           Claude Code Hub - One-Click Deployment              ║" -Type Header
+    Write-ColorOutput "║                      Version $VERSION                            ║" -Type Header
+    Write-ColorOutput "║                                                                ║" -Type Header
+    Write-ColorOutput "╚════════════════════════════════════════════════════════════════╝" -Type Header
+    Write-Host ""
+}
+
+function Test-DockerInstalled {
+    Write-ColorOutput "Checking Docker installation..." -Type Info
+    
+    try {
+        $dockerVersion = docker --version 2>$null
+        if ($LASTEXITCODE -ne 0) {
+            Write-ColorOutput "Docker is not installed" -Type Warning
+            return $false
+        }
+        
+        $composeVersion = docker compose version 2>$null
+        if ($LASTEXITCODE -ne 0) {
+            Write-ColorOutput "Docker Compose is not installed" -Type Warning
+            return $false
+        }
+        
+        Write-ColorOutput "Docker and Docker Compose are installed" -Type Success
+        Write-Host $dockerVersion
+        Write-Host $composeVersion
+        return $true
+    }
+    catch {
+        Write-ColorOutput "Docker is not installed" -Type Warning
+        return $false
+    }
+}
+
+function Show-DockerInstallInstructions {
+    Write-ColorOutput "Docker is not installed on this system" -Type Error
+    Write-Host ""
+    Write-ColorOutput "Please install Docker Desktop for Windows:" -Type Info
+    Write-Host "  1. Download from: https://www.docker.com/products/docker-desktop/" -ForegroundColor Cyan
+    Write-Host "  2. Run the installer and follow the instructions"
+    Write-Host "  3. Restart your computer after installation"
+    Write-Host "  4. Run this script again"
+    Write-Host ""
+    Write-ColorOutput "Press any key to open Docker Desktop download page..." -Type Info
+    $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
+    Start-Process "https://www.docker.com/products/docker-desktop/"
+    exit 1
+}
+
+function Select-Branch {
+    Write-Host ""
+    Write-ColorOutput "Please select the branch to deploy:" -Type Info
+    Write-Host "  1) main   (Stable release - recommended for production)" -ForegroundColor Green
+    Write-Host "  2) dev    (Latest features - for testing)" -ForegroundColor Yellow
+    Write-Host ""
+    
+    while ($true) {
+        $choice = Read-Host "Enter your choice [1]"
+        if ([string]::IsNullOrWhiteSpace($choice)) {
+            $choice = "1"
+        }
+        
+        switch ($choice) {
+            "1" {
+                $script:IMAGE_TAG = "latest"
+                $script:BRANCH_NAME = "main"
+                Write-ColorOutput "Selected branch: main (image tag: latest)" -Type Success
+                break
+            }
+            "2" {
+                $script:IMAGE_TAG = "dev"
+                $script:BRANCH_NAME = "dev"
+                Write-ColorOutput "Selected branch: dev (image tag: dev)" -Type Success
+                break
+            }
+            default {
+                Write-ColorOutput "Invalid choice. Please enter 1 or 2." -Type Error
+                continue
+            }
+        }
+        break
+    }
+}
+
+function New-RandomSuffix {
+    $chars = "abcdefghijklmnopqrstuvwxyz0123456789"
+    $script:SUFFIX = -join ((1..4) | ForEach-Object { $chars[(Get-Random -Maximum $chars.Length)] })
+    Write-ColorOutput "Generated random suffix: $SUFFIX" -Type Info
+}
+
+function New-AdminToken {
+    $bytes = New-Object byte[] 24
+    $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create()
+    $rng.GetBytes($bytes)
+    $script:ADMIN_TOKEN = [Convert]::ToBase64String($bytes) -replace '[/+=]', '' | Select-Object -First 32
+    $script:ADMIN_TOKEN = $script:ADMIN_TOKEN.Substring(0, 32)
+    Write-ColorOutput "Generated secure admin token" -Type Info
+}
+
+function New-DbPassword {
+    $bytes = New-Object byte[] 18
+    $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create()
+    $rng.GetBytes($bytes)
+    $script:DB_PASSWORD = [Convert]::ToBase64String($bytes) -replace '[/+=]', '' | Select-Object -First 24
+    $script:DB_PASSWORD = $script:DB_PASSWORD.Substring(0, 24)
+    Write-ColorOutput "Generated secure database password" -Type Info
+}
+
+function New-DeploymentDirectory {
+    Write-ColorOutput "Creating deployment directory: $DEPLOY_DIR" -Type Info
+    
+    try {
+        if (-not (Test-Path $DEPLOY_DIR)) {
+            New-Item -ItemType Directory -Path $DEPLOY_DIR -Force | Out-Null
+        }
+        
+        New-Item -ItemType Directory -Path "$DEPLOY_DIR\data\postgres" -Force | Out-Null
+        New-Item -ItemType Directory -Path "$DEPLOY_DIR\data\redis" -Force | Out-Null
+        
+        Write-ColorOutput "Deployment directory created" -Type Success
+    }
+    catch {
+        Write-ColorOutput "Failed to create deployment directory: $_" -Type Error
+        exit 1
+    }
+}
+
+function Write-ComposeFile {
+    Write-ColorOutput "Writing docker-compose.yaml..." -Type Info
+    
+    $composeContent = @"
+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
+"@
+    
+    try {
+        Set-Content -Path "$DEPLOY_DIR\docker-compose.yaml" -Value $composeContent -Encoding UTF8
+        Write-ColorOutput "docker-compose.yaml created" -Type Success
+    }
+    catch {
+        Write-ColorOutput "Failed to write docker-compose.yaml: $_" -Type Error
+        exit 1
+    }
+}
+
+function Write-EnvFile {
+    Write-ColorOutput "Writing .env file..." -Type Info
+    
+    $envContent = @"
+# 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
+"@
+    
+    try {
+        Set-Content -Path "$DEPLOY_DIR\.env" -Value $envContent -Encoding UTF8
+        Write-ColorOutput ".env file created" -Type Success
+    }
+    catch {
+        Write-ColorOutput "Failed to write .env file: $_" -Type Error
+        exit 1
+    }
+}
+
+function Start-Services {
+    Write-ColorOutput "Starting Docker services..." -Type Info
+    
+    try {
+        Push-Location $DEPLOY_DIR
+        
+        docker compose pull
+        if ($LASTEXITCODE -ne 0) {
+            throw "Failed to pull Docker images"
+        }
+        
+        docker compose up -d
+        if ($LASTEXITCODE -ne 0) {
+            throw "Failed to start services"
+        }
+        
+        Pop-Location
+        Write-ColorOutput "Docker services started" -Type Success
+    }
+    catch {
+        Pop-Location
+        Write-ColorOutput "Failed to start services: $_" -Type Error
+        exit 1
+    }
+}
+
+function Wait-ForHealth {
+    Write-ColorOutput "Waiting for services to become healthy (max 60 seconds)..." -Type Info
+    
+    $maxAttempts = 12
+    $attempt = 0
+    
+    Push-Location $DEPLOY_DIR
+    
+    while ($attempt -lt $maxAttempts) {
+        $attempt++
+        
+        try {
+            $postgresHealth = (docker inspect --format='{{.State.Health.Status}}' "claude-code-hub-db-$SUFFIX" 2>$null)
+            $redisHealth = (docker inspect --format='{{.State.Health.Status}}' "claude-code-hub-redis-$SUFFIX" 2>$null)
+            $appHealth = (docker inspect --format='{{.State.Health.Status}}' "claude-code-hub-app-$SUFFIX" 2>$null)
+            
+            if (-not $postgresHealth) { $postgresHealth = "unknown" }
+            if (-not $redisHealth) { $redisHealth = "unknown" }
+            if (-not $appHealth) { $appHealth = "unknown" }
+            
+            Write-ColorOutput "Health status - Postgres: $postgresHealth, Redis: $redisHealth, App: $appHealth" -Type Info
+            
+            if ($postgresHealth -eq "healthy" -and $redisHealth -eq "healthy" -and $appHealth -eq "healthy") {
+                Pop-Location
+                Write-ColorOutput "All services are healthy!" -Type Success
+                return $true
+            }
+        }
+        catch {
+            # Continue waiting
+        }
+        
+        if ($attempt -lt $maxAttempts) {
+            Start-Sleep -Seconds 5
+        }
+    }
+    
+    Pop-Location
+    Write-ColorOutput "Services did not become healthy within 60 seconds" -Type Warning
+    Write-ColorOutput "You can check the logs with: cd $DEPLOY_DIR; docker compose logs -f" -Type Info
+    return $false
+}
+
+function Get-NetworkAddresses {
+    $addresses = @()
+    
+    try {
+        $adapters = Get-NetIPAddress -AddressFamily IPv4 | 
+            Where-Object { 
+                $_.InterfaceAlias -notlike '*Loopback*' -and 
+                $_.InterfaceAlias -notlike '*Docker*' -and
+                $_.IPAddress -notlike '169.254.*'
+            }
+        
+        foreach ($adapter in $adapters) {
+            $addresses += $adapter.IPAddress
+        }
+    }
+    catch {
+        # Silently continue
+    }
+    
+    $addresses += "localhost"
+    return $addresses
+}
+
+function Show-SuccessMessage {
+    $addresses = Get-NetworkAddresses
+    
+    Write-Host ""
+    Write-Host "╔════════════════════════════════════════════════════════════════╗" -ForegroundColor Green
+    Write-Host "║                                                                ║" -ForegroundColor Green
+    Write-Host "║          🎉 Claude Code Hub Deployed Successfully! 🎉         ║" -ForegroundColor Green
+    Write-Host "║                                                                ║" -ForegroundColor Green
+    Write-Host "╚════════════════════════════════════════════════════════════════╝" -ForegroundColor Green
+    Write-Host ""
+    
+    Write-Host "📍 Deployment Directory:" -ForegroundColor Blue
+    Write-Host "   $DEPLOY_DIR"
+    Write-Host ""
+    
+    Write-Host "🌐 Access URLs:" -ForegroundColor Blue
+    foreach ($addr in $addresses) {
+        Write-Host "   http://${addr}:23000" -ForegroundColor Green
+    }
+    Write-Host ""
+    
+    Write-Host "🔑 Admin Token (KEEP THIS SECRET!):" -ForegroundColor Blue
+    Write-Host "   $ADMIN_TOKEN" -ForegroundColor Yellow
+    Write-Host ""
+    
+    Write-Host "📚 Usage Documentation:" -ForegroundColor Blue
+    $firstAddr = $addresses[0]
+    Write-Host "   Chinese: http://${firstAddr}:23000/zh-CN/usage-doc" -ForegroundColor Green
+    Write-Host "   English: http://${firstAddr}:23000/en-US/usage-doc" -ForegroundColor Green
+    Write-Host ""
+    
+    Write-Host "🔧 Useful Commands:" -ForegroundColor Blue
+    Write-Host "   View logs:     cd $DEPLOY_DIR; docker compose logs -f" -ForegroundColor Yellow
+    Write-Host "   Stop services: cd $DEPLOY_DIR; docker compose down" -ForegroundColor Yellow
+    Write-Host "   Restart:       cd $DEPLOY_DIR; docker compose restart" -ForegroundColor Yellow
+    Write-Host ""
+    
+    Write-Host "⚠️  IMPORTANT: Please save the admin token in a secure location!" -ForegroundColor Red
+    Write-Host ""
+}
+
+function Main {
+    Show-Header
+    
+    if (-not (Test-DockerInstalled)) {
+        Show-DockerInstallInstructions
+        exit 1
+    }
+    
+    Select-Branch
+    
+    New-RandomSuffix
+    New-AdminToken
+    New-DbPassword
+    
+    New-DeploymentDirectory
+    Write-ComposeFile
+    Write-EnvFile
+    
+    Start-Services
+    
+    $isHealthy = Wait-ForHealth
+    
+    if ($isHealthy) {
+        Show-SuccessMessage
+    }
+    else {
+        Write-ColorOutput "Deployment completed but some services may not be fully healthy yet" -Type Warning
+        Write-ColorOutput "Please check the logs: cd $DEPLOY_DIR; docker compose logs -f" -Type Info
+        Show-SuccessMessage
+    }
+}
+
+# Run main function
+Main

+ 473 - 0
scripts/deploy.sh

@@ -0,0 +1,473 @@
+#!/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() {
+    log_info "Detecting 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 | 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 | 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 | 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 "$@"