| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752 |
- # Claude Code Hub - One-Click Deployment Script for Windows
- # PowerShell 5.1+ required
- #Requires -Version 5.1
- [CmdletBinding()]
- param(
- [Alias("b")]
- [ValidateSet("main", "dev", "")]
- [string]$Branch = "",
-
- [Alias("p")]
- [ValidateRange(1, 65535)]
- [int]$Port = 0,
-
- [Alias("t")]
- [string]$AdminToken = "",
-
- [Alias("d")]
- [string]$DeployDir = "",
-
- [string]$Domain = "",
-
- [switch]$EnableCaddy,
-
- [Alias("y")]
- [switch]$Yes,
-
- [Alias("h")]
- [switch]$Help
- )
- # Script version
- $VERSION = "1.1.0"
- # Global variables
- $script:SUFFIX = ""
- $script:ADMIN_TOKEN = ""
- $script:DB_PASSWORD = ""
- $script:DEPLOY_DIR = "C:\ProgramData\claude-code-hub"
- $script:IMAGE_TAG = "latest"
- $script:BRANCH_NAME = "main"
- $script:APP_PORT = "23000"
- $script:ENABLE_CADDY = $false
- $script:DOMAIN_ARG = ""
- function Show-Help {
- $helpText = @"
- Claude Code Hub - One-Click Deployment Script v$VERSION
- Usage: .\deploy.ps1 [OPTIONS]
- Options:
- -Branch, -b <name> Branch to deploy: main (default) or dev
- -Port, -p <port> App external port (default: 23000)
- -AdminToken, -t <token> Custom admin token (default: auto-generated)
- -DeployDir, -d <path> Custom deployment directory
- -Domain <domain> Domain for Caddy HTTPS (enables Caddy automatically)
- -EnableCaddy Enable Caddy reverse proxy without HTTPS (HTTP only)
- -Yes, -y Non-interactive mode (skip prompts, use defaults)
- -Help, -h Show this help message
- Examples:
- .\deploy.ps1 # Interactive deployment
- .\deploy.ps1 -Yes # Non-interactive with defaults
- .\deploy.ps1 -Branch dev -Port 8080 -Yes # Deploy dev branch on port 8080
- .\deploy.ps1 -AdminToken "my-secure-token" -Yes # Use custom admin token
- .\deploy.ps1 -Domain hub.example.com -Yes # Deploy with Caddy HTTPS
- .\deploy.ps1 -EnableCaddy -Yes # Deploy with Caddy HTTP-only
- For more information, visit: https://github.com/ding113/claude-code-hub
- "@
- Write-Host $helpText
- }
- function Initialize-Parameters {
- # Apply CLI parameters
- if ($Branch) {
- if ($Branch -eq "main") {
- $script:IMAGE_TAG = "latest"
- $script:BRANCH_NAME = "main"
- } elseif ($Branch -eq "dev") {
- $script:IMAGE_TAG = "dev"
- $script:BRANCH_NAME = "dev"
- }
- }
-
- if ($Port -gt 0) {
- $script:APP_PORT = $Port.ToString()
- }
-
- if ($AdminToken) {
- if ($AdminToken.Length -lt 16) {
- Write-ColorOutput "Admin token too short: minimum 16 characters required" -Type Error
- exit 1
- }
- $script:ADMIN_TOKEN = $AdminToken
- }
-
- if ($DeployDir) {
- $script:DEPLOY_DIR = $DeployDir
- }
-
- if ($Domain) {
- # Validate domain format
- if ($Domain -notmatch '^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$') {
- Write-ColorOutput "Invalid domain format: $Domain" -Type Error
- exit 1
- }
- $script:DOMAIN_ARG = $Domain
- $script:ENABLE_CADDY = $true
- }
-
- if ($EnableCaddy) {
- $script:ENABLE_CADDY = $true
- }
- }
- 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 {
- # Skip if branch already set via CLI or non-interactive mode
- if ($Branch) {
- Write-ColorOutput "Using branch from CLI argument: $script:BRANCH_NAME" -Type Info
- return
- }
-
- if ($Yes) {
- Write-ColorOutput "Non-interactive mode: using default branch (main)" -Type Info
- return
- }
- 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 {
- # Skip if token already set via CLI
- if ($script:ADMIN_TOKEN) {
- Write-ColorOutput "Using admin token from CLI argument" -Type Info
- return
- }
- # Generate more bytes to ensure we have enough after removing special chars
- $bytes = New-Object byte[] 48
- $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create()
- $rng.GetBytes($bytes)
- $rng.Dispose()
- $token = [Convert]::ToBase64String($bytes) -replace '[/+=]', ''
- $script:ADMIN_TOKEN = $token.Substring(0, [Math]::Min(32, $token.Length))
- Write-ColorOutput "Generated secure admin token" -Type Info
- }
- function New-DbPassword {
- # Generate more bytes to ensure we have enough after removing special chars
- $bytes = New-Object byte[] 36
- $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create()
- $rng.GetBytes($bytes)
- $rng.Dispose()
- $password = [Convert]::ToBase64String($bytes) -replace '[/+=]', ''
- $script:DB_PASSWORD = $password.Substring(0, [Math]::Min(24, $password.Length))
- 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
-
- # Build ports section for app (only if Caddy is not enabled)
- $appPortsSection = ""
- if (-not $script:ENABLE_CADDY) {
- $appPortsSection = @"
- ports:
- - "`${APP_PORT:-$($script:APP_PORT)}:`${APP_PORT:-$($script:APP_PORT)}"
- "@
- }
- $composeContent = @"
- services:
- postgres:
- image: postgres:18
- container_name: claude-code-hub-db-$SUFFIX
- restart: unless-stopped
- ports:
- - "127.0.0.1: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:-$($script:APP_PORT)}
- 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
- $appPortsSection
- restart: unless-stopped
- networks:
- - claude-code-hub-net-$SUFFIX
- healthcheck:
- test: ["CMD-SHELL", "curl -f http://localhost:`${APP_PORT:-$($script:APP_PORT)}/api/actions/health || exit 1"]
- interval: 30s
- timeout: 5s
- retries: 3
- start_period: 30s
- "@
- # Add Caddy service if enabled
- if ($script:ENABLE_CADDY) {
- $composeContent += @"
- caddy:
- image: caddy:2-alpine
- container_name: claude-code-hub-caddy-$SUFFIX
- restart: unless-stopped
- ports:
- - "80:80"
- - "443:443"
- volumes:
- - ./Caddyfile:/etc/caddy/Caddyfile:ro
- - caddy_data:/data
- - caddy_config:/config
- depends_on:
- app:
- condition: service_healthy
- networks:
- - claude-code-hub-net-$SUFFIX
- "@
- }
- $composeContent += @"
- networks:
- claude-code-hub-net-${SUFFIX}:
- driver: bridge
- name: claude-code-hub-net-$SUFFIX
- "@
- # Add Caddy volumes if enabled
- if ($script:ENABLE_CADDY) {
- $composeContent += @"
- volumes:
- caddy_data:
- caddy_config:
- "@
- }
-
- 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-Caddyfile {
- if (-not $script:ENABLE_CADDY) {
- return
- }
- Write-ColorOutput "Writing Caddyfile..." -Type Info
- if ($script:DOMAIN_ARG) {
- # HTTPS mode with domain (Let's Encrypt automatic)
- $caddyContent = @"
- $($script:DOMAIN_ARG) {
- reverse_proxy app:$($script:APP_PORT)
- encode gzip
- }
- "@
- Write-ColorOutput "Caddyfile created (HTTPS mode with domain: $($script:DOMAIN_ARG))" -Type Success
- }
- else {
- # HTTP-only mode
- $caddyContent = @"
- :80 {
- reverse_proxy app:$($script:APP_PORT)
- encode gzip
- }
- "@
- Write-ColorOutput "Caddyfile created (HTTP-only mode)" -Type Success
- }
- try {
- Set-Content -Path "$DEPLOY_DIR\Caddyfile" -Value $caddyContent -Encoding UTF8
- }
- catch {
- Write-ColorOutput "Failed to write Caddyfile: $_" -Type Error
- exit 1
- }
- }
- function Write-EnvFile {
- Write-ColorOutput "Writing .env file..." -Type Info
-
- # Determine secure cookies setting based on Caddy and domain
- $secureCookies = "true"
- if ($script:ENABLE_CADDY -and -not $script:DOMAIN_ARG) {
- # HTTP-only Caddy mode - disable secure cookies
- $secureCookies = "false"
- }
- # If domain is set, APP_URL should use https
- $appUrl = ""
- if ($script:DOMAIN_ARG) {
- $appUrl = "https://$($script:DOMAIN_ARG)"
- }
-
- $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=$($script:APP_PORT)
- APP_URL=$appUrl
- # 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
- STORE_SESSION_RESPONSE_BODY=true
- # Cookie Security
- ENABLE_SECURE_COOKIES=$secureCookies
- # 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
- # W-015: Restrict .env file permissions (equivalent to chmod 600)
- # Remove inheritance and set owner-only access
- $envFile = "$DEPLOY_DIR\.env"
- $acl = Get-Acl $envFile
- $acl.SetAccessRuleProtection($true, $false) # Disable inheritance, don't copy existing rules
- $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
- $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
- $currentUser,
- "FullControl",
- "Allow"
- )
- $acl.SetAccessRule($accessRule)
- Set-Acl -Path $envFile -AclObject $acl
- 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
- if ($script:ENABLE_CADDY) {
- if ($script:DOMAIN_ARG) {
- # HTTPS mode with domain
- Write-Host " https://$($script:DOMAIN_ARG)" -ForegroundColor Green
- }
- else {
- # HTTP-only Caddy mode
- foreach ($addr in $addresses) {
- Write-Host " http://${addr}" -ForegroundColor Green
- }
- }
- }
- else {
- # Direct app access
- foreach ($addr in $addresses) {
- Write-Host " http://${addr}:$($script:APP_PORT)" -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
- if ($script:ENABLE_CADDY -and $script:DOMAIN_ARG) {
- Write-Host " Chinese: https://$($script:DOMAIN_ARG)/zh-CN/usage-doc" -ForegroundColor Green
- Write-Host " English: https://$($script:DOMAIN_ARG)/en-US/usage-doc" -ForegroundColor Green
- }
- else {
- $firstAddr = $addresses[0]
- $portSuffix = ""
- if (-not $script:ENABLE_CADDY) {
- $portSuffix = ":$($script:APP_PORT)"
- }
- Write-Host " Chinese: http://${firstAddr}${portSuffix}/zh-CN/usage-doc" -ForegroundColor Green
- Write-Host " English: http://${firstAddr}${portSuffix}/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
- if ($script:ENABLE_CADDY) {
- Write-Host ""
- Write-Host "Caddy Configuration:" -ForegroundColor Blue
- if ($script:DOMAIN_ARG) {
- Write-Host " Mode: HTTPS with Let's Encrypt (domain: $($script:DOMAIN_ARG))"
- Write-Host " Ports: 80 (HTTP redirect), 443 (HTTPS)"
- }
- else {
- Write-Host " Mode: HTTP-only reverse proxy"
- Write-Host " Port: 80"
- }
- }
- Write-Host ""
- Write-Host "IMPORTANT: Please save the admin token in a secure location!" -ForegroundColor Red
- Write-Host ""
- }
- function Main {
- # Handle help flag first
- if ($Help) {
- Show-Help
- exit 0
- }
- # Initialize parameters from CLI args
- Initialize-Parameters
- Show-Header
-
- if (-not (Test-DockerInstalled)) {
- Show-DockerInstallInstructions
- exit 1
- }
-
- Select-Branch
-
- New-RandomSuffix
- New-AdminToken
- New-DbPassword
-
- New-DeploymentDirectory
- Write-ComposeFile
- Write-Caddyfile
- 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
|