Skip to content

Commit eac7ea0

Browse files
improvements to identify
1 parent 36338dc commit eac7ea0

5 files changed

Lines changed: 68 additions & 21 deletions

File tree

packages/web/src/app/layout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Toaster } from "@/components/ui/toaster";
77
import { TooltipProvider } from "@/components/ui/tooltip";
88
import { SessionProvider } from "next-auth/react";
99
import { env } from "@sourcebot/shared";
10+
import { env as clientEnv } from "@sourcebot/shared/client";
1011
import { PlanProvider } from "@/features/entitlements/planProvider";
1112
import { getEntitlements } from "@sourcebot/shared";
1213

@@ -42,6 +43,8 @@ export default function RootLayout({
4243
// @note: the posthog api key doesn't need to be kept secret,
4344
// so we are safe to send it to the client.
4445
posthogApiKey={env.POSTHOG_PAPIK}
46+
sourcebotVersion={clientEnv.NEXT_PUBLIC_SOURCEBOT_VERSION}
47+
sourcebotInstallId={env.SOURCEBOT_INSTALL_ID}
4548
>
4649
<ThemeProvider
4750
attribute="class"

packages/web/src/app/posthogProvider.tsx

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,17 @@ interface PostHogProviderProps {
3434
children: React.ReactNode
3535
isDisabled: boolean
3636
posthogApiKey: string
37+
sourcebotVersion: string
38+
sourcebotInstallId: string
3739
}
3840

39-
export function PostHogProvider({ children, isDisabled, posthogApiKey }: PostHogProviderProps) {
41+
export function PostHogProvider({
42+
children,
43+
isDisabled,
44+
posthogApiKey,
45+
sourcebotVersion,
46+
sourcebotInstallId,
47+
}: PostHogProviderProps) {
4048
const { data: session } = useSession();
4149

4250
useEffect(() => {
@@ -61,27 +69,33 @@ export function PostHogProvider({ children, isDisabled, posthogApiKey }: PostHog
6169
'$referrer',
6270
'$referring_domain',
6371
'$ip',
64-
] : []
72+
] : [],
73+
loaded: (posthog) => {
74+
// Include install id & version in all events.
75+
posthog.register({
76+
sourcebot_version: sourcebotVersion,
77+
install_id: sourcebotInstallId,
78+
});
79+
}
6580
});
6681
} else {
6782
console.debug("PostHog telemetry disabled");
6883
}
69-
}, [isDisabled, posthogApiKey]);
84+
}, [isDisabled, posthogApiKey, sourcebotInstallId, sourcebotVersion]);
7085

7186
useEffect(() => {
7287
if (!session) {
7388
return;
7489
}
7590

76-
// Only identify the user if we are running in a cloud environment.
77-
if (env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT !== undefined) {
78-
posthog.identify(session.user.id, {
91+
posthog.identify(
92+
session.user.id,
93+
// Only include email & name when running in a cloud environment.
94+
env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT !== undefined ? {
7995
email: session.user.email,
8096
name: session.user.name,
81-
});
82-
} else {
83-
console.debug("PostHog identify skipped");
84-
}
97+
} : undefined
98+
);
8599
}, [session]);
86100

87101
return (

packages/web/src/hooks/useCaptureEvent.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,13 @@
33
import { CaptureOptions } from "posthog-js";
44
import posthog from "posthog-js";
55
import { PosthogEvent, PosthogEventMap } from "../lib/posthogEvents";
6-
import { env } from "@sourcebot/shared/client";
76

87
export function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E], options?: CaptureOptions) {
98
if(!options) {
109
options = {};
1110
}
1211
options.send_instantly = true;
13-
posthog.capture(event, {
14-
...properties,
15-
sourcebot_version: env.NEXT_PUBLIC_SOURCEBOT_VERSION,
16-
}, options);
12+
posthog.capture(event, properties, options);
1713
}
1814

1915
/**

packages/web/src/lib/posthog.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { PostHog } from 'posthog-node'
22
import { env } from '@sourcebot/shared'
3+
import { env as clientEnv } from '@sourcebot/shared/client';
34
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
45
import * as Sentry from "@sentry/nextjs";
56
import { PosthogEvent, PosthogEventMap } from './posthogEvents';
6-
import { cookies } from 'next/headers';
7+
import { cookies, headers } from 'next/headers';
8+
import { auth } from '@/auth';
9+
import { getVerifiedApiObject } from '@/withAuthV2';
710

811
/**
912
* @note: This is a subset of the properties stored in the
@@ -47,13 +50,40 @@ const getPostHogCookie = (cookieStore: Pick<RequestCookies, 'get'>): PostHogCook
4750
return undefined;
4851
}
4952

53+
/**
54+
* Attempts to retrieve the distinct id of the current user.
55+
*/
56+
const tryGetDistinctId = async () => {
57+
// First, attempt to retrieve the distinct id from the cookie.
58+
const cookieStore = await cookies();
59+
const cookie = getPostHogCookie(cookieStore);
60+
if (cookie) {
61+
return cookie.distinct_id;
62+
}
63+
64+
// Next, from the session.
65+
const session = await auth();
66+
if (session) {
67+
return session.user.id;
68+
}
69+
70+
// Finally, from the api key.
71+
const headersList = await headers();
72+
const apiKeyString = headersList.get("X-Sourcebot-Api-Key") ?? undefined;
73+
if (!apiKeyString) {
74+
return undefined;
75+
}
76+
77+
const apiKey = await getVerifiedApiObject(apiKeyString);
78+
return apiKey?.createdById;
79+
}
80+
5081
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E]) {
5182
if (env.SOURCEBOT_TELEMETRY_DISABLED === 'true') {
5283
return;
5384
}
5485

55-
const cookieStore = await cookies();
56-
const cookie = getPostHogCookie(cookieStore);
86+
const distinctId = await tryGetDistinctId();
5787

5888
const posthog = new PostHog(env.POSTHOG_PAPIK, {
5989
host: 'https://us.i.posthog.com',
@@ -63,7 +93,11 @@ export async function captureEvent<E extends PosthogEvent>(event: E, properties:
6393

6494
posthog.capture({
6595
event,
66-
properties,
67-
distinctId: cookie?.distinct_id ?? '',
96+
properties: {
97+
...properties,
98+
sourcebot_version: clientEnv.NEXT_PUBLIC_SOURCEBOT_VERSION,
99+
install_id: env.SOURCEBOT_INSTALL_ID,
100+
},
101+
distinctId,
68102
});
69103
}

packages/web/src/withAuthV2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export const getAuthenticatedUser = async () => {
156156
/**
157157
* Returns a API key object if the API key string is valid, otherwise returns undefined.
158158
*/
159-
const getVerifiedApiObject = async (apiKeyString: string): Promise<ApiKey | undefined> => {
159+
export const getVerifiedApiObject = async (apiKeyString: string): Promise<ApiKey | undefined> => {
160160
const parts = apiKeyString.split("-");
161161
if (parts.length !== 2 || parts[0] !== "sourcebot") {
162162
return undefined;

0 commit comments

Comments
 (0)