Skip to content

Commit f51e826

Browse files
authored
feat(nextjs): Support Hybrid Cloud DSNs with tunnelRoute option (#10959)
1 parent 04a1ebe commit f51e826

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

packages/nextjs/src/client/tunnelRoute.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ export function applyTunnelRouteOption(options: BrowserOptions): void {
1717
if (!dsnComponents) {
1818
return;
1919
}
20-
const sentrySaasDsnMatch = dsnComponents.host.match(/^o(\d+)\.ingest\.sentry\.io$/);
20+
const sentrySaasDsnMatch = dsnComponents.host.match(/^o(\d+)\.ingest(?:\.([a-z]{2}))?\.sentry\.io$/);
2121
if (sentrySaasDsnMatch) {
2222
const orgId = sentrySaasDsnMatch[1];
23-
const tunnelPath = `${tunnelRouteOption}?o=${orgId}&p=${dsnComponents.projectId}`;
23+
const regionCode = sentrySaasDsnMatch[2];
24+
let tunnelPath = `${tunnelRouteOption}?o=${orgId}&p=${dsnComponents.projectId}`;
25+
if (regionCode) {
26+
tunnelPath += `&r=${regionCode}`;
27+
}
2428
options.tunnel = tunnelPath;
2529
DEBUG_BUILD && logger.info(`Tunneling events to "${tunnelPath}"`);
2630
} else {

packages/nextjs/src/config/withSentryConfig.ts

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function setUpTunnelRewriteRules(userNextConfig: NextConfigObject, tunnelPath: s
9494
// This function doesn't take any arguments at the time of writing but we future-proof
9595
// here in case Next.js ever decides to pass some
9696
userNextConfig.rewrites = async (...args: unknown[]) => {
97-
const injectedRewrite = {
97+
const tunnelRouteRewrite = {
9898
// Matched rewrite routes will look like the following: `[tunnelPath]?o=[orgid]&p=[projectid]`
9999
// Nextjs will automatically convert `source` into a regex for us
100100
source: `${tunnelPath}(/?)`,
@@ -113,19 +113,43 @@ function setUpTunnelRewriteRules(userNextConfig: NextConfigObject, tunnelPath: s
113113
destination: 'https://o:orgid.ingest.sentry.io/api/:projectid/envelope/?hsts=0',
114114
};
115115

116+
const tunnelRouteRewriteWithRegion = {
117+
// Matched rewrite routes will look like the following: `[tunnelPath]?o=[orgid]&p=[projectid]?r=[region]`
118+
// Nextjs will automatically convert `source` into a regex for us
119+
source: `${tunnelPath}(/?)`,
120+
has: [
121+
{
122+
type: 'query',
123+
key: 'o', // short for orgId - we keep it short so matching is harder for ad-blockers
124+
value: '(?<orgid>\\d*)',
125+
},
126+
{
127+
type: 'query',
128+
key: 'p', // short for projectId - we keep it short so matching is harder for ad-blockers
129+
value: '(?<projectid>\\d*)',
130+
},
131+
{
132+
type: 'query',
133+
key: 'r', // short for region - we keep it short so matching is harder for ad-blockers
134+
value: '(?<region>\\[a-z\\]{2})',
135+
},
136+
],
137+
destination: 'https://o:orgid.ingest.:region.sentry.io/api/:projectid/envelope/?hsts=0',
138+
};
139+
116140
if (typeof originalRewrites !== 'function') {
117-
return [injectedRewrite];
141+
return [tunnelRouteRewriteWithRegion, tunnelRouteRewrite];
118142
}
119143

120144
// @ts-expect-error Expected 0 arguments but got 1 - this is from the future-proofing mentioned above, so we don't care about it
121145
const originalRewritesResult = await originalRewrites(...args);
122146

123147
if (Array.isArray(originalRewritesResult)) {
124-
return [injectedRewrite, ...originalRewritesResult];
148+
return [tunnelRouteRewriteWithRegion, tunnelRouteRewrite, ...originalRewritesResult];
125149
} else {
126150
return {
127151
...originalRewritesResult,
128-
beforeFiles: [injectedRewrite, ...(originalRewritesResult.beforeFiles || [])],
152+
beforeFiles: [tunnelRouteRewriteWithRegion, tunnelRouteRewrite, ...(originalRewritesResult.beforeFiles || [])],
129153
};
130154
}
131155
};

packages/nextjs/test/utils/tunnelRoute.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,15 @@ describe('applyTunnelRouteOption()', () => {
6464

6565
expect(options.tunnel).toBeUndefined();
6666
});
67+
68+
it('Correctly applies `tunnelRoute` option to region DSNs', () => {
69+
globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
70+
const options: any = {
71+
dsn: 'https://[email protected]/3333333',
72+
} as BrowserOptions;
73+
74+
applyTunnelRouteOption(options);
75+
76+
expect(options.tunnel).toBe('/my-error-monitoring-route?o=2222222&p=3333333&r=us');
77+
});
6778
});

0 commit comments

Comments
 (0)