Skip to content

Commit 17c607f

Browse files
authored
Merge pull request #277 from RhysSullivan/rs/sentry-tunnel
fix(cloud): forward /api/sentry-tunnel to Sentry ingest
2 parents 36ef838 + 03a15ef commit 17c607f

1 file changed

Lines changed: 48 additions & 1 deletion

File tree

‎apps/cloud/src/start.ts‎

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,48 @@ const mcpRequestMiddleware = createMiddleware({ type: "request" }).server(
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(
79121
);
80122

81123
export const startInstance = createStart(() => ({
82-
requestMiddleware: [marketingMiddleware, mcpRequestMiddleware, apiRequestMiddleware],
124+
requestMiddleware: [
125+
marketingMiddleware,
126+
mcpRequestMiddleware,
127+
sentryTunnelMiddleware,
128+
apiRequestMiddleware,
129+
],
83130
}));

0 commit comments

Comments
 (0)