7ea78d3b54
- Create detailed deployment documentation - Add Caddyfile.example with security headers and API proxy - Document SystemD service setup for Go API - Include database backup strategy - Add troubleshooting guide - Document OAuth configuration steps - Add build and deployment commands - Test production build successfully (340KB static site)
351 lines
6.9 KiB
Markdown
351 lines
6.9 KiB
Markdown
# Opal-Web Deployment Guide
|
|
|
|
## Overview
|
|
This guide covers deploying the Opal Task Manager PWA to production on `opal.example.com` with Caddy as the web server.
|
|
|
|
## Prerequisites
|
|
- Server with Caddy installed
|
|
- Domain: `opal.example.com` (DNS configured)
|
|
- Authentik OAuth configured at `auth.example.com`
|
|
- Opal Go API server binary built
|
|
|
|
## Architecture
|
|
```
|
|
Browser → Caddy (opal.example.com)
|
|
├─ / → Static PWA (SvelteKit build)
|
|
└─ /api/* → Go API Server (:8080)
|
|
```
|
|
|
|
## Step 1: Build the Frontend
|
|
|
|
### Local Build
|
|
```bash
|
|
cd opal-web
|
|
|
|
# Install dependencies if needed
|
|
bun install
|
|
|
|
# Build for production
|
|
bun run build
|
|
|
|
# Output will be in: build/
|
|
```
|
|
|
|
The build creates a static site ready to deploy.
|
|
|
|
## Step 2: Deploy Static Files
|
|
|
|
### Option A: Direct Copy
|
|
```bash
|
|
# On your local machine
|
|
rsync -avz --delete build/ user@server:/var/www/opal/
|
|
```
|
|
|
|
### Option B: Server-Side Build
|
|
```bash
|
|
# On the server
|
|
cd /opt/opal-web
|
|
git pull
|
|
bun install
|
|
bun run build
|
|
cp -r build/* /var/www/opal/
|
|
```
|
|
|
|
### Set Permissions
|
|
```bash
|
|
sudo chown -R www-data:www-data /var/www/opal
|
|
sudo chmod -R 755 /var/www/opal
|
|
```
|
|
|
|
## Step 3: Configure Backend
|
|
|
|
### Create Environment File
|
|
```bash
|
|
sudo mkdir -p /etc/opal
|
|
sudo nano /etc/opal/opal.env
|
|
```
|
|
|
|
Add:
|
|
```bash
|
|
# Server
|
|
SERVER_ADDR=:8080
|
|
|
|
# Database
|
|
OPAL_DB_PATH=/var/lib/opal/opal.db
|
|
|
|
# OAuth (from Authentik setup)
|
|
OAUTH_ENABLED=true
|
|
OAUTH_ISSUER=https://auth.example.com/application/o/opal/
|
|
OAUTH_CLIENT_ID=your_client_id_from_authentik
|
|
OAUTH_CLIENT_SECRET=your_client_secret_from_authentik
|
|
OAUTH_REDIRECT_URI=https://opal.example.com/auth/callback
|
|
|
|
# JWT (generate with: openssl rand -hex 32)
|
|
JWT_SECRET=your_generated_jwt_secret_here
|
|
JWT_EXPIRY=3600
|
|
|
|
# Refresh Token
|
|
REFRESH_TOKEN_EXPIRY=604800
|
|
```
|
|
|
|
### Create SystemD Service
|
|
```bash
|
|
sudo nano /etc/systemd/system/opal-api.service
|
|
```
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=Opal Task API Server
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=opal
|
|
Group=opal
|
|
WorkingDirectory=/var/lib/opal
|
|
EnvironmentFile=/etc/opal/opal.env
|
|
ExecStart=/usr/local/bin/opal server start --addr :8080 --db /var/lib/opal/opal.db
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
# Security hardening
|
|
NoNewPrivileges=true
|
|
PrivateTmp=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
ReadWritePaths=/var/lib/opal
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
### Setup Database and User
|
|
```bash
|
|
# Create user
|
|
sudo useradd -r -s /bin/false opal
|
|
|
|
# Create directories
|
|
sudo mkdir -p /var/lib/opal
|
|
sudo chown opal:opal /var/lib/opal
|
|
sudo chmod 750 /var/lib/opal
|
|
|
|
# Build and install binary
|
|
cd opal-task
|
|
go build -o opal main.go
|
|
sudo cp opal /usr/local/bin/
|
|
sudo chmod 755 /usr/local/bin/opal
|
|
|
|
# Generate first API key (optional - for CLI access)
|
|
sudo -u opal opal server keygen --name "Admin CLI" --db /var/lib/opal/opal.db
|
|
# Save the generated key!
|
|
|
|
# Start service
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable opal-api
|
|
sudo systemctl start opal-api
|
|
sudo systemctl status opal-api
|
|
```
|
|
|
|
## Step 4: Configure Caddy
|
|
|
|
### Create Caddyfile
|
|
```bash
|
|
sudo nano /etc/caddy/Caddyfile
|
|
```
|
|
|
|
Add:
|
|
```caddy
|
|
opal.example.com {
|
|
# Root directory for static PWA
|
|
root * /var/www/opal
|
|
|
|
# API proxy
|
|
handle /api/* {
|
|
uri strip_prefix /api
|
|
reverse_proxy localhost:8080
|
|
}
|
|
|
|
# Static file serving with SPA fallback
|
|
handle {
|
|
try_files {path} /index.html
|
|
file_server
|
|
}
|
|
|
|
# Security headers
|
|
header {
|
|
# HTTPS only
|
|
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
|
|
# Prevent clickjacking
|
|
X-Frame-Options "SAMEORIGIN"
|
|
|
|
# XSS protection
|
|
X-Content-Type-Options "nosniff"
|
|
|
|
# CSP (adjust as needed)
|
|
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://auth.example.com;"
|
|
}
|
|
|
|
# Logging
|
|
log {
|
|
output file /var/log/caddy/opal.log
|
|
format json
|
|
}
|
|
|
|
# Compression
|
|
encode gzip
|
|
}
|
|
```
|
|
|
|
### Test and Reload Caddy
|
|
```bash
|
|
# Test configuration
|
|
sudo caddy validate --config /etc/caddy/Caddyfile
|
|
|
|
# Reload
|
|
sudo systemctl reload caddy
|
|
|
|
# Check status
|
|
sudo systemctl status caddy
|
|
```
|
|
|
|
## Step 5: Verify Deployment
|
|
|
|
### Check Services
|
|
```bash
|
|
# API server
|
|
sudo systemctl status opal-api
|
|
sudo journalctl -u opal-api -n 50
|
|
|
|
# Caddy
|
|
sudo systemctl status caddy
|
|
sudo journalctl -u caddy -n 50
|
|
```
|
|
|
|
### Test Endpoints
|
|
```bash
|
|
# Health check
|
|
curl https://opal.example.com/api/health
|
|
|
|
# OAuth login URL
|
|
curl https://opal.example.com/api/auth/login
|
|
|
|
# Static files
|
|
curl -I https://opal.example.com/
|
|
```
|
|
|
|
### Browser Testing
|
|
1. Navigate to `https://opal.example.com`
|
|
2. Should see the Opal login page
|
|
3. Click "Login with OAuth"
|
|
4. Should redirect to Authentik
|
|
5. After login, should redirect back to task list
|
|
6. Test creating a task
|
|
7. Test PWA install prompt (mobile/desktop)
|
|
|
|
## Step 6: Update Deployment (Future)
|
|
|
|
```bash
|
|
# Frontend update
|
|
cd opal-web
|
|
git pull
|
|
bun install
|
|
bun run build
|
|
rsync -avz --delete build/ server:/var/www/opal/
|
|
|
|
# Backend update
|
|
cd opal-task
|
|
git pull
|
|
go build -o opal main.go
|
|
scp opal server:/tmp/
|
|
ssh server "sudo systemctl stop opal-api && sudo cp /tmp/opal /usr/local/bin/ && sudo systemctl start opal-api"
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### API Not Responding
|
|
```bash
|
|
# Check if running
|
|
sudo systemctl status opal-api
|
|
|
|
# Check logs
|
|
sudo journalctl -u opal-api -f
|
|
|
|
# Test locally
|
|
curl http://localhost:8080/health
|
|
```
|
|
|
|
### OAuth Not Working
|
|
- Verify redirect URI in Authentik matches exactly
|
|
- Check OAuth client ID and secret in env file
|
|
- Ensure JWT secret is set
|
|
- Check Caddy is proxying /api/* correctly
|
|
|
|
### PWA Not Installing
|
|
- Ensure HTTPS is working
|
|
- Check manifest.json is accessible
|
|
- Verify service worker is registered (Browser DevTools → Application)
|
|
- Icons must be served correctly
|
|
|
|
### Static Files 404
|
|
- Check file permissions in /var/www/opal
|
|
- Verify Caddy root directive points to correct path
|
|
- Ensure index.html exists in root
|
|
|
|
## Monitoring
|
|
|
|
### Logs
|
|
```bash
|
|
# API logs
|
|
sudo journalctl -u opal-api -f
|
|
|
|
# Caddy logs
|
|
sudo tail -f /var/log/caddy/opal.log
|
|
|
|
# System logs
|
|
dmesg | grep opal
|
|
```
|
|
|
|
### Database Backup
|
|
```bash
|
|
# Backup script
|
|
#!/bin/bash
|
|
BACKUP_DIR="/var/backups/opal"
|
|
DATE=$(date +%Y%m%d_%H%M%S)
|
|
|
|
mkdir -p "$BACKUP_DIR"
|
|
sudo -u opal sqlite3 /var/lib/opal/opal.db ".backup '$BACKUP_DIR/opal_$DATE.db'"
|
|
|
|
# Keep last 7 days
|
|
find "$BACKUP_DIR" -name "opal_*.db" -mtime +7 -delete
|
|
```
|
|
|
|
Add to crontab:
|
|
```bash
|
|
0 2 * * * /usr/local/bin/backup-opal.sh
|
|
```
|
|
|
|
## Security Checklist
|
|
- ✅ HTTPS enforced (Caddy auto)
|
|
- ✅ OAuth2 authentication configured
|
|
- ✅ JWT tokens with expiry
|
|
- ✅ API key hashing (bcrypt)
|
|
- ✅ SystemD service hardening
|
|
- ✅ Database file permissions (750)
|
|
- ✅ Security headers (CSP, HSTS, etc.)
|
|
- ✅ Regular backups configured
|
|
- ⬜ Fail2ban for API rate limiting (optional)
|
|
- ⬜ Log monitoring/alerting (optional)
|
|
|
|
## Performance Tips
|
|
- Caddy handles compression and HTTP/2
|
|
- Service worker caches static assets
|
|
- API responses cached by service worker
|
|
- Consider CDN for static assets (future)
|
|
|
|
## Support
|
|
- Check logs first
|
|
- Verify all environment variables
|
|
- Test OAuth flow in incognito
|
|
- Check network tab in DevTools
|