1 Commits

Author SHA1 Message Date
5a2c54dabf feat(jsn-bridge): identity / donation / social-link / Rocket.Chat bridges
Four bearer-secret-gated server-to-server endpoints that mirror JSN
supporters into cmlite. All share a constant-time `requireBridgeSecret`
(timingSafeEqual on `Bearer ${JSN_BRIDGE_SECRET}`) and per-route
Redis-backed rate limiting.

- POST /api/auth/bridge/users — identity provisioning, idempotent on email.
  Tags users with `permissions._jsnProvenance` (jsn-funnel | jsn-donate |
  jsn-organize | jsn-organic) and createdVia=JSN_BRIDGE. On new-user
  creation, fire-and-forget RC account provisioning via the existing
  rocketchatClient (createUser with throwaway password; SSO uses admin-side
  createUserToken). Stores RC user id on User.rocketChatUserId.

- POST /api/payments/bridge/record — donation mirror, idempotent on
  stripePaymentIntentId. Creates Invoice (type='donation', status='paid')
  + Payment in one transaction. 404s if no cmlite user for the email
  (JSN provisions first).

- POST /api/auth/bridge/social-links — handle sync, mirror semantics
  keyed on (userId, platform). Mirror per the existing handoff spec at
  docs/docs/handoff/cmlite-bridge.md on the JSN side.

- POST /api/rocketchat/bridge/auth — returns RC SSO token by looking up
  User.rocketChatUserId and calling rocketchatClient.createUserToken.
  Returns {enabled: false} when chat is off or the user has no RC account.

env.ts gains JSN_BRIDGE_SECRET (z.string().min(32).optional()).

Prisma migrations:
- add_jsn_bridge_to_user_created_via — enum value JSN_BRIDGE
- extend_social_platforms_and_unique — facebook/threads/bluesky/linkedin +
  composite unique (user_id, platform)
- add_user_rocketchat_user_id — User.rocketChatUserId column

JSN-side spec lives in the JSN repo at
docs/docs/integrations/changemaker.md.

Bunker Admin
2026-05-25 19:06:07 -06:00