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>
This commit is contained in:
2026-08-14 19:56:01 +02:00
parent 01a914f31e
commit 1a51b37097
4 changed files with 167 additions and 7 deletions
+3 -2
View File
@@ -18,7 +18,7 @@ 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/`, still scaffolding-only as of this writing).
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`
@@ -59,7 +59,8 @@ FEEDBACK_RATE_LIMIT_MAX=
FEEDBACK_RATE_LIMIT_WINDOW_MIN=
SALESFORCE_ENABLED=
SALESFORCE_API_URL=
SALESFORCE_API_TOKEN=
SALESFORCE_CLIENT_ID=
SALESFORCE_CLIENT_SECRET=
EMAIL_HOST=
EMAIL_USERNAME=
EMAIL_PASSWORD=