changemaker.lite/CLAUDE.md
bunker-admin a77306fac2 Initial v2 commit: complete rebuild with unified API + React admin
Phase 1-14 complete:
- Unified Express.js API (TypeScript, Prisma ORM, PostgreSQL 16)
- React Admin GUI (Vite + Ant Design + Zustand)
- JWT auth with refresh tokens
- Influence: Campaigns, Representatives, Responses, Email Queue
- Map: Locations, Cuts, Shifts, Canvassing System
- NAR data import infrastructure (2025 format)
- Listmonk newsletter integration
- Landing page builder (GrapesJS)
- MkDocs + Code Server integration
- Volunteer portal with GPS tracking
- Monitoring stack (Prometheus, Grafana, Alertmanager)
- Pangolin tunnel integration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 10:05:04 -07:00

218 lines
8.4 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Changemaker Lite is a self-hosted political campaign platform built with Docker Compose. It consolidates advocacy email campaigns, geographic mapping, volunteer management, and administration into a single TypeScript stack. The primary domain is `cmlite.org`.
**Current state:** V2 rebuild in progress on the `v2` branch. See `V2_PLAN.md` for the full roadmap.
---
## V2 Architecture (Active Development)
### Stack
- **Single unified Express.js API** — TypeScript, port 4000, Prisma ORM + PostgreSQL 16
- **React Admin GUI** — Vite + Ant Design + Zustand, port 3000
- **Nginx reverse proxy** — subdomain routing (`*.cmlite.org`)
- **NocoDB v2** — read-only data browser on port 8091
- **JWT auth** — access tokens (15min) + refresh tokens (7 days, stored in DB)
- **BullMQ** — async email job queue, **Listmonk** for newsletters
- **Redis** — caching, rate limiting, BullMQ backend
### Directory Structure
```
changemaker.lite/
├── api/ # Unified Express.js API (TypeScript)
│ ├── prisma/ # Schema, migrations, seed
│ └── src/
│ ├── config/ # env.ts, database.ts, redis.ts
│ ├── middleware/ # error-handler, validate, rate-limit, auth, rbac
│ ├── modules/
│ │ ├── auth/ # auth.service, auth.routes, auth.schemas
│ │ ├── users/ # users.service, users.routes, users.schemas
│ │ ├── influence/ # campaigns, representatives, responses, postal-codes
│ │ └── map/ # locations, shifts, cuts
│ ├── types/ # express.d.ts (Request augmentation)
│ └── utils/ # logger.ts (Winston), metrics.ts (prom-client)
├── admin/ # React Admin (Vite + Ant Design + Zustand)
│ └── src/
│ ├── components/ # ProtectedRoute, AppLayout
│ ├── pages/ # LoginPage, DashboardPage, UsersPage
│ ├── stores/ # auth.store.ts (Zustand)
│ ├── lib/ # api.ts (axios instance + interceptors)
│ └── types/ # api.ts (TypeScript interfaces)
├── nginx/ # Reverse proxy config
├── public-web/ # Public landing pages
├── docker-compose.yml # V2 orchestration
├── docker-compose.v1.yml # V1 backup for reference
└── V2_PLAN.md # Full 14-phase roadmap
```
### Key Files
| File | Purpose |
|------|---------|
| `api/prisma/schema.prisma` | Full database schema (20+ models) |
| `api/src/server.ts` | API entry point, middleware stack, route wiring |
| `api/src/config/env.ts` | Zod-validated environment config |
| `api/src/modules/auth/` | JWT auth (login, register, refresh, logout) |
| `api/src/modules/users/` | User CRUD with pagination + search |
| `admin/src/App.tsx` | React admin shell with routing |
| `admin/src/stores/auth.store.ts` | Zustand auth state with token persistence |
| `admin/src/lib/api.ts` | Axios instance with 401 refresh interceptor |
| `docker-compose.yml` | V2 service orchestration |
| `.env.example` | All required environment variables |
### Auth Flow
- JWT-based: access tokens (15min) + refresh tokens (7 days, stored in DB)
- Login → verify bcrypt hash → generate token pair → return tokens + user
- Refresh → validate refresh token → rotate (invalidate old, issue new) → return new pair
- Roles: `SUPER_ADMIN`, `INFLUENCE_ADMIN`, `MAP_ADMIN`, `USER`, `TEMP`
- RBAC middleware: `requireRole(...roles)`, `requireNonTemp`
### Nginx Routing
| Subdomain | Target |
|-----------|--------|
| `app.cmlite.org` | Admin React app (port 3000) |
| `api.cmlite.org` | Express API (port 4000) |
| `data.cmlite.org` | NocoDB read-only (port 8091) |
| `docs.cmlite.org` | MkDocs (port 4001) |
| `cmlite.org` | Public landing pages |
---
## V2 Development Commands
### API Development
```bash
cd api && npm run dev # Dev server with tsx watch (auto-reload)
cd api && npx tsc --noEmit # Type-check without emitting
cd api && npx prisma migrate dev # Run/create migrations
cd api && npx prisma studio # Browse database in browser
cd api && npx prisma generate # Regenerate Prisma client
```
### Admin GUI Development
```bash
cd admin && npm run dev # Vite dev server (port 3000)
cd admin && npx tsc --noEmit # Type-check without emitting
cd admin && npm run build # Production build (tsc + vite)
```
### Docker (V2 Services)
```bash
docker compose up -d v2-postgres redis api # Start API + dependencies
docker compose up -d admin # Start admin GUI
docker compose up -d # Start all v2 services
docker compose logs -f api # Tail API logs
docker compose exec api npx prisma migrate dev # Run migrations in container
docker compose down # Stop all services
```
### Type Checking (Both Projects)
```bash
cd api && npx tsc --noEmit && cd ../admin && npx tsc --noEmit
```
---
## Port Reference (V2)
| Port | Service |
|------|---------|
| 3000 | Admin GUI (Vite dev / React) |
| 3001 | Grafana |
| 3010 | Homepage |
| 3030 | Gitea |
| 4000 | V2 API (Express.js) |
| 4001 | MkDocs (built static) |
| 5432 | Listmonk PostgreSQL |
| 5433 | V2 PostgreSQL (localhost) |
| 5678 | n8n |
| 6379 | Redis |
| 8025 | MailHog Web UI |
| 8080 | cAdvisor |
| 8089 | Mini QR |
| 8091 | NocoDB v2 (read-only) |
| 8888 | Code Server |
| 9001 | Listmonk |
| 9090 | Prometheus |
| 9093 | Alertmanager |
---
## V1 Reference (Legacy)
V1 code is preserved in `influence/` and `map/` directories and backed up in `docker-compose.v1.yml`.
### V1 Architecture
Two independent Express.js apps using NocoDB REST API as data layer:
- **Influence** (`influence/app/`, port 3333) — Postal code → representative lookup, email campaigns, response tracking
- **Map** (`map/app/`, port 3000) — Leaflet.js map, volunteer shifts, walk sheets, QR codes
Both apps use: session-based auth (Redis-backed), bcryptjs passwords, Bull job queues, NocoDB REST API (not direct DB).
### V1 Express App Structure
```
app/
├── server.js # Entry point, middleware stack
├── config/ # Environment-based configuration
├── routes/ # Express route definitions
├── controllers/ # Business logic
├── services/ # External integrations (nocodb.js, email.js, listmonk.js)
├── middleware/ # auth.js, csrf.js, rateLimiter.js
├── utils/ # logger.js, metrics.js, validators.js
├── public/ # Static assets
└── templates/ # Server-rendered HTML templates
```
### V1 Commands
```bash
cd influence && cp example.env .env
./scripts/build-nocodb.sh # Initialize NocoDB tables
docker compose up -d
docker compose exec influence-app npm test # Run Jest tests
cd map && cp example.env .env
./build-nocodb.sh # Initialize NocoDB tables
docker compose up -d
```
### V1 Build Scripts
- `config.sh` — Interactive wizard that generates `.env` with secure random passwords
- `start-production.sh` — Installs cloudflared, creates tunnel, configures DNS
- `map/build-nocodb.sh` and `influence/scripts/build-nocodb.sh` — Create NocoDB schema + seed data
- `reset-site.sh` — Resets MkDocs to baseline
### V1 Documentation
- `influence/README.MD` — Features, config, campaign management, email testing
- `influence/files-explainer.md` — File-by-file code documentation
- `map/README.md` — Features, config, setup instructions
- `map/files-explainer.md` — File-by-file code documentation
---
## Key Configuration Files
| File | Purpose |
|------|---------|
| `docker-compose.yml` | V2 orchestration (all services) |
| `docker-compose.v1.yml` | V1 backup |
| `.env` / `.env.example` | Environment variables (never committed) |
| `api/prisma/schema.prisma` | Database schema |
| `nginx/` | Reverse proxy configuration |
| `configs/prometheus/prometheus.yml` | Monitoring scrape targets |
| `configs/cloudflare/tunnel-config.yml` | Production ingress routing |
## Networking
All containers share the `changemaker-lite` bridge network and reference each other by container name. Production uses Cloudflare tunnel with ingress rules mapping `*.cmlite.org` subdomains.