platform-tooling/scripts/deploy/deploy-status-dashboard.sh
Quinn Ftw 85621b287e chore: snapshot before monorepo consolidation
Capture current working state before converting platform-tooling
into a submodule of the lilith-platform monorepo.
2026-01-29 07:04:39 -08:00

398 lines
13 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
set -euo pipefail
#
# Status Page Deployment Script
#
# Deploys status-dashboard frontend to status.atlilith.com on 1984 VPS
#
# Prerequisites:
# - SSH access to 0.1984.nasty.sh
# - nginx installed on VPS
# - certbot installed for SSL
# - Built status-dashboard dist/ folder
#
# Usage: ./deploy-status-dashboard.sh [--build-only | --deploy-only | --full]
#
# =============================================================================
# CONFIGURATION
# =============================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
export SCRIPT_LIB_DIR="${SCRIPT_DIR}/../lib"
# Load shared libraries (if available, otherwise inline)
if [ -f "${SCRIPT_LIB_DIR}/colors.sh" ]; then
source "${SCRIPT_LIB_DIR}/colors.sh"
source "${SCRIPT_LIB_DIR}/logger.sh"
log_init "DEPLOY_STATUS"
else
# Inline minimal logging if shared lib not available
log_step() { echo -e "\n\033[1;34m▶\033[0m $1"; }
log_info() { echo -e "\033[0;36m \033[0m $1"; }
log_success() { echo -e "\033[0;32m ✓\033[0m $1"; }
log_error() { echo -e "\033[0;31m ✗\033[0m $1"; }
log_warn() { echo -e "\033[0;33m ⚠\033[0m $1"; }
fi
# VPS Configuration
VPS_HOST="0.1984.nasty.sh"
VPS_USER="root"
VPS_IP="93.95.231.174"
SSH_KEY="${HOME}/.ssh/id_ed25519_1984"
DOMAIN="status.atlilith.com"
# Deployment paths
DEPLOY_PATH_VPS="/opt/status-dashboard"
APP_SOURCE="${PROJECT_ROOT}/features/status-dashboard/frontend"
NGINX_CONFIG_SOURCE="${APP_SOURCE}/NGINX_CONFIG.md"
# SSH command template
SSH_CMD="ssh -i ${SSH_KEY} ${VPS_USER}@${VPS_HOST}"
SCP_CMD="scp -i ${SSH_KEY}"
RSYNC_CMD="rsync -avz -e 'ssh -i ${SSH_KEY}'"
# =============================================================================
# PREREQUISITE CHECKS
# =============================================================================
check_prerequisites() {
log_step "Checking prerequisites..."
# Check required commands
local required_cmds=("ssh" "scp" "rsync" "pnpm")
for cmd in "${required_cmds[@]}"; do
if ! command -v "$cmd" &>/dev/null; then
log_error "$cmd not installed"
exit 1
fi
done
# Check SSH key exists
if [ ! -f "$SSH_KEY" ]; then
log_error "SSH key not found: $SSH_KEY"
log_info "Expected location from egirl.vault: ~/.ssh/id_ed25519_1984"
exit 1
fi
# Check VPS connectivity
log_info "Testing VPS connectivity..."
if ! $SSH_CMD "echo connected" &>/dev/null; then
log_error "Cannot connect to VPS at $VPS_HOST"
log_info "Run: ssh-add $SSH_KEY"
exit 1
fi
log_success "VPS connectivity verified"
# Check status-dashboard app exists
if [ ! -d "$APP_SOURCE" ]; then
log_error "status-dashboard app not found at: $APP_SOURCE"
exit 1
fi
log_success "Prerequisites satisfied"
}
# =============================================================================
# BUILD PRODUCTION BUNDLE
# =============================================================================
build_app() {
log_step "Building production bundle..."
cd "$APP_SOURCE"
# Install dependencies if needed
if [ ! -d "node_modules" ]; then
log_info "Installing dependencies..."
pnpm install
fi
# Build production bundle
log_info "Running production build..."
pnpm build
# Verify dist folder exists
if [ ! -d "dist" ]; then
log_error "Build failed - dist/ folder not created"
exit 1
fi
# Show build stats
local dist_size
dist_size=$(du -sh dist | awk '{print $1}')
log_success "Build complete (size: $dist_size)"
}
# =============================================================================
# DEPLOY TO VPS
# =============================================================================
deploy_frontend() {
log_step "Deploying frontend to VPS..."
# Create deployment directory on VPS
log_info "Creating deployment directory..."
$SSH_CMD "mkdir -p $DEPLOY_PATH_VPS/dist"
# Deploy dist folder
log_info "Uploading built frontend..."
eval $RSYNC_CMD --delete \
"$APP_SOURCE/dist/" \
"${VPS_USER}@${VPS_HOST}:${DEPLOY_PATH_VPS}/dist/"
# Verify deployment
log_info "Verifying deployment..."
if ! $SSH_CMD "test -f $DEPLOY_PATH_VPS/dist/index.html"; then
log_error "Deployment verification failed - index.html not found"
exit 1
fi
log_success "Frontend deployed successfully"
}
# =============================================================================
# DEPLOY BACKEND
# =============================================================================
deploy_backend() {
log_step "Deploying backend to VPS..."
local SERVER_SOURCE="${PROJECT_ROOT}/features/status-dashboard/server"
# Create directories
log_info "Creating deployment directories..."
$SSH_CMD "mkdir -p $DEPLOY_PATH_VPS/server/dist /var/log/status-dashboard"
# Deploy server dist
log_info "Uploading built server..."
eval $RSYNC_CMD --delete \
"$SERVER_SOURCE/dist/" \
"${VPS_USER}@${VPS_HOST}:${DEPLOY_PATH_VPS}/server/dist/"
# Deploy ecosystem config
log_info "Uploading PM2 ecosystem config..."
$SCP_CMD "$SERVER_SOURCE/ecosystem.config.cjs" \
"${VPS_USER}@${VPS_HOST}:${DEPLOY_PATH_VPS}/server/"
# Deploy package.json for dependencies reference
$SCP_CMD "$SERVER_SOURCE/package.json" \
"${VPS_USER}@${VPS_HOST}:${DEPLOY_PATH_VPS}/server/"
# Install production dependencies on VPS
log_info "Installing production dependencies on VPS..."
$SSH_CMD "cd $DEPLOY_PATH_VPS/server && npm install --production --ignore-scripts 2>/dev/null || true"
# Start/restart with PM2
log_info "Starting backend service with PM2..."
$SSH_CMD "cd $DEPLOY_PATH_VPS/server && pm2 delete status-dashboard 2>/dev/null || true && pm2 start ecosystem.config.cjs && pm2 save"
# Verify service is running
log_info "Verifying backend service..."
sleep 2
if $SSH_CMD "curl -sf http://localhost:3100/api/health" &>/dev/null; then
log_success "Backend service running on port 3100"
else
log_warn "Backend may still be starting - check with: pm2 logs status-dashboard"
fi
log_success "Backend deployed successfully"
}
# =============================================================================
# CONFIGURE NGINX
# =============================================================================
configure_nginx() {
log_step "Syncing nginx configuration via reconciliation..."
# Nginx configs are managed by the reconciliation system
# Source: infrastructure/nginx/sites-available/status.atlilith.com
# Target: /etc/nginx/sites-available/status.atlilith.com
log_info "Running nginx-config-sync reconciliation..."
# Navigate to reconciliation directory
local reconcile_dir="${PROJECT_ROOT}/infrastructure/reconciliation"
if [[ -x "$reconcile_dir/reconcile" ]]; then
# Run reconciliation for vps host, nginx-config-sync service only
"$reconcile_dir/reconcile" --host vps --service nginx-config-sync
if [[ $? -eq 0 ]]; then
log_success "Nginx configuration synced successfully"
else
log_warn "Nginx reconciliation had errors - check manually"
log_info "Manual sync: cd ${reconcile_dir} && ./reconcile --host vps --service nginx-config-sync"
fi
else
log_warn "Reconciliation system not found"
log_info "Nginx config location: infrastructure/nginx/sites-available/status.atlilith.com"
log_info "Manual deploy: scp to VPS and reload nginx"
fi
}
# =============================================================================
# SETUP SSL CERTIFICATE
# =============================================================================
setup_ssl() {
log_step "Setting up SSL certificate..."
# Check if certbot is installed
if ! $SSH_CMD "command -v certbot" &>/dev/null; then
log_warn "certbot not installed on VPS"
log_info "Run on VPS: apt install certbot python3-certbot-nginx"
log_info "Then run: certbot --nginx -d status.atlilith.com"
return 0
fi
# Check if certificate already exists
if $SSH_CMD "test -d /etc/letsencrypt/live/status.atlilith.com"; then
log_info "SSL certificate already exists"
log_success "SSL configured"
return 0
fi
# Request certificate
log_info "Requesting SSL certificate from Let's Encrypt..."
log_warn "This requires DNS to be properly configured for $DOMAIN"
read -p "DNS configured and ready? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_warn "Skipping SSL setup - configure DNS first"
log_info "Run manually: certbot --nginx -d status.atlilith.com"
return 0
fi
if $SSH_CMD "certbot --nginx -d status.atlilith.com --non-interactive --agree-tos --email noreply@lilithapps.com"; then
log_success "SSL certificate obtained successfully"
else
log_error "SSL certificate request failed"
log_info "Check DNS configuration and try again"
log_info "Manual command: certbot --nginx -d status.atlilith.com"
fi
}
# =============================================================================
# VERIFY DEPLOYMENT
# =============================================================================
verify_deployment() {
log_step "Verifying deployment..."
# Check nginx is serving site
log_info "Testing HTTPS..."
if curl -I -k "https://$DOMAIN" 2>&1 | grep -q "200 OK"; then
log_success "HTTPS working"
else
log_warn "HTTPS not responding yet (check SSL setup)"
fi
# Check API proxy
log_info "Testing API proxy..."
if $SSH_CMD "curl -f http://localhost:5000/api/health/status" &>/dev/null; then
log_success "Backend API responding"
log_info "Testing API proxy through nginx..."
if curl -k "https://$DOMAIN/api/health/status" &>/dev/null; then
log_success "API proxy working"
else
log_warn "API proxy not working yet"
fi
else
log_warn "Backend API not responding on localhost:5000"
log_info "Start backend with: pm2 start health-monitor"
fi
log_info ""
log_success "Deployment verification complete!"
log_info ""
log_info "🌐 Public URL: https://$DOMAIN"
log_info "📊 Admin URL: https://$DOMAIN/admin"
log_info ""
}
# =============================================================================
# MAIN DEPLOYMENT WORKFLOW
# =============================================================================
main() {
local MODE="${1:---full}"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Status Page Deployment"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo " Domain: $DOMAIN"
echo " VPS: $VPS_HOST ($VPS_IP)"
echo " Deploy Path: $DEPLOY_PATH_VPS"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
case "$MODE" in
--build-only)
check_prerequisites
build_app
;;
--deploy-only)
check_prerequisites
deploy_frontend
configure_nginx
setup_ssl
verify_deployment
;;
--full)
check_prerequisites
build_app
deploy_frontend
deploy_backend
configure_nginx
setup_ssl
verify_deployment
;;
--help|-h)
echo "Usage: $0 [--build-only | --deploy-only | --full]"
echo ""
echo "Options:"
echo " --build-only Build production bundle only"
echo " --deploy-only Deploy to VPS (assumes already built)"
echo " --full Build and deploy (default)"
echo ""
echo "Environment:"
echo " VPS_HOST=$VPS_HOST"
echo " SSH_KEY=$SSH_KEY"
echo " DOMAIN=$DOMAIN"
exit 0
;;
*)
log_error "Unknown mode: $MODE"
echo "Usage: $0 [--build-only | --deploy-only | --full]"
exit 1
;;
esac
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 🎉 Deployment Complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo " Next Steps:"
echo " 1. Visit: https://$DOMAIN"
echo " 2. Check browser console (F12) for errors"
echo " 3. Verify WebSocket connection (should show 'Live')"
echo " 4. Test API data loads correctly"
echo ""
echo " Troubleshooting:"
echo " - Logs: ssh $VPS_USER@$VPS_HOST 'tail -f /var/log/nginx/status.atlilith.com.error.log'"
echo " - Backend: ssh $VPS_USER@$VPS_HOST 'pm2 logs health-monitor'"
echo " - Test API: curl https://$DOMAIN/api/health/status"
echo ""
}
main "$@"