mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-13 12:55:19 +00:00
improvements to identify
This commit is contained in:
parent
36338dc8d2
commit
eac7ea0555
5 changed files with 68 additions and 21 deletions
|
|
@ -7,6 +7,7 @@ import { Toaster } from "@/components/ui/toaster";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
import { SessionProvider } from "next-auth/react";
|
import { SessionProvider } from "next-auth/react";
|
||||||
import { env } from "@sourcebot/shared";
|
import { env } from "@sourcebot/shared";
|
||||||
|
import { env as clientEnv } from "@sourcebot/shared/client";
|
||||||
import { PlanProvider } from "@/features/entitlements/planProvider";
|
import { PlanProvider } from "@/features/entitlements/planProvider";
|
||||||
import { getEntitlements } from "@sourcebot/shared";
|
import { getEntitlements } from "@sourcebot/shared";
|
||||||
|
|
||||||
|
|
@ -42,6 +43,8 @@ export default function RootLayout({
|
||||||
// @note: the posthog api key doesn't need to be kept secret,
|
// @note: the posthog api key doesn't need to be kept secret,
|
||||||
// so we are safe to send it to the client.
|
// so we are safe to send it to the client.
|
||||||
posthogApiKey={env.POSTHOG_PAPIK}
|
posthogApiKey={env.POSTHOG_PAPIK}
|
||||||
|
sourcebotVersion={clientEnv.NEXT_PUBLIC_SOURCEBOT_VERSION}
|
||||||
|
sourcebotInstallId={env.SOURCEBOT_INSTALL_ID}
|
||||||
>
|
>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,17 @@ interface PostHogProviderProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
isDisabled: boolean
|
isDisabled: boolean
|
||||||
posthogApiKey: string
|
posthogApiKey: string
|
||||||
|
sourcebotVersion: string
|
||||||
|
sourcebotInstallId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PostHogProvider({ children, isDisabled, posthogApiKey }: PostHogProviderProps) {
|
export function PostHogProvider({
|
||||||
|
children,
|
||||||
|
isDisabled,
|
||||||
|
posthogApiKey,
|
||||||
|
sourcebotVersion,
|
||||||
|
sourcebotInstallId,
|
||||||
|
}: PostHogProviderProps) {
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -61,27 +69,33 @@ export function PostHogProvider({ children, isDisabled, posthogApiKey }: PostHog
|
||||||
'$referrer',
|
'$referrer',
|
||||||
'$referring_domain',
|
'$referring_domain',
|
||||||
'$ip',
|
'$ip',
|
||||||
] : []
|
] : [],
|
||||||
|
loaded: (posthog) => {
|
||||||
|
// Include install id & version in all events.
|
||||||
|
posthog.register({
|
||||||
|
sourcebot_version: sourcebotVersion,
|
||||||
|
install_id: sourcebotInstallId,
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.debug("PostHog telemetry disabled");
|
console.debug("PostHog telemetry disabled");
|
||||||
}
|
}
|
||||||
}, [isDisabled, posthogApiKey]);
|
}, [isDisabled, posthogApiKey, sourcebotInstallId, sourcebotVersion]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only identify the user if we are running in a cloud environment.
|
posthog.identify(
|
||||||
if (env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT !== undefined) {
|
session.user.id,
|
||||||
posthog.identify(session.user.id, {
|
// Only include email & name when running in a cloud environment.
|
||||||
|
env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT !== undefined ? {
|
||||||
email: session.user.email,
|
email: session.user.email,
|
||||||
name: session.user.name,
|
name: session.user.name,
|
||||||
});
|
} : undefined
|
||||||
} else {
|
);
|
||||||
console.debug("PostHog identify skipped");
|
|
||||||
}
|
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,13 @@
|
||||||
import { CaptureOptions } from "posthog-js";
|
import { CaptureOptions } from "posthog-js";
|
||||||
import posthog from "posthog-js";
|
import posthog from "posthog-js";
|
||||||
import { PosthogEvent, PosthogEventMap } from "../lib/posthogEvents";
|
import { PosthogEvent, PosthogEventMap } from "../lib/posthogEvents";
|
||||||
import { env } from "@sourcebot/shared/client";
|
|
||||||
|
|
||||||
export function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E], options?: CaptureOptions) {
|
export function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E], options?: CaptureOptions) {
|
||||||
if(!options) {
|
if(!options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
options.send_instantly = true;
|
options.send_instantly = true;
|
||||||
posthog.capture(event, {
|
posthog.capture(event, properties, options);
|
||||||
...properties,
|
|
||||||
sourcebot_version: env.NEXT_PUBLIC_SOURCEBOT_VERSION,
|
|
||||||
}, options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import { PostHog } from 'posthog-node'
|
import { PostHog } from 'posthog-node'
|
||||||
import { env } from '@sourcebot/shared'
|
import { env } from '@sourcebot/shared'
|
||||||
|
import { env as clientEnv } from '@sourcebot/shared/client';
|
||||||
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
|
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
import { PosthogEvent, PosthogEventMap } from './posthogEvents';
|
import { PosthogEvent, PosthogEventMap } from './posthogEvents';
|
||||||
import { cookies } from 'next/headers';
|
import { cookies, headers } from 'next/headers';
|
||||||
|
import { auth } from '@/auth';
|
||||||
|
import { getVerifiedApiObject } from '@/withAuthV2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @note: This is a subset of the properties stored in the
|
* @note: This is a subset of the properties stored in the
|
||||||
|
|
@ -47,13 +50,40 @@ const getPostHogCookie = (cookieStore: Pick<RequestCookies, 'get'>): PostHogCook
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to retrieve the distinct id of the current user.
|
||||||
|
*/
|
||||||
|
const tryGetDistinctId = async () => {
|
||||||
|
// First, attempt to retrieve the distinct id from the cookie.
|
||||||
|
const cookieStore = await cookies();
|
||||||
|
const cookie = getPostHogCookie(cookieStore);
|
||||||
|
if (cookie) {
|
||||||
|
return cookie.distinct_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, from the session.
|
||||||
|
const session = await auth();
|
||||||
|
if (session) {
|
||||||
|
return session.user.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, from the api key.
|
||||||
|
const headersList = await headers();
|
||||||
|
const apiKeyString = headersList.get("X-Sourcebot-Api-Key") ?? undefined;
|
||||||
|
if (!apiKeyString) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiKey = await getVerifiedApiObject(apiKeyString);
|
||||||
|
return apiKey?.createdById;
|
||||||
|
}
|
||||||
|
|
||||||
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E]) {
|
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E]) {
|
||||||
if (env.SOURCEBOT_TELEMETRY_DISABLED === 'true') {
|
if (env.SOURCEBOT_TELEMETRY_DISABLED === 'true') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cookieStore = await cookies();
|
const distinctId = await tryGetDistinctId();
|
||||||
const cookie = getPostHogCookie(cookieStore);
|
|
||||||
|
|
||||||
const posthog = new PostHog(env.POSTHOG_PAPIK, {
|
const posthog = new PostHog(env.POSTHOG_PAPIK, {
|
||||||
host: 'https://us.i.posthog.com',
|
host: 'https://us.i.posthog.com',
|
||||||
|
|
@ -63,7 +93,11 @@ export async function captureEvent<E extends PosthogEvent>(event: E, properties:
|
||||||
|
|
||||||
posthog.capture({
|
posthog.capture({
|
||||||
event,
|
event,
|
||||||
properties,
|
properties: {
|
||||||
distinctId: cookie?.distinct_id ?? '',
|
...properties,
|
||||||
|
sourcebot_version: clientEnv.NEXT_PUBLIC_SOURCEBOT_VERSION,
|
||||||
|
install_id: env.SOURCEBOT_INSTALL_ID,
|
||||||
|
},
|
||||||
|
distinctId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -156,7 +156,7 @@ export const getAuthenticatedUser = async () => {
|
||||||
/**
|
/**
|
||||||
* Returns a API key object if the API key string is valid, otherwise returns undefined.
|
* Returns a API key object if the API key string is valid, otherwise returns undefined.
|
||||||
*/
|
*/
|
||||||
const getVerifiedApiObject = async (apiKeyString: string): Promise<ApiKey | undefined> => {
|
export const getVerifiedApiObject = async (apiKeyString: string): Promise<ApiKey | undefined> => {
|
||||||
const parts = apiKeyString.split("-");
|
const parts = apiKeyString.split("-");
|
||||||
if (parts.length !== 2 || parts[0] !== "sourcebot") {
|
if (parts.length !== 2 || parts[0] !== "sourcebot") {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue