mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Add user and org context to Sentry (#92)
* Add user and org context to Sentry * PR feedback
This commit is contained in:
parent
6922e4b3ac
commit
4d40eb6e2b
8 changed files with 125 additions and 1 deletions
|
|
@ -1,6 +1,10 @@
|
|||
import { redirect } from 'next/navigation';
|
||||
import { auth } from '@clerk/nextjs/server';
|
||||
|
||||
import {
|
||||
setSentryUserContext,
|
||||
setSentryOrganizationContext,
|
||||
} from '@/lib/server/sentry-context';
|
||||
import { NavbarHeader, NavbarMenu, Section } from '@/components/layout';
|
||||
import { Usage } from './usage/Usage';
|
||||
|
||||
|
|
@ -15,6 +19,19 @@ export default async function AuthenticatedLayout({
|
|||
redirect('/select-org');
|
||||
}
|
||||
|
||||
// Set enhanced Sentry context for authenticated users
|
||||
if (userId) {
|
||||
setSentryUserContext({
|
||||
id: userId,
|
||||
orgId,
|
||||
orgRole,
|
||||
});
|
||||
|
||||
if (orgId) {
|
||||
setSentryOrganizationContext(orgId, orgRole);
|
||||
}
|
||||
}
|
||||
|
||||
// Members get access to usage page only, filtered to their own data
|
||||
if (orgRole === 'org:member' || (orgRole && orgRole !== 'org:admin')) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { auth } from '@clerk/nextjs/server';
|
|||
|
||||
import { getClerkLocale } from '@/i18n/locale';
|
||||
import { syncAuth } from '@/actions/sync';
|
||||
import { setSentryUserContext } from '@/lib/server/sentry-context';
|
||||
import { Toaster } from '@/components/ui';
|
||||
import {
|
||||
ThemeProvider,
|
||||
|
|
@ -52,7 +53,17 @@ export default async function RootLayout({
|
|||
const locale = await getLocale();
|
||||
setRequestLocale(locale);
|
||||
|
||||
await syncAuth(await auth());
|
||||
const authData = await auth();
|
||||
await syncAuth(authData);
|
||||
|
||||
// Set Sentry user context for server-side error tracking
|
||||
if (authData.userId) {
|
||||
setSentryUserContext({
|
||||
id: authData.userId,
|
||||
orgId: authData.orgId,
|
||||
orgRole: authData.orgRole,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<html lang={locale} suppressHydrationWarning>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { ClerkProvider } from '@clerk/nextjs';
|
|||
import { dark } from '@clerk/themes';
|
||||
|
||||
import { getClerkLocale } from '@/i18n/locale';
|
||||
import { SentryUserContext } from './SentryUserContext';
|
||||
import { useTheme } from 'next-themes';
|
||||
|
||||
type Localization = ReturnType<typeof getClerkLocale>;
|
||||
|
|
@ -24,6 +25,7 @@ export const AuthProvider = ({
|
|||
localization={localization}
|
||||
afterSignOutUrl={'/sign-in'}
|
||||
>
|
||||
<SentryUserContext />
|
||||
{children}
|
||||
</ClerkProvider>
|
||||
);
|
||||
|
|
|
|||
53
src/components/layout/SentryUserContext.tsx
Normal file
53
src/components/layout/SentryUserContext.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useUser, useOrganization } from '@clerk/nextjs';
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
|
||||
/**
|
||||
* Client-side component that sets Sentry user context based on Clerk authentication
|
||||
*/
|
||||
export function SentryUserContext() {
|
||||
const { user, isLoaded: userLoaded } = useUser();
|
||||
const { organization, isLoaded: orgLoaded } = useOrganization();
|
||||
|
||||
useEffect(() => {
|
||||
// Wait for both user and organization data to be loaded
|
||||
if (!userLoaded || !orgLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
// Set user context with organization information if available
|
||||
Sentry.setUser({
|
||||
id: user.id,
|
||||
...(organization && { orgId: organization.id }),
|
||||
...(organization &&
|
||||
user.organizationMemberships?.[0]?.role && {
|
||||
orgRole: user.organizationMemberships[0].role,
|
||||
}),
|
||||
});
|
||||
|
||||
// Set organization context if available
|
||||
if (organization) {
|
||||
Sentry.setContext('organization', {
|
||||
id: organization.id,
|
||||
name: organization.name,
|
||||
slug: organization.slug,
|
||||
...(user.organizationMemberships?.[0]?.role && {
|
||||
role: user.organizationMemberships[0].role,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
Sentry.setContext('organization', null);
|
||||
}
|
||||
} else {
|
||||
// Clear user context when not authenticated
|
||||
Sentry.setUser(null);
|
||||
Sentry.setContext('organization', null);
|
||||
}
|
||||
}, [user, organization, userLoaded, orgLoaded]);
|
||||
|
||||
// This component doesn't render anything
|
||||
return null;
|
||||
}
|
||||
|
|
@ -11,3 +11,4 @@ export { DataTable } from './DataTable';
|
|||
export { ThemeProvider } from './ThemeProvider';
|
||||
export { AuthProvider } from './AuthProvider';
|
||||
export { ReactQueryProvider } from './ReactQueryProvider';
|
||||
export { SentryUserContext } from './SentryUserContext';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// This file configures the initialization of Sentry on the client.
|
||||
// The config you add here will be used whenever a users loads a page in their browser.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
// Note: User context (userId, orgId, orgRole) is set via SentryUserContext component
|
||||
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
// import * as Spotlight from '@spotlightjs/spotlight';
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export async function register() {
|
|||
|
||||
if (process.env.NEXT_RUNTIME === 'edge') {
|
||||
// Edge Sentry configuration
|
||||
// Note: User context (userId, orgId, orgRole) is set in layouts via setSentryUserContext()
|
||||
Sentry.init({
|
||||
// Sentry DSN
|
||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||
|
|
|
|||
38
src/lib/server/sentry-context.ts
Normal file
38
src/lib/server/sentry-context.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import * as Sentry from '@sentry/nextjs';
|
||||
|
||||
export interface SentryUserContext {
|
||||
id: string;
|
||||
orgId?: string | null;
|
||||
orgRole?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Sentry user context for server-side error tracking
|
||||
*/
|
||||
export function setSentryUserContext(context: SentryUserContext) {
|
||||
Sentry.setUser({
|
||||
id: context.id,
|
||||
...(context.orgId && { orgId: context.orgId }),
|
||||
...(context.orgRole && { orgRole: context.orgRole }),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the Sentry user context
|
||||
*/
|
||||
export function clearSentryUserContext() {
|
||||
Sentry.setUser(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Sentry context with additional organization information
|
||||
*/
|
||||
export function setSentryOrganizationContext(
|
||||
orgId: string,
|
||||
orgRole?: string | null,
|
||||
) {
|
||||
Sentry.setContext('organization', {
|
||||
id: orgId,
|
||||
...(orgRole && { role: orgRole }),
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue