- Migrated from HIVE branding to WHOOSH across all components - Enhanced backend API with new services: AI models, BZZZ integration, templates, members - Added comprehensive testing suite with security, performance, and integration tests - Improved frontend with new components for project setup, AI models, and team management - Updated MCP server implementation with WHOOSH-specific tools and resources - Enhanced deployment configurations with production-ready Docker setups - Added comprehensive documentation and setup guides - Implemented age encryption service and UCXL integration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
395 lines
11 KiB
Bash
Executable File
395 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
# WHOOSH Production Deployment Script
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
DEPLOY_ENV="${DEPLOY_ENV:-production}"
|
|
REGISTRY="${REGISTRY:-registry.home.deepblack.cloud}"
|
|
PROJECT_NAME="whoosh"
|
|
DOMAIN="${DOMAIN:-whoosh.deepblack.cloud}"
|
|
BACKUP_DIR="/rust/containers/whoosh/backups"
|
|
COMPOSE_FILE="docker-compose.prod.yml"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
log() {
|
|
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
|
|
}
|
|
|
|
success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
exit 1
|
|
}
|
|
|
|
# Function to check prerequisites
|
|
check_prerequisites() {
|
|
log "Checking deployment prerequisites..."
|
|
|
|
# Check if running as non-root
|
|
if [[ $EUID -eq 0 ]]; then
|
|
error "This script should not be run as root"
|
|
fi
|
|
|
|
# Check required commands
|
|
for cmd in docker docker-compose git curl; do
|
|
if ! command -v $cmd &> /dev/null; then
|
|
error "$cmd is not installed"
|
|
fi
|
|
done
|
|
|
|
# Check Docker daemon
|
|
if ! docker info >/dev/null 2>&1; then
|
|
error "Docker daemon is not running"
|
|
fi
|
|
|
|
# Check if in swarm mode (optional)
|
|
if docker info | grep -q "Swarm: active"; then
|
|
log "Docker Swarm is active"
|
|
SWARM_MODE=true
|
|
else
|
|
log "Docker Swarm is not active, using compose mode"
|
|
SWARM_MODE=false
|
|
fi
|
|
|
|
success "Prerequisites check completed"
|
|
}
|
|
|
|
# Function to setup secrets
|
|
setup_secrets() {
|
|
log "Setting up production secrets..."
|
|
|
|
if [[ "$SWARM_MODE" == "true" ]]; then
|
|
# Docker Swarm secrets
|
|
echo "Setting up Docker Swarm secrets..."
|
|
|
|
# Check if secrets exist, create if they don't
|
|
if ! docker secret ls | grep -q "whoosh_postgres_password"; then
|
|
openssl rand -base64 32 | docker secret create whoosh_postgres_password -
|
|
fi
|
|
|
|
if ! docker secret ls | grep -q "whoosh_secret_key"; then
|
|
openssl rand -base64 64 | docker secret create whoosh_secret_key -
|
|
fi
|
|
|
|
if ! docker secret ls | grep -q "whoosh_age_master_key"; then
|
|
age-keygen | grep "AGE-SECRET-KEY" | docker secret create whoosh_age_master_key -
|
|
fi
|
|
|
|
# GITEA token should be provided externally
|
|
if ! docker secret ls | grep -q "whoosh_gitea_token"; then
|
|
warning "GITEA token secret not found. Please create it manually:"
|
|
echo " echo 'your_gitea_token' | docker secret create whoosh_gitea_token -"
|
|
fi
|
|
|
|
else
|
|
# Docker Compose secrets (using .env file)
|
|
if [[ ! -f ".env.prod" ]]; then
|
|
log "Creating .env.prod file..."
|
|
cat > .env.prod << EOF
|
|
POSTGRES_PASSWORD=$(openssl rand -base64 32)
|
|
SECRET_KEY=$(openssl rand -base64 64)
|
|
AGE_MASTER_KEY=$(age-keygen | grep "AGE-SECRET-KEY")
|
|
GITEA_TOKEN=${GITEA_TOKEN:-""}
|
|
SENTRY_DSN=${SENTRY_DSN:-""}
|
|
GRAFANA_PASSWORD=$(openssl rand -base64 16)
|
|
EOF
|
|
warning "Created .env.prod file. Please update GITEA_TOKEN and SENTRY_DSN"
|
|
fi
|
|
fi
|
|
|
|
success "Secrets setup completed"
|
|
}
|
|
|
|
# Function to build and push images
|
|
build_and_push() {
|
|
log "Building and pushing Docker images..."
|
|
|
|
# Build backend
|
|
log "Building backend image..."
|
|
docker build -f backend/Dockerfile.prod -t ${REGISTRY}/${PROJECT_NAME}/backend:latest backend/
|
|
docker build -f backend/Dockerfile.prod -t ${REGISTRY}/${PROJECT_NAME}/backend:$(git rev-parse --short HEAD) backend/
|
|
|
|
# Build frontend
|
|
log "Building frontend image..."
|
|
docker build -f frontend/Dockerfile.prod -t ${REGISTRY}/${PROJECT_NAME}/frontend:latest frontend/
|
|
docker build -f frontend/Dockerfile.prod -t ${REGISTRY}/${PROJECT_NAME}/frontend:$(git rev-parse --short HEAD) frontend/
|
|
|
|
# Push to registry
|
|
if [[ "${PUSH_IMAGES:-true}" == "true" ]]; then
|
|
log "Pushing images to registry..."
|
|
docker push ${REGISTRY}/${PROJECT_NAME}/backend:latest
|
|
docker push ${REGISTRY}/${PROJECT_NAME}/backend:$(git rev-parse --short HEAD)
|
|
docker push ${REGISTRY}/${PROJECT_NAME}/frontend:latest
|
|
docker push ${REGISTRY}/${PROJECT_NAME}/frontend:$(git rev-parse --short HEAD)
|
|
fi
|
|
|
|
success "Images built and pushed successfully"
|
|
}
|
|
|
|
# Function to backup database
|
|
backup_database() {
|
|
if docker ps | grep -q "whoosh_postgres"; then
|
|
log "Creating database backup..."
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
BACKUP_FILE="$BACKUP_DIR/whoosh_backup_$(date +%Y%m%d_%H%M%S).sql"
|
|
|
|
docker exec whoosh_postgres_prod pg_dump -U whoosh whoosh > "$BACKUP_FILE"
|
|
gzip "$BACKUP_FILE"
|
|
|
|
success "Database backup created: ${BACKUP_FILE}.gz"
|
|
|
|
# Keep only last 7 backups
|
|
find "$BACKUP_DIR" -name "whoosh_backup_*.sql.gz" -mtime +7 -delete
|
|
else
|
|
log "No existing database found to backup"
|
|
fi
|
|
}
|
|
|
|
# Function to deploy application
|
|
deploy_application() {
|
|
log "Deploying WHOOSH application..."
|
|
|
|
# Create necessary directories
|
|
mkdir -p logs nginx/ssl monitoring/grafana/{dashboards,datasources}
|
|
|
|
if [[ "$SWARM_MODE" == "true" ]]; then
|
|
# Deploy using Docker Swarm
|
|
log "Deploying to Docker Swarm..."
|
|
docker stack deploy -c docker-compose.prod.yml ${PROJECT_NAME}
|
|
|
|
# Wait for services to be ready
|
|
log "Waiting for services to be ready..."
|
|
for i in {1..30}; do
|
|
if docker service ls | grep -q "${PROJECT_NAME}_whoosh_backend" &&
|
|
docker service ls | grep -q "${PROJECT_NAME}_whoosh_frontend"; then
|
|
break
|
|
fi
|
|
echo -n "."
|
|
sleep 10
|
|
done
|
|
echo
|
|
|
|
else
|
|
# Deploy using Docker Compose
|
|
log "Deploying with Docker Compose..."
|
|
docker-compose -f $COMPOSE_FILE --env-file .env.prod up -d
|
|
|
|
# Wait for services to be ready
|
|
log "Waiting for services to be ready..."
|
|
for i in {1..30}; do
|
|
if docker-compose -f $COMPOSE_FILE ps | grep -q "Up" &&
|
|
curl -f http://localhost:8087/health >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
echo -n "."
|
|
sleep 10
|
|
done
|
|
echo
|
|
fi
|
|
|
|
success "Application deployed successfully"
|
|
}
|
|
|
|
# Function to run health checks
|
|
run_health_checks() {
|
|
log "Running health checks..."
|
|
|
|
# Check backend health
|
|
if curl -f http://localhost:8087/health >/dev/null 2>&1; then
|
|
success "Backend health check passed"
|
|
else
|
|
error "Backend health check failed"
|
|
fi
|
|
|
|
# Check frontend
|
|
if curl -f http://localhost:3000 >/dev/null 2>&1; then
|
|
success "Frontend health check passed"
|
|
else
|
|
warning "Frontend health check failed"
|
|
fi
|
|
|
|
# Check database
|
|
if docker exec whoosh_postgres_prod pg_isready -U whoosh >/dev/null 2>&1; then
|
|
success "Database health check passed"
|
|
else
|
|
error "Database health check failed"
|
|
fi
|
|
|
|
success "Health checks completed"
|
|
}
|
|
|
|
# Function to setup monitoring
|
|
setup_monitoring() {
|
|
log "Setting up monitoring and alerting..."
|
|
|
|
# Create Prometheus configuration
|
|
cat > monitoring/prometheus.yml << EOF
|
|
global:
|
|
scrape_interval: 15s
|
|
evaluation_interval: 15s
|
|
|
|
rule_files:
|
|
- "alert_rules.yml"
|
|
|
|
alerting:
|
|
alertmanagers:
|
|
- static_configs:
|
|
- targets:
|
|
- alertmanager:9093
|
|
|
|
scrape_configs:
|
|
- job_name: 'whoosh-backend'
|
|
static_configs:
|
|
- targets: ['whoosh_backend:8087']
|
|
metrics_path: /metrics
|
|
scrape_interval: 30s
|
|
|
|
- job_name: 'whoosh-postgres'
|
|
static_configs:
|
|
- targets: ['postgres_exporter:9187']
|
|
|
|
- job_name: 'whoosh-redis'
|
|
static_configs:
|
|
- targets: ['redis_exporter:9121']
|
|
|
|
- job_name: 'node-exporter'
|
|
static_configs:
|
|
- targets: ['node_exporter:9100']
|
|
EOF
|
|
|
|
# Create Grafana datasource
|
|
mkdir -p monitoring/grafana/datasources
|
|
cat > monitoring/grafana/datasources/prometheus.yml << EOF
|
|
apiVersion: 1
|
|
|
|
datasources:
|
|
- name: Prometheus
|
|
type: prometheus
|
|
access: proxy
|
|
url: http://whoosh_prometheus:9090
|
|
isDefault: true
|
|
EOF
|
|
|
|
success "Monitoring setup completed"
|
|
}
|
|
|
|
# Function to cleanup old deployments
|
|
cleanup() {
|
|
log "Cleaning up old deployments..."
|
|
|
|
# Remove old containers
|
|
docker container prune -f
|
|
|
|
# Remove old images
|
|
docker image prune -f
|
|
|
|
# Remove old volumes (careful!)
|
|
if [[ "${CLEANUP_VOLUMES:-false}" == "true" ]]; then
|
|
warning "Cleaning up old volumes..."
|
|
docker volume prune -f
|
|
fi
|
|
|
|
success "Cleanup completed"
|
|
}
|
|
|
|
# Function to show deployment status
|
|
show_status() {
|
|
log "Deployment Status:"
|
|
echo "===================="
|
|
|
|
if [[ "$SWARM_MODE" == "true" ]]; then
|
|
docker stack services ${PROJECT_NAME}
|
|
else
|
|
docker-compose -f $COMPOSE_FILE ps
|
|
fi
|
|
|
|
echo
|
|
log "Application URLs:"
|
|
echo "Frontend: http://localhost:3000"
|
|
echo "Backend API: http://localhost:8087"
|
|
echo "Prometheus: http://localhost:9090"
|
|
echo "Grafana: http://localhost:3001"
|
|
echo
|
|
log "Logs:"
|
|
echo "Backend: docker logs whoosh_backend_prod"
|
|
echo "Frontend: docker logs whoosh_frontend_prod"
|
|
echo "Database: docker logs whoosh_postgres_prod"
|
|
}
|
|
|
|
# Main deployment flow
|
|
main() {
|
|
log "Starting WHOOSH Production Deployment"
|
|
echo "======================================"
|
|
|
|
case "${1:-deploy}" in
|
|
"check")
|
|
check_prerequisites
|
|
;;
|
|
"secrets")
|
|
setup_secrets
|
|
;;
|
|
"build")
|
|
build_and_push
|
|
;;
|
|
"backup")
|
|
backup_database
|
|
;;
|
|
"deploy")
|
|
check_prerequisites
|
|
setup_secrets
|
|
backup_database
|
|
build_and_push
|
|
setup_monitoring
|
|
deploy_application
|
|
run_health_checks
|
|
show_status
|
|
success "WHOOSH deployment completed successfully!"
|
|
;;
|
|
"status")
|
|
show_status
|
|
;;
|
|
"cleanup")
|
|
cleanup
|
|
;;
|
|
"rollback")
|
|
log "Rolling back to previous deployment..."
|
|
if [[ "$SWARM_MODE" == "true" ]]; then
|
|
docker service update --rollback ${PROJECT_NAME}_whoosh_backend
|
|
docker service update --rollback ${PROJECT_NAME}_whoosh_frontend
|
|
else
|
|
docker-compose -f $COMPOSE_FILE down
|
|
# Would need previous image tags for proper rollback
|
|
warning "Manual rollback required for compose mode"
|
|
fi
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {check|secrets|build|backup|deploy|status|cleanup|rollback}"
|
|
echo " check - Check prerequisites"
|
|
echo " secrets - Setup production secrets"
|
|
echo " build - Build and push images"
|
|
echo " backup - Backup database"
|
|
echo " deploy - Full deployment (default)"
|
|
echo " status - Show deployment status"
|
|
echo " cleanup - Clean up old resources"
|
|
echo " rollback- Rollback to previous version"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Run main function with all arguments
|
|
main "$@" |