History

Merge pull request #277 from RhysSullivan/rs/sentry-tunnel

fix(cloud): forward /api/sentry-tunnel to Sentry ingest

Rhys Sullivan committed 6 months ago

Commit 17c607f2 parents 36ef838 03a15ef1 file · +48 −1Browse files

Unified · Split

apps/cloud/src/start.ts+48−1
@@ -63,6 +63,48 @@ const mcpRequestMiddleware = createMiddleware({ type: "request" }).server( · 62 unmodified lines
6363 },
6464 );
6565
66+// ---------------------------------------------------------------------------
67+// Sentry tunnel — the browser SDK POSTs envelopes to /api/sentry-tunnel
68+// (configured in routes/__root.tsx) to dodge adblockers and CSP. We parse
69+// the envelope header to recover the DSN, validate against our own, and
70+// forward the body to Sentry's ingest endpoint. See
71+// https://docs.sentry.io/platforms/javascript/troubleshooting/#using-the-tunnel-option
72+// ---------------------------------------------------------------------------
73+
74+const sentryTunnelMiddleware = createMiddleware({ type: "request" }).server(
75+ async ({ pathname, request, next }) => {
76+ if (pathname !== "/api/sentry-tunnel" || request.method !== "POST") {
77+ return next();
78+ }
79+
80+ const configuredDsn = (env as { SENTRY_DSN?: string }).SENTRY_DSN;
81+ if (!configuredDsn) return new Response(null, { status: 204 });
82+
83+ try {
84+ const envelope = await request.text();
85+ const firstLine = envelope.slice(0, envelope.indexOf("\n"));
86+ const header = JSON.parse(firstLine) as { dsn?: string };
87+ if (!header.dsn) return new Response("missing dsn", { status: 400 });
88+
89+ const envelopeDsn = new URL(header.dsn);
90+ const ourDsn = new URL(configuredDsn);
91+ if (envelopeDsn.host !== ourDsn.host || envelopeDsn.pathname !== ourDsn.pathname) {
92+ return new Response("dsn mismatch", { status: 400 });
93+ }
94+
95+ const projectId = envelopeDsn.pathname.replace(/^\//, "");
96+ const ingestUrl = `https://${envelopeDsn.host}/api/${projectId}/envelope/`;
97+ return fetch(ingestUrl, {
98+ method: "POST",
99+ body: envelope,
100+ headers: { "Content-Type": "application/x-sentry-envelope" },
101+ });
102+ } catch {
103+ return new Response("bad envelope", { status: 400 });
104+ }
105+ },
106+);
107+
66108 // ---------------------------------------------------------------------------
67109 // API middleware — routes /api/* to the Effect HTTP layer
68110 // ---------------------------------------------------------------------------
@@ -79,5 +121,10 @@ const apiRequestMiddleware = createMiddleware({ type: "request" }).server( · 10 unmodified lines
79121 );
80122
81123 export const startInstance = createStart(() => ({
82− requestMiddleware: [marketingMiddleware, mcpRequestMiddleware, apiRequestMiddleware],
124+ requestMiddleware: [
125+ marketingMiddleware,
126+ mcpRequestMiddleware,
127+ sentryTunnelMiddleware,
128+ apiRequestMiddleware,
129+ ],
83130 }));