# Audit request endpoint Small Express service that receives the form on `/de/audit` and forwards it by email. It is deployed **separately** from the static site: the website is built with Vite and uploaded by FTP, this service runs on the VPS. Its dependencies are intentionally kept out of the frontend `package.json` so they never end up in the browser bundle. ## Endpoints | Method | Path | Purpose | | ------ | --------------------- | ----------------------------- | | `GET` | `/health` | Liveness probe | | `POST` | `/api/audit-request` | Accepts one audit request | Expected JSON body: ```json { "name": "…", "email": "…", "url": "https://…", "role": "agency | client", "motivation": "…", "gdprConsent": "true", "website": "" } ``` `website` is a honeypot and must stay empty. A filled honeypot is answered with `200` on purpose so bots learn nothing and do not retry. ## Setup ```bash cd backend npm install cp .env.example .env # then fill in the SMTP credentials npm start ``` The process binds to `127.0.0.1` only. Put nginx in front of it with TLS, for example on `api.sascha-bach.de`, and proxy to the port from `.env`. Then point the frontend at it by setting `VITE_AUDIT_ENDPOINT` before building: ```bash VITE_AUDIT_ENDPOINT=https://api.sascha-bach.de/api/audit-request npm run build ``` If the CSP `connect-src` in `public/.htaccess` names a different host than the one you deploy to, the browser blocks the request - keep the two in sync. ## Protections in place - CORS allowlist from `ALLOWED_ORIGINS`, never `*` - Rate limit: 3 requests per IP per 15 minutes - Request body capped at 10 kB - Server-side validation of every field (`express-validator`) - Honeypot field - Consent is required, not assumed - CR/LF stripped from values used in mail headers, HTML escaped in the body - No personal data written to logs `app.set('trust proxy', 1)` assumes exactly one proxy hop (nginx). Adjust it if you add a second one, otherwise the rate limiter sees a single IP for everyone.