@@ -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
81123export const startInstance = createStart ( ( ) => ( {
82- requestMiddleware : [ marketingMiddleware , mcpRequestMiddleware , apiRequestMiddleware ] ,
124+ requestMiddleware : [
125+ marketingMiddleware ,
126+ mcpRequestMiddleware ,
127+ sentryTunnelMiddleware ,
128+ apiRequestMiddleware ,
129+ ] ,
83130} ) ) ;
0 commit comments