added glitchtip using sentry sdk
This commit is contained in:
@@ -16,12 +16,14 @@ import { EventProvider } from './context/EventContext';
|
||||
import MatomoTracker from '@/components/analytics/MatomoTracker';
|
||||
import { ConsentProvider } from '@/contexts/consent';
|
||||
import CookieBanner from '@/components/consent/CookieBanner';
|
||||
import { Sentry, initSentry } from '@/lib/sentry';
|
||||
|
||||
const DevTenantSwitcher = React.lazy(() => import('./DevTenantSwitcher'));
|
||||
|
||||
const enableDevSwitcher = import.meta.env.DEV || import.meta.env.VITE_ENABLE_TENANT_SWITCHER === 'true';
|
||||
|
||||
initializeTheme();
|
||||
initSentry('admin');
|
||||
const rootEl = document.getElementById('root')!;
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
@@ -43,11 +45,19 @@ if ('serviceWorker' in navigator) {
|
||||
});
|
||||
}
|
||||
|
||||
const AdminFallback: React.FC<{ message: string }> = ({ message }) => (
|
||||
<div className="flex min-h-screen items-center justify-center text-sm text-muted-foreground">
|
||||
{message}
|
||||
</div>
|
||||
);
|
||||
|
||||
createRoot(rootEl).render(
|
||||
<React.StrictMode>
|
||||
<AppearanceProvider>
|
||||
<AdminApp />
|
||||
</AppearanceProvider>
|
||||
<Sentry.ErrorBoundary fallback={<AdminFallback message="Oberfläche konnte nicht geladen werden." />}>
|
||||
<AppearanceProvider>
|
||||
<AdminApp />
|
||||
</AppearanceProvider>
|
||||
</Sentry.ErrorBoundary>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
|
||||
@@ -13,8 +13,16 @@ import CookieBanner from '@/components/consent/CookieBanner';
|
||||
import React from 'react';
|
||||
import { usePage } from '@inertiajs/react';
|
||||
import { useEffect } from 'react';
|
||||
import { Sentry, initSentry } from './lib/sentry';
|
||||
|
||||
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
|
||||
const InertiaFallback: React.FC = () => (
|
||||
<div className="flex min-h-screen items-center justify-center text-sm text-muted-foreground">
|
||||
Oberfläche konnte nicht geladen werden.
|
||||
</div>
|
||||
);
|
||||
|
||||
initSentry('inertia');
|
||||
|
||||
const LocaleSync: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
// usePage is only available inside Inertia-provided tree; guard for SSR/raw mounts
|
||||
@@ -63,17 +71,19 @@ createInertiaApp({
|
||||
}
|
||||
|
||||
root.render(
|
||||
<AppearanceProvider>
|
||||
<ConsentProvider>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<LocaleSync>
|
||||
<App {...props} />
|
||||
</LocaleSync>
|
||||
<CookieBanner />
|
||||
<Toaster position="top-right" toastOptions={{ duration: 4000 }} />
|
||||
</I18nextProvider>
|
||||
</ConsentProvider>
|
||||
</AppearanceProvider>
|
||||
<Sentry.ErrorBoundary fallback={<InertiaFallback />}>
|
||||
<AppearanceProvider>
|
||||
<ConsentProvider>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<LocaleSync>
|
||||
<App {...props} />
|
||||
</LocaleSync>
|
||||
<CookieBanner />
|
||||
<Toaster position="top-right" toastOptions={{ duration: 4000 }} />
|
||||
</I18nextProvider>
|
||||
</ConsentProvider>
|
||||
</AppearanceProvider>
|
||||
</Sentry.ErrorBoundary>
|
||||
);
|
||||
},
|
||||
progress: {
|
||||
|
||||
@@ -3,8 +3,16 @@ import { createRoot } from 'react-dom/client';
|
||||
import '../../css/app.css';
|
||||
import { AppearanceProvider, initializeTheme } from '@/hooks/use-appearance';
|
||||
import { enableGuestDemoMode, shouldEnableGuestDemoMode } from './demo/demoMode';
|
||||
import { Sentry, initSentry } from '@/lib/sentry';
|
||||
|
||||
const GuestFallback: React.FC<{ message: string }> = ({ message }) => (
|
||||
<div className="flex min-h-screen items-center justify-center text-sm text-muted-foreground">
|
||||
{message}
|
||||
</div>
|
||||
);
|
||||
|
||||
initializeTheme();
|
||||
initSentry('guest');
|
||||
if (shouldEnableGuestDemoMode()) {
|
||||
enableGuestDemoMode();
|
||||
}
|
||||
@@ -14,11 +22,13 @@ const isShareRoute = typeof window !== 'undefined' && window.location.pathname.s
|
||||
const shareRoot = async () => {
|
||||
const { SharedPhotoStandalone } = await import('./pages/SharedPhotoPage');
|
||||
createRoot(rootEl).render(
|
||||
<React.StrictMode>
|
||||
<AppearanceProvider>
|
||||
<SharedPhotoStandalone />
|
||||
</AppearanceProvider>
|
||||
</React.StrictMode>
|
||||
<Sentry.ErrorBoundary fallback={<GuestFallback message="Dieses Foto kann gerade nicht geladen werden." />}>
|
||||
<React.StrictMode>
|
||||
<AppearanceProvider>
|
||||
<SharedPhotoStandalone />
|
||||
</AppearanceProvider>
|
||||
</React.StrictMode>
|
||||
</Sentry.ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -53,41 +63,29 @@ const appRoot = async () => {
|
||||
}
|
||||
|
||||
createRoot(rootEl).render(
|
||||
<React.StrictMode>
|
||||
<AppearanceProvider>
|
||||
<LocaleProvider>
|
||||
<ToastProvider>
|
||||
<MatomoTracker config={matomoConfig} />
|
||||
<Suspense
|
||||
fallback={(
|
||||
<div className="flex min-h-screen items-center justify-center text-sm text-muted-foreground">
|
||||
Erlebnisse werden geladen …
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<RouterProvider router={router} />
|
||||
</Suspense>
|
||||
</ToastProvider>
|
||||
</LocaleProvider>
|
||||
</AppearanceProvider>
|
||||
</React.StrictMode>
|
||||
<Sentry.ErrorBoundary fallback={<GuestFallback message="Erlebnisse können nicht geladen werden." />}>
|
||||
<React.StrictMode>
|
||||
<AppearanceProvider>
|
||||
<LocaleProvider>
|
||||
<ToastProvider>
|
||||
<MatomoTracker config={matomoConfig} />
|
||||
<Suspense fallback={<GuestFallback message="Erlebnisse werden geladen …" />}>
|
||||
<RouterProvider router={router} />
|
||||
</Suspense>
|
||||
</ToastProvider>
|
||||
</LocaleProvider>
|
||||
</AppearanceProvider>
|
||||
</React.StrictMode>
|
||||
</Sentry.ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
if (isShareRoute) {
|
||||
shareRoot().catch(() => {
|
||||
createRoot(rootEl).render(
|
||||
<div className="flex min-h-screen items-center justify-center text-sm text-muted-foreground">
|
||||
Dieses Foto kann gerade nicht geladen werden.
|
||||
</div>
|
||||
);
|
||||
createRoot(rootEl).render(<GuestFallback message="Dieses Foto kann gerade nicht geladen werden." />);
|
||||
});
|
||||
} else {
|
||||
appRoot().catch(() => {
|
||||
createRoot(rootEl).render(
|
||||
<div className="flex min-h-screen items-center justify-center text-sm text-muted-foreground">
|
||||
Erlebnisse können nicht geladen werden.
|
||||
</div>
|
||||
);
|
||||
createRoot(rootEl).render(<GuestFallback message="Erlebnisse können nicht geladen werden." />);
|
||||
});
|
||||
}
|
||||
|
||||
41
resources/js/lib/sentry.ts
Normal file
41
resources/js/lib/sentry.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
|
||||
type AppSurface = 'guest' | 'admin' | 'inertia';
|
||||
|
||||
const defaultTracesSampleRate = 0.05;
|
||||
|
||||
const parseSampleRate = (value: string | undefined): number => {
|
||||
const parsed = Number.parseFloat(value ?? '');
|
||||
|
||||
if (Number.isNaN(parsed) || parsed < 0) {
|
||||
return defaultTracesSampleRate;
|
||||
}
|
||||
|
||||
return parsed;
|
||||
};
|
||||
|
||||
export const initSentry = (app: AppSurface): void => {
|
||||
const dsn = import.meta.env.VITE_SENTRY_DSN;
|
||||
|
||||
if (! dsn) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sentry.init({
|
||||
dsn,
|
||||
environment: import.meta.env.VITE_SENTRY_ENV ?? import.meta.env.MODE,
|
||||
release: import.meta.env.VITE_SENTRY_RELEASE,
|
||||
tracesSampleRate: parseSampleRate(import.meta.env.VITE_SENTRY_TRACES_SAMPLE_RATE),
|
||||
beforeSend(event) {
|
||||
event.tags = {
|
||||
...event.tags,
|
||||
app,
|
||||
};
|
||||
|
||||
return event;
|
||||
},
|
||||
normalizeDepth: 6,
|
||||
});
|
||||
};
|
||||
|
||||
export { Sentry };
|
||||
Reference in New Issue
Block a user