Compare commits
1 Commits
fix/879
...
burn/1339-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3cef2e4dc |
78
.gitea/workflows/deploy-nginx.yml
Normal file
78
.gitea/workflows/deploy-nginx.yml
Normal 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
108
DEPLOYMENT.md
Normal 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
56
Dockerfile.nginx
Normal 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;"]
|
||||
55
deploy.sh
55
deploy.sh
@@ -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
42
docker-compose.nginx.yml
Normal 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
31
docker-entrypoint.sh
Executable 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
62
nginx.conf
Normal 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
120
setup-vps.sh
Executable 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 ""
|
||||
Reference in New Issue
Block a user