- Add Go modules with required dependencies (Gin, UUID, JWT, etc.) - Implement main web server with landing page endpoint - Add comprehensive API endpoints for health and status - Include proper error handling and request validation - Set up CORS middleware and security headers
624 lines
14 KiB
Bash
Executable File
624 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# YourDreamNameHere Production Deployment Script
|
|
# This script deploys the YDN application to a fresh Ubuntu 24.04 server
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
DEPLOYMENT_USER="${DEPLOYMENT_USER:-root}"
|
|
DEPLOYMENT_HOST="${DEPLOYMENT_HOST:-}"
|
|
DOMAIN="${DOMAIN:-yourdreamnamehere.com}"
|
|
DOCKER_REGISTRY="${DOCKER_REGISTRY:-}"
|
|
VERSION="${VERSION:-latest}"
|
|
ENVIRONMENT="${ENVIRONMENT:-production}"
|
|
ENABLE_MONITORING="${ENABLE_MONITORING:-true}"
|
|
ENABLE_LOGGING="${ENABLE_LOGGING:-true}"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Functions
|
|
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"
|
|
}
|
|
|
|
# Validate environment
|
|
validate_environment() {
|
|
log_info "Validating deployment environment..."
|
|
|
|
if [ -z "$DEPLOYMENT_HOST" ]; then
|
|
log_error "DEPLOYMENT_HOST environment variable is required"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$DOMAIN" ]; then
|
|
log_error "DOMAIN environment variable is required"
|
|
exit 1
|
|
fi
|
|
|
|
# Test SSH connection
|
|
if ! ssh -o ConnectTimeout=10 -o BatchMode=yes "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" "echo 'SSH connection successful'"; then
|
|
log_error "Failed to connect to $DEPLOYMENT_HOST via SSH"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "Environment validation passed"
|
|
}
|
|
|
|
# Prepare remote server
|
|
prepare_server() {
|
|
log_info "Preparing remote server..."
|
|
|
|
ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << 'EOF'
|
|
set -euo pipefail
|
|
|
|
# Update system
|
|
apt-get update
|
|
apt-get upgrade -y
|
|
|
|
# Install required packages
|
|
apt-get install -y \
|
|
curl \
|
|
wget \
|
|
git \
|
|
htop \
|
|
unzip \
|
|
software-properties-common \
|
|
apt-transport-https \
|
|
ca-certificates \
|
|
gnupg \
|
|
lsb-release \
|
|
ufw \
|
|
fail2ban \
|
|
logrotate \
|
|
certbot \
|
|
python3-certbot-nginx
|
|
|
|
# Install Docker
|
|
if ! command -v docker &> /dev/null; then
|
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
apt-get update
|
|
apt-get install -y docker-ce docker-ce-cli containerd.io
|
|
systemctl enable docker
|
|
systemctl start docker
|
|
fi
|
|
|
|
# Install Docker Compose
|
|
if ! command -v docker-compose &> /dev/null; then
|
|
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
chmod +x /usr/local/bin/docker-compose
|
|
fi
|
|
|
|
# Create deployment directory
|
|
mkdir -p /opt/ydn
|
|
cd /opt/ydn
|
|
|
|
# Create application user
|
|
if ! id "ydn" &>/dev/null; then
|
|
useradd -m -s /bin/bash ydn
|
|
usermod -aG docker ydn
|
|
fi
|
|
|
|
# Set up directory structure
|
|
mkdir -p {configs,scripts,logs,ssl,backups,data}
|
|
chown -R ydn:ydn /opt/ydn
|
|
|
|
# Configure firewall
|
|
ufw --force reset
|
|
ufw default deny incoming
|
|
ufw default allow outgoing
|
|
ufw allow ssh
|
|
ufw allow 80/tcp
|
|
ufw allow 443/tcp
|
|
ufw --force enable
|
|
|
|
# Configure fail2ban
|
|
cat > /etc/fail2ban/jail.local << 'FAIL2BAN'
|
|
[DEFAULT]
|
|
bantime = 3600
|
|
findtime = 600
|
|
maxretry = 3
|
|
|
|
[sshd]
|
|
enabled = true
|
|
port = ssh
|
|
logpath = /var/log/auth.log
|
|
|
|
[nginx-http-auth]
|
|
enabled = true
|
|
port = http,https
|
|
logpath = /var/log/nginx/error.log
|
|
FAIL2BAN
|
|
|
|
systemctl enable fail2ban
|
|
systemctl restart fail2ban
|
|
|
|
# Set up log rotation
|
|
cat > /etc/logrotate.d/ydn << 'LOGROTATE'
|
|
/opt/ydn/logs/*.log {
|
|
daily
|
|
missingok
|
|
rotate 30
|
|
compress
|
|
delaycompress
|
|
notifempty
|
|
create 644 ydn ydn
|
|
postrotate
|
|
docker kill -s USR1 ydn-nginx 2>/dev/null || true
|
|
endscript
|
|
}
|
|
LOGROTATE
|
|
|
|
log_success "Server preparation completed"
|
|
EOF
|
|
|
|
log_success "Remote server prepared"
|
|
}
|
|
|
|
# Deploy application files
|
|
deploy_files() {
|
|
log_info "Deploying application files..."
|
|
|
|
# Create temporary deployment package
|
|
TEMP_DIR=$(mktemp -d)
|
|
trap "rm -rf $TEMP_DIR" EXIT
|
|
|
|
# Copy necessary files
|
|
cp -r "$PROJECT_DIR"/* "$TEMP_DIR/"
|
|
|
|
# Create production environment file
|
|
cat > "$TEMP_DIR/.env.prod" << EOF
|
|
# Production Environment Configuration
|
|
APP_ENV=production
|
|
APP_NAME=YourDreamNameHere
|
|
DOMAIN=$DOMAIN
|
|
|
|
# Database Configuration
|
|
DB_HOST=ydn-db
|
|
DB_PORT=5432
|
|
DB_USER=ydn_user
|
|
DB_PASSWORD=$(openssl rand -base64 32)
|
|
DB_NAME=ydn_db
|
|
DB_SSLMODE=require
|
|
|
|
# Redis Configuration
|
|
REDIS_HOST=ydn-redis
|
|
REDIS_PORT=6379
|
|
REDIS_PASSWORD=$(openssl rand -base64 32)
|
|
REDIS_DB=0
|
|
|
|
# JWT Configuration
|
|
JWT_SECRET=$(openssl rand -base64 64)
|
|
JWT_EXPIRY=24h
|
|
|
|
# Stripe Configuration
|
|
STRIPE_PUBLIC_KEY=$STRIPE_PUBLIC_KEY
|
|
STRIPE_SECRET_KEY=$STRIPE_SECRET_KEY
|
|
STRIPE_WEBHOOK_SECRET=$STRIPE_WEBHOOK_SECRET
|
|
STRIPE_PRICE_ID=$STRIPE_PRICE_ID
|
|
|
|
# OVH Configuration
|
|
OVH_ENDPOINT=$OVH_ENDPOINT
|
|
OVH_APPLICATION_KEY=$OVH_APPLICATION_KEY
|
|
OVH_APPLICATION_SECRET=$OVH_APPLICATION_SECRET
|
|
OVH_CONSUMER_KEY=$OVH_CONSUMER_KEY
|
|
|
|
# Email Configuration
|
|
SMTP_HOST=$SMTP_HOST
|
|
SMTP_PORT=$SMTP_PORT
|
|
SMTP_USER=$SMTP_USER
|
|
SMTP_PASSWORD=$SMTP_PASSWORD
|
|
SMTP_FROM=$SMTP_FROM
|
|
|
|
# Dolibarr Configuration
|
|
DOLIBARR_URL=https://$DOMAIN/dolibarr
|
|
DOLIBARR_API_TOKEN=$DOLIBARR_API_TOKEN
|
|
|
|
# Monitoring
|
|
GRAFANA_ADMIN_PASSWORD=$(openssl rand -base64 16)
|
|
|
|
# Version
|
|
VERSION=$VERSION
|
|
EOF
|
|
|
|
# Copy files to remote server
|
|
scp -r "$TEMP_DIR/"* "$DEPLOYMENT_USER@$DEPLOYMENT_HOST:/opt/ydn/"
|
|
|
|
# Set correct permissions
|
|
ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" "chown -R ydn:ydn /opt/ydn"
|
|
|
|
log_success "Application files deployed"
|
|
}
|
|
|
|
# Generate SSL certificates
|
|
setup_ssl() {
|
|
log_info "Setting up SSL certificates..."
|
|
|
|
ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF
|
|
set -euo pipefail
|
|
|
|
cd /opt/ydn
|
|
|
|
# Generate initial nginx config for SSL challenge
|
|
cat > configs/nginx.init.conf << 'NGINX'
|
|
events {
|
|
worker_connections 1024;
|
|
}
|
|
|
|
http {
|
|
server {
|
|
listen 80;
|
|
server_name $DOMAIN;
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/html;
|
|
}
|
|
|
|
location / {
|
|
return 301 https://\$server_name\$request_uri;
|
|
}
|
|
}
|
|
}
|
|
NGINX
|
|
|
|
# Start temporary nginx for SSL challenge
|
|
docker run -d \
|
|
--name ydn-nginx-temp \
|
|
-p 80:80 \
|
|
-v /opt/ydn/configs/nginx.init.conf:/etc/nginx/nginx.conf:ro \
|
|
-v /var/www/html:/var/www/html \
|
|
nginx:alpine
|
|
|
|
# Wait for nginx to start
|
|
sleep 10
|
|
|
|
# Request SSL certificate
|
|
if [ ! -d "/etc/letsencrypt/live/$DOMAIN" ]; then
|
|
certbot certonly --webroot \
|
|
-w /var/www/html \
|
|
-d $DOMAIN \
|
|
--email admin@$DOMAIN \
|
|
--agree-tos \
|
|
--no-eff-email \
|
|
--non-interactive
|
|
fi
|
|
|
|
# Stop temporary nginx
|
|
docker stop ydn-nginx-temp
|
|
docker rm ydn-nginx-temp
|
|
|
|
# Copy certificates to project directory
|
|
mkdir -p ssl
|
|
cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem ssl/
|
|
cp /etc/letsencrypt/live/$DOMAIN/privkey.pem ssl/
|
|
chown -R ydn:ydn ssl
|
|
|
|
# Set up automatic renewal
|
|
echo "0 12 * * * /usr/bin/certbot renew --quiet --deploy-hook 'docker kill -s HUP ydn-nginx'" | crontab -
|
|
|
|
EOF
|
|
|
|
log_success "SSL certificates configured"
|
|
}
|
|
|
|
# Deploy application stack
|
|
deploy_application() {
|
|
log_info "Deploying application stack..."
|
|
|
|
ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF
|
|
set -euo pipefail
|
|
|
|
cd /opt/ydn
|
|
|
|
# Switch to ydn user
|
|
sudo -u ydn bash << 'USER_SCRIPT'
|
|
set -euo pipefail
|
|
|
|
# Load environment variables
|
|
source .env.prod
|
|
|
|
# Build and push Docker image (if registry is configured)
|
|
if [ -n "$DOCKER_REGISTRY" ]; then
|
|
docker build -t $DOCKER_REGISTRY/ydn-app:\$VERSION .
|
|
docker push $DOCKER_REGISTRY/ydn-app:\$VERSION
|
|
fi
|
|
|
|
# Create production docker-compose override
|
|
cat > docker-compose.override.yml << 'OVERRIDE'
|
|
version: '3.8'
|
|
|
|
services:
|
|
ydn-app:
|
|
image: ${DOCKER_REGISTRY:-ydn-app}:ydn-app:\${VERSION:-latest}
|
|
environment:
|
|
- \${APP_ENV}
|
|
- \${DB_HOST}
|
|
- \${DB_USER}
|
|
- \${DB_PASSWORD}
|
|
- \${DB_NAME}
|
|
- \${DB_SSLMODE}
|
|
- \${REDIS_HOST}
|
|
- \${REDIS_PASSWORD}
|
|
- \${JWT_SECRET}
|
|
- \${STRIPE_PUBLIC_KEY}
|
|
- \${STRIPE_SECRET_KEY}
|
|
- \${STRIPE_WEBHOOK_SECRET}
|
|
- \${STRIPE_PRICE_ID}
|
|
- \${OVH_ENDPOINT}
|
|
- \${OVH_APPLICATION_KEY}
|
|
- \${OVH_APPLICATION_SECRET}
|
|
- \${OVH_CONSUMER_KEY}
|
|
- \${SMTP_HOST}
|
|
- \${SMTP_USER}
|
|
- \${SMTP_PASSWORD}
|
|
- \${SMTP_FROM}
|
|
- \${DOLIBARR_URL}
|
|
- \${DOLIBARR_API_TOKEN}
|
|
|
|
ydn-nginx:
|
|
volumes:
|
|
- ./ssl:/etc/nginx/ssl:ro
|
|
environment:
|
|
- DOMAIN=\${DOMAIN}
|
|
|
|
ydn-backup:
|
|
environment:
|
|
- DB_PASSWORD=\${DB_PASSWORD}
|
|
- BACKUP_WEBHOOK_URL=\${BACKUP_WEBHOOK_URL:-}
|
|
OVERRIDE
|
|
|
|
# Deploy with monitoring if enabled
|
|
if [ "$ENABLE_MONITORING" = "true" ]; then
|
|
docker-compose -f docker-compose.prod.yml --profile monitoring up -d
|
|
else
|
|
docker-compose -f docker-compose.prod.yml up -d
|
|
fi
|
|
|
|
# Wait for services to be ready
|
|
sleep 30
|
|
|
|
# Run database migrations
|
|
docker-compose -f docker-compose.prod.yml exec ydn-app /app/main migrate || true
|
|
|
|
USER_SCRIPT
|
|
|
|
EOF
|
|
|
|
log_success "Application stack deployed"
|
|
}
|
|
|
|
# Health check
|
|
health_check() {
|
|
log_info "Performing health checks..."
|
|
|
|
# Wait for application to start
|
|
sleep 60
|
|
|
|
# Check if services are running
|
|
ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF
|
|
set -euo pipefail
|
|
|
|
cd /opt/ydn
|
|
|
|
# Check Docker containers
|
|
if ! docker-compose -f docker-compose.prod.yml ps | grep -q "Up"; then
|
|
echo "ERROR: Some containers are not running"
|
|
docker-compose -f docker-compose.prod.yml ps
|
|
exit 1
|
|
fi
|
|
|
|
# Check application health
|
|
for i in {1..30}; do
|
|
if curl -f http://localhost/health > /dev/null 2>&1; then
|
|
echo "Application health check passed"
|
|
break
|
|
fi
|
|
|
|
if [ \$i -eq 30 ]; then
|
|
echo "ERROR: Application health check failed"
|
|
exit 1
|
|
fi
|
|
|
|
sleep 10
|
|
done
|
|
|
|
# Check SSL certificate
|
|
if ! curl -f https://$DOMAIN/health > /dev/null 2>&1; then
|
|
echo "ERROR: SSL health check failed"
|
|
exit 1
|
|
fi
|
|
|
|
echo "All health checks passed"
|
|
EOF
|
|
|
|
log_success "Health checks completed"
|
|
}
|
|
|
|
# Setup monitoring
|
|
setup_monitoring() {
|
|
if [ "$ENABLE_MONITORING" != "true" ]; then
|
|
log_info "Monitoring is disabled"
|
|
return 0
|
|
fi
|
|
|
|
log_info "Setting up monitoring..."
|
|
|
|
ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF
|
|
set -euo pipefail
|
|
|
|
cd /opt/ydn
|
|
|
|
# Configure Prometheus
|
|
cat > configs/prometheus.prod.yml << 'PROMETHEUS'
|
|
global:
|
|
scrape_interval: 15s
|
|
evaluation_interval: 15s
|
|
|
|
rule_files:
|
|
- "rules/*.yml"
|
|
|
|
scrape_configs:
|
|
- job_name: 'ydn-app'
|
|
static_configs:
|
|
- targets: ['ydn-app:8080']
|
|
metrics_path: /metrics
|
|
|
|
- job_name: 'nginx'
|
|
static_configs:
|
|
- targets: ['ydn-nginx:9113']
|
|
|
|
- job_name: 'postgres'
|
|
static_configs:
|
|
- targets: ['ydn-db:5432']
|
|
|
|
- job_name: 'redis'
|
|
static_configs:
|
|
- targets: ['ydn-redis:6379']
|
|
|
|
alerting:
|
|
alertmanagers:
|
|
- static_configs:
|
|
- targets:
|
|
- alertmanager:9093
|
|
PROMETHEUS
|
|
|
|
# Restart monitoring services
|
|
sudo -u ydn docker-compose -f docker-compose.prod.yml --profile monitoring restart ydn-prometheus ydn-grafana
|
|
|
|
EOF
|
|
|
|
log_success "Monitoring configured"
|
|
}
|
|
|
|
# Deploy to production
|
|
deploy_production() {
|
|
log_info "Starting production deployment..."
|
|
|
|
validate_environment
|
|
prepare_server
|
|
deploy_files
|
|
setup_ssl
|
|
deploy_application
|
|
setup_monitoring
|
|
health_check
|
|
|
|
log_success "🎉 Production deployment completed successfully!"
|
|
log_info "Application is available at: https://$DOMAIN"
|
|
|
|
if [ "$ENABLE_MONITORING" = "true" ]; then
|
|
log_info "Grafana is available at: https://$DOMAIN/grafana"
|
|
fi
|
|
|
|
log_info "Dolibarr is available at: https://$DOMAIN/dolibarr"
|
|
}
|
|
|
|
# Rollback deployment
|
|
rollback() {
|
|
log_warning "Rolling back deployment..."
|
|
|
|
ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF
|
|
set -euo pipefail
|
|
|
|
cd /opt/ydn
|
|
|
|
# Get previous version from git
|
|
PREVIOUS_VERSION=\$(git log --format=%H -n 2 | tail -n 1)
|
|
|
|
# Checkout previous version
|
|
sudo -u ydn git checkout \$PREVIOUS_VERSION
|
|
|
|
# Redeploy
|
|
sudo -u ydn docker-compose -f docker-compose.prod.yml down
|
|
sudo -u ydn docker-compose -f docker-compose.prod.yml up -d
|
|
|
|
EOF
|
|
|
|
log_success "Rollback completed"
|
|
}
|
|
|
|
# Show help
|
|
show_help() {
|
|
cat << EOF
|
|
YourDreamNameHere Production Deployment Script
|
|
|
|
Usage: $0 [OPTIONS]
|
|
|
|
Commands:
|
|
deploy Deploy to production (default)
|
|
rollback Rollback to previous version
|
|
status Show deployment status
|
|
help Show this help message
|
|
|
|
Environment Variables:
|
|
DEPLOYMENT_HOST Target server hostname/IP (required)
|
|
DOMAIN Domain name (required)
|
|
DOCKER_REGISTRY Docker registry URL (optional)
|
|
VERSION Version tag (default: latest)
|
|
ENABLE_MONITORING Enable monitoring (default: true)
|
|
ENABLE_LOGGING Enable logging (default: true)
|
|
STRIPE_PUBLIC_KEY Stripe public key (required)
|
|
STRIPE_SECRET_KEY Stripe secret key (required)
|
|
STRIPE_WEBHOOK_SECRET Stripe webhook secret (required)
|
|
STRIPE_PRICE_ID Stripe price ID (required)
|
|
OVH_ENDPOINT OVH API endpoint
|
|
OVH_APPLICATION_KEY OVH application key
|
|
OVH_APPLICATION_SECRET OVH application secret
|
|
OVH_CONSUMER_KEY OVH consumer key
|
|
SMTP_HOST SMTP server hostname
|
|
SMTP_PORT SMTP server port
|
|
SMTP_USER SMTP username
|
|
SMTP_PASSWORD SMTP password
|
|
SMTP_FROM From email address
|
|
DOLIBARR_API_TOKEN Dolibarr API token
|
|
BACKUP_WEBHOOK_URL Backup notification webhook URL (optional)
|
|
|
|
Examples:
|
|
# Deploy to production
|
|
DEPLOYMENT_HOST=192.168.1.100 DOMAIN=example.com $0 deploy
|
|
|
|
# Deploy with monitoring disabled
|
|
DEPLOYMENT_HOST=192.168.1.100 DOMAIN=example.com ENABLE_MONITORING=false $0 deploy
|
|
|
|
# Rollback deployment
|
|
DEPLOYMENT_HOST=192.168.1.100 DOMAIN=example.com $0 rollback
|
|
EOF
|
|
}
|
|
|
|
# Main execution
|
|
case "${1:-deploy}" in
|
|
deploy)
|
|
deploy_production
|
|
;;
|
|
rollback)
|
|
rollback
|
|
;;
|
|
status)
|
|
ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" "cd /opt/ydn && docker-compose -f docker-compose.prod.yml ps"
|
|
;;
|
|
help|--help|-h)
|
|
show_help
|
|
;;
|
|
*)
|
|
log_error "Unknown command: $1"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac |