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
63 },63 },
64);64);
65 65
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
74const 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
66// ---------------------------------------------------------------------------108// ---------------------------------------------------------------------------
67// API middleware — routes /api/* to the Effect HTTP layer109// API middleware — routes /api/* to the Effect HTTP layer
68// ---------------------------------------------------------------------------110// ---------------------------------------------------------------------------
@@ -79,5 +121,10 @@ const apiRequestMiddleware = createMiddleware({ type: "request" }).server( · 10 unmodified lines
79);121);
80 122
81export const startInstance = createStart(() => ({123export const startInstance = createStart(() => ({
82 requestMiddleware: [marketingMiddleware, mcpRequestMiddleware, apiRequestMiddleware],124 requestMiddleware: [
125 marketingMiddleware,
126 mcpRequestMiddleware,
127 sentryTunnelMiddleware,
128 apiRequestMiddleware,
129 ],
83}));130}));