Skip to main content

Local development

git clone https://github.com/Every-Man-A-Warrior/emaw-crm-app.git
cd emaw-crm-app
git checkout develop
npm install
cp .env.example .env.local # optional overrides
npm run dev # http://localhost:5173

Sign in with a dev backend account that is in the Django group CRM Staff (or has capabilities granted). Create the account on the dev website first, then ask a superuser to add the group in Django admin at https://dev.emaw-app.com/admin/.

How requests reach the backend​

.env.development sets VITE_API_BASE_URL=https://dev.emaw-app.com and VITE_API_PROXY=true. With the proxy on, the app calls its own origin at /rest/...:

  • In npm run dev, Vite's dev server proxies /rest to VITE_API_BASE_URL (vite.config.ts).
  • In deployment, the Worker does the same.

So the backend sees a same-origin client, the refresh cookie is first-party, and there is no CORS to configure. Locally there is no Cloudflare Access in front of you; the dev backend only enforces the Access JWT when CF_ACCESS_* are set on it, and the Worker is what forwards that header in deployed environments. If a staff endpoint returns 403 locally, the account lacks the group or capability.

Scripts​

CommandDoes
npm run devVite on 5173 (ngrok hosts allowed)
npm run buildtsc -b && vite build → dist/
npm run previewServe the production build
npm run lintESLint (no-console is an error except warn/error, because bodies can contain personal data)
npm test / npm run test:watchVitest
npm run deploy:develop / deploy:productionBuild with the mode's env file and wrangler deploy (Deployment)

Development rules​

These come from README.md and src/api/README.md; reviewers enforce them.

  • Dev sends go to the "Email Testing" group only (group 36). The dev database holds about 4,000 real, mailable people. The app shows a banner and disables the send button unless the audience is limited to VITE_DEV_SEND_GROUP_IDS; the backend has the matching CAMPAIGN_SEND_ALLOWED_GROUP_IDS guard.
  • Access tokens live in memory only. Refresh uses the backend's HttpOnly cookie through the proxy. Nothing auth-related goes in localStorage; only the display email is in sessionStorage.
  • No email address in a URL except the q search parameter. Never log request or response bodies. Show X-Request-ID on error toasts.
  • CSV exports run only on an explicit click and are never cached.
  • Env vars are read in exactly one place, src/config.ts.
  • Each feature owns its API functions (features/<area>/api.ts), types and screens; routes are lazy modules exporting Component.
  • Paths must end with a trailing slash; apiFetch enforces it.

Working against a local backend​

Set VITE_API_BASE_URL=http://localhost:8000 in .env.local. Keep VITE_API_PROXY=true so cookies stay first-party. Your local backend must have the account in CRM Staff and CF_ACCESS_* unset.

Map tiles​

VITE_CARTO_BASEMAP_KEY enables the CARTO Light/Dark basemaps; without it they render with a watermark. OSM and Esri World Imagery need no key (features/map/layers.ts).