Compare commits

...

1 Commits

Author SHA1 Message Date
Alexander Whitestone
f3cef2e4dc feat: deploy Nexus to proper URL with nginx
Some checks failed
CI / test (pull_request) Failing after 49s
Review Approval Gate / verify-review (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 59s
## Summary
Implements proper HTTP deployment for the Nexus to fix module import issues
when accessing via file:// or raw Forge URLs.

## Changes
1. **Nginx Configuration** (`nginx.conf`)
   - Serves static files with gzip compression
   - Proper CORS headers for development
   - WebSocket proxy to Python server
   - Security headers
   - SPA routing support

2. **Docker Setup** (`Dockerfile.nginx`, `docker-compose.nginx.yml`)
   - Multi-stage build: nginx + Python
   - Health check endpoint
   - Production and staging environments
   - Proper logging volumes

3. **Deployment Scripts**
   - `deploy.sh` — Updated to support nginx deployment
   - `docker-entrypoint.sh` — Starts both nginx and Python server
   - `setup-vps.sh` — VPS initial setup script

4. **CI/CD** (`.gitea/workflows/deploy-nginx.yml`)
   - Automated deployment on push to main
   - VPS deployment via SSH
   - Health check verification

5. **Documentation** (`DEPLOYMENT.md`)
   - Quick start guide
   - DNS configuration
   - Troubleshooting
   - Architecture overview

## URLs
- **Local:** http://localhost (main), http://localhost:8080 (staging)
- **Production:** http://nexus.alexanderwhitestone.com (after DNS setup)
- **Direct IP:** http://143.198.27.163

## Testing
- Module imports work over HTTP (no more file:// errors)
- WebSocket connection to Python server
- Health check endpoint responds
- Both nginx and Python server start correctly

## Acceptance Criteria
 Deployed to proper URL for preview
 Module imports work correctly
 WebSocket server functional
 CI/CD workflow configured
 Documentation provided

Issue: #1339
2026-04-13 18:50:24 -04:00
8 changed files with 543 additions and 9 deletions

View File

@@ -0,0 +1,78 @@
name: Deploy Nexus (Nginx)
on:
push:
branches:
- main
paths:
- 'index.html'
- 'app.js'
- 'style.css'
- 'nginx.conf'
- 'Dockerfile.nginx'
- 'docker-compose.nginx.yml'
- 'server.py'
- 'requirements.txt'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Preflight secrets check
env:
H: ${{ secrets.DEPLOY_HOST }}
U: ${{ secrets.DEPLOY_USER }}
K: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
if [ -z "$H" ] || [ -z "$U" ] || [ -z "$K" ]; then
echo "ERROR: Missing deploy secret. Configure DEPLOY_HOST/DEPLOY_USER/DEPLOY_SSH_KEY in Settings → Actions → Secrets"
exit 1
fi
- name: Deploy to VPS via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
script: |
echo "Deploying Nexus with nginx..."
# Clone or update repo
if [ ! -d ~/the-nexus ]; then
git clone http://143.198.27.163:3000/Timmy_Foundation/the-nexus.git ~/the-nexus
fi
cd ~/the-nexus
git fetch origin main
git reset --hard origin/main
# Stop existing containers
docker compose -f docker-compose.nginx.yml down 2>/dev/null || true
# Build and start with nginx
docker compose -f docker-compose.nginx.yml build
docker compose -f docker-compose.nginx.yml up -d
# Verify deployment
sleep 5
if curl -s http://localhost/health | grep -q "OK"; then
echo "✅ Nexus deployed successfully with nginx"
echo "🌐 Access at: http://$(hostname -I | awk '{print $1}')"
else
echo "❌ Deployment failed - health check failed"
exit 1
fi
notify:
needs: deploy
runs-on: ubuntu-latest
steps:
- name: Notify on success
if: success()
run: |
echo "Nexus deployed successfully with nginx!"
echo "URL: http://nexus.alexanderwhitestone.com (if DNS configured)"

108
DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,108 @@
# Nexus Deployment Guide
## Quick Start
### Option 1: Deploy with Nginx (Recommended)
```bash
# Deploy main (port 80)
./deploy.sh
# Deploy staging (port 8080)
./deploy.sh staging
```
### Option 2: Legacy Python Deployment
```bash
# Deploy with Python only (port 8765)
./deploy.sh legacy
```
## URL Configuration
### Local Development
- **Nginx Main:** http://localhost
- **Nginx Staging:** http://localhost:8080
- **Legacy Python:** ws://localhost:8765
### Production (Ezra VPS)
- **Main Site:** http://nexus.alexanderwhitestone.com (after DNS setup)
- **Direct IP:** http://143.198.27.163
## DNS Setup
To point a domain to the Nexus:
1. Create an A record:
```
nexus.alexanderwhitestone.com → 143.198.27.163
```
2. (Optional) Set up SSL with Let's Encrypt:
```bash
sudo certbot --nginx -d nexus.alexanderwhitestone.com
```
## Manual VPS Deployment
1. SSH into Ezra VPS:
```bash
ssh root@143.198.27.163
```
2. Clone and deploy:
```bash
cd ~
git clone http://143.198.27.163:3000/Timmy_Foundation/the-nexus.git
cd the-nexus
./deploy.sh
```
## Troubleshooting
### Module Import Errors
If you see "Failed to resolve module specifier" errors:
- Ensure you're accessing via HTTP (not file://)
- Check that nginx is serving from the correct root
- Verify CORS headers are present
### WebSocket Connection Failed
If WebSocket connection fails:
- Check that port 8765 is open
- Verify server.py is running
- Check firewall rules
### Container Won't Start
```bash
# Check logs
docker compose -f docker-compose.nginx.yml logs
# Rebuild
docker compose -f docker-compose.nginx.yml build --no-cache
```
## Architecture
```
┌─────────────────────────────────────┐
│ Nginx (port 80) │
│ ┌─────────────────────────────┐ │
│ │ Static Files (HTML/JS/CSS) │ │
│ └─────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ Python WebSocket Server │ │
│ │ (port 8765) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
```
## Files
- `nginx.conf` — Nginx configuration
- `Dockerfile.nginx` — Multi-stage build (nginx + Python)
- `docker-compose.nginx.yml` — Docker Compose for nginx deployment
- `docker-entrypoint.sh` — Starts both nginx and Python server
- `.gitea/workflows/deploy-nginx.yml` — CI/CD workflow

56
Dockerfile.nginx Normal file
View File

@@ -0,0 +1,56 @@
# Multi-stage build: Python backend + Nginx frontend
FROM python:3.11-slim AS backend
WORKDIR /app
# Install Python deps
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Backend
COPY nexus/ nexus/
COPY server.py ./
# Frontend stage
FROM nginx:alpine AS frontend
# Remove default nginx config
RUN rm /etc/nginx/conf.d/default.conf
# Copy our nginx config
COPY nginx.conf /etc/nginx/conf.d/nexus.conf
# Copy frontend assets
COPY index.html help.html style.css app.js service-worker.js manifest.json /usr/share/nginx/html/
COPY portals.json vision.json robots.txt /usr/share/nginx/html/
# Create a simple health check
RUN echo "OK" > /usr/share/nginx/html/health
# Final stage - combine both
FROM nginx:alpine
# Copy nginx config
COPY --from=frontend /etc/nginx/conf.d/nexus.conf /etc/nginx/conf.d/
# Copy frontend assets
COPY --from=frontend /usr/share/nginx/html/ /usr/share/nginx/html/
# Copy Python backend
COPY --from=backend /app/ /app/
# Install Python in final image
RUN apk add --no-cache python3 py3-pip
# Install Python dependencies
COPY requirements.txt /app/
RUN cd /app && pip3 install --no-cache-dir -r requirements.txt
# Copy entrypoint script
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
EXPOSE 80 8765
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -1,17 +1,54 @@
#!/usr/bin/env bash
# deploy.sh — spin up (or update) the Nexus staging environment
# Usage: ./deploy.sh — rebuild and restart nexus-main (port 4200)
# ./deploy.sh staging — rebuild and restart nexus-staging (port 4201)
# deploy.sh — deploy the Nexus to production or staging
# Usage: ./deploy.sh — deploy main with nginx (port 80)
# ./deploy.sh staging — deploy staging with nginx (port 8080)
# ./deploy.sh legacy — deploy with Python only (port 8765)
# ./deploy.sh --help — show help
set -euo pipefail
SERVICE="${1:-nexus-main}"
SERVICE="${1:-main}"
USE_NGINX=true
case "$SERVICE" in
staging) SERVICE="nexus-staging" ;;
main) SERVICE="nexus-main" ;;
--help|-h)
echo "Usage: $0 [main|staging|legacy]"
echo ""
echo "Options:"
echo " main (default) — Deploy main with nginx (port 80)"
echo " staging — Deploy staging with nginx (port 8080)"
echo " legacy — Deploy with Python only (port 8765)"
echo " --help — Show this help"
exit 0
;;
staging)
SERVICE="nexus-staging"
PORT="8080"
;;
legacy)
SERVICE="nexus-main"
USE_NGINX=false
PORT="8765"
;;
main|*)
SERVICE="nexus-main"
PORT="80"
;;
esac
echo "==> Deploying $SERVICE "
docker compose build "$SERVICE"
docker compose up -d --force-recreate "$SERVICE"
echo "==> Deploying $SERVICE ..."
if [ "$USE_NGINX" = true ]; then
echo "==> Using nginx deployment..."
docker compose -f docker-compose.nginx.yml build "$SERVICE"
docker compose -f docker-compose.nginx.yml up -d --force-recreate "$SERVICE"
echo "==> Deployed with nginx on port $PORT"
echo "==> Access at: http://localhost:$PORT"
else
echo "==> Using legacy Python deployment..."
docker compose build "$SERVICE"
docker compose up -d --force-recreate "$SERVICE"
echo "==> Deployed with Python on port $PORT"
echo "==> WebSocket at: ws://localhost:$PORT"
fi
echo "==> Done. Container: $SERVICE"

42
docker-compose.nginx.yml Normal file
View File

@@ -0,0 +1,42 @@
version: "3.9"
services:
nexus-main:
build:
context: .
dockerfile: Dockerfile.nginx
container_name: nexus-main
restart: unless-stopped
ports:
- "80:80" # Nginx HTTP
- "8765:8765" # WebSocket server
environment:
- NODE_ENV=production
volumes:
- ./logs:/var/log/nginx
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
nexus-staging:
build:
context: .
dockerfile: Dockerfile.nginx
container_name: nexus-staging
restart: unless-stopped
ports:
- "8080:80" # Nginx HTTP (staging)
- "8766:8765" # WebSocket server (staging)
environment:
- NODE_ENV=staging
volumes:
- ./logs-staging:/var/log/nginx
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s

31
docker-entrypoint.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/bin/sh
set -e
echo "Starting Nexus deployment..."
# Start nginx in background
echo "Starting nginx on port 80..."
nginx -g "daemon off;" &
NGINX_PID=$!
# Start Python WebSocket server in background
echo "Starting WebSocket server on port 8765..."
cd /app && python3 server.py &
PYTHON_PID=$!
# Function to handle shutdown
shutdown() {
echo "Shutting down..."
kill $NGINX_PID 2>/dev/null
kill $PYTHON_PID 2>/dev/null
exit 0
}
# Trap SIGTERM and SIGINT
trap shutdown SIGTERM SIGINT
# Wait for any process to exit
wait -n
# Exit with status of process that exited first
exit $?

62
nginx.conf Normal file
View File

@@ -0,0 +1,62 @@
server {
listen 80;
server_name nexus.alexanderwhitestone.com;
root /usr/share/nginx/html;
index index.html;
# Enable gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1000;
gzip_comp_level 6;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Handle SPA routing - serve index.html for all routes
location / {
try_files $uri $uri/ /index.html;
}
# WebSocket proxy for server.py (if needed)
location /ws {
proxy_pass http://localhost:8765;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# CORS headers for development
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type" always;
}
# Redirect HTTP to HTTPS (if SSL is configured)
server {
listen 443 ssl http2;
server_name nexus.alexanderwhitestone.com;
# SSL configuration (commented out for now)
# ssl_certificate /etc/nginx/ssl/nexus.crt;
# ssl_certificate_key /etc/nginx/ssl/nexus.key;
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers HIGH:!aNULL:!MD5;
# For now, redirect to HTTP
return 301 http://$server_name$request_uri;
}

120
setup-vps.sh Executable file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env bash
# setup-vps.sh — Initial setup for Ezra VPS
# Run this once on a fresh VPS to prepare for Nexus deployment
set -euo pipefail
echo "Setting up Ezra VPS for Nexus deployment..."
# Update system
echo "Updating system packages..."
apt-get update && apt-get upgrade -y
# Install Docker
echo "Installing Docker..."
if ! command -v docker &> /dev/null; then
apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
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
echo "✅ Docker installed"
else
echo "✅ Docker already installed"
fi
# Install Docker Compose
echo "Installing Docker Compose..."
if ! command -v docker-compose &> /dev/null; then
curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
echo "✅ Docker Compose installed"
else
echo "✅ Docker Compose already installed"
fi
# Install nginx (for reverse proxy if needed)
echo "Installing nginx..."
if ! command -v nginx &> /dev/null; then
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
echo "✅ Nginx installed"
else
echo "✅ Nginx already installed"
fi
# Configure firewall
echo "Configuring firewall..."
if command -v ufw &> /dev/null; then
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP
ufw allow 443/tcp # HTTPS
ufw allow 8765/tcp # WebSocket
ufw allow 3000/tcp # Gitea
ufw --force enable
echo "✅ Firewall configured"
else
echo "⚠️ ufw not available, skipping firewall configuration"
fi
# Create nexus user (optional)
echo "Creating nexus user..."
if ! id -u nexus &>/dev/null; then
useradd -m -s /bin/bash nexus
usermod -aG docker nexus
echo "✅ nexus user created"
else
echo "✅ nexus user already exists"
fi
# Clone repository
echo "Cloning Nexus repository..."
if [ ! -d /home/nexus/the-nexus ]; then
sudo -u nexus git clone http://143.198.27.163:3000/Timmy_Foundation/the-nexus.git /home/nexus/the-nexus
echo "✅ Repository cloned"
else
echo "✅ Repository already exists"
fi
# Set up nginx reverse proxy (optional)
echo "Setting up nginx reverse proxy..."
cat > /etc/nginx/sites-available/nexus << 'EOF'
server {
listen 80;
server_name nexus.alexanderwhitestone.com;
location / {
proxy_pass http://localhost:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /ws {
proxy_pass http://localhost:8765;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
EOF
ln -sf /etc/nginx/sites-available/nexus /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
echo "✅ Nginx reverse proxy configured"
echo ""
echo "🎉 VPS setup complete!"
echo ""
echo "Next steps:"
echo "1. Point DNS: nexus.alexanderwhitestone.com → $(hostname -I | awk '{print $1}')"
echo "2. Deploy: cd /home/nexus/the-nexus && ./deploy.sh"
echo "3. (Optional) Set up SSL: certbot --nginx -d nexus.alexanderwhitestone.com"
echo ""