Files
etcprs/DEPLOY.md
2026-03-18 03:06:27 -06:00

373 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Deployment Guide — ETC PRS Viewer & Editor
This guide covers deploying the app to a **Digital Ocean Droplet** running Ubuntu 24.04.
---
## Prerequisites
- A Digital Ocean Droplet (1 GB RAM minimum, 2 GB recommended)
- A domain name pointed at your droplet's IP
- SSH access to the droplet
---
## 1. Initial Server Setup
```bash
# Update packages
sudo apt update && sudo apt upgrade -y
# Install Node.js 20 (LTS)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Verify
node --version # should be v20.x
npm --version
# Install build tools (needed for better-sqlite3)
sudo apt install -y build-essential python3
# Install PM2 globally
sudo npm install -g pm2
# Install Nginx
sudo apt install -y nginx
# Install Certbot for SSL
sudo apt install -y certbot python3-certbot-nginx
```
---
## 2. Create the App User and Directories
```bash
# Create a dedicated user for the app (no login shell)
sudo useradd --system --shell /bin/false --home /opt/etc-prs prs
# Create app directory
sudo mkdir -p /opt/etc-prs/app
sudo chown -R prs:prs /opt/etc-prs
# Create data directory for SQLite (outside the app, survives redeployment)
sudo mkdir -p /var/lib/etc-prs
sudo chown -R prs:prs /var/lib/etc-prs
# Create log directory
sudo mkdir -p /var/log/etc-prs
sudo chown -R prs:prs /var/log/etc-prs
```
---
## 3. Deploy the Application
```bash
# Clone or copy your project to the server
# Option A: Git
cd /opt/etc-prs
sudo -u prs git clone https://github.com/yourusername/etc-prs-ui.git app
# Option B: Copy files via scp from your local machine
# scp -r ./etc-prs-ui user@your-droplet-ip:/tmp/
# sudo mv /tmp/etc-prs-ui /opt/etc-prs/app
# sudo chown -R prs:prs /opt/etc-prs/app
cd /opt/etc-prs/app
# Install dependencies (as the prs user)
sudo -u prs npm install
# Build the app
sudo -u prs npm run build
```
---
## 4. Environment Configuration
```bash
# Create the production .env file
sudo -u prs nano /opt/etc-prs/app/.env
```
Add the following contents:
```env
DATABASE_URL=/var/lib/etc-prs/personalities.db
RATE_LIMIT_PUBLISH=5
RATE_LIMIT_READ=100
PUBLIC_BASE_URL=https://yourdomain.com
```
Replace `yourdomain.com` with your actual domain.
---
## 5. Configure PM2
Create the PM2 ecosystem file:
```bash
sudo -u prs nano /opt/etc-prs/app/ecosystem.config.cjs
```
```javascript
module.exports = {
apps: [{
name: 'etc-prs',
script: '/opt/etc-prs/app/build/index.js',
cwd: '/opt/etc-prs/app',
user: 'prs',
env: {
NODE_ENV: 'production',
PORT: '3000',
HOST: '127.0.0.1',
},
error_file: '/var/log/etc-prs/error.log',
out_file: '/var/log/etc-prs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
restart_delay: 3000,
max_restarts: 10,
}]
};
```
Start the app with PM2:
```bash
# Start
sudo -u prs pm2 start /opt/etc-prs/app/ecosystem.config.cjs
# Save the process list so PM2 restores it on reboot
sudo -u prs pm2 save
# Set PM2 to start on system boot
sudo pm2 startup systemd -u prs --hp /opt/etc-prs
# Run the command PM2 outputs
```
Verify the app is running:
```bash
sudo -u prs pm2 status
# Should show etc-prs as "online"
# Check logs
sudo -u prs pm2 logs etc-prs --lines 50
```
---
## 6. Configure Nginx
```bash
sudo nano /etc/nginx/sites-available/etc-prs
```
```nginx
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# Gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
# Proxy to SvelteKit
location / {
proxy_pass http://127.0.0.1:3000;
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;
# Increase timeout for large uploads
proxy_read_timeout 30s;
client_max_body_size 2M;
}
}
```
Enable and test:
```bash
sudo ln -s /etc/nginx/sites-available/etc-prs /etc/nginx/sites-enabled/
sudo nginx -t # should say "syntax is ok"
sudo systemctl reload nginx
```
---
## 7. SSL with Let's Encrypt
```bash
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
```
Follow the prompts. Certbot will automatically modify your Nginx config to add HTTPS and set up auto-renewal.
Verify auto-renewal works:
```bash
sudo certbot renew --dry-run
```
---
## 8. Database Backups
Set up a nightly cron job to back up the SQLite database:
```bash
sudo mkdir -p /var/backups/etc-prs
# Create backup script
sudo nano /opt/etc-prs/backup.sh
```
```bash
#!/bin/bash
DATE=$(date +%Y-%m-%d)
BACKUP_DIR=/var/backups/etc-prs
DB_PATH=/var/lib/etc-prs/personalities.db
# Create backup using SQLite's online backup
sqlite3 "$DB_PATH" ".backup $BACKUP_DIR/personalities-$DATE.db"
# Keep only the last 30 days of backups
find "$BACKUP_DIR" -name "personalities-*.db" -mtime +30 -delete
echo "Backup completed: personalities-$DATE.db"
```
```bash
sudo chmod +x /opt/etc-prs/backup.sh
# Add to cron (runs at 2am daily)
sudo crontab -e
```
Add this line:
```cron
0 2 * * * /opt/etc-prs/backup.sh >> /var/log/etc-prs/backup.log 2>&1
```
---
## 9. Redeployment
When you push updates:
```bash
cd /opt/etc-prs/app
# Pull latest code
sudo -u prs git pull
# Install any new dependencies
sudo -u prs npm install
# Rebuild
sudo -u prs npm run build
# Restart the app (zero-downtime reload)
sudo -u prs pm2 reload etc-prs
```
The SQLite database at `/var/lib/etc-prs/personalities.db` is **never touched** by redeployment since it lives outside the app directory.
---
## 10. Useful Commands
```bash
# View live logs
sudo -u prs pm2 logs etc-prs
# Restart app
sudo -u prs pm2 restart etc-prs
# Stop app
sudo -u prs pm2 stop etc-prs
# Check Nginx status
sudo systemctl status nginx
# View Nginx error log
sudo tail -f /var/log/nginx/error.log
# Open SQLite database directly
sqlite3 /var/lib/etc-prs/personalities.db
# Example queries
sqlite3 /var/lib/etc-prs/personalities.db "SELECT id, name, manufacturer, channel_count, created_at FROM personalities ORDER BY created_at DESC LIMIT 20;"
sqlite3 /var/lib/etc-prs/personalities.db "SELECT COUNT(*) FROM personalities;"
# Delete a personality by ID (if you need to moderate)
sqlite3 /var/lib/etc-prs/personalities.db "DELETE FROM personalities WHERE id = 'the_id_here';"
```
---
## 11. Firewall
```bash
# Allow SSH, HTTP, and HTTPS
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
```
---
## 12. Creating Admin Users
Admins are managed via a CLI script — there is no self-registration UI.
```bash
# Create your first admin (run from the app directory on the server)
cd /opt/etc-prs/app
node scripts/create-admin.js your-username your-password
# To update an existing admin's password (same command — it upserts)
node scripts/create-admin.js your-username new-password
# To add another admin
node scripts/create-admin.js another-user their-password
```
Requirements:
- Username: 232 characters
- Password: minimum 8 characters (use something strong)
The admin panel is available at `https://yourdomain.com/admin`.
---
## Local Development
```bash
# Install dependencies
npm install
# Start dev server (uses ./dev.db automatically)
npm run dev
# The app runs at http://localhost:5173
# SQLite database is created at ./dev.db on first run
```