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

8.4 KiB

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

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

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)

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)

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

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.