deafmissoula-website/cline_docs/techContext.md

3.6 KiB

Technical Context

Stack (v2 — current)

Frontend

  • React 19 with TypeScript 5.8
  • Tailwind CSS (via PostCSS, built into src/tailwind.css)
  • Framer Motion 12.x for animations
  • React Router 7.x for client-side routing
  • Build tool: Create React App (react-scripts 5.0.1)
  • Install note: npm install --legacy-peer-deps required (react-scripts peer dep conflict with TS 5.x)

Backend

  • Node.js 22 LTS, Express 5.1, TypeScript 5.8
  • Mongoose 8.x ODM → MongoDB 7.0
  • jose 5.x for JWT signing/verification
  • argon2 for password hashing (native compilation — requires python3 make g++ in Docker)
  • multer 1.4.5-lts.1 for file uploads
  • nodemailer 6.x for email via Google Workspace SMTP
  • express-rate-limit 8.5.x on auth routes (5 attempts / 5 min)
  • zod 3.x for request validation
  • Dev runner: tsx watch

Express 5 Breaking Change

app.get('*', handler) is invalid in Express 5. SPA catch-all must use:

app.use((_req, res) => { res.sendFile(path.join(clientBuild, 'index.html')); });

Infrastructure

Docker (Production)

Two containers managed by docker-compose.yml:

  • deafmissoulaorg-app-1 — Node.js app (Express serves React build + API) on port 801
  • deafmissoulaorg-mongodb-1 — MongoDB 7.0, internal network only (not exposed)

Named volumes:

  • mcdi-mongo-data — MongoDB data
  • mcdi-uploads-data — Dashboard file uploads

Networks:

  • caddy_network (external) — shared with Caddy reverse proxy; app joins this so Caddy can route by container name
  • internal (bridge) — app ↔ MongoDB only

Caddy Reverse Proxy

  • Production server: 10.4.0.205 (WireGuard VPN)
  • Config location: ~/docker/caddy/config/Caddyfile

Because the app joins caddy_network, Caddy can use the container name:

deafmissoula.org {
    reverse_proxy deafmissoulaorg-app-1:801
}

If you see a 502 after a rebuild, check the container name hasn't changed:

docker ps --filter name=deafmissoula
docker exec caddy caddy reload --config /etc/caddy/Caddyfile

Production Server CPU Note

The Proxmox VM running 10.4.0.205 has CPU type set to host (passthrough). This is required for MongoDB 7.0+ which needs AVX instructions. Do not change the VM CPU type back to kvm64 or Common KVM processor — MongoDB will crash.

Authentication

  • Board members log in at /manage with their @deafmissoula.org email
  • First login: mustChangePassword: true → server returns tempToken (1-hour JWT) → client redirects to force-change screen
  • Forgot password: generates resetToken + resetTokenExpiry (1 hour) on BoardMember, emails ${APP_URL}/manage/reset-password?token=xxx
  • New board member added via dashboard: auto-sends welcome email with 7-day reset link
  • Admin fallback: admin@deafmissoula.org is a User model (not BoardMember), password set via ADMIN_PASSWORD in stack.env

File Serving

Two separate file trees:

  1. Static assets (baked into Docker image from client/public/) served at root paths: /photos/, /videos/, /minutes/, /headshots/, /sponsors/, /logos/
  2. Dashboard uploads (Docker volume mcdi-uploads-data) served at /uploads/headshots/, /uploads/photos/, /uploads/sponsors/, /uploads/minutes/

Deployment

ssh chaulmark@10.4.0.205
cd ~/websites/deafmissoula.org
git pull origin main
docker compose down && docker compose up -d --build
# First deploy only:
docker exec deafmissoulaorg-app-1 node dist/seed.js

CACHEBUST in stack.env forces a full Docker layer cache bust when incremented. Update it if a rebuild is pulling stale cached layers.