Files
API/CLAUDE.md
T
Paddy 1a51b37097 Wire up the Salesforce newsletter sync integration
Implements integrations/salesforce.service.ts per the plan's §5.6 seam
(syncNewsletterSignup(signupId)), against the real contract now that the
Salesforce side exists (see the nk-salesforce repo's
feature/newsletter-signup-integration branch): OAuth2 client-credentials
auth, POST to /services/apexrest/newsletter/signup with
{firstName, lastName, email, eventName}, response gives back which object
(Lead or Person Account) and its id. Token is cached in memory with a
conservative TTL and refreshed on a 401 rather than trusting expires_in,
which Salesforce's client-credentials token response doesn't reliably
return.

submissions.service.ts now captures the newsletter_signups insert's id
and fires syncNewsletterSignup after commit, fire-and-forget - the one
piece that was previously entirely missing, so flipping
SALESFORCE_ENABLED=true would have left every signup stuck at PENDING
forever with nothing to process it (found during an earlier review pass).

Replaced the placeholder SALESFORCE_API_TOKEN env var with
SALESFORCE_CLIENT_ID/SALESFORCE_CLIENT_SECRET in .env.example and
CLAUDE.md, matching the real auth mechanism instead of the static-token
guess from before the contract was known. Also fixed CLAUDE.md's stale
"still scaffolding-only" note about the Feedback domain.

Not yet covered by tests - the Salesforce-side contract was validated
end-to-end against a real sandbox, but this file has no unit tests yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 19:56:01 +02:00

75 lines
2.8 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
npm run build # Compile TypeScript → dist/
npm run start # Build and start (tsc && node ./dist/app.js)
npm run debug # Start with DEBUG=* environment variable
npm run test # Run Jest tests with coverage (outputs sonar-report.xml)
```
Run a single test file:
```bash
npx jest test/some.test.ts
```
## Architecture
Express.js REST API in TypeScript with a service-oriented layering. Domains: `Calendar` (events, users) and `Feedback` (concert feedback forms, mounted at `/feedback`, backed by its own `FEEDBACK_DB` — see `src/models/feedback/`: public submission flow, admin CRUD, reporting, and a Salesforce newsletter-sync integration).
**Request path:**
1. `app.ts` mounts `Calendar.router.ts` at `/calendar`
2. `Calendar.router.ts` delegates to `events.router.ts` and `users.router.ts`
3. Routers call services; services call the MariaDB pool in `Calendar.db.ts`
**Key layers:**
| Layer | Location |
|---|---|
| Router | `src/models/calendar/Calendar.router.ts`, `…/events/events.router.ts`, `…/users/users.router.ts` |
| Services | `…/events/events.service.ts`, `…/users/users.service.ts`, `…/events/credentials.service.ts`, `…/events/icalgenerator.service.ts` |
| DB pool | `src/models/calendar/Calendar.db.ts` (MariaDB, pool size 5) |
| Shared | `src/common/` (base route class, nodemailer wrapper), `src/middleware/logger.ts` (Winston) |
**Auth model:** Users must have a `@nachklang.art` email. After activation they receive a session token (30-day window); the token hash + IP are stored in the DB. Credentials for non-user calendar access (`MEMBER_CREDENTIAL`, `CHOIR_CREDENTIAL`, `MANAGEMENT_CREDENTIAL`) come from `.env`.
**Event versioning:** Events have a companion `event_versions` table. `events.service.ts` manages writes to both.
**Calendar types and IDs:** `public` (1), `members` (2), `management` (3), `choir` (4), `birthdays` (5). `credentials.service.ts` enforces which session/credential can read each calendar.
**iCal export:** `icalgenerator.service.ts` converts DB events to RFC 5545 format; reachable via `GET /calendar/events/{calendar}/ical`.
**API docs:** Swagger UI served at `/docs`, generated from JSDoc annotations in the router files.
## Environment
Copy `.env.example` (or create `.env`) with:
```
PORT=
DB_HOST=
DB_USER=
DB_PASSWORD=
CALENDAR_DB=
FEEDBACK_DB=
FEEDBACK_IP_SALT=
FEEDBACK_RATE_LIMIT_MAX=
FEEDBACK_RATE_LIMIT_WINDOW_MIN=
SALESFORCE_ENABLED=
SALESFORCE_API_URL=
SALESFORCE_CLIENT_ID=
SALESFORCE_CLIENT_SECRET=
EMAIL_HOST=
EMAIL_USERNAME=
EMAIL_PASSWORD=
MEMBER_CREDENTIAL=
CHOIR_CREDENTIAL=
MANAGEMENT_CREDENTIAL=
```
## TypeScript config
Strict mode enabled, target ES2016, compiled output in `./dist`, inline source maps. Tests run through `ts-jest` directly against `.ts` sources.