deafmissoula-website/cline_docs/techContext.md

90 lines
3.6 KiB
Markdown

# 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:
```typescript
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:
```bash
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
```bash
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.