Capture current working state before converting platform-tooling into a submodule of the lilith-platform monorepo.
398 lines
13 KiB
Bash
Executable file
398 lines
13 KiB
Bash
Executable file
#!/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 "$@"
|