371 lines
6.8 KiB
Markdown

# Database Models
Changemaker Lite V2 uses a comprehensive PostgreSQL database schema with 30+ models across authentication, campaigns, locations, media, and content management. The schema is managed via Prisma ORM (main API) and Drizzle ORM (media API).
## Model Organization
Models are organized by feature area:
### [Authentication & Users](auth.md)
Core authentication and user management:
- **User** - User accounts with roles and authentication
- **RefreshToken** - JWT refresh token tracking
- **Session** - User session management (future)
### [Influence Module](influence.md)
Advocacy campaign models:
- **Campaign** - Campaign definitions and settings
- **CampaignEmail** - Sent email tracking
- **Response** - Public response wall submissions
- **PostalCodeCache** - Representative lookup cache
### [Map Module](map.md)
Location and geographic models:
- **Location** - Address database with geocoding
- **Cut** - Geographic polygon organization
- **Shift** - Volunteer shift scheduling
- **MapSettings** - Map configuration singleton
### [Canvassing](canvass.md)
Door-to-door canvassing models:
- **CanvassSession** - Canvassing session tracking
- **CanvassVisit** - Visit outcome recording
- **TrackingSession** - GPS tracking (future)
### [Content Management](pages.md)
Landing pages and content:
- **Page** - Landing page definitions
- **PageBlock** - Reusable content blocks
### [Email Templates](email-templates.md)
Email template system:
- **EmailTemplate** - Template definitions
- **EmailTemplateVersion** - Version history (future)
### [Media](media.md)
Video library (Drizzle ORM):
- **videos** - Video metadata and files
- **shared_media** - Public gallery assignments
- **media_reactions** - Emoji reactions
- **media_jobs** - Background job queue
### [Settings](settings.md)
Global configuration:
- **Settings** - Site-wide settings singleton
## ORM Architecture
### Prisma (Main API)
Used for 95% of models:
- **Schema:** `api/prisma/schema.prisma`
- **Migrations:** `api/prisma/migrations/`
- **Client:** Auto-generated TypeScript types
- **Database:** PostgreSQL 16
### Drizzle (Media API)
Used for media models only:
- **Schema:** `api/src/modules/media/db/schema.ts`
- **Migrations:** None (push-based)
- **Client:** Manual schema definition
- **Database:** Same PostgreSQL 16
## Common Patterns
### Timestamps
Most models include:
```prisma
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
```
### Foreign Keys
Relations use explicit foreign key fields:
```prisma
model Campaign {
id Int @id @default(autoincrement())
createdByUserId Int
createdBy User @relation(fields: [createdByUserId], references: [id])
}
```
### JSON Fields
Flexible data stored as JSON:
```prisma
model Campaign {
emailTemplate Json?
settings Json?
}
```
TypeScript types:
```typescript
import { Prisma } from '@prisma/client';
const template: Prisma.InputJsonValue = {
subject: 'Email subject',
body: 'Email body',
};
```
### Enums
Type-safe enumerations:
```prisma
enum Role {
SUPER_ADMIN
INFLUENCE_ADMIN
MAP_ADMIN
USER
TEMP
}
enum VisitOutcome {
SUCCESS
NOT_HOME
MOVED
REFUSED
WRONG_ADDRESS
INACCESSIBLE
OTHER
}
```
## Model Count by Category
| Category | Models | ORM |
|----------|--------|-----|
| Authentication | 3 | Prisma |
| Influence | 4 | Prisma |
| Map | 4 | Prisma |
| Canvassing | 3 | Prisma |
| Content | 2 | Prisma |
| Email Templates | 2 | Prisma |
| Media | 4 | Drizzle |
| Settings | 2 | Prisma |
| **Total** | **24** | Mixed |
## Database Operations
### Migrations (Prisma)
```bash
# Create migration
cd api && npx prisma migrate dev --name add_field
# Deploy migrations
cd api && npx prisma migrate deploy
# Reset database (dev only)
cd api && npx prisma migrate reset
```
### Schema Push (Drizzle)
```bash
# Push schema changes (media API)
cd api && npx drizzle-kit push
```
### Database Browser
View data via:
- **Prisma Studio:** `npx prisma studio`
- **NocoDB:** http://localhost:8091 (read-only)
## Indexes
Key indexes for performance:
```prisma
model Location {
@@index([cutId])
@@index([lastVisitedAt])
}
model Campaign {
@@index([published])
@@index([createdByUserId])
}
model CanvassSession {
@@index([userId])
@@index([status])
}
```
## Constraints
### Unique Constraints
```prisma
model User {
email String @unique
}
model Page {
slug String @unique
}
model Cut {
name String @unique
}
```
### Check Constraints
Enforced at application level:
- Email format validation
- Password complexity (12+ chars)
- Coordinate bounds (-90 to 90 lat, -180 to 180 lng)
## Relations
### One-to-Many
```prisma
model User {
id Int @id @default(autoincrement())
campaigns Campaign[]
}
model Campaign {
id Int @id @default(autoincrement())
createdByUserId Int
createdBy User @relation(fields: [createdByUserId], references: [id])
}
```
### Many-to-Many
Via junction tables:
```prisma
model Shift {
id Int @id @default(autoincrement())
signups ShiftSignup[]
}
model User {
id Int @id @default(autoincrement())
signups ShiftSignup[]
}
model ShiftSignup {
id Int @id @default(autoincrement())
shiftId Int
userId Int
shift Shift @relation(fields: [shiftId], references: [id])
user User @relation(fields: [userId], references: [id])
@@unique([shiftId, userId])
}
```
## Seeding
Initial data in `api/prisma/seed.ts`:
- Admin user (admin@example.com)
- Default settings
- Sample page blocks
- System email templates
```bash
# Run seed
cd api && npx prisma db seed
```
## Data Types
### Common Types
- **ID:** `Int @id @default(autoincrement())`
- **String:** `String` or `String @db.Text` (long text)
- **Number:** `Int` or `Float`
- **Boolean:** `Boolean @default(false)`
- **Date:** `DateTime @default(now())`
- **JSON:** `Json` or `Json?`
- **Enum:** `Role`, `VisitOutcome`, etc.
### Spatial Data
GeoJSON stored as JSON:
```prisma
model Cut {
geometry Json // GeoJSON Polygon
}
```
Coordinates as separate fields:
```prisma
model Location {
latitude Float
longitude Float
}
```
## Database Configuration
### Connection String
```bash
DATABASE_URL="postgresql://user:password@localhost:5432/changemaker_v2?schema=public"
```
### Connection Pool
Prisma connection pool:
```typescript
// api/src/server.ts
const prisma = new PrismaClient({
log: ['error', 'warn'],
});
```
## Related Documentation
- [Authentication Models](auth.md)
- [Influence Models](influence.md)
- [Map Models](map.md)
- [Canvassing Models](canvass.md)
- [Content Models](pages.md)
- [Email Template Models](email-templates.md)
- [Media Models](media.md)
- [Settings Models](settings.md)
- [Database Overview](../index.md)
- [Migrations Guide](../../development/migrations.md)
- [Backend Modules](../../backend/modules/index.md)