mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
98 lines
3.4 KiB
TypeScript
98 lines
3.4 KiB
TypeScript
'use client'
|
|
|
|
import posthog from 'posthog-js'
|
|
import { PostHogProvider as PHProvider } from 'posthog-js/react'
|
|
import type { CapturedNetworkRequest, CaptureResult } from 'posthog-js'
|
|
import { useEffect } from 'react'
|
|
import { usePathname } from 'next/navigation'
|
|
|
|
const INVITE_PATH_PATTERN = /\/join\/[^/?#\s]+/g
|
|
|
|
function redactInviteUrl(defaultUrl: string): string {
|
|
const url = new URL(defaultUrl)
|
|
|
|
if (url.pathname.startsWith('/join/')) {
|
|
return `${url.origin}/join/[invite]`
|
|
}
|
|
|
|
return defaultUrl
|
|
}
|
|
|
|
export function redactInvitePaths<T>(value: T): T {
|
|
if (typeof value === 'string') {
|
|
return value.replace(INVITE_PATH_PATTERN, '/join/[invite]') as T
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
return value.map(redactInvitePaths) as T
|
|
}
|
|
|
|
if (value && typeof value === 'object' && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) {
|
|
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, redactInvitePaths(entry)])) as T
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
export function redactInvitePathsBeforeSend(event: CaptureResult | null): CaptureResult | null {
|
|
return event ? redactInvitePaths(event) : null
|
|
}
|
|
|
|
export function redactInviteNetworkRequest(request: CapturedNetworkRequest): CapturedNetworkRequest {
|
|
return {
|
|
...request,
|
|
name: redactInvitePaths(request.name),
|
|
requestHeaders: request.requestHeaders ? redactInvitePaths({ ...request.requestHeaders }) : request.requestHeaders,
|
|
requestBody: redactInvitePaths(request.requestBody),
|
|
responseHeaders: request.responseHeaders ? redactInvitePaths({ ...request.responseHeaders }) : request.responseHeaders,
|
|
responseBody: redactInvitePaths(request.responseBody),
|
|
}
|
|
}
|
|
|
|
export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
|
const pathname = usePathname()
|
|
|
|
useEffect(() => {
|
|
if (typeof window !== 'undefined' && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
|
|
const isInvitePage = window.location.pathname.startsWith('/join/')
|
|
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
|
|
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://eu.i.posthog.com',
|
|
person_profiles: 'identified_only',
|
|
capture_pageview: false,
|
|
capture_pageleave: false,
|
|
get_current_url: redactInviteUrl,
|
|
before_send: redactInvitePathsBeforeSend,
|
|
session_recording: {
|
|
maskCapturedNetworkRequestFn: redactInviteNetworkRequest,
|
|
},
|
|
// This must be part of initialization, not only the navigation effect
|
|
// below, so an initially loaded invite page is protected before its DOM
|
|
// can be considered for capture or recording.
|
|
autocapture: !isInvitePage,
|
|
disable_session_recording: isInvitePage,
|
|
mask_all_text: isInvitePage,
|
|
})
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (!process.env.NEXT_PUBLIC_POSTHOG_KEY) return
|
|
|
|
// Invite codes are credentials. Do not let their page enter PostHog's DOM
|
|
// autocapture or session-recording pipeline, even though event properties
|
|
// and URLs are separately redacted below.
|
|
const isInvitePage = pathname?.startsWith('/join/')
|
|
posthog.set_config({
|
|
autocapture: !isInvitePage,
|
|
disable_session_recording: isInvitePage,
|
|
mask_all_text: isInvitePage,
|
|
})
|
|
}, [pathname])
|
|
|
|
if (!process.env.NEXT_PUBLIC_POSTHOG_KEY) {
|
|
return <>{children}</>
|
|
}
|
|
|
|
return <PHProvider client={posthog}>{children}</PHProvider>
|
|
}
|