forked from andika/membership-be
Prod Deployment Preparation
This commit is contained in:
379
DEPLOYMENT.md
Normal file
379
DEPLOYMENT.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# Deployment Guide - LOAF Membership Platform
|
||||
|
||||
## Fresh Database Installation
|
||||
|
||||
Follow these steps in order for a **brand new deployment**:
|
||||
|
||||
### Step 1: Create PostgreSQL Database
|
||||
|
||||
```bash
|
||||
# Connect to PostgreSQL
|
||||
psql -U postgres
|
||||
|
||||
# Create database
|
||||
CREATE DATABASE membership_db;
|
||||
|
||||
# Create user (if needed)
|
||||
CREATE USER loaf_admin WITH PASSWORD 'your-secure-password';
|
||||
GRANT ALL PRIVILEGES ON DATABASE membership_db TO loaf_admin;
|
||||
|
||||
# Exit PostgreSQL
|
||||
\q
|
||||
```
|
||||
|
||||
### Step 2: Run Initial Schema
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
|
||||
# Apply the complete schema (creates all 17 tables, 8 enums, indexes)
|
||||
psql -U loaf_admin -d membership_db -f migrations/000_initial_schema.sql
|
||||
```
|
||||
|
||||
**What this creates:**
|
||||
- ✅ 17 tables: users, events, subscriptions, roles, permissions, etc.
|
||||
- ✅ 8 custom enums: UserStatus, UserRole, RSVPStatus, etc.
|
||||
- ✅ All indexes and foreign keys
|
||||
- ✅ All constraints and defaults
|
||||
|
||||
### Step 3: Mark Database for Alembic Tracking
|
||||
|
||||
```bash
|
||||
# Mark the database as being at the baseline
|
||||
alembic stamp head
|
||||
```
|
||||
|
||||
### Step 4: Verify Setup
|
||||
|
||||
```bash
|
||||
# Check Alembic status
|
||||
alembic current
|
||||
# Expected output: 001_initial_baseline (head)
|
||||
|
||||
# Check database tables
|
||||
psql -U loaf_admin -d membership_db -c "\dt"
|
||||
# Should show 17 tables
|
||||
```
|
||||
|
||||
### Step 5: Set Environment Variables
|
||||
|
||||
Create `backend/.env`:
|
||||
|
||||
```env
|
||||
# Database
|
||||
DATABASE_URL=postgresql://loaf_admin:your-password@localhost:5432/membership_db
|
||||
|
||||
# JWT
|
||||
JWT_SECRET=your-secret-key-minimum-32-characters-long
|
||||
JWT_ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
|
||||
# Email (SMTP)
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USERNAME=your-email@gmail.com
|
||||
SMTP_PASSWORD=your-app-password
|
||||
SMTP_FROM_EMAIL=noreply@loafmembers.org
|
||||
SMTP_FROM_NAME=LOAF Membership
|
||||
|
||||
# Frontend URL
|
||||
FRONTEND_URL=https://members.loafmembers.org
|
||||
|
||||
# Cloudflare R2
|
||||
R2_ENDPOINT_URL=https://your-account-id.r2.cloudflarestorage.com
|
||||
R2_ACCESS_KEY_ID=your-r2-access-key
|
||||
R2_SECRET_ACCESS_KEY=your-r2-secret-key
|
||||
R2_BUCKET_NAME=loaf-membership
|
||||
R2_PUBLIC_URL=https://cdn.loafmembers.org
|
||||
|
||||
# Stripe
|
||||
STRIPE_SECRET_KEY=sk_live_...
|
||||
STRIPE_WEBHOOK_SECRET=whsec_...
|
||||
STRIPE_PRICE_ID_ANNUAL=price_...
|
||||
STRIPE_PRICE_ID_GROUP=price_...
|
||||
```
|
||||
|
||||
### Step 6: Install Dependencies
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd backend
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Frontend
|
||||
cd ../frontend
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Step 7: Start Services
|
||||
|
||||
```bash
|
||||
# Backend (in backend/)
|
||||
uvicorn server:app --host 0.0.0.0 --port 8000
|
||||
|
||||
# Frontend (in frontend/)
|
||||
yarn start
|
||||
```
|
||||
|
||||
### Step 8: Create First Superadmin User
|
||||
|
||||
```bash
|
||||
# Connect to database
|
||||
psql -U loaf_admin -d membership_db
|
||||
|
||||
# Create superadmin user
|
||||
INSERT INTO users (
|
||||
id, email, password_hash, first_name, last_name,
|
||||
status, role, email_verified, created_at, updated_at
|
||||
) VALUES (
|
||||
gen_random_uuid(),
|
||||
'admin@loafmembers.org',
|
||||
'$2b$12$your-bcrypt-hashed-password-here', -- Use bcrypt to hash password
|
||||
'Admin',
|
||||
'User',
|
||||
'active',
|
||||
'superadmin',
|
||||
true,
|
||||
NOW(),
|
||||
NOW()
|
||||
);
|
||||
```
|
||||
|
||||
**Generate password hash:**
|
||||
```python
|
||||
import bcrypt
|
||||
password = b"your-secure-password"
|
||||
hashed = bcrypt.hashpw(password, bcrypt.gensalt())
|
||||
print(hashed.decode())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Existing Database Update
|
||||
|
||||
For **updating an existing deployment** with new code:
|
||||
|
||||
### Step 1: Backup Database
|
||||
|
||||
```bash
|
||||
pg_dump -U loaf_admin membership_db > backup_$(date +%Y%m%d_%H%M%S).sql
|
||||
```
|
||||
|
||||
### Step 2: Pull Latest Code
|
||||
|
||||
```bash
|
||||
git pull origin main
|
||||
```
|
||||
|
||||
### Step 3: Install New Dependencies
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd backend
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Frontend
|
||||
cd ../frontend
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Step 4: Apply Database Migrations
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
|
||||
# Check current migration status
|
||||
alembic current
|
||||
|
||||
# Apply pending migrations
|
||||
alembic upgrade head
|
||||
|
||||
# Verify
|
||||
alembic current
|
||||
```
|
||||
|
||||
### Step 5: Restart Services
|
||||
|
||||
```bash
|
||||
# Restart backend
|
||||
systemctl restart membership-backend
|
||||
|
||||
# Rebuild and restart frontend
|
||||
cd frontend
|
||||
yarn build
|
||||
systemctl restart membership-frontend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## First-Time Alembic Setup (Existing Database)
|
||||
|
||||
If you have an **existing database** that was created with `000_initial_schema.sql` but hasn't been marked for Alembic tracking:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
|
||||
# Mark database as being at the baseline (one-time only)
|
||||
alembic stamp head
|
||||
|
||||
# Verify
|
||||
alembic current
|
||||
# Expected output: 001_initial_baseline (head)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Schema Verification
|
||||
|
||||
**Check all tables exist:**
|
||||
```bash
|
||||
psql -U loaf_admin -d membership_db -c "\dt"
|
||||
```
|
||||
|
||||
**Expected tables (17 total):**
|
||||
- users
|
||||
- events
|
||||
- event_rsvps
|
||||
- subscriptions
|
||||
- subscription_plans
|
||||
- permissions
|
||||
- roles
|
||||
- role_permissions
|
||||
- user_invitations
|
||||
- import_jobs
|
||||
- import_rollback_audit
|
||||
- event_galleries
|
||||
- newsletter_archives
|
||||
- financial_reports
|
||||
- bylaws_documents
|
||||
- donations
|
||||
- storage_usage
|
||||
|
||||
**Check enums:**
|
||||
```bash
|
||||
psql -U loaf_admin -d membership_db -c "\dT"
|
||||
```
|
||||
|
||||
**Expected enums (8 total):**
|
||||
- userstatus
|
||||
- userrole
|
||||
- rsvpstatus
|
||||
- subscriptionstatus
|
||||
- donationtype
|
||||
- donationstatus
|
||||
- invitationstatus
|
||||
- importjobstatus
|
||||
|
||||
---
|
||||
|
||||
## Rollback Procedures
|
||||
|
||||
### Rollback Last Migration
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
alembic downgrade -1
|
||||
```
|
||||
|
||||
### Rollback to Specific Revision
|
||||
|
||||
```bash
|
||||
alembic downgrade <revision_id>
|
||||
```
|
||||
|
||||
### Complete Database Reset
|
||||
|
||||
```bash
|
||||
# WARNING: This deletes ALL data!
|
||||
|
||||
# 1. Backup first
|
||||
pg_dump -U loaf_admin membership_db > emergency_backup.sql
|
||||
|
||||
# 2. Drop database
|
||||
dropdb membership_db
|
||||
|
||||
# 3. Recreate database
|
||||
createdb membership_db
|
||||
|
||||
# 4. Run initial schema
|
||||
psql -U loaf_admin -d membership_db -f backend/migrations/000_initial_schema.sql
|
||||
|
||||
# 5. Mark for Alembic
|
||||
cd backend
|
||||
alembic stamp head
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "relation does not exist" error
|
||||
|
||||
The database wasn't initialized properly.
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
psql -U loaf_admin -d membership_db -f backend/migrations/000_initial_schema.sql
|
||||
alembic stamp head
|
||||
```
|
||||
|
||||
### "Target database is not up to date"
|
||||
|
||||
Migrations haven't been applied.
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
cd backend
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
### "Can't locate revision"
|
||||
|
||||
Alembic tracking is out of sync.
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Check what revision the database thinks it's at
|
||||
alembic current
|
||||
|
||||
# If empty or wrong, manually set it
|
||||
alembic stamp head
|
||||
```
|
||||
|
||||
### Database connection errors
|
||||
|
||||
Check `.env` file has correct `DATABASE_URL`.
|
||||
|
||||
**Format:**
|
||||
```
|
||||
DATABASE_URL=postgresql://username:password@host:port/database
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Production Checklist
|
||||
|
||||
Before going live:
|
||||
|
||||
- [ ] Database created and schema applied
|
||||
- [ ] Alembic marked as up-to-date (`alembic current` shows baseline)
|
||||
- [ ] All environment variables set in `.env`
|
||||
- [ ] Dependencies installed (Python + Node)
|
||||
- [ ] Superadmin user created
|
||||
- [ ] SSL certificates configured
|
||||
- [ ] Backup system in place
|
||||
- [ ] Monitoring configured
|
||||
- [ ] Domain DNS pointing to server
|
||||
- [ ] Email sending verified (SMTP working)
|
||||
- [ ] Stripe webhook endpoint configured
|
||||
- [ ] R2 bucket accessible and CORS configured
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For issues:
|
||||
1. Check logs: `tail -f backend/logs/app.log`
|
||||
2. Check Alembic status: `alembic current`
|
||||
3. Verify environment variables: `cat backend/.env`
|
||||
4. Test database connection: `psql -U loaf_admin -d membership_db`
|
||||
Reference in New Issue
Block a user