#!/bin/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 echo -e "${GREEN}PicPeak Deployment Script${NC}" echo "========================================" # Default values STACK_NAME="picpeak" ENV_FILE="../../.env.production" REGISTRY_URL="${REGISTRY_URL:-}" VERSION="${VERSION:-latest}" # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in --env) ENV_FILE="$2" shift 2 ;; --registry) REGISTRY_URL="$2" shift 2 ;; --version) VERSION="$2" shift 2 ;; --stack-name) STACK_NAME="$2" shift 2 ;; --help) echo "Usage: $0 [options]" echo "Options:" echo " --env FILE Path to environment file (default: ../../.env.production)" echo " --registry URL Docker registry URL" echo " --version VERSION Image version to deploy (default: latest)" echo " --stack-name NAME Stack name (default: picpeak)" exit 0 ;; *) echo -e "${RED}Unknown option: $1${NC}" exit 1 ;; esac done # Check if Docker is in swarm mode if ! docker info | grep -q "Swarm: active"; then echo -e "${RED}Docker is not in swarm mode. Run init-swarm.sh first.${NC}" exit 1 fi # Check if environment file exists if [ ! -f "$ENV_FILE" ]; then echo -e "${RED}Environment file not found: $ENV_FILE${NC}" echo "Please create it from .env.production.example" exit 1 fi # Load environment variables echo -e "${GREEN}Loading environment variables...${NC}" set -a source "$ENV_FILE" set +a # Export deployment variables export REGISTRY_URL export VERSION # Validate required environment variables required_vars=( "FRONTEND_HOST" "BACKEND_HOST" "ADMIN_URL" "FRONTEND_URL" "ACME_EMAIL" "DB_NAME" "EMAIL_FROM" ) echo -e "${GREEN}Validating configuration...${NC}" for var in "${required_vars[@]}"; do if [ -z "${!var}" ]; then echo -e "${RED}Missing required environment variable: $var${NC}" exit 1 fi done # Check if Traefik is running if ! docker service ls | grep -q "traefik_traefik"; then echo -e "${YELLOW}Traefik is not running. Deploy it first with:${NC}" echo "cd ../traefik && docker stack deploy -c docker-compose.traefik.yml traefik" exit 1 fi # Check if secrets exist echo -e "${GREEN}Checking Docker secrets...${NC}" required_secrets=( "jwt_secret" "db_user" "db_password" "smtp_user" "smtp_pass" ) for secret in "${required_secrets[@]}"; do if ! docker secret ls | grep -q $secret; then echo -e "${RED}Missing required secret: $secret${NC}" echo "Run create-secrets.sh first" exit 1 fi done # Pull latest images if registry is specified if [ ! -z "$REGISTRY_URL" ]; then echo -e "${GREEN}Pulling latest images...${NC}" docker pull ${REGISTRY_URL}/photo-sharing-backend:${VERSION} || true docker pull ${REGISTRY_URL}/photo-sharing-frontend:${VERSION} || true fi # Deploy the stack echo -e "${GREEN}Deploying stack: $STACK_NAME${NC}" echo -e "${BLUE}Version: $VERSION${NC}" echo -e "${BLUE}Registry: ${REGISTRY_URL:-local}${NC}" cd .. docker stack deploy \ -c docker-stack.yml \ --with-registry-auth \ $STACK_NAME # Wait for services to start echo -e "${GREEN}Waiting for services to start...${NC}" sleep 10 # Check service status echo -e "${GREEN}Service status:${NC}" docker service ls --filter "label=com.docker.stack.namespace=$STACK_NAME" # Wait for database to be ready echo -e "${GREEN}Waiting for database to be ready...${NC}" max_attempts=30 attempt=1 while [ $attempt -le $max_attempts ]; do if docker exec $(docker ps -q -f name=${STACK_NAME}_db) pg_isready -U postgres > /dev/null 2>&1; then echo -e "${GREEN}Database is ready!${NC}" break fi echo -n "." sleep 2 attempt=$((attempt + 1)) done if [ $attempt -gt $max_attempts ]; then echo -e "${RED}Database failed to start in time${NC}" exit 1 fi # Run database migrations echo -e "${GREEN}Running database migrations...${NC}" sleep 5 docker exec $(docker ps -q -f name=${STACK_NAME}_backend -f status=running | head -1) npm run migrate || { echo -e "${YELLOW}Migration failed. This might be normal if migrations already ran.${NC}" } # Show deployment information echo "" echo -e "${GREEN}Deployment complete!${NC}" echo "" echo -e "${BLUE}Access URLs:${NC}" echo "Frontend: https://${FRONTEND_HOST}" echo "Backend API: https://${BACKEND_HOST}/api" if [ ! -z "$UMAMI_HOST" ]; then echo "Analytics: https://${UMAMI_HOST}" fi if [ ! -z "$TRAEFIK_HOST" ]; then echo "Traefik Dashboard: https://${TRAEFIK_HOST}/dashboard/" fi echo "" echo -e "${BLUE}Useful commands:${NC}" echo "View logs: docker service logs ${STACK_NAME}_backend" echo "Scale service: docker service scale ${STACK_NAME}_backend=5" echo "Update service: docker service update ${STACK_NAME}_backend" echo "Remove stack: docker stack rm $STACK_NAME" echo "" echo -e "${GREEN}Health check:${NC}" curl -s -o /dev/null -w "Frontend: %{http_code}\n" https://${FRONTEND_HOST}/health || true curl -s -o /dev/null -w "Backend: %{http_code}\n" https://${BACKEND_HOST}/api/health || true