feat: support posthog analytics

This commit is contained in:
javayhu 2025-09-09 00:23:48 +08:00
parent e556f72fc7
commit 6262432b64
5 changed files with 111 additions and 16 deletions

View File

@ -132,6 +132,12 @@ NEXT_PUBLIC_SELINE_TOKEN=""
# -----------------------------------------------------------------------------
NEXT_PUBLIC_DATAFAST_WEBSITE_ID=""
NEXT_PUBLIC_DATAFAST_DOMAIN=""
# -----------------------------------------------------------------------------
# PostHog Analytics (https://posthog.com)
# https://mksaas.com/docs/analytics#posthog
# -----------------------------------------------------------------------------
NEXT_PUBLIC_POSTHOG_KEY=""
NEXT_PUBLIC_POSTHOG_HOST=""
# -----------------------------------------------------------------------------

View File

@ -113,6 +113,7 @@
"next-themes": "^0.4.4",
"nuqs": "^2.5.1",
"postgres": "^3.4.5",
"posthog-js": "^1.261.7",
"radix-ui": "^1.4.2",
"react": "^19.0.0",
"react-day-picker": "8.10.1",

47
pnpm-lock.yaml generated
View File

@ -272,6 +272,9 @@ importers:
postgres:
specifier: ^3.4.5
version: 3.4.5
posthog-js:
specifier: ^1.261.7
version: 1.261.7
radix-ui:
specifier: ^1.4.2
version: 1.4.2(@types/react-dom@19.0.3(@types/react@19.0.9))(@types/react@19.0.9)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -1926,6 +1929,9 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@posthog/core@1.0.2':
resolution: {integrity: sha512-hWk3rUtJl2crQK0WNmwg13n82hnTwB99BT99/XI5gZSvIlYZ1TPmMZE8H2dhJJ98J/rm9vYJ/UXNzw3RV5HTpQ==}
'@radix-ui/number@1.1.0':
resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
@ -4025,6 +4031,9 @@ packages:
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
engines: {node: '>=18'}
core-js@3.45.1:
resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==}
cors@2.8.5:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
@ -4503,6 +4512,9 @@ packages:
picomatch:
optional: true
fflate@0.4.8:
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
@ -5627,6 +5639,20 @@ packages:
resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==}
engines: {node: '>=12'}
posthog-js@1.261.7:
resolution: {integrity: sha512-Fjpbz6VfIMsEbKIN/UyTWhU1DGgVIngqoRjPGRolemIMOVzTfI77OZq8WwiBhMug+rU+wNhGCQhC41qRlR5CxA==}
peerDependencies:
'@rrweb/types': 2.0.0-alpha.17
rrweb-snapshot: 2.0.0-alpha.17
peerDependenciesMeta:
'@rrweb/types':
optional: true
rrweb-snapshot:
optional: true
preact@10.27.1:
resolution: {integrity: sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==}
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@ -6355,6 +6381,9 @@ packages:
web-namespaces@2.0.1:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
web-vitals@4.2.4:
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@ -7593,6 +7622,8 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@posthog/core@1.0.2': {}
'@radix-ui/number@1.1.0': {}
'@radix-ui/number@1.1.1': {}
@ -9808,6 +9839,8 @@ snapshots:
cookie@1.0.2: {}
core-js@3.45.1: {}
cors@2.8.5:
dependencies:
object-assign: 4.1.1
@ -10335,6 +10368,8 @@ snapshots:
optionalDependencies:
picomatch: 4.0.2
fflate@0.4.8: {}
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
@ -11784,6 +11819,16 @@ snapshots:
postgres@3.4.5: {}
posthog-js@1.261.7:
dependencies:
'@posthog/core': 1.0.2
core-js: 3.45.1
fflate: 0.4.8
preact: 10.27.1
web-vitals: 4.2.4
preact@10.27.1: {}
prelude-ls@1.2.1: {}
prettier@3.4.2: {}
@ -12744,6 +12789,8 @@ snapshots:
web-namespaces@2.0.1: {}
web-vitals@4.2.4: {}
which@2.0.2:
dependencies:
isexe: 2.0.0

View File

@ -0,0 +1,35 @@
'use client';
import posthog from 'posthog-js';
import { PostHogProvider as PHProvider } from 'posthog-js/react';
import { useEffect } from 'react';
/**
* PostHog Analytics
*
* https://posthog.com
* https://posthog.com/docs/libraries/next-js?tab=PostHog+provider
* https://mksaas.com/docs/analytics#posthog
*/
export function PostHogProvider({ children }: { children: React.ReactNode }) {
const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY;
const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST;
const isProduction = process.env.NODE_ENV === 'production';
const isPostHogEnabled = posthogKey && posthogHost && isProduction;
useEffect(() => {
if (isPostHogEnabled) {
posthog.init(posthogKey, {
api_host: posthogHost,
defaults: '2025-05-24',
});
}
}, [isPostHogEnabled, posthogKey, posthogHost]);
// If PostHog is not enabled, just return children without the provider
if (!isPostHogEnabled) {
return <>{children}</>;
}
return <PHProvider client={posthog}>{children}</PHProvider>;
}

View File

@ -1,5 +1,6 @@
'use client';
import { PostHogProvider } from '@/analytics/posthog-analytics';
import { ActiveThemeProvider } from '@/components/layout/active-theme-provider';
import { QueryProvider } from '@/components/providers/query-provider';
import { TooltipProvider } from '@/components/ui/tooltip';
@ -20,12 +21,12 @@ interface ProvidersProps {
*
* This component is used to wrap the app in the providers.
*
* - PostHogProvider: Provides the PostHog analytics to the app.
* - QueryProvider: Provides the query client to the app.
* - ThemeProvider: Provides the theme to the app.
* - ActiveThemeProvider: Provides the active theme to the app.
* - RootProvider: Provides the root provider for Fumadocs UI.
* - TooltipProvider: Provides the tooltip to the app.
* - PaymentProvider: Provides the payment state to the app.
* - CreditsProvider: Provides the credits state to the app.
*/
export function Providers({ children, locale }: ProvidersProps) {
const theme = useTheme();
@ -53,19 +54,24 @@ export function Providers({ children, locale }: ProvidersProps) {
};
return (
<QueryProvider>
<ThemeProvider
attribute="class"
defaultTheme={defaultMode}
enableSystem={true}
disableTransitionOnChange
>
<ActiveThemeProvider>
<RootProvider theme={theme} i18n={{ locale, locales, translations }}>
<TooltipProvider>{children}</TooltipProvider>
</RootProvider>
</ActiveThemeProvider>
</ThemeProvider>
</QueryProvider>
<PostHogProvider>
<QueryProvider>
<ThemeProvider
attribute="class"
defaultTheme={defaultMode}
enableSystem={true}
disableTransitionOnChange
>
<ActiveThemeProvider>
<RootProvider
theme={theme}
i18n={{ locale, locales, translations }}
>
<TooltipProvider>{children}</TooltipProvider>
</RootProvider>
</ActiveThemeProvider>
</ThemeProvider>
</QueryProvider>
</PostHogProvider>
);
}