diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index add4c7f..dd3a67b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build +name: Build and Deploy on: push: @@ -9,6 +9,7 @@ on: jobs: build: runs-on: linux_amd64 + timeout-minutes: 15 steps: - name: Checkout code @@ -16,13 +17,43 @@ jobs: - name: Install dependencies run: npm ci + timeout-minutes: 5 - name: Build Front End run: npm run build + timeout-minutes: 10 env: NODE_OPTIONS: '--max-old-space-size=4096' - - name: Restart Server - run: | - # Start server in background with nohup - nohup npm run start > /tmp/immersive.log 2>&1 & \ No newline at end of file + - name: Stop Service + run: | + sudo rc-service immersive stop || true + + - name: Deploy to /opt/immersive + run: | + # Remove old deployment (keep data directory if it exists) + rm -rf /opt/immersive.old + [ -d /opt/immersive ] && mv /opt/immersive /opt/immersive.old + + # Copy built files to target + mkdir -p /opt/immersive + cp -r . /opt/immersive/ + + # Remove unnecessary directories + rm -rf /opt/immersive/.git /opt/immersive/.github + + # Restore data directory from old deployment if it existed + [ -d /opt/immersive.old/data ] && mv /opt/immersive.old/data /opt/immersive/ + + # Clean up old deployment + rm -rf /opt/immersive.old + + # Set permissions on start.sh + chmod +x /opt/immersive/start.sh + + # Set ownership to immersive user + sudo chown -R immersive:immersive /opt/immersive + + - name: Start Service + run: | + sudo rc-service immersive start \ No newline at end of file diff --git a/.gitignore b/.gitignore index a7762e1..ea7f841 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ dist-ssr # Local Netlify folder .netlify /data/ +/.env.production diff --git a/ALPINE_SERVICE.md b/ALPINE_SERVICE.md new file mode 100644 index 0000000..6b0697d --- /dev/null +++ b/ALPINE_SERVICE.md @@ -0,0 +1,280 @@ +# Alpine Linux Service Setup + +This guide covers installing and running Immersive as a service on Alpine Linux using OpenRC. + +## Prerequisites + +```bash +# Update packages +apk update + +# Install Node.js 18+ and npm +apk add nodejs npm + +# Install build dependencies (required for native modules like leveldown) +apk add python3 make g++ git + +# Verify Node version (must be >= 18) +node --version +``` + +## Create Service User + +Create a dedicated user to run the service (security best practice): + +```bash +# Create immersive group and user (no login shell, no home directory) +addgroup -S immersive +adduser -S -G immersive -H -s /sbin/nologin immersive + +# Create directories with proper ownership +mkdir -p /opt/immersive +mkdir -p /var/log/immersive +mkdir -p /var/run/immersive + +chown -R immersive:immersive /opt/immersive +chown -R immersive:immersive /var/log/immersive +chown -R immersive:immersive /var/run/immersive +``` + +## Installation + +```bash +# Create application directory +mkdir -p /opt/immersive +cd /opt/immersive + +# Clone or copy the application +git clone . +# OR copy files manually + +# Install dependencies +npm ci --production=false + +# Build the application +NODE_OPTIONS='--max-old-space-size=4096' npm run build + +# Copy Havok physics WASM (if not already done by build) +npm run havok + +# Create data directory for PouchDB +mkdir -p /opt/immersive/data + +# Set ownership to immersive user +chown -R immersive:immersive /opt/immersive +``` + +## Start Script + +The `start.sh` script is included in the repository. After deployment, ensure it's executable: + +```bash +chmod +x /opt/immersive/start.sh +``` + +The script sets up the environment and starts the Node.js server, logging output to `/var/log/immersive/`. + +## OpenRC Service + +Create `/etc/init.d/immersive`: + +```bash +#!/sbin/openrc-run + +name="immersive" +description="Immersive WebXR Diagramming Application" + +command="/opt/immersive/start.sh" +command_user="immersive:immersive" +command_background="yes" +pidfile="/var/run/immersive/immersive.pid" + +directory="/opt/immersive" +output_log="/var/log/immersive/app.log" +error_log="/var/log/immersive/error.log" + +depend() { + need net + after firewall +} + +start_pre() { + checkpath --directory --owner immersive:immersive --mode 0755 /var/log/immersive + checkpath --directory --owner immersive:immersive --mode 0755 /var/run/immersive + checkpath --file --owner immersive:immersive --mode 0644 /var/log/immersive/app.log + checkpath --file --owner immersive:immersive --mode 0644 /var/log/immersive/error.log +} +``` + +Make it executable and enable: + +```bash +chmod +x /etc/init.d/immersive +rc-update add immersive default +``` + +## Service Management + +```bash +# Start the service +rc-service immersive start + +# Stop the service +rc-service immersive stop + +# Restart the service +rc-service immersive restart + +# Check status +rc-service immersive status + +# View logs +tail -f /var/log/immersive/app.log +tail -f /var/log/immersive/error.log +``` + +## Log Rotation + +Create `/etc/logrotate.d/immersive`: + +``` +/var/log/immersive/*.log { + daily + rotate 7 + compress + delaycompress + missingok + notifempty + create 0644 immersive immersive + postrotate + rc-service immersive restart > /dev/null 2>&1 || true + endscript +} +``` + +Install logrotate if not present: + +```bash +apk add logrotate +``` + +## Environment Variables + +Create `/opt/immersive/.env.production` for production settings: + +```bash +# Server +NODE_ENV=production +PORT=3001 + +# Auth0 (if using authentication) +# VITE_AUTH0_DOMAIN=your-domain.auth0.com +# VITE_AUTH0_CLIENT_ID=your-client-id + +# Database sync endpoint (optional) +# VITE_SYNCDB_ENDPOINT=https://your-couchdb-server.com +``` + +## Firewall (if using iptables) + +```bash +# Allow port 3001 +iptables -A INPUT -p tcp --dport 3001 -j ACCEPT + +# Save rules +rc-service iptables save +``` + +## Reverse Proxy (Optional) + +If using nginx as a reverse proxy: + +```bash +apk add nginx +``` + +Create `/etc/nginx/http.d/immersive.conf`: + +```nginx +server { + listen 80; + server_name your-domain.com; + + location / { + proxy_pass http://127.0.0.1:3001; + 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_cache_bypass $http_upgrade; + } +} +``` + +Enable and start nginx: + +```bash +rc-update add nginx default +rc-service nginx start +``` + +## Gitea CI/CD Runner (Optional) + +If using a Gitea Actions runner to deploy, grant the runner user write access to `/opt/immersive`: + +```bash +# Add gitea-runner to immersive group +adduser gitea-runner immersive + +# Set group write permissions on /opt/immersive +chmod -R g+w /opt/immersive + +# Ensure new files inherit group ownership +chmod g+s /opt/immersive + +# Allow runner to manage the service +# Add to /etc/sudoers.d/gitea-runner: +echo 'gitea-runner ALL=(ALL) NOPASSWD: /sbin/rc-service immersive *' > /etc/sudoers.d/gitea-runner +echo 'gitea-runner ALL=(ALL) NOPASSWD: /bin/chown -R immersive\:immersive /opt/immersive' >> /etc/sudoers.d/gitea-runner +chmod 440 /etc/sudoers.d/gitea-runner +``` + +The GitHub Actions workflow in `.github/workflows/build.yml` will handle deployment automatically on push to main. + +## Troubleshooting + +**Service fails to start:** +```bash +# Check logs +cat /var/log/immersive/error.log + +# Run manually as immersive user to see errors +su -s /bin/sh immersive -c "cd /opt/immersive && NODE_ENV=production node server.js" +``` + +**Native module errors (leveldown):** +```bash +# Rebuild native modules +cd /opt/immersive +npm rebuild leveldown +``` + +**Permission issues:** +```bash +# Ensure proper ownership (must be immersive user) +chown -R immersive:immersive /opt/immersive +chown -R immersive:immersive /var/log/immersive +chown -R immersive:immersive /var/run/immersive +chmod -R 755 /opt/immersive +``` + +**Port already in use:** +```bash +# Find process using port 3001 +lsof -i :3001 +# Or +netstat -tlnp | grep 3001 +``` diff --git a/package.json b/package.json index 6dbf8e7..80cb498 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "immersive", "private": true, - "version": "0.0.8-43", + "version": "0.0.8-44", "type": "module", "license": "MIT", "engines": { diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..3a0135c --- /dev/null +++ b/start.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# Immersive start script for Alpine Linux + +APP_DIR="/opt/immersive" +LOG_DIR="/var/log/immersive" +PID_FILE="/var/run/immersive.pid" + +cd "$APP_DIR" + +# Environment +export NODE_ENV=production +export NODE_OPTIONS="--max-old-space-size=2048" + +# Optional: Set port (default 3001) +# export PORT=3001 + +# Start the server +exec node server.js >> "$LOG_DIR/app.log" 2>> "$LOG_DIR/error.log" \ No newline at end of file