feat: add database migration guide, update env examples, and add nginx reverse proxy config

Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-10-19 04:42:01 +00:00
parent 6e9ad8fd69
commit bd04fd3438
7 changed files with 388 additions and 8 deletions

3
.gitignore vendored
View File

@@ -89,4 +89,7 @@ frontend/dist/
frontend/build/
backend/logs/
# Nginx SSL certificates
nginx/ssl/
*.zip

291
MIGRATION.md Normal file
View File

@@ -0,0 +1,291 @@
# Database Migration Guide: SQLite to PostgreSQL
This guide explains how to migrate your Discord Spywatcher database from SQLite to PostgreSQL when switching to Docker.
## Overview
The application now supports PostgreSQL as the primary database for production deployments. This provides better:
- Concurrency handling
- Data integrity
- Scalability
- Production-ready features
## For New Installations
If you're starting fresh with Docker, no migration is needed. Simply:
1. Start the Docker environment:
```bash
docker-compose -f docker-compose.dev.yml up
```
2. The PostgreSQL database will be initialized automatically with all migrations.
## For Existing Installations (SQLite → PostgreSQL)
If you have existing data in SQLite and want to migrate to PostgreSQL:
### Option 1: Start Fresh (Recommended for Development)
If your existing data is test data or not critical:
1. Backup your existing data (optional):
```bash
cp backend/prisma/dev.db backend/prisma/dev.db.backup
```
2. Start with Docker:
```bash
docker-compose -f docker-compose.dev.yml up
```
3. Your data will be in the new PostgreSQL database (empty initially).
### Option 2: Manual Migration (For Production Data)
If you need to preserve existing SQLite data:
#### Step 1: Export SQLite Data
```bash
cd backend
# Export data to SQL format
sqlite3 prisma/dev.db .dump > sqlite_export.sql
# Or use Prisma Studio to export data manually
npx prisma studio
```
#### Step 2: Transform and Import to PostgreSQL
1. Start PostgreSQL with Docker:
```bash
docker-compose -f docker-compose.dev.yml up postgres -d
```
2. Run migrations on PostgreSQL:
```bash
docker-compose -f docker-compose.dev.yml exec postgres psql -U spywatcher -d spywatcher
```
3. Transform SQLite SQL to PostgreSQL format:
SQLite and PostgreSQL have syntax differences. You'll need to:
- Remove SQLite-specific syntax
- Adjust data types
- Handle AUTOINCREMENT → SERIAL/BIGSERIAL conversions
- Fix boolean values (0/1 → false/true)
4. Import the transformed data:
```bash
docker-compose -f docker-compose.dev.yml exec -T postgres psql -U spywatcher -d spywatcher < postgres_import.sql
```
#### Step 3: Verify Data
```bash
# Connect to PostgreSQL
docker-compose -f docker-compose.dev.yml exec postgres psql -U spywatcher -d spywatcher
# Check tables
\dt
# Verify data
SELECT COUNT(*) FROM "User";
SELECT COUNT(*) FROM "PresenceEvent";
# ... check other tables
```
### Option 3: Using Migration Tools
For automated migration, you can use tools like:
#### pgloader (Recommended)
```bash
# Install pgloader (if not using Docker)
# Ubuntu/Debian: apt-get install pgloader
# macOS: brew install pgloader
# Create migration config
cat > migrate.load <<EOF
LOAD DATABASE
FROM sqlite://backend/prisma/dev.db
INTO postgresql://spywatcher:password@localhost:5432/spywatcher
WITH include drop, create tables, create indexes, reset sequences
SET work_mem to '16MB', maintenance_work_mem to '512 MB';
EOF
# Run migration
pgloader migrate.load
```
## Schema Changes
The main schema change from SQLite to PostgreSQL:
### Guild Model
- **SQLite**: `permissions Int`
- **PostgreSQL**: `permissions BigInt`
This change is necessary because Discord permission values can exceed the maximum value of a 32-bit integer.
### Other Models
All other models remain compatible between SQLite and PostgreSQL.
## Switching Between SQLite and PostgreSQL
### Use SQLite (Local Development Without Docker)
1. Update `backend/prisma/schema.prisma`:
```prisma
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Guild {
// ... other fields
permissions Int // Use Int for SQLite
// ... other fields
}
```
2. Update `.env`:
```env
DATABASE_URL="file:./prisma/dev.db"
```
3. Generate Prisma Client and migrate:
```bash
cd backend
npx prisma generate
npx prisma migrate dev
```
### Use PostgreSQL (Docker or Production)
1. Update `backend/prisma/schema.prisma`:
```prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Guild {
// ... other fields
permissions BigInt // Use BigInt for PostgreSQL
// ... other fields
}
```
2. Update `.env`:
```env
DATABASE_URL="postgresql://spywatcher:password@localhost:5432/spywatcher"
```
3. Generate Prisma Client and migrate:
```bash
cd backend
npx prisma generate
npx prisma migrate deploy
```
## Production Deployment
For production deployment with PostgreSQL:
1. Set up PostgreSQL database (RDS, Cloud SQL, etc.)
2. Configure environment variables:
```env
DATABASE_URL="postgresql://user:password@host:5432/database?sslmode=require"
```
3. Run migrations:
```bash
docker-compose -f docker-compose.prod.yml exec backend npx prisma migrate deploy
```
4. Verify deployment:
```bash
docker-compose -f docker-compose.prod.yml exec backend npx prisma db seed
```
## Troubleshooting
### Connection Issues
**Problem**: Cannot connect to PostgreSQL
```
Error: P1001: Can't reach database server at `postgres:5432`
```
**Solution**:
- Ensure PostgreSQL container is running: `docker-compose -f docker-compose.dev.yml ps`
- Check network connectivity between containers
- Verify DATABASE_URL is correct
### Migration Failures
**Problem**: Migration fails with schema mismatch
```
Error: P3009: migrate found failed migrations
```
**Solution**:
```bash
# Reset migrations (WARNING: This will delete all data)
docker-compose -f docker-compose.dev.yml exec backend npx prisma migrate reset
# Or mark migrations as applied
docker-compose -f docker-compose.dev.yml exec backend npx prisma migrate resolve --applied "migration_name"
```
### Permission Errors
**Problem**: Permission denied for PostgreSQL
```
Error: FATAL: password authentication failed for user "spywatcher"
```
**Solution**:
- Check DB_PASSWORD in `.env` matches PostgreSQL configuration
- Recreate PostgreSQL container with correct credentials:
```bash
docker-compose -f docker-compose.dev.yml down -v
docker-compose -f docker-compose.dev.yml up postgres -d
```
### Data Type Issues
**Problem**: BigInt serialization errors in JavaScript
```
Do not know how to serialize a BigInt
```
**Solution**:
Add BigInt serialization support in your code:
```javascript
BigInt.prototype.toJSON = function() {
return this.toString();
};
```
## Best Practices
1. **Always backup before migration**: Create backups of your SQLite database before attempting migration
2. **Test migrations**: Test the migration process in a development environment first
3. **Use transactions**: When manually importing data, use transactions to ensure data consistency
4. **Verify data integrity**: After migration, verify row counts and sample data in all tables
5. **Update application code**: Ensure your application properly handles BigInt types for Discord permissions
## Support
For issues or questions about database migration:
- Check the [DOCKER.md](./DOCKER.md) for Docker-specific troubleshooting
- Review [Prisma Migration Docs](https://www.prisma.io/docs/concepts/components/prisma-migrate)
- Open an issue on GitHub with details about your migration problem

View File

@@ -1,4 +1,11 @@
# This is an example of the backend environment variables file.
# Database Configuration
# For SQLite (legacy): file:./prisma/dev.db
# For PostgreSQL (recommended): postgresql://user:password@host:5432/database
DATABASE_URL=postgresql://spywatcher:your_password@localhost:5432/spywatcher
# Discord Configuration
ADMIN_DISCORD_IDS=your_admin_discord_ids
BOT_GUILD_IDS=your_bot_guild_ids
DISCORD_BOT_TOKEN=your_discord_bot_token
@@ -6,7 +13,11 @@ DISCORD_CLIENT_ID=your_discord_client_id
DISCORD_CLIENT_SECRET=your_discord_client_secret
DISCORD_GUILD_ID=your_discord_guild_id
DISCORD_REDIRECT_URI=your_discord_redirect_uri
# JWT Secrets
JWT_REFRESH_SECRET=your_jwt_refresh_secret
JWT_SECRET=your_jwt_secret
NODE_ENV=your_node_env
PORT=your_port
# Server Configuration
NODE_ENV=development
PORT=3001

View File

@@ -1,5 +1,3 @@
version: '3.8'
services:
postgres:
image: postgres:15-alpine

View File

@@ -1,5 +1,3 @@
version: '3.8'
services:
postgres:
image: postgres:15-alpine

View File

@@ -1,5 +1,3 @@
version: '3.8'
services:
postgres-test:
image: postgres:15-alpine

81
nginx/nginx.conf Normal file
View File

@@ -0,0 +1,81 @@
events {
worker_connections 1024;
}
http {
upstream backend {
server backend:3001;
}
upstream frontend {
server frontend:80;
}
# Rate limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=general_limit:10m rate=50r/s;
server {
listen 80;
server_name _;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# API proxy
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend;
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;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Frontend proxy
location / {
limit_req zone=general_limit burst=100 nodelay;
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# Health check endpoint
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}
# SSL configuration (uncomment and configure for production)
# server {
# listen 443 ssl http2;
# server_name your-domain.com;
#
# ssl_certificate /etc/nginx/ssl/cert.pem;
# ssl_certificate_key /etc/nginx/ssl/key.pem;
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
#
# # Include the same location blocks as above
# }
}