feat: Add Docker containerization for consistent deployment

- Multi-stage Dockerfile with Node.js 20 Alpine base
- Production and development docker-compose configurations
- Health check API endpoint for container monitoring
- Build and deployment scripts with versioning support
- Port 3000 configuration for nginx compatibility
- Non-root user and security hardening
- Resource limits and logging configuration
- Package.json scripts for Docker operations

This eliminates dependency conflicts and provides reproducible deployments.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-12 18:47:36 -06:00
parent 086aa9de6d
commit 2e575f894e
8 changed files with 523 additions and 1 deletions

178
scripts/docker-deploy.sh Executable file
View File

@@ -0,0 +1,178 @@
#!/bin/bash
# Docker deployment script for Black Canyon Tickets
# Usage: ./scripts/docker-deploy.sh [environment]
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
# Configuration
ENVIRONMENT="${1:-production}"
IMAGE_NAME="bct-whitelabel"
VERSION="${2:-latest}"
COMPOSE_FILE="docker-compose.prod.yml"
echo -e "${GREEN}🚀 Deploying Black Canyon Tickets${NC}"
echo -e "${YELLOW}Environment: ${ENVIRONMENT}${NC}"
echo -e "${YELLOW}Version: ${VERSION}${NC}"
# Function to check if required environment variables are set
check_env_vars() {
local required_vars=(
"PUBLIC_SUPABASE_URL"
"PUBLIC_SUPABASE_ANON_KEY"
"SUPABASE_SERVICE_ROLE_KEY"
"STRIPE_PUBLISHABLE_KEY"
"STRIPE_SECRET_KEY"
"STRIPE_WEBHOOK_SECRET"
"RESEND_API_KEY"
)
echo -e "${BLUE}🔍 Checking environment variables...${NC}"
local missing_vars=()
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
missing_vars+=("$var")
fi
done
if [ ${#missing_vars[@]} -ne 0 ]; then
echo -e "${RED}❌ Missing required environment variables:${NC}"
for var in "${missing_vars[@]}"; do
echo -e " - $var"
done
echo -e "${YELLOW}💡 Please set these variables in your .env file or environment${NC}"
exit 1
fi
echo -e "${GREEN}✅ All required environment variables are set${NC}"
}
# Function to check if .env file exists
check_env_file() {
if [ ! -f ".env" ]; then
echo -e "${YELLOW}⚠️ No .env file found. Creating from environment variables...${NC}"
# You might want to create a template .env file here
echo -e "${YELLOW}💡 Please ensure all required environment variables are available${NC}"
else
echo -e "${GREEN}✅ .env file found${NC}"
fi
}
# Function to pull latest image
pull_image() {
echo -e "${BLUE}📥 Pulling latest image...${NC}"
if ! docker pull "${IMAGE_NAME}:${VERSION}" 2>/dev/null; then
echo -e "${YELLOW}⚠️ Could not pull image from registry. Using local image.${NC}"
fi
}
# Function to stop existing containers
stop_containers() {
echo -e "${BLUE}🛑 Stopping existing containers...${NC}"
docker-compose -f "$COMPOSE_FILE" down --remove-orphans || true
}
# Function to start containers
start_containers() {
echo -e "${BLUE}🔄 Starting containers...${NC}"
# Set the image version
export DOCKER_IMAGE_TAG="$VERSION"
# Start services
docker-compose -f "$COMPOSE_FILE" up -d
echo -e "${GREEN}✅ Containers started!${NC}"
}
# Function to wait for health check
wait_for_health() {
echo -e "${BLUE}🏥 Waiting for application to be healthy...${NC}"
local max_attempts=30
local attempt=1
while [ $attempt -le $max_attempts ]; do
if docker-compose -f "$COMPOSE_FILE" ps --services --filter "status=running" | grep -q "bct-app"; then
if docker-compose -f "$COMPOSE_FILE" exec -T bct-app node -e "const http=require('http');const options={hostname:'localhost',port:4321,path:'/api/health',timeout:2000};const req=http.request(options,(res)=>{process.exit(res.statusCode===200?0:1)});req.on('error',()=>{process.exit(1)});req.end();" 2>/dev/null; then
echo -e "${GREEN}✅ Application is healthy!${NC}"
return 0
fi
fi
echo -e "${YELLOW}⏳ Waiting... (attempt $attempt/$max_attempts)${NC}"
sleep 10
((attempt++))
done
echo -e "${RED}❌ Application failed to become healthy${NC}"
return 1
}
# Function to show deployment status
show_status() {
echo -e "${GREEN}📊 Deployment Status:${NC}"
docker-compose -f "$COMPOSE_FILE" ps
echo -e "\n${GREEN}📋 Container Logs (last 20 lines):${NC}"
docker-compose -f "$COMPOSE_FILE" logs --tail=20 bct-app
echo -e "\n${GREEN}🌐 Application should be available at:${NC}"
echo -e "${BLUE} http://localhost:3000${NC}"
}
# Function to cleanup old images
cleanup() {
echo -e "${BLUE}🧹 Cleaning up old images...${NC}"
docker image prune -f || true
echo -e "${GREEN}✅ Cleanup complete${NC}"
}
# Main deployment process
main() {
echo -e "${GREEN}Starting deployment process...${NC}"
# Pre-deployment checks
check_env_file
check_env_vars
# Deployment steps
pull_image
stop_containers
start_containers
# Post-deployment verification
if wait_for_health; then
show_status
cleanup
echo -e "${GREEN}🎉 Deployment completed successfully!${NC}"
else
echo -e "${RED}💥 Deployment failed!${NC}"
echo -e "${YELLOW}📋 Check logs with: docker-compose -f $COMPOSE_FILE logs${NC}"
exit 1
fi
}
# Handle script arguments
case "${1:-}" in
--help|-h)
echo "Usage: $0 [environment] [version]"
echo " environment: production (default)"
echo " version: Docker image tag (default: latest)"
echo ""
echo "Examples:"
echo " $0 # Deploy latest to production"
echo " $0 production v1.2.3 # Deploy specific version"
exit 0
;;
*)
main
;;
esac