Skip to content

Commit 87ea857

Browse files
feat(astro): add tunnelRoute option (#10309)
1 parent 6fe349f commit 87ea857

File tree

7 files changed

+130
-2
lines changed

7 files changed

+130
-2
lines changed

packages/astro/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
"import": "./build/esm/integration/middleware/index.js",
4141
"require": "./build/cjs/integration/middleware/index.js",
4242
"types": "./build/types/integration/middleware/index.types.d.ts"
43+
},
44+
"./tunnel": {
45+
"node": "./build/esm/integration/tunnel.js",
46+
"import": "./build/esm/integration/tunnel.js",
47+
"require": "./build/cjs/integration/tunnel.js",
48+
"types": "./build/types/integration/tunnel.types.d.ts"
4349
}
4450
},
4551
"publishConfig": {

packages/astro/src/integration/index.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => {
1313
name: PKG_NAME,
1414
hooks: {
1515
// eslint-disable-next-line complexity
16-
'astro:config:setup': async ({ updateConfig, injectScript, addMiddleware, config, command, logger }) => {
16+
'astro:config:setup': async ({
17+
updateConfig,
18+
injectScript,
19+
addMiddleware,
20+
config,
21+
command,
22+
logger,
23+
injectRoute,
24+
}) => {
1725
// The third param here enables loading of all env vars, regardless of prefix
1826
// see: https://main.vitejs.dev/config/#using-environment-variables-in-config
1927

@@ -111,6 +119,19 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => {
111119
entrypoint: '@sentry/astro/middleware',
112120
});
113121
}
122+
123+
if (options.tunnelRoute) {
124+
if (options.tunnelRoute.projectIds.length === 0) {
125+
throw new Error(
126+
'When using the `tunnelRoute` configuration, you need to provide at least one project Id in `projectIds`',
127+
);
128+
}
129+
130+
injectRoute({
131+
pattern: options.tunnelRoute.pattern,
132+
entryPoint: '@sentry/astro/tunnel',
133+
});
134+
}
114135
},
115136
},
116137
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { APIRoute } from 'astro';
2+
import { config } from 'virtual:@sentry/astro/tunnel-config';
3+
4+
export const prerender = false;
5+
6+
export const POST: APIRoute = async ({ request }) => {
7+
try {
8+
const envelope = await request.text();
9+
const piece = envelope.split('\n')[0];
10+
const header = JSON.parse(piece);
11+
12+
const dsn = new URL(header.dsn);
13+
if (dsn.hostname !== config.host) {
14+
throw new Error(`Invalid Sentry host: ${dsn.hostname}`);
15+
}
16+
17+
const projectId = dsn.pathname.substring(1);
18+
if (!config.projectIds.includes(projectId)) {
19+
throw new Error(`Invalid Project ID: ${projectId}`);
20+
}
21+
22+
const url = `https://${config.host}/api/${projectId}/envelope/`;
23+
const res = await fetch(url, {
24+
method: 'POST',
25+
body: envelope,
26+
headers: {
27+
'Content-Type': 'application/x-sentry-envelope',
28+
},
29+
});
30+
31+
if (!res.ok) {
32+
throw new Error(await res.text());
33+
}
34+
35+
return Response(null, { status: 204 });
36+
} catch (error) {
37+
return Response.json({ message: error.message }, { status: 400 });
38+
}
39+
};

packages/astro/src/integration/types.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,33 @@ type SdkEnabledOptions = {
126126
};
127127
};
128128

129+
type SdkTunnelOptions = {
130+
/**
131+
* Options for tunneling.
132+
*/
133+
tunnelRoute?: {
134+
/**
135+
* Where the route should be output in the browser, for example `/api/sy`. It's recommended
136+
* not to put terms related to Sentry or tracking as they can be flagged and blocked by
137+
* ad-blockers.
138+
*
139+
* @see https://docs.astro.build/en/reference/integrations-reference/#injectroute-option
140+
*/
141+
pattern: string;
142+
/**
143+
* The project slug of your Sentry project.
144+
*
145+
* @default "sentry.io"
146+
*/
147+
host?: string;
148+
/**
149+
* Restricts to what project your tunnel can forward Sentry events to. There should be at least
150+
* one project id.
151+
*/
152+
projectIds: Array<string>;
153+
};
154+
};
155+
129156
/**
130157
* A subset of Sentry SDK options that can be set via the `sentryAstro` integration.
131158
* Some options (e.g. integrations) are set by default and cannot be changed here.
@@ -140,4 +167,5 @@ export type SentryOptions = SdkInitPaths &
140167
Pick<BrowserOptions, 'replaysSessionSampleRate' | 'replaysOnErrorSampleRate'> &
141168
SourceMapsOptions &
142169
InstrumentationOptions &
143-
SdkEnabledOptions;
170+
SdkEnabledOptions &
171+
SdkTunnelOptions;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { Plugin } from 'vite';
2+
import type { SentryOptions } from './types';
3+
4+
function resolveVirtualModuleId<T extends string>(id: T): `\0${T}` {
5+
return `\0${id}`;
6+
}
7+
8+
export type VirtualImportsParams = Pick<Required<SentryOptions>['tunnelRoute'], 'host' | 'projectIds'>;
9+
10+
export const virtualImportsPlugin = ({ host = 'sentry.io', projectIds }: VirtualImportsParams): Plugin => {
11+
const modules: Record<string, string> = {
12+
'virtual:@sentry/astro/tunnel-config': `export const config = ${JSON.stringify({ host, projectIds })}`,
13+
};
14+
15+
/** Mapping names prefixed with `\0` to their original form. */
16+
const resolutionMap = Object.fromEntries(
17+
(Object.keys(modules) as (keyof typeof modules)[]).map(key => [resolveVirtualModuleId(key), key]),
18+
);
19+
20+
return {
21+
name: '@sentry/astro/virtual',
22+
resolveId(id) {
23+
if (id in modules) return resolveVirtualModuleId(id);
24+
},
25+
load(id) {
26+
const resolution = resolutionMap[id];
27+
if (resolution) return modules[resolution];
28+
},
29+
};
30+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declare module 'virtual:@sentry/astro/tunnel-config' {
2+
export const config: import('./tunnel').VirtualImportsParams;
3+
}

packages/astro/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"include": ["src/**/*"],
55

66
"compilerOptions": {
7+
"lib": ["ES2019"]
78
// package-specific options
89
}
910
}

0 commit comments

Comments
 (0)