From adf8ae79d5b6f626e6b3ca28903299716a5ec2ad Mon Sep 17 00:00:00 2001 From: CodingOnStar Date: Fri, 7 Nov 2025 11:22:26 +0800 Subject: [PATCH] Enhance Amplitude tracking by validating user ID and properties - Updated middleware to include Amplitude domains. - Improved setUserId function to handle empty user IDs and log warnings in development. - Enhanced setUserProperties function to filter out invalid properties and log results in development. - Refactored user tracking in AppContextProvider to streamline setting user and workspace properties. --- web/app/components/base/amplitude/utils.ts | 38 +++++++++++++++- web/context/app-context.tsx | 53 ++++++++++++++-------- web/middleware.ts | 2 +- 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/web/app/components/base/amplitude/utils.ts b/web/app/components/base/amplitude/utils.ts index 8423c43bb2..95b8af508d 100644 --- a/web/app/components/base/amplitude/utils.ts +++ b/web/app/components/base/amplitude/utils.ts @@ -14,6 +14,14 @@ export const trackEvent = (eventName: string, eventProperties?: Record { + if (!userId) { + console.warn('[Amplitude] ⚠️ Cannot set empty user ID') + return + } + + if (process.env.NODE_ENV === 'development') + console.log('[Amplitude] 👤 Setting User ID:', userId) + amplitude.setUserId(userId) } @@ -22,11 +30,37 @@ export const setUserId = (userId: string) => { * @param properties User properties */ export const setUserProperties = (properties: Record) => { + // Filter out undefined and null values + const validProperties = Object.entries(properties).reduce((acc, [key, value]) => { + if (value !== undefined && value !== null) + acc[key] = value + + return acc + }, {} as Record) + + if (Object.keys(validProperties).length === 0) { + if (process.env.NODE_ENV === 'development') + console.warn('[Amplitude] ⚠️ No valid properties to set') + return + } + + if (process.env.NODE_ENV === 'development') + console.log('[Amplitude] 📊 Setting user properties:', validProperties) + const identifyEvent = new amplitude.Identify() - Object.entries(properties).forEach(([key, value]) => { + Object.entries(validProperties).forEach(([key, value]) => { identifyEvent.set(key, value) }) - amplitude.identify(identifyEvent) + + const result = amplitude.identify(identifyEvent) + + // Log the result in development + result.promise.then(() => { + if (process.env.NODE_ENV === 'development') + console.log('[Amplitude] ✅ User properties set successfully') + }).catch((err) => { + console.error('[Amplitude] ❌ Failed to set user properties:', err) + }) } /** diff --git a/web/context/app-context.tsx b/web/context/app-context.tsx index 8feaad9e80..6093467a36 100644 --- a/web/context/app-context.tsx +++ b/web/context/app-context.tsx @@ -163,28 +163,41 @@ export const AppContextProvider: FC = ({ children }) => // #region Amplitude user tracking useEffect(() => { // Report user info to Amplitude when loaded - if (userProfile?.id) { - setUserId(userProfile.id) - setUserProperties({ - email: userProfile.email, - name: userProfile.name, - has_password: userProfile.is_password_set, - }) - } - }, [userProfile?.id, userProfile?.email, userProfile?.name, userProfile?.is_password_set]) + if (!userProfile?.id) + return - useEffect(() => { - // Report workspace info to Amplitude when loaded - if (currentWorkspace?.id && userProfile?.id) { - setUserProperties({ - workspace_id: currentWorkspace.id, - workspace_name: currentWorkspace.name, - workspace_plan: currentWorkspace.plan, - workspace_status: currentWorkspace.status, - workspace_role: currentWorkspace.role, - }) + // Step 1: Set User ID first + setUserId(userProfile.id) + + // Step 2: Set user properties + const userProperties: Record = { + email: userProfile.email, + name: userProfile.name, + has_password: userProfile.is_password_set, } - }, [currentWorkspace?.id, currentWorkspace?.name, currentWorkspace?.plan, currentWorkspace?.status, currentWorkspace?.role, userProfile?.id]) + + // Step 3: Add workspace properties if available + if (currentWorkspace?.id) { + userProperties.workspace_id = currentWorkspace.id + userProperties.workspace_name = currentWorkspace.name + userProperties.workspace_plan = currentWorkspace.plan + userProperties.workspace_status = currentWorkspace.status + userProperties.workspace_role = currentWorkspace.role + } + + // Set all properties at once + setUserProperties(userProperties) + }, [ + userProfile?.id, + userProfile?.email, + userProfile?.name, + userProfile?.is_password_set, + currentWorkspace?.id, + currentWorkspace?.name, + currentWorkspace?.plan, + currentWorkspace?.status, + currentWorkspace?.role, + ]) // #endregion Amplitude user tracking return ( diff --git a/web/middleware.ts b/web/middleware.ts index 3fee535ea4..9fa7d85b2f 100644 --- a/web/middleware.ts +++ b/web/middleware.ts @@ -1,7 +1,7 @@ import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' -const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://api.github.com' +const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://api.github.com https://api2.amplitude.com *.amplitude.com' const wrapResponseWithXFrameOptions = (response: NextResponse, pathname: string) => { // prevent clickjacking: https://owasp.org/www-community/attacks/Clickjacking