initial deployment v1.0
This commit is contained in:
372
DEPLOY.md
Normal file
372
DEPLOY.md
Normal file
@@ -0,0 +1,372 @@
|
||||
# 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: 2–32 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
|
||||
```
|
||||
Reference in New Issue
Block a user