1164 lines
26 KiB
Markdown
1164 lines
26 KiB
Markdown
# Frequently Asked Questions (FAQ)
|
||
|
||
Comprehensive answers to common questions about Changemaker Lite V2.
|
||
|
||
## General Questions
|
||
|
||
### What is Changemaker Lite?
|
||
|
||
**Changemaker Lite** is a self-hosted political campaign platform that consolidates:
|
||
|
||
- **Advocacy email campaigns** - Contact elected representatives
|
||
- **Geographic mapping** - Location management and visualization
|
||
- **Canvassing system** - Door-to-door volunteer coordination
|
||
- **Volunteer management** - Shift scheduling and tracking
|
||
- **Landing pages** - Custom campaign pages with GrapesJS editor
|
||
- **Newsletter platform** - Listmonk integration for marketing emails
|
||
- **Media library** - Video management and public galleries
|
||
- **Admin dashboard** - Comprehensive management interface
|
||
|
||
**Key features:**
|
||
|
||
- 100% self-hosted (no external services required except email)
|
||
- Docker Compose deployment (single command to start)
|
||
- Full TypeScript stack (type-safe development)
|
||
- Production-ready security (JWT auth, bcrypt passwords, rate limiting)
|
||
- Monitoring included (Prometheus + Grafana)
|
||
- Canadian electoral data support (NAR format)
|
||
|
||
---
|
||
|
||
### V1 vs V2 Differences
|
||
|
||
| Aspect | V1 | V2 |
|
||
|--------|----|----|
|
||
| **Architecture** | Two separate Node apps (Influence + Map) | Single unified Express API |
|
||
| **Database** | NocoDB REST API | PostgreSQL 16 + Prisma ORM |
|
||
| **Authentication** | Sessions (express-session) | JWT (access + refresh tokens) |
|
||
| **Frontend** | EJS templates | React + Vite + Ant Design |
|
||
| **State** | Server-side | Zustand (client-side) |
|
||
| **Email** | Bull queues | BullMQ queues |
|
||
| **Monitoring** | Basic logging | Prometheus + Grafana + Alertmanager |
|
||
| **Security** | Basic | Production-grade (audit completed) |
|
||
| **Status** | Legacy (reference only) | Current (active development) |
|
||
|
||
**Migration path:** V1 → V2 requires data export/import. See [Migration Guide](../migration/v1-to-v2.md).
|
||
|
||
---
|
||
|
||
### System Requirements
|
||
|
||
**Minimum (Development):**
|
||
|
||
- **CPU:** 2 cores
|
||
- **RAM:** 4GB
|
||
- **Disk:** 10GB
|
||
- **OS:** Linux, macOS, or Windows with WSL2
|
||
- **Docker:** 20.10+ and Docker Compose v2+
|
||
|
||
**Recommended (Production):**
|
||
|
||
- **CPU:** 4+ cores
|
||
- **RAM:** 8-16GB
|
||
- **Disk:** 50GB+ SSD
|
||
- **OS:** Ubuntu 22.04 LTS or similar
|
||
- **Docker:** Latest stable version
|
||
|
||
**External services (optional):**
|
||
|
||
- SMTP server (for emails) - can use Gmail, SendGrid, Mailgun, etc.
|
||
- Pangolin/Cloudflare tunnel (for HTTPS) - or use your own reverse proxy
|
||
|
||
---
|
||
|
||
### Browser Compatibility
|
||
|
||
**Supported browsers:**
|
||
|
||
- ✅ Chrome 90+ (recommended)
|
||
- ✅ Firefox 88+
|
||
- ✅ Safari 14+
|
||
- ✅ Edge 90+
|
||
- ❌ Internet Explorer (not supported)
|
||
|
||
**Mobile browsers:**
|
||
|
||
- ✅ Chrome on Android
|
||
- ✅ Safari on iOS
|
||
- ⚠️ Some features desktop-only (GrapesJS editor, map drawing)
|
||
|
||
**Required features:**
|
||
|
||
- JavaScript enabled
|
||
- Local Storage enabled
|
||
- Cookies enabled (for Listmonk only)
|
||
- WebSockets supported (for real-time features)
|
||
|
||
---
|
||
|
||
## Installation & Setup
|
||
|
||
### How to Install?
|
||
|
||
**Quick start:**
|
||
|
||
```bash
|
||
# 1. Clone repository
|
||
git clone <repo-url> changemaker.lite
|
||
cd changemaker.lite
|
||
git checkout v2
|
||
|
||
# 2. Create environment file
|
||
cp .env.example .env
|
||
nano .env # Edit and set passwords/secrets
|
||
|
||
# 3. Start services
|
||
docker compose up -d v2-postgres redis api admin
|
||
|
||
# 4. Run migrations
|
||
docker compose exec api npx prisma migrate deploy
|
||
docker compose exec api npx prisma db seed
|
||
|
||
# 5. Access application
|
||
# Admin GUI: http://localhost:3000
|
||
# API: http://localhost:4000
|
||
# Login: admin@example.com / Admin123!
|
||
|
||
# 6. Change default password immediately
|
||
```
|
||
|
||
See [Installation Guide](../user/installation.md) for detailed instructions.
|
||
|
||
---
|
||
|
||
### Default Credentials
|
||
|
||
**Admin user (created by seed):**
|
||
|
||
- **Email:** admin@example.com
|
||
- **Password:** Admin123!
|
||
- **Role:** SUPER_ADMIN
|
||
|
||
⚠️ **IMPORTANT:** Change this password immediately after first login!
|
||
|
||
**Other services:**
|
||
|
||
- **Grafana:** admin / admin
|
||
- **NocoDB:** Set via NC_ADMIN_EMAIL / NC_ADMIN_PASSWORD in .env
|
||
- **Listmonk:** Set via LISTMONK_WEB_ADMIN_USER / LISTMONK_WEB_ADMIN_PASSWORD in .env
|
||
|
||
---
|
||
|
||
### How to Change Password?
|
||
|
||
**Via Admin UI (recommended):**
|
||
|
||
1. Login to admin at http://localhost:3000
|
||
2. Navigate to Users (/app/users)
|
||
3. Click user row
|
||
4. Click Edit
|
||
5. Enter new password (12+ chars, uppercase, lowercase, digit)
|
||
6. Save
|
||
|
||
**Via database:**
|
||
|
||
```bash
|
||
# Generate bcrypt hash
|
||
docker compose exec api node -e "
|
||
const bcrypt = require('bcryptjs');
|
||
console.log(bcrypt.hashSync('NewPassword123!', 10));
|
||
"
|
||
|
||
# Update password
|
||
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
||
-c "UPDATE \"User\" SET password = 'PASTE_HASH_HERE' WHERE email = 'admin@example.com';"
|
||
```
|
||
|
||
**Password requirements:**
|
||
|
||
- Minimum 12 characters
|
||
- At least 1 uppercase letter
|
||
- At least 1 lowercase letter
|
||
- At least 1 digit
|
||
- No maximum length
|
||
|
||
---
|
||
|
||
### How to Enable HTTPS?
|
||
|
||
Changemaker Lite doesn't include HTTPS natively. Use one of these options:
|
||
|
||
**Option 1: Pangolin Tunnel (Recommended)**
|
||
|
||
Built-in integration:
|
||
|
||
1. Navigate to `/app/pangolin`
|
||
2. Follow setup wizard
|
||
3. Configure tunnel
|
||
4. Access via HTTPS URL provided by Pangolin
|
||
|
||
See [Pangolin Integration](../features/pangolin.md).
|
||
|
||
**Option 2: Cloudflare Tunnel**
|
||
|
||
1. Install cloudflared
|
||
2. Configure tunnel
|
||
3. Point to localhost:3000 (admin) and localhost:4000 (API)
|
||
|
||
**Option 3: Reverse Proxy**
|
||
|
||
Add nginx/Caddy in front:
|
||
|
||
```yaml
|
||
# docker-compose.yml
|
||
reverse-proxy:
|
||
image: nginx:alpine
|
||
ports:
|
||
- "443:443"
|
||
volumes:
|
||
- ./nginx/ssl.conf:/etc/nginx/nginx.conf
|
||
- ./ssl:/etc/nginx/ssl # Your SSL certificates
|
||
```
|
||
|
||
**Option 4: Hosting Provider**
|
||
|
||
Deploy to provider with built-in HTTPS:
|
||
- DigitalOcean App Platform
|
||
- Heroku
|
||
- Railway
|
||
- Render
|
||
|
||
---
|
||
|
||
## User Management
|
||
|
||
### How to Create Users?
|
||
|
||
**Via Admin UI (recommended):**
|
||
|
||
1. Navigate to `/app/users`
|
||
2. Click "Create User"
|
||
3. Fill in form:
|
||
- Email (required, unique)
|
||
- Password (required, 12+ chars)
|
||
- Name (required)
|
||
- Role (default: USER)
|
||
4. Click "Create"
|
||
|
||
**Via API:**
|
||
|
||
```bash
|
||
curl -X POST http://localhost:4000/api/auth/register \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"email": "newuser@example.com",
|
||
"password": "SecurePass123!",
|
||
"name": "New User"
|
||
}'
|
||
```
|
||
|
||
**Via database:**
|
||
|
||
```bash
|
||
# Generate password hash first
|
||
docker compose exec api node -e "
|
||
const bcrypt = require('bcryptjs');
|
||
console.log(bcrypt.hashSync('Password123!', 10));
|
||
"
|
||
|
||
# Create user
|
||
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
||
-c "INSERT INTO \"User\" (id, email, password, name, role)
|
||
VALUES (gen_random_uuid(), 'user@example.com', 'HASH_HERE', 'User Name', 'USER');"
|
||
```
|
||
|
||
---
|
||
|
||
### How to Reset Passwords?
|
||
|
||
**Current:** V2 doesn't have password reset flow yet (planned for Phase 15).
|
||
|
||
**Workaround:** Reset manually via database (see "How to Change Password?" above).
|
||
|
||
**Future:** Will include:
|
||
- Forgot password form
|
||
- Email with reset link
|
||
- 24-hour expiration
|
||
- One-time use tokens
|
||
|
||
---
|
||
|
||
### What are the User Roles?
|
||
|
||
| Role | Level | Capabilities |
|
||
|------|-------|-------------|
|
||
| **SUPER_ADMIN** | 5 | Full access to everything (users, settings, all features) |
|
||
| **INFLUENCE_ADMIN** | 4 | Manage campaigns, responses, email queue |
|
||
| **MAP_ADMIN** | 3 | Manage locations, cuts, shifts, canvassing |
|
||
| **USER** | 2 | View public content, participate in canvassing (if assigned) |
|
||
| **TEMP** | 1 | Very limited - shift signup confirmation only |
|
||
|
||
**Permission matrix:**
|
||
|
||
| Feature | SUPER_ADMIN | INFLUENCE_ADMIN | MAP_ADMIN | USER | TEMP |
|
||
|---------|-------------|-----------------|-----------|------|------|
|
||
| User management | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||
| Site settings | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||
| Campaigns (admin) | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||
| Responses (moderation) | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||
| Email queue | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||
| Locations (admin) | ✅ | ❌ | ✅ | ❌ | ❌ |
|
||
| Cuts (admin) | ✅ | ❌ | ✅ | ❌ | ❌ |
|
||
| Shifts (admin) | ✅ | ❌ | ✅ | ❌ | ❌ |
|
||
| Canvass dashboard | ✅ | ❌ | ✅ | ❌ | ❌ |
|
||
| View public campaigns | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
| View public map | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
| Sign up for shifts | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||
| Canvass (volunteer) | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
|
||
**Login redirects:**
|
||
|
||
- SUPER_ADMIN / INFLUENCE_ADMIN / MAP_ADMIN → `/app` (admin dashboard)
|
||
- USER / TEMP → `/volunteer` (volunteer portal)
|
||
|
||
---
|
||
|
||
### How to Suspend Users?
|
||
|
||
**Current:** V2 doesn't have user suspension yet (planned for Phase 15).
|
||
|
||
**Workaround:** Delete user account or change role to TEMP (limited permissions).
|
||
|
||
**Future:** Will include:
|
||
- Suspended flag on User model
|
||
- Suspension reason tracking
|
||
- Auto-logout suspended users
|
||
- Reactivation workflow
|
||
|
||
---
|
||
|
||
## Campaigns
|
||
|
||
### How to Create Campaign?
|
||
|
||
1. Navigate to `/app/influence/campaigns`
|
||
2. Click "Create Campaign"
|
||
3. Fill in form:
|
||
- **Name** (required) - Campaign title
|
||
- **Slug** (required, unique) - URL-friendly name
|
||
- **Description** (optional) - Campaign details
|
||
- **Email Subject** (optional) - Default email subject
|
||
- **Email Body** (optional) - Default email template
|
||
- **Active** (checkbox) - Show on public site
|
||
- **Allow Custom Message** (checkbox) - Let users edit message
|
||
4. Click "Create"
|
||
5. Campaign now appears in admin table and public listing (if active)
|
||
|
||
---
|
||
|
||
### How to Publish Campaign?
|
||
|
||
1. Navigate to `/app/influence/campaigns`
|
||
2. Find campaign in table
|
||
3. Click row to expand
|
||
4. Toggle "Active" switch to ON
|
||
5. Campaign now visible at `/campaigns` (public)
|
||
|
||
---
|
||
|
||
### How to Track Emails?
|
||
|
||
1. Navigate to `/app/influence/campaigns`
|
||
2. Click campaign row
|
||
3. Click "View Emails" button
|
||
4. Drawer shows:
|
||
- Total emails sent
|
||
- Email list with timestamps
|
||
- Recipient addresses
|
||
- Email status (sent/failed)
|
||
|
||
**Via Email Queue Page:**
|
||
|
||
1. Navigate to `/app/influence/email-queue`
|
||
2. View stats:
|
||
- Total emails processed
|
||
- Success/fail counts
|
||
- Queue depth
|
||
3. View recent jobs
|
||
4. Retry failed jobs if needed
|
||
|
||
---
|
||
|
||
### How to Moderate Responses?
|
||
|
||
1. Navigate to `/app/influence/responses`
|
||
2. Table shows all responses with:
|
||
- Participant name/email
|
||
- Campaign
|
||
- Message excerpt
|
||
- Submission timestamp
|
||
- Verification status
|
||
3. Filters:
|
||
- Campaign dropdown
|
||
- Verified/unverified toggle
|
||
4. Click row to view full response
|
||
5. Actions:
|
||
- Verify (if unverified)
|
||
- Delete (if inappropriate)
|
||
|
||
**Response verification workflow:**
|
||
|
||
1. User submits response → marked unverified
|
||
2. User receives verification email
|
||
3. User clicks verification link → marked verified
|
||
4. Only verified responses show on public response wall
|
||
|
||
---
|
||
|
||
## Map & Canvassing
|
||
|
||
### How to Import Locations?
|
||
|
||
**Via CSV:**
|
||
|
||
1. Navigate to `/app/map/locations`
|
||
2. Click "Import CSV"
|
||
3. Prepare CSV with columns:
|
||
```
|
||
address,city,province,postalCode,notes
|
||
123 Main St,Toronto,ON,M5H 2N2,Corner house
|
||
456 Oak Ave,Toronto,ON,M5H 2N3,Blue door
|
||
```
|
||
4. Upload file
|
||
5. Map columns (if headers don't match exactly)
|
||
6. Click "Import"
|
||
7. Locations imported, geocoding starts automatically
|
||
|
||
**Via NAR (Canadian Electoral Data):**
|
||
|
||
1. Obtain NAR data files (Location + Address)
|
||
2. Place in `/data` directory (mapped volume)
|
||
3. Navigate to `/app/map/locations`
|
||
4. Click "NAR Import" tab
|
||
5. Select province
|
||
6. Select dataset
|
||
7. Apply filters (city, postal code, cut, residential only)
|
||
8. Preview count
|
||
9. Click "Import"
|
||
10. Import processes in background (can take minutes for large files)
|
||
|
||
See [NAR Import Guide](../user-guides/nar-import.md).
|
||
|
||
---
|
||
|
||
### How to Create Cuts?
|
||
|
||
**Via Map Drawing:**
|
||
|
||
1. Navigate to `/app/map/cuts`
|
||
2. Click "Map Drawing" tab
|
||
3. Map shows with drawing controls
|
||
4. Click "Draw Cut" button
|
||
5. Click on map to place vertices
|
||
6. Click first vertex again to close polygon (or click "Finish")
|
||
7. Fill in form:
|
||
- Name (required)
|
||
- Description (optional)
|
||
- Color (pick color for map display)
|
||
8. Click "Save"
|
||
|
||
**Via GeoJSON Import:**
|
||
|
||
1. Prepare GeoJSON file:
|
||
```json
|
||
{
|
||
"type": "Polygon",
|
||
"coordinates": [[
|
||
[-79.38, 43.65],
|
||
[-79.37, 43.65],
|
||
[-79.37, 43.64],
|
||
[-79.38, 43.64],
|
||
[-79.38, 43.65]
|
||
]]
|
||
}
|
||
```
|
||
2. Navigate to `/app/map/cuts`
|
||
3. Click "Create Cut"
|
||
4. Paste GeoJSON in geometry field
|
||
5. Fill in name/description
|
||
6. Click "Create"
|
||
|
||
---
|
||
|
||
### How to Organize Shifts?
|
||
|
||
1. Navigate to `/app/map/shifts`
|
||
2. Click "Create Shift"
|
||
3. Fill in form:
|
||
- **Title** (required) - Shift name
|
||
- **Description** (optional) - Shift details
|
||
- **Start Time** (required) - When shift starts
|
||
- **End Time** (required) - When shift ends
|
||
- **Cut** (optional) - Assign to specific cut
|
||
- **Max Volunteers** (optional) - Capacity limit
|
||
- **Public** (checkbox) - Show on public shifts page
|
||
4. Click "Create"
|
||
5. Shift appears in admin table and public listing (if public)
|
||
|
||
**Manage signups:**
|
||
|
||
1. Click shift row in table
|
||
2. Click "View Signups"
|
||
3. Drawer shows:
|
||
- Signup count
|
||
- List of volunteers
|
||
- Email addresses
|
||
4. Actions:
|
||
- "Email All" - Send message to all volunteers
|
||
- Remove individual signups if needed
|
||
|
||
---
|
||
|
||
### How to Start Canvassing?
|
||
|
||
**For volunteers:**
|
||
|
||
1. Login to volunteer portal
|
||
2. Navigate to "My Assignments" (/volunteer/assignments)
|
||
3. Find assigned shift
|
||
4. Click "Start Canvassing"
|
||
5. Full-screen map opens (/volunteer/canvass/:cutId)
|
||
6. GPS tracks your location
|
||
7. Map shows:
|
||
- Your current position (blue dot)
|
||
- Locations in cut (markers)
|
||
- Walking route (blue line)
|
||
- Legend (outcome colors)
|
||
8. Click location marker to record visit:
|
||
- Select outcome (Home, Away, Refused, etc.)
|
||
- Add notes (optional)
|
||
- Save
|
||
9. Continue until all locations visited
|
||
10. Session auto-saves progress
|
||
|
||
**For admins (monitoring):**
|
||
|
||
1. Navigate to `/app/canvass/dashboard`
|
||
2. View:
|
||
- Active sessions count
|
||
- Total visits recorded
|
||
- Recent activity feed
|
||
- Cut progress (% complete)
|
||
- Leaderboard (top canvassers)
|
||
3. Click activity item to see details
|
||
|
||
See [Canvassing Guide](../user-guides/canvassing.md).
|
||
|
||
---
|
||
|
||
## Technical Questions
|
||
|
||
### Which Database?
|
||
|
||
**PostgreSQL 16** with two ORMs:
|
||
|
||
1. **Prisma** - Main API (Express)
|
||
- Schema: `api/prisma/schema.prisma`
|
||
- Migrations: `api/prisma/migrations/`
|
||
- 30+ models (User, Campaign, Location, etc.)
|
||
|
||
2. **Drizzle** - Media API (Fastify)
|
||
- Schema: `api/src/modules/media/db/schema.ts`
|
||
- Tables: `media_videos`, `media_reactions`, etc.
|
||
|
||
**Connection:**
|
||
|
||
- Host: v2-postgres (container) or localhost:5433 (host)
|
||
- Database: changemaker_v2
|
||
- User: changemaker
|
||
- Password: V2_POSTGRES_PASSWORD (from .env)
|
||
|
||
**Shared database:** Both ORMs use same PostgreSQL database, different tables.
|
||
|
||
---
|
||
|
||
### Which ORM?
|
||
|
||
**Prisma** for main API:
|
||
|
||
- Type-safe queries
|
||
- Auto-generated client
|
||
- Migrations workflow
|
||
- Prisma Studio GUI
|
||
|
||
**Drizzle** for media API:
|
||
|
||
- Lightweight
|
||
- SQL-like API
|
||
- Schema-first approach
|
||
- No migration files (push to sync)
|
||
|
||
**Why two ORMs?**
|
||
|
||
Media API was added later as separate Fastify microservice. Using Drizzle allowed faster development without modifying main Prisma schema.
|
||
|
||
---
|
||
|
||
### API Architecture?
|
||
|
||
**Dual API architecture:**
|
||
|
||
1. **Express API** (Main)
|
||
- Port: 4000
|
||
- Language: TypeScript
|
||
- ORM: Prisma
|
||
- Features: Auth, campaigns, locations, shifts, canvass, pages
|
||
- Endpoints: `/api/*`
|
||
|
||
2. **Fastify Media API** (Microservice)
|
||
- Port: 4100
|
||
- Language: TypeScript
|
||
- ORM: Drizzle
|
||
- Features: Video library, uploads, reactions
|
||
- Endpoints: `/api/media/*`
|
||
|
||
**Shared:**
|
||
|
||
- Same PostgreSQL database
|
||
- Same Redis instance
|
||
- Same Docker network
|
||
- Separate containerization (can scale independently)
|
||
|
||
**Frontend:**
|
||
|
||
- React SPA (Vite)
|
||
- Port: 3000
|
||
- State: Zustand
|
||
- UI: Ant Design
|
||
- Routing: React Router v6
|
||
|
||
---
|
||
|
||
### Authentication Method?
|
||
|
||
**JWT-based authentication:**
|
||
|
||
**Tokens:**
|
||
|
||
1. **Access Token**
|
||
- Duration: 15 minutes
|
||
- Stored: Memory (localStorage)
|
||
- Contains: userId, email, role
|
||
- Used: All authenticated requests
|
||
|
||
2. **Refresh Token**
|
||
- Duration: 7 days
|
||
- Stored: Database + localStorage
|
||
- Used: Renew access token
|
||
- Rotation: New refresh token on each refresh
|
||
|
||
**Flow:**
|
||
|
||
1. Login → Returns access + refresh tokens
|
||
2. Store in localStorage (Zustand persist)
|
||
3. Add access token to Authorization header
|
||
4. Access token expires after 15min
|
||
5. Frontend auto-refreshes using refresh token
|
||
6. New access + refresh tokens returned
|
||
7. Continue seamlessly
|
||
|
||
**Security features:**
|
||
|
||
- bcrypt password hashing (10 rounds)
|
||
- Token rotation prevents replay attacks
|
||
- Refresh tokens stored in database (can revoke)
|
||
- Rate limiting on auth endpoints (10/min)
|
||
- User enumeration prevention
|
||
- Redis authentication required
|
||
|
||
See [Authentication Flow](../technical/authentication.md).
|
||
|
||
---
|
||
|
||
## Performance
|
||
|
||
### How Many Users Supported?
|
||
|
||
**Concurrent users:**
|
||
|
||
- **Development:** 10-50 users
|
||
- **Production (default config):** 100-500 users
|
||
- **Production (optimized):** 1000+ users
|
||
|
||
**Factors:**
|
||
|
||
- Database connection pool (default: 10 connections)
|
||
- API worker concurrency (default: 1 worker)
|
||
- Server resources (CPU/RAM)
|
||
- Network bandwidth
|
||
|
||
**Scaling:**
|
||
|
||
- Horizontal: Run multiple API instances
|
||
- Vertical: Increase server resources
|
||
- Database: Read replicas for read-heavy loads
|
||
- Caching: Redis caching for frequently accessed data
|
||
|
||
---
|
||
|
||
### How to Scale?
|
||
|
||
**Horizontal scaling (recommended):**
|
||
|
||
```yaml
|
||
# docker-compose.yml
|
||
api:
|
||
deploy:
|
||
replicas: 3 # Run 3 API instances
|
||
# Each instance:
|
||
# - Handles requests independently
|
||
# - Connects to same database
|
||
# - Processes queue jobs
|
||
# - Shares Redis cache
|
||
```
|
||
|
||
Add load balancer in front:
|
||
|
||
```yaml
|
||
nginx:
|
||
image: nginx:alpine
|
||
ports:
|
||
- "80:80"
|
||
volumes:
|
||
- ./nginx/lb.conf:/etc/nginx/nginx.conf
|
||
# Distributes requests across API instances
|
||
```
|
||
|
||
**Vertical scaling:**
|
||
|
||
Increase resources:
|
||
|
||
```yaml
|
||
api:
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
cpus: '4.0' # More CPU
|
||
memory: 8G # More RAM
|
||
```
|
||
|
||
**Database scaling:**
|
||
|
||
- Add read replicas for read-heavy queries
|
||
- Use connection pooler (PgBouncer)
|
||
- Optimize queries and indexes
|
||
|
||
**Caching:**
|
||
|
||
- Redis caching for geocoding results
|
||
- Redis caching for representative lookups
|
||
- HTTP caching headers (Nginx)
|
||
- Static asset CDN
|
||
|
||
---
|
||
|
||
### Database Size Limits?
|
||
|
||
**PostgreSQL:**
|
||
|
||
- Maximum database size: ~32 TB (theoretical)
|
||
- Practical limit: Depends on storage and backup strategy
|
||
|
||
**Typical sizes (after 1 year):**
|
||
|
||
- **Small campaign:** 100MB-500MB (1k locations, 10 campaigns)
|
||
- **Medium campaign:** 500MB-2GB (10k locations, 50 campaigns)
|
||
- **Large campaign:** 2GB-10GB (100k locations, 200 campaigns)
|
||
|
||
**Storage requirements:**
|
||
|
||
- **Database:** 1-10GB
|
||
- **Uploads:** 5-50GB (videos)
|
||
- **Backups:** 2× database size (keep multiple backups)
|
||
- **Logs:** 1-5GB/month
|
||
- **Total:** 20-100GB recommended
|
||
|
||
**Optimization:**
|
||
|
||
- Regular VACUUM (auto-enabled)
|
||
- Archive old campaigns
|
||
- Delete old logs
|
||
- Compress backups
|
||
|
||
---
|
||
|
||
## Security
|
||
|
||
### Is Data Encrypted?
|
||
|
||
**At rest:**
|
||
|
||
- Database: Not encrypted by default (enable PostgreSQL encryption if needed)
|
||
- Passwords: bcrypt hashed (cannot be decrypted)
|
||
- Sensitive fields: ENCRYPTION_KEY env var for encrypting secrets
|
||
|
||
**In transit:**
|
||
|
||
- HTTPS: Use Pangolin/Cloudflare tunnel (encrypts all traffic)
|
||
- Database: PostgreSQL connections within Docker network (isolated)
|
||
- Redis: Authenticated (password required)
|
||
|
||
**Recommendations:**
|
||
|
||
- Use HTTPS in production
|
||
- Rotate ENCRYPTION_KEY periodically
|
||
- Enable PostgreSQL SSL if database exposed
|
||
- Use strong passwords for all services
|
||
|
||
---
|
||
|
||
### Password Requirements?
|
||
|
||
**Enforced policy:**
|
||
|
||
- Minimum 12 characters
|
||
- At least 1 uppercase letter (A-Z)
|
||
- At least 1 lowercase letter (a-z)
|
||
- At least 1 digit (0-9)
|
||
- No maximum length
|
||
|
||
**Valid examples:**
|
||
|
||
- `SecurePass123!`
|
||
- `MyPassword99`
|
||
- `Admin12345678`
|
||
|
||
**Invalid:**
|
||
|
||
- `short` (too short)
|
||
- `nouppercase123` (no uppercase)
|
||
- `NOLOWERCASE123` (no lowercase)
|
||
- `NoDigitsHere` (no digit)
|
||
|
||
**Storage:**
|
||
|
||
- bcrypt hashed with salt (10 rounds)
|
||
- Hash stored in database (not plaintext)
|
||
- Cannot be decrypted (one-way hash)
|
||
|
||
---
|
||
|
||
### How to Backup?
|
||
|
||
**Manual backup:**
|
||
|
||
```bash
|
||
# Use provided script
|
||
./scripts/backup.sh
|
||
|
||
# Creates:
|
||
# - PostgreSQL dump
|
||
# - Listmonk dump (if enabled)
|
||
# - Uploads archive (videos, images)
|
||
# - Timestamped filename: backup_2026-02-13_100000.tar.gz
|
||
```
|
||
|
||
**What's included:**
|
||
|
||
- Complete database dump (all tables)
|
||
- Uploaded files (videos, images, documents)
|
||
- Listmonk database (if enabled)
|
||
|
||
**What's NOT included:**
|
||
|
||
- Docker images (rebuild from Dockerfile)
|
||
- .env file (keep separate, has secrets)
|
||
- Temporary files (logs, cache)
|
||
|
||
**Automated backups:**
|
||
|
||
Add cron job:
|
||
|
||
```bash
|
||
# Run daily at 2 AM
|
||
0 2 * * * cd /path/to/changemaker.lite && ./scripts/backup.sh
|
||
|
||
# With S3 upload (if configured)
|
||
0 2 * * * cd /path/to/changemaker.lite && ./scripts/backup.sh --upload-s3
|
||
```
|
||
|
||
**Restore:**
|
||
|
||
```bash
|
||
# Stop services
|
||
docker compose down
|
||
|
||
# Restore database
|
||
gunzip -c backup.sql.gz | docker compose exec -T v2-postgres psql -U changemaker -d changemaker_v2
|
||
|
||
# Restore uploads
|
||
tar -xzf uploads.tar.gz -C ./uploads
|
||
|
||
# Start services
|
||
docker compose up -d
|
||
```
|
||
|
||
See [Backup Guide](../deployment/backup-restore.md).
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### Where are Logs?
|
||
|
||
**Docker logs:**
|
||
|
||
```bash
|
||
# View API logs
|
||
docker compose logs api
|
||
|
||
# View all logs
|
||
docker compose logs
|
||
|
||
# Follow logs (real-time)
|
||
docker compose logs -f api
|
||
|
||
# Last 100 lines
|
||
docker compose logs api --tail=100
|
||
|
||
# Since timestamp
|
||
docker compose logs api --since="2026-02-13T10:00:00"
|
||
|
||
# Save to file
|
||
docker compose logs api > api-logs.txt
|
||
```
|
||
|
||
**Log locations inside containers:**
|
||
|
||
- API: Console output (docker logs)
|
||
- PostgreSQL: Console + `/var/lib/postgresql/data/log/` (if logging enabled)
|
||
- Nginx: `/var/log/nginx/access.log`, `/var/log/nginx/error.log`
|
||
|
||
**Log levels:**
|
||
|
||
- **ERROR:** Errors requiring attention
|
||
- **WARN:** Warnings (not critical)
|
||
- **INFO:** Informational messages
|
||
- **DEBUG:** Debugging information (enable with LOG_LEVEL=debug)
|
||
|
||
---
|
||
|
||
### How to Restart Services?
|
||
|
||
**Restart specific service:**
|
||
|
||
```bash
|
||
# Restart API
|
||
docker compose restart api
|
||
|
||
# Restart multiple services
|
||
docker compose restart api admin v2-postgres
|
||
```
|
||
|
||
**Restart all services:**
|
||
|
||
```bash
|
||
# Graceful restart (preserves data)
|
||
docker compose restart
|
||
|
||
# Stop and start (recreates containers)
|
||
docker compose down
|
||
docker compose up -d
|
||
```
|
||
|
||
**Force recreate:**
|
||
|
||
```bash
|
||
# Rebuild and recreate
|
||
docker compose up -d --build --force-recreate
|
||
|
||
# Recreate specific service
|
||
docker compose up -d --build --force-recreate api
|
||
```
|
||
|
||
**Restart single container:**
|
||
|
||
```bash
|
||
# Get container name
|
||
docker compose ps
|
||
|
||
# Restart by name
|
||
docker restart changemaker-lite-api-1
|
||
```
|
||
|
||
---
|
||
|
||
### How to Reset Database?
|
||
|
||
⚠️ **WARNING:** This deletes ALL data!
|
||
|
||
**Full reset:**
|
||
|
||
```bash
|
||
# Stop services
|
||
docker compose down
|
||
|
||
# Delete database volume
|
||
docker volume rm changemaker-lite_postgres-data
|
||
|
||
# Start fresh
|
||
docker compose up -d v2-postgres
|
||
|
||
# Wait for database ready
|
||
sleep 10
|
||
|
||
# Run migrations
|
||
docker compose exec api npx prisma migrate deploy
|
||
|
||
# Seed initial data
|
||
docker compose exec api npx prisma db seed
|
||
|
||
# Default admin: admin@example.com / Admin123!
|
||
```
|
||
|
||
**Reset specific tables:**
|
||
|
||
```bash
|
||
# Delete all users (keeps schema)
|
||
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
|
||
-c 'TRUNCATE "User" CASCADE;'
|
||
|
||
# Re-seed
|
||
docker compose exec api npx prisma db seed
|
||
```
|
||
|
||
**Reset without deleting volumes:**
|
||
|
||
```bash
|
||
# Drop and recreate database
|
||
docker compose exec v2-postgres psql -U postgres \
|
||
-c 'DROP DATABASE changemaker_v2;'
|
||
|
||
docker compose exec v2-postgres psql -U postgres \
|
||
-c 'CREATE DATABASE changemaker_v2 OWNER changemaker;'
|
||
|
||
# Run migrations
|
||
docker compose exec api npx prisma migrate deploy
|
||
docker compose exec api npx prisma db seed
|
||
```
|
||
|
||
---
|
||
|
||
## Getting Help
|
||
|
||
### Documentation Links
|
||
|
||
**User Guides:**
|
||
|
||
- [Installation Guide](../user/installation.md)
|
||
- [User Guide](../user/user-guide.md)
|
||
- [Admin Guide](../user/admin-guide.md)
|
||
- [Canvassing Guide](../user-guides/canvassing.md)
|
||
- [NAR Import Guide](../user-guides/nar-import.md)
|
||
|
||
**Technical Documentation:**
|
||
|
||
- [Architecture Overview](../technical/architecture.md)
|
||
- [API Reference](../technical/api-reference.md)
|
||
- [Database Schema](../technical/database-schema.md)
|
||
- [Authentication Flow](../technical/authentication.md)
|
||
|
||
**Troubleshooting:**
|
||
|
||
- [Common Errors](common-errors.md)
|
||
- [Docker Issues](docker-issues.md)
|
||
- [Database Issues](database-issues.md)
|
||
- [Auth Issues](auth-issues.md)
|
||
- [Email Issues](email-issues.md)
|
||
- [Geocoding Issues](geocoding-issues.md)
|
||
- [Monitoring Issues](monitoring-issues.md)
|
||
- [Performance Optimization](performance-optimization.md)
|
||
|
||
---
|
||
|
||
### GitHub Issues
|
||
|
||
**Before creating issue:**
|
||
|
||
1. Check [existing issues](https://github.com/yourusername/changemaker.lite/issues)
|
||
2. Search closed issues (may already be fixed)
|
||
3. Check [Troubleshooting guides](common-errors.md)
|
||
4. Try latest version (`git pull origin v2`)
|
||
|
||
**Creating good issues:**
|
||
|
||
**Bug reports:**
|
||
|
||
```markdown
|
||
**Describe the bug**
|
||
Clear description of what's wrong.
|
||
|
||
**To Reproduce**
|
||
1. Go to '...'
|
||
2. Click on '...'
|
||
3. See error
|
||
|
||
**Expected behavior**
|
||
What should happen instead.
|
||
|
||
**Screenshots**
|
||
If applicable, add screenshots.
|
||
|
||
**Environment**
|
||
- OS: [e.g. Ubuntu 22.04]
|
||
- Docker version: [e.g. 20.10.21]
|
||
- Browser: [e.g. Chrome 120]
|
||
|
||
**Logs**
|
||
Paste relevant logs (sanitize sensitive data).
|
||
```
|
||
|
||
**Feature requests:**
|
||
|
||
```markdown
|
||
**Is your feature request related to a problem?**
|
||
Description of problem.
|
||
|
||
**Describe the solution you'd like**
|
||
Clear description of feature.
|
||
|
||
**Describe alternatives you've considered**
|
||
Other solutions considered.
|
||
|
||
**Additional context**
|
||
Any other context or screenshots.
|
||
```
|
||
|
||
---
|
||
|
||
### Community Support
|
||
|
||
**Official channels:**
|
||
|
||
- GitHub Issues (bugs and features)
|
||
- GitHub Discussions (questions and ideas)
|
||
- Documentation (this site)
|
||
|
||
**Response time:**
|
||
|
||
- Bug reports: 1-7 days
|
||
- Feature requests: Variable (depends on priority)
|
||
- Questions: 1-3 days
|
||
|
||
**Contributing:**
|
||
|
||
- Pull requests welcome
|
||
- Follow [Contributing Guide](../contributing/guidelines.md)
|
||
- Follow [Code of Conduct](../contributing/code-of-conduct.md)
|
||
|
||
---
|
||
|
||
**Last Updated:** February 2026
|
||
**Version:** V2.0
|
||
**Status:** Complete
|