Skip to content

Latest commit

 

History

History
28 lines (24 loc) · 11.3 KB

File metadata and controls

28 lines (24 loc) · 11.3 KB

API Reference

All routes live under src/pages/api/ and run server-side only (export const prerender = false where set). See ARCHITECTURE.md for the middleware that wraps every response (cache-control forces no-store on all /api/* routes regardless of what a route sets) and SECURITY.md for the shared validation/CORS rules these routes follow.

Route Method Auth / origin check Upstream Notes
/api/events GET none (public read) ICS feed (fetchEvents, EVENTS_ICS_URL) Flat array; consumers group into category sections via eventSections.ts
/api/faq GET none Notion (fetchNotionFAQ) ?showAll=yes bypasses Visibility filter
/api/ideas GET none Notion (fetchNotionIdeas)
/api/speakers GET none Notion (fetchNotionAgenda) Returns { agendaItems, speakers }; resolves moderator relations to speaker profiles
/api/e4p-signatories GET none (public read) Notion (Signatories DB) Only returns rows where Approved is checked
/api/e4p-pledge-sign POST Origin: https://techforpalestine.org Notion (Signatories DB) Validates required fields, email format, LinkedIn URL, 2000-char cap; writes with Approved: false (manual moderation)
/api/endorsement-request POST Origin: https://techforpalestine.org Notion (Endorsements DB) Same validation pattern as pledge-sign
/api/donation-complete POST + OPTIONS Qgiv transaction verification (server-side lookup via QGIV_API_TOKEN) + Origin allowlist as defence in depth EmailOctopus Request body is { transactionId } only. Email and name are read from Qgiv's Transactions API. Verification failure returns 402. On success, tags contact donor.
/api/membership-complete POST + OPTIONS Qgiv transaction verification (server-side lookup via QGIV_API_TOKEN) + Origin allowlist as defence in depth Hub API (HUB_API_URL) + EmailOctopus Request body is { transactionId } only. Email, name, and tier are read from Qgiv's Transactions API. Verification failure returns 402. On success, tags the contact in EmailOctopus and (for full members) invites them to the Hub (type: "paid"), in parallel via Promise.allSettled.
/api/pipe POST Origin allowlist (prod + *.pages.dev) Plausible Analytics Server-side proxy for the Plausible events endpoint (avoids ad-blockers hitting plausible.io directly from the browser); on a dropped conversion event it also writes a fallback record to the DROPPED_CONVERSIONS KV namespace — see DONATIONS.md
/api/sentry-webhook POST HMAC signature (sentry-hook-signature header, SENTRY_WEBHOOK_SECRET) + rejects if an Origin header is present (webhooks shouldn't have one) Mattermost Relays Sentry issue/alert webhooks into a Mattermost channel
/api/projects GET none (public read) ProjectHub (projecthub.techforpalestine.org/api/public/projects) Sanitizes all URL-shaped fields to strip non-http(s) protocols (XSS guard); retries on 5xx with backoff. Logic lives in src/store/projectsClient.ts, shared with /projects/<slug>. The list is cached server-side for 5 minutes (24 hours stale-if-error); the X-Projects-Source response header reads hit, miss or stale
/api/project-proxy GET, POST none at this layer — adds Authorization server-side Generic upstream (PUBLIC_API_URL) Generic authenticated passthrough used by the volunteer/incubator application forms (src/store/api.ts); only forwards paths under /api/method/, explicit header allowlist — see SECURITY.md for the pattern
/api/admin/conversion-stats GET HTTP Basic Auth (ADMIN_USERNAME/ADMIN_PASSWORD, constant-time compare) Plausible Stats API v2 + DROPPED_CONVERSIONS KV Backs the /admin/conversions dashboard; merges live Plausible numbers with the KV fallback log — see DONATIONS.md

Conventions to follow for new routes

  • export const prerender = false; on any route that reads request data or calls an external API.
  • Errors: call reportError(error, { context: "route-name" }) from src/lib/report-error.ts, then ctx?.waitUntil(Promise.resolve(Sentry.flush(2000))) before returning — Cloudflare Workers can terminate the request before an unflushed Sentry event is sent.
  • Return generic error messages to the client ("Failed to process request"); never leak the caught error or stack trace.
  • Public write endpoints: validate required fields, email regex, URL fields via try { new URL(x) } catch, and cap free-text fields at 2000 characters — check Origin before parsing the body.
  • Use getEnv(name, locals), never process.env directly.