Add Docker Compose configuration and environment files for local and production setups
- Created docker-compose.docker-local.yml for local testing of frontend and backend services. - Added .env.development for development environment configuration. - Introduced .env.docker-local for local Docker environment settings. - Added .env.production for production environment configuration for Synology deployment.
This commit is contained in:
@@ -10,18 +10,21 @@
|
||||
# ./deploy.sh [options]
|
||||
#
|
||||
# Options:
|
||||
# --target <internal|external|local> Deployment target (default: internal)
|
||||
# --port <number> Frontend HTTP port (default: 8080)
|
||||
# --ssl-port <number> Frontend HTTPS port (optional)
|
||||
# --backend-port <number> Backend port (default: 3000)
|
||||
# --skip-tests Skip test execution
|
||||
# --skip-build Skip build step (use existing dist/)
|
||||
# --no-backup Skip backup creation
|
||||
# --dry-run Show what would be deployed without deploying
|
||||
# --target <local|production> Deployment target (default: local)
|
||||
# --port <number> Frontend HTTP port (default: 8099)
|
||||
# --backend-port <number> Backend port (default: 3099)
|
||||
# --skip-tests Skip test execution
|
||||
# --skip-build Skip build step (use existing dist/)
|
||||
# --no-backup Skip backup creation
|
||||
# --dry-run Show what would be deployed without deploying
|
||||
#
|
||||
# Deployment Strategies:
|
||||
# local - Docker deployment on localhost:8099 for production testing
|
||||
# production - Deploy to Synology at https://app.pokedex.online
|
||||
#
|
||||
# Examples:
|
||||
# ./deploy.sh --target internal
|
||||
# ./deploy.sh --target external --port 8080 --backend-port 3000
|
||||
# ./deploy.sh --target local
|
||||
# ./deploy.sh --target production
|
||||
# ./deploy.sh --dry-run
|
||||
###############################################################################
|
||||
|
||||
@@ -35,9 +38,8 @@ BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Default configuration
|
||||
TARGET="internal"
|
||||
TARGET="local"
|
||||
PORT=8099
|
||||
SSL_PORT=""
|
||||
BACKEND_PORT=3099
|
||||
SKIP_TESTS=false
|
||||
SKIP_BUILD=false
|
||||
@@ -89,10 +91,6 @@ parse_args() {
|
||||
PORT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ssl-port)
|
||||
SSL_PORT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--backend-port)
|
||||
BACKEND_PORT="$2"
|
||||
shift 2
|
||||
@@ -166,21 +164,32 @@ check_prerequisites() {
|
||||
check_environment() {
|
||||
log_step "🔧 Checking Environment Configuration"
|
||||
|
||||
# Check if .env exists
|
||||
if [ ! -f "$PROJECT_ROOT/server/.env" ]; then
|
||||
log_warning ".env file not found in server/"
|
||||
log_info "Copy server/.env.example to server/.env and configure it"
|
||||
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
read -p "Continue anyway? (y/N) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_success "Environment configuration found"
|
||||
# Validate target
|
||||
if [ "$TARGET" != "local" ] && [ "$TARGET" != "production" ]; then
|
||||
log_error "Invalid target: $TARGET"
|
||||
log_info "Valid targets: local, production"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for mode-specific env files
|
||||
local mode
|
||||
if [ "$TARGET" = "local" ]; then
|
||||
mode="docker-local"
|
||||
else
|
||||
mode="production"
|
||||
fi
|
||||
|
||||
if [ ! -f "$PROJECT_ROOT/.env.$mode" ]; then
|
||||
log_error "Environment file .env.$mode not found"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Frontend environment configuration found (.env.$mode)"
|
||||
|
||||
if [ ! -f "$PROJECT_ROOT/server/.env.$mode" ]; then
|
||||
log_error "Environment file server/.env.$mode not found"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Backend environment configuration found (server/.env.$mode)"
|
||||
}
|
||||
|
||||
run_tests() {
|
||||
@@ -209,39 +218,17 @@ run_tests() {
|
||||
# Build
|
||||
###############################################################################
|
||||
|
||||
prepare_env_for_build() {
|
||||
# Create/update .env.local with correct Discord redirect URI for the target
|
||||
local env_file="$PROJECT_ROOT/.env.local"
|
||||
local redirect_uri
|
||||
prepare_build_mode() {
|
||||
# Determine Vite build mode based on deployment target
|
||||
if [ "$TARGET" = "local" ]; then
|
||||
BUILD_MODE="docker-local"
|
||||
else
|
||||
BUILD_MODE="production"
|
||||
fi
|
||||
|
||||
# Determine correct redirect URI based on target and ports
|
||||
case "$TARGET" in
|
||||
local)
|
||||
# For Docker, use the frontend port
|
||||
redirect_uri="http://localhost:${PORT}/oauth/callback"
|
||||
;;
|
||||
internal|external)
|
||||
# For dev server, use default Vite port
|
||||
redirect_uri="http://localhost:5173/oauth/callback"
|
||||
;;
|
||||
*)
|
||||
redirect_uri="http://localhost:5173/oauth/callback"
|
||||
;;
|
||||
esac
|
||||
|
||||
log_info "Preparing environment for ${TARGET} deployment..."
|
||||
log_info "Discord redirect URI: ${redirect_uri}"
|
||||
|
||||
# Create/update .env.local with Discord credentials
|
||||
cat > "$env_file" << EOF
|
||||
# Generated by deploy.sh for ${TARGET} deployment
|
||||
# These variables are embedded into the frontend bundle at build time
|
||||
|
||||
VITE_DISCORD_CLIENT_ID=1466544972059775223
|
||||
VITE_DISCORD_REDIRECT_URI=${redirect_uri}
|
||||
EOF
|
||||
|
||||
log_info ".env.local created with correct redirect URI"
|
||||
log_info "Preparing build for ${TARGET} deployment..."
|
||||
log_info "Vite build mode: ${BUILD_MODE}"
|
||||
log_info "Environment file: .env.${BUILD_MODE}"
|
||||
}
|
||||
|
||||
build_application() {
|
||||
@@ -259,18 +246,18 @@ build_application() {
|
||||
|
||||
log_step "🔨 Building Application"
|
||||
|
||||
# Prepare environment before building
|
||||
prepare_env_for_build
|
||||
# Prepare build mode
|
||||
prepare_build_mode
|
||||
|
||||
log_info "Building frontend..."
|
||||
npm run build:frontend || {
|
||||
log_info "Building frontend with mode: $BUILD_MODE..."
|
||||
npx vite build --mode "$BUILD_MODE" || {
|
||||
log_error "Frontend build failed"
|
||||
exit 1
|
||||
}
|
||||
log_success "Frontend built successfully"
|
||||
|
||||
log_info "Verifying build..."
|
||||
npm run build:verify || {
|
||||
BUILD_TARGET="$TARGET" npm run build:verify || {
|
||||
log_error "Build verification failed"
|
||||
exit 1
|
||||
}
|
||||
@@ -327,26 +314,162 @@ deploy_to_server() {
|
||||
log_info "DRY RUN - Would deploy with following configuration:"
|
||||
log_info " Target: $TARGET"
|
||||
log_info " Frontend Port: $PORT"
|
||||
[ -n "$SSL_PORT" ] && log_info " SSL Port: $SSL_PORT"
|
||||
log_info " Backend Port: $BACKEND_PORT"
|
||||
log_success "Dry run completed"
|
||||
return
|
||||
fi
|
||||
|
||||
# Build deployment command
|
||||
DEPLOY_CMD="node ../../utils/deploy-pokedex.js --target $TARGET --port $PORT --backend-port $BACKEND_PORT"
|
||||
[ -n "$SSL_PORT" ] && DEPLOY_CMD="$DEPLOY_CMD --ssl-port $SSL_PORT"
|
||||
if [ "$TARGET" = "local" ]; then
|
||||
log_info "Deploying to local Docker..."
|
||||
|
||||
# Copy server env file
|
||||
cp "$PROJECT_ROOT/server/.env.docker-local" "$PROJECT_ROOT/server/.env" || {
|
||||
log_error "Failed to copy server environment file"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Use docker-compose.docker-local.yml
|
||||
log_info "Starting Docker containers..."
|
||||
docker compose -f docker-compose.docker-local.yml down || true
|
||||
docker compose -f docker-compose.docker-local.yml up -d --build || {
|
||||
log_error "Docker deployment failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
else
|
||||
log_info "Deploying to production (Synology)..."
|
||||
deploy_to_synology
|
||||
fi
|
||||
|
||||
log_info "Executing deployment..."
|
||||
log_info "Command: $DEPLOY_CMD"
|
||||
log_success "Deployment completed successfully"
|
||||
}
|
||||
|
||||
deploy_to_synology() {
|
||||
# SSH Configuration
|
||||
local SSH_HOST="10.0.0.81"
|
||||
local SSH_PORT="2323"
|
||||
local SSH_USER="GregRJacobs"
|
||||
local SSH_KEY="$HOME/.ssh/ds3627xs_gregrjacobs"
|
||||
local REMOTE_PATH="/volume1/docker/pokedex-online"
|
||||
|
||||
eval "$DEPLOY_CMD" || {
|
||||
log_error "Deployment failed"
|
||||
log_info "Check logs above for details"
|
||||
log_info "SSH Config: ${SSH_USER}@${SSH_HOST}:${SSH_PORT}"
|
||||
|
||||
# Verify SSH key exists
|
||||
if [ ! -f "$SSH_KEY" ]; then
|
||||
log_error "SSH key not found: $SSH_KEY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test SSH connection
|
||||
log_info "Testing SSH connection..."
|
||||
if ! ssh -i "$SSH_KEY" -p "$SSH_PORT" -o ConnectTimeout=10 -o BatchMode=yes "$SSH_USER@$SSH_HOST" "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||
log_error "SSH connection failed"
|
||||
log_info "Please verify:"
|
||||
log_info " 1. SSH key exists: $SSH_KEY"
|
||||
log_info " 2. Key has correct permissions: chmod 600 $SSH_KEY"
|
||||
log_info " 3. Public key is in authorized_keys on server"
|
||||
log_info " 4. You can connect manually: ssh -i $SSH_KEY -p $SSH_PORT $SSH_USER@$SSH_HOST"
|
||||
exit 1
|
||||
fi
|
||||
log_success "SSH connection verified"
|
||||
|
||||
# Create remote directory
|
||||
log_info "Creating remote directory..."
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" "mkdir -p $REMOTE_PATH/dist $REMOTE_PATH/server" || {
|
||||
log_error "Failed to create remote directory"
|
||||
exit 1
|
||||
}
|
||||
|
||||
log_success "Deployment completed successfully"
|
||||
# Transfer dist directory using tar over SSH
|
||||
log_info "Transferring frontend build (dist/)..."
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" "rm -rf $REMOTE_PATH/dist && mkdir -p $REMOTE_PATH/dist"
|
||||
tar -czf - -C "$PROJECT_ROOT" dist 2>/dev/null | \
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"tar -xzf - -C $REMOTE_PATH --strip-components=0 2>/dev/null" || {
|
||||
log_error "Failed to transfer dist directory"
|
||||
exit 1
|
||||
}
|
||||
log_success "Frontend build transferred"
|
||||
|
||||
# Transfer server directory (excluding node_modules) using tar over SSH
|
||||
log_info "Transferring backend code (server/)..."
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" "rm -rf $REMOTE_PATH/server && mkdir -p $REMOTE_PATH/server"
|
||||
tar -czf - -C "$PROJECT_ROOT" \
|
||||
--exclude='node_modules' \
|
||||
--exclude='.env*' \
|
||||
--exclude='data' \
|
||||
--exclude='logs' \
|
||||
server 2>/dev/null | \
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"tar -xzf - -C $REMOTE_PATH --strip-components=0 2>/dev/null" || {
|
||||
log_error "Failed to transfer server directory"
|
||||
exit 1
|
||||
}
|
||||
log_success "Backend code transferred"
|
||||
|
||||
# Copy production environment file to server (after server code transfer)
|
||||
log_info "Copying server environment file..."
|
||||
cat "$PROJECT_ROOT/server/.env.production" | \
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"cat > $REMOTE_PATH/server/.env" || {
|
||||
log_error "Failed to copy server .env file"
|
||||
exit 1
|
||||
}
|
||||
log_success "Server environment configured"
|
||||
|
||||
# Create required directories for volume mounts
|
||||
log_info "Creating volume mount directories..."
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"mkdir -p $REMOTE_PATH/server/data $REMOTE_PATH/server/logs" || {
|
||||
log_error "Failed to create volume mount directories"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Transfer Docker configuration files
|
||||
log_info "Transferring Docker configuration..."
|
||||
for file in docker-compose.production.yml nginx.conf Dockerfile.frontend; do
|
||||
cat "$PROJECT_ROOT/$file" | \
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"cat > $REMOTE_PATH/$file" || {
|
||||
log_error "Failed to transfer $file"
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
cat "$PROJECT_ROOT/server/Dockerfile" | \
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"cat > $REMOTE_PATH/server/Dockerfile" || {
|
||||
log_error "Failed to transfer server Dockerfile"
|
||||
exit 1
|
||||
}
|
||||
log_success "Docker configuration transferred"
|
||||
|
||||
# Find Docker on remote system
|
||||
log_info "Locating Docker on remote system..."
|
||||
DOCKER_PATH=$(ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"which docker 2>/dev/null || echo /usr/local/bin/docker")
|
||||
log_info "Using Docker at: $DOCKER_PATH"
|
||||
|
||||
# Stop and remove existing containers (force cleanup)
|
||||
log_info "Stopping and removing existing containers..."
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"cd $REMOTE_PATH && $DOCKER_PATH compose -f docker-compose.production.yml down --remove-orphans 2>/dev/null || true"
|
||||
|
||||
# Force remove any lingering containers with matching names
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"$DOCKER_PATH rm -f pokedex-frontend pokedex-backend 2>/dev/null || true"
|
||||
|
||||
# Start containers
|
||||
log_info "Starting Docker containers..."
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"cd $REMOTE_PATH && $DOCKER_PATH compose -f docker-compose.production.yml up -d --build" || {
|
||||
log_error "Failed to start Docker containers"
|
||||
log_info "Rolling back..."
|
||||
ssh -i "$SSH_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" \
|
||||
"cd $REMOTE_PATH && $DOCKER_PATH compose -f docker-compose.production.yml down --remove-orphans"
|
||||
exit 1
|
||||
}
|
||||
log_success "Containers started"
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
@@ -362,10 +485,8 @@ verify_deployment() {
|
||||
fi
|
||||
|
||||
# Determine host based on target
|
||||
if [ "$TARGET" = "internal" ]; then
|
||||
if [ "$TARGET" = "production" ]; then
|
||||
HOST="10.0.0.81"
|
||||
elif [ "$TARGET" = "external" ]; then
|
||||
HOST="home.gregrjacobs.com"
|
||||
else
|
||||
HOST="localhost"
|
||||
fi
|
||||
@@ -395,32 +516,32 @@ print_summary() {
|
||||
return
|
||||
fi
|
||||
|
||||
# Determine host based on target
|
||||
if [ "$TARGET" = "internal" ]; then
|
||||
HOST="10.0.0.81"
|
||||
elif [ "$TARGET" = "external" ]; then
|
||||
HOST="home.gregrjacobs.com"
|
||||
# Determine URLs based on target
|
||||
if [ "$TARGET" = "production" ]; then
|
||||
FRONTEND_URL="https://app.pokedex.online"
|
||||
BACKEND_URL="http://10.0.0.81:$BACKEND_PORT"
|
||||
else
|
||||
HOST="localhost"
|
||||
FRONTEND_URL="http://localhost:$PORT"
|
||||
BACKEND_URL="http://localhost:$BACKEND_PORT"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${GREEN} 🎉 Deployment Successful!${NC}"
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
echo -e " ${BLUE}Frontend:${NC} http://$HOST:$PORT"
|
||||
[ -n "$SSL_PORT" ] && echo -e " ${BLUE}HTTPS:${NC} https://$HOST:$SSL_PORT"
|
||||
echo -e " ${BLUE}Backend:${NC} http://$HOST:$BACKEND_PORT"
|
||||
echo -e " ${BLUE}Frontend:${NC} $FRONTEND_URL"
|
||||
echo -e " ${BLUE}Backend:${NC} $BACKEND_URL"
|
||||
echo -e " ${BLUE}Target:${NC} $TARGET"
|
||||
echo ""
|
||||
echo -e " ${YELLOW}Next Steps:${NC}"
|
||||
echo -e " • Test the application manually"
|
||||
if [ "$TARGET" = "local" ]; then
|
||||
echo -e " • Check logs: docker compose -f docker-compose.tmp.yml logs -f"
|
||||
echo -e " • Check logs: docker compose -f docker-compose.local.yml logs -f"
|
||||
echo -e " • Stop containers: docker compose -f docker-compose.local.yml down"
|
||||
else
|
||||
echo -e " • Check logs: npm run docker:logs"
|
||||
echo -e " • Check logs via SSH or Docker commands on Synology"
|
||||
fi
|
||||
echo -e " • Monitor backend: curl http://$HOST:$BACKEND_PORT/health"
|
||||
echo -e " • Monitor backend: curl $BACKEND_URL/health"
|
||||
echo ""
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user