diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 00000000..786074e2 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,35 @@ +name: Deploy Nexus Preview to Pages +on: + push: + branches: [main] + workflow_dispatch: +permissions: + contents: read + pages: write + id-token: write +concurrency: + group: "pages" + cancel-in-progress: false +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/configure-pages@v5 + - name: Prepare static assets + run: | + mkdir -p _site + cp index.html app.js style.css boot.js gofai_worker.js _site/ + cp service-worker.js manifest.json robots.txt help.html _site/ + cp portals.json vision.json _site/ + cp -r nexus/ _site/nexus/ + cp -r icons/ _site/icons/ 2>/dev/null || true + cp -r assets/ _site/assets/ 2>/dev/null || true + - uses: actions/upload-pages-artifact@v3 + with: + path: '_site' + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/Dockerfile.preview b/Dockerfile.preview new file mode 100644 index 00000000..2b637dbd --- /dev/null +++ b/Dockerfile.preview @@ -0,0 +1,9 @@ +FROM nginx:alpine +RUN rm /etc/nginx/conf.d/default.conf +COPY preview/nginx.conf /etc/nginx/conf.d/default.conf +COPY index.html app.js style.css boot.js gofai_worker.js /usr/share/nginx/html/ +COPY service-worker.js manifest.json robots.txt help.html portals.json vision.json /usr/share/nginx/html/ +COPY nexus/ /usr/share/nginx/html/nexus/ +COPY icons/ /usr/share/nginx/html/icons/ +COPY assets/ /usr/share/nginx/html/assets/ +EXPOSE 8080 diff --git a/PREVIEW.md b/PREVIEW.md new file mode 100644 index 00000000..5b0c6b5e --- /dev/null +++ b/PREVIEW.md @@ -0,0 +1,18 @@ +# Nexus Preview + +ES module imports fail via `file://` or raw Forge URLs. `boot.js` warns: _"Serve over HTTP."_ + +## Quick Start + +```bash +./preview.sh # http://localhost:8080 +./preview.sh docker # nginx + WS proxy +docker compose up -d nexus-preview nexus-backend +``` + +## Files + +- `Dockerfile.preview` — nginx container +- `preview/nginx.conf` — MIME types + WebSocket proxy +- `preview.sh` — Python preview server +- `.github/workflows/pages.yml` — GitHub Pages auto-deploy diff --git a/deploy.sh b/deploy.sh index 76f1fd3b..e7fc1084 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,17 +1,25 @@ #!/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 — Nexus environment +# ./deploy.sh — nexus-main (8765) +# ./deploy.sh staging — nexus-staging (8766) +# ./deploy.sh preview — static preview (8080) +# ./deploy.sh full — preview + backend set -euo pipefail - SERVICE="${1:-nexus-main}" - case "$SERVICE" in staging) SERVICE="nexus-staging" ;; main) SERVICE="nexus-main" ;; + preview) + docker compose build nexus-preview + docker compose up -d --force-recreate nexus-preview + echo "==> http://localhost:8080" + exit 0 ;; + full) + docker compose build nexus-preview nexus-backend + docker compose up -d --force-recreate nexus-preview nexus-backend + echo "==> Preview: http://localhost:8080" + exit 0 ;; esac - -echo "==> Deploying $SERVICE …" docker compose build "$SERVICE" docker compose up -d --force-recreate "$SERVICE" -echo "==> Done. Container: $SERVICE" +echo "==> Done: $SERVICE" diff --git a/docker-compose.yml b/docker-compose.yml index ab351d5c..209aff9b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,9 +7,28 @@ services: restart: unless-stopped ports: - "8765:8765" + nexus-staging: build: . container_name: nexus-staging restart: unless-stopped ports: - - "8766:8765" \ No newline at end of file + - "8766:8765" + + nexus-backend: + build: . + container_name: nexus-backend + restart: unless-stopped + expose: + - "8765" + + nexus-preview: + build: + context: . + dockerfile: Dockerfile.preview + container_name: nexus-preview + restart: unless-stopped + ports: + - "8080:8080" + depends_on: + - nexus-backend diff --git a/preview.sh b/preview.sh new file mode 100755 index 00000000..1aeb7976 --- /dev/null +++ b/preview.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail +PORT="${1:-8080}" +if [ "$PORT" = "docker" ]; then + docker compose up -d nexus-preview + echo "==> http://localhost:8080" + exit 0 +fi +if ! command -v python3 &> /dev/null; then + echo "Error: python3 not found. Use './preview.sh docker'" + exit 1 +fi +echo "==> http://localhost:$PORT" +python3 -c " +import http.server,socketserver +class H(http.server.SimpleHTTPRequestHandler): + def end_headers(self): + self.send_header('Access-Control-Allow-Origin','*') + super().end_headers() + def guess_type(self,p): + if p.endswith(('.js','.mjs')): return 'application/javascript' + if p.endswith('.css'): return 'text/css' + if p.endswith('.json'): return 'application/json' + return super().guess_type(p) +with socketserver.TCPServer(('', $PORT), H) as s: + print(f'Serving http://localhost:{$PORT}'); s.serve_forever() +" diff --git a/preview/nginx.conf b/preview/nginx.conf new file mode 100644 index 00000000..a64d5f25 --- /dev/null +++ b/preview/nginx.conf @@ -0,0 +1,51 @@ +server { + listen 8080; + server_name _; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + } + + location ~* \.js$ { + types { application/javascript js; } + add_header Cache-Control "public, max-age=3600"; + } + + location ~* \.css$ { + types { text/css css; } + add_header Cache-Control "public, max-age=3600"; + } + + location ~* \.json$ { + types { application/json json; } + add_header Cache-Control "no-cache"; + } + + location /api/world/ws { + proxy_pass http://nexus-backend: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_read_timeout 86400; + } + + location /ws { + proxy_pass http://nexus-backend:8765; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_read_timeout 86400; + } + + location /health { + return 200 '{"status":"ok","service":"nexus-preview"}'; + add_header Content-Type application/json; + } +}