|
1 | 1 | import { VERSION } from "@goauthentik/common/constants";
|
2 |
| -import { ServerContext } from "@goauthentik/common/server-context"; |
3 |
| -import { me } from "@goauthentik/common/users"; |
4 |
| -import { |
5 |
| - ErrorEvent, |
6 |
| - EventHint, |
7 |
| - browserTracingIntegration, |
8 |
| - init, |
9 |
| - setTag, |
10 |
| - setUser, |
11 |
| -} from "@sentry/browser"; |
| 2 | +import { RouteInterfaceName, readInterfaceRouteParam } from "@goauthentik/elements/router/utils"; |
| 3 | +import { BrowserOptions, browserTracingIntegration, init, setTag, setUser } from "@sentry/browser"; |
12 | 4 |
|
13 |
| -import { CapabilitiesEnum, ResponseError } from "@goauthentik/api"; |
| 5 | +import { CapabilitiesEnum, Config, ResponseError, UserSelf } from "@goauthentik/api"; |
14 | 6 |
|
15 | 7 | /**
|
16 | 8 | * A generic error that can be thrown without triggering Sentry's reporting.
|
| 9 | + * |
| 10 | + * @category Sentry |
17 | 11 | */
|
18 | 12 | export class SentryIgnoredError extends Error {}
|
19 | 13 |
|
20 |
| -export const TAG_SENTRY_COMPONENT = "authentik.component"; |
21 |
| -export const TAG_SENTRY_CAPABILITIES = "authentik.capabilities"; |
| 14 | +/** |
| 15 | + * Attempt initializing Spotlight. |
| 16 | + * |
| 17 | + * @see {@link https://spotlightjs.com/ Spotlight} |
| 18 | + * @category Sentry |
| 19 | + */ |
| 20 | +export async function tryInitializingSpotlight() { |
| 21 | + return import("@spotlightjs/spotlight").then((Spotlight) => |
| 22 | + Spotlight.init({ injectImmediately: true }), |
| 23 | + ); |
| 24 | +} |
22 | 25 |
|
23 |
| -export async function configureSentry(canDoPpi = false): Promise<void> { |
24 |
| - const { errorReporting, capabilities } = ServerContext.config; |
| 26 | +/** |
| 27 | + * Default Sentry options for the browser. |
| 28 | + * |
| 29 | + * @category Sentry |
| 30 | + */ |
| 31 | +const DEFAULT_SENTRY_BROWSER_OPTIONS = { |
| 32 | + ignoreErrors: [ |
| 33 | + /network/gi, |
| 34 | + /fetch/gi, |
| 35 | + /module/gi, |
| 36 | + // Error on edge on ios, |
| 37 | + // https://stackoverflow.com/questions/69261499/what-is-instantsearchsdkjsbridgeclearhighlight |
| 38 | + /instantSearchSDKJSBridgeClearHighlight/gi, |
| 39 | + // Seems to be an issue in Safari and Firefox |
| 40 | + /MutationObserver.observe/gi, |
| 41 | + /NS_ERROR_FAILURE/gi, |
| 42 | + ], |
| 43 | + release: `authentik@${VERSION}`, |
| 44 | + integrations: [ |
| 45 | + browserTracingIntegration({ |
| 46 | + shouldCreateSpanForRequest: (url: string) => { |
| 47 | + return url.startsWith(window.location.host); |
| 48 | + }, |
| 49 | + }), |
| 50 | + ], |
| 51 | + beforeSend: (event, hint) => { |
| 52 | + if (!hint) { |
| 53 | + return event; |
| 54 | + } |
| 55 | + if (hint.originalException instanceof SentryIgnoredError) { |
| 56 | + return null; |
| 57 | + } |
| 58 | + if ( |
| 59 | + hint.originalException instanceof ResponseError || |
| 60 | + hint.originalException instanceof DOMException |
| 61 | + ) { |
| 62 | + return null; |
| 63 | + } |
| 64 | + return event; |
| 65 | + }, |
| 66 | +} as const satisfies BrowserOptions; |
25 | 67 |
|
26 |
| - if (!errorReporting.enabled) return; |
| 68 | +/** |
| 69 | + * Include the given user in Sentry events. |
| 70 | + * |
| 71 | + * @category Sentry |
| 72 | + */ |
| 73 | +export function setSentryPII(user: UserSelf): void { |
| 74 | + console.debug("authentik/config: Sentry with PII enabled."); |
27 | 75 |
|
| 76 | + setUser({ email: user.email }); |
| 77 | +} |
| 78 | + |
| 79 | +/** |
| 80 | + * Include the given capabilities in Sentry events. |
| 81 | + * |
| 82 | + * @category Sentry |
| 83 | + */ |
| 84 | +export function setSentryCapabilities(capabilities: CapabilitiesEnum[]): void { |
| 85 | + setTag("authentik.capabilities", capabilities.join(",")); |
| 86 | +} |
| 87 | + |
| 88 | +/** |
| 89 | + * Include the given route interface in Sentry events. |
| 90 | + * |
| 91 | + * @category Sentry |
| 92 | + */ |
| 93 | +export function setSentryInterface(interfaceName: RouteInterfaceName) { |
| 94 | + setTag("authentik.component", `web/${interfaceName}}`); |
| 95 | +} |
| 96 | + |
| 97 | +/** |
| 98 | + * Attempt to initialize Sentry with the given configuration. |
| 99 | + * |
| 100 | + * @see {@linkcode setSentryPII} |
| 101 | + * @see {@linkcode setSentryCapabilities} |
| 102 | + * @see {@linkcode setSentryInterface} |
| 103 | + * @category Sentry |
| 104 | + */ |
| 105 | +export function tryInitializeSentry({ errorReporting, capabilities }: Config): void { |
28 | 106 | init({
|
| 107 | + ...DEFAULT_SENTRY_BROWSER_OPTIONS, |
29 | 108 | dsn: errorReporting.sentryDsn,
|
30 |
| - ignoreErrors: [ |
31 |
| - /network/gi, |
32 |
| - /fetch/gi, |
33 |
| - /module/gi, |
34 |
| - // Error on edge on ios, |
35 |
| - // https://stackoverflow.com/questions/69261499/what-is-instantsearchsdkjsbridgeclearhighlight |
36 |
| - /instantSearchSDKJSBridgeClearHighlight/gi, |
37 |
| - // Seems to be an issue in Safari and Firefox |
38 |
| - /MutationObserver.observe/gi, |
39 |
| - /NS_ERROR_FAILURE/gi, |
40 |
| - ], |
41 |
| - release: `authentik@${VERSION}`, |
42 |
| - integrations: [ |
43 |
| - browserTracingIntegration({ |
44 |
| - shouldCreateSpanForRequest: (url: string) => { |
45 |
| - return url.startsWith(window.location.host); |
46 |
| - }, |
47 |
| - }), |
48 |
| - ], |
49 | 109 | tracesSampleRate: errorReporting.tracesSampleRate,
|
50 | 110 | environment: errorReporting.environment,
|
51 |
| - beforeSend: ( |
52 |
| - event: ErrorEvent, |
53 |
| - hint: EventHint, |
54 |
| - ): ErrorEvent | PromiseLike<ErrorEvent | null> | null => { |
55 |
| - if (!hint) { |
56 |
| - return event; |
57 |
| - } |
58 |
| - if (hint.originalException instanceof SentryIgnoredError) { |
59 |
| - return null; |
60 |
| - } |
61 |
| - if ( |
62 |
| - hint.originalException instanceof ResponseError || |
63 |
| - hint.originalException instanceof DOMException |
64 |
| - ) { |
65 |
| - return null; |
66 |
| - } |
67 |
| - return event; |
68 |
| - }, |
| 111 | + enabled: process.env.NODE_ENV !== "development", |
69 | 112 | });
|
70 | 113 |
|
71 |
| - setTag(TAG_SENTRY_CAPABILITIES, capabilities.join(",")); |
72 |
| - |
73 |
| - if (window.location.pathname.includes("if/")) { |
74 |
| - setTag(TAG_SENTRY_COMPONENT, `web/${currentInterface()}`); |
75 |
| - } |
| 114 | + setSentryCapabilities(capabilities); |
| 115 | + setSentryInterface(readInterfaceRouteParam()); |
76 | 116 |
|
77 | 117 | if (capabilities.includes(CapabilitiesEnum.CanDebug)) {
|
78 |
| - const Spotlight = await import("@spotlightjs/spotlight"); |
79 |
| - |
80 |
| - Spotlight.init({ injectImmediately: true }); |
81 |
| - } |
82 |
| - |
83 |
| - if (errorReporting.sendPii && canDoPpi) { |
84 |
| - me().then((user) => { |
85 |
| - setUser({ email: user.user.email }); |
86 |
| - console.debug("authentik/config: Sentry with PII enabled."); |
87 |
| - }); |
88 |
| - } else { |
89 |
| - console.debug("authentik/config: Sentry enabled."); |
90 |
| - } |
91 |
| -} |
92 |
| - |
93 |
| -// Get the interface name from URL |
94 |
| -export function currentInterface(): string { |
95 |
| - const pathMatches = window.location.pathname.match(/.+if\/(\w+)\//); |
96 |
| - let currentInterface = "unknown"; |
97 |
| - if (pathMatches && pathMatches.length >= 2) { |
98 |
| - currentInterface = pathMatches[1]; |
| 118 | + tryInitializingSpotlight() |
| 119 | + .then(() => { |
| 120 | + console.debug("authentik/config: Sentry with spotlight enabled."); |
| 121 | + }) |
| 122 | + .catch((err) => { |
| 123 | + console.warn("sentry: Failed to load spotlight", err); |
| 124 | + }); |
99 | 125 | }
|
100 |
| - return currentInterface.toLowerCase(); |
101 | 126 | }
|
0 commit comments