Skip to content

Commit 4b745ec

Browse files
authored
Merge pull request #75 from reqcore-inc/fix/fix-posthog-integration
feat(posthog): update PostHog integration with environment variables and consent handling
2 parents 8bd4bd5 + da4e78d commit 4b745ec

7 files changed

Lines changed: 51 additions & 10 deletions

File tree

.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,11 @@ NUXT_PUBLIC_SITE_URL=http://localhost:3000
6666
# GITHUB_FEEDBACK_TOKEN=ghp_...
6767
# GitHub repo in "owner/repo" format
6868
# GITHUB_FEEDBACK_REPO=reqcore/reqcore
69+
70+
# ─── Optional: Analytics (PostHog) ──────────────────────────────────────────
71+
# Privacy-focused product analytics & feature flags powered by PostHog.
72+
# Get your project API key from https://posthog.com → Project settings.
73+
# Users must accept the consent banner before any events are captured (GDPR).
74+
# POSTHOG_PUBLIC_KEY=phc_...
75+
# EU data center (default). Use https://us.i.posthog.com for US.
76+
# POSTHOG_HOST=https://eu.i.posthog.com

Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ COPY . .
1313
ARG NUXT_PUBLIC_SITE_URL=http://localhost:3000
1414
ENV NUXT_PUBLIC_SITE_URL=${NUXT_PUBLIC_SITE_URL}
1515

16+
# PostHog — the @posthog/nuxt module is conditionally loaded at build time.
17+
# Pass your project API key so the module is included in the production bundle.
18+
# Railway auto-passes service variables as Docker build args.
19+
ARG POSTHOG_PUBLIC_KEY
20+
ENV POSTHOG_PUBLIC_KEY=${POSTHOG_PUBLIC_KEY}
21+
ARG POSTHOG_HOST
22+
ENV POSTHOG_HOST=${POSTHOG_HOST}
23+
1624
RUN npm run build
1725

1826
# ─── Stage 2: Run ────────────────────────────────────────────────────────────

app/composables/useAnalyticsConsent.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export function useAnalyticsConsent() {
1515
// usePostHog() is auto-imported by @posthog/nuxt, but the module is
1616
// conditionally loaded. Replicate the safe accessor so this composable
1717
// works even when PostHog is not configured (CI, self-hosted without key).
18-
const posthog = (useNuxtApp() as Record<string, unknown>).$posthog as ((() => { opt_in_capturing: () => void, opt_out_capturing: () => void }) | undefined)
18+
const posthog = (useNuxtApp() as Record<string, unknown>).$posthog as ((() => { opt_in_capturing: () => void, opt_out_capturing: () => void, capture: (event: string, properties?: Record<string, unknown>) => void }) | undefined)
1919
const ph = posthog?.()
2020

2121
const consentState = useState<ConsentState>('analytics-consent', () => null)
@@ -43,6 +43,16 @@ export function useAnalyticsConsent() {
4343
localStorage.setItem(CONSENT_KEY, 'granted')
4444
}
4545
ph?.opt_in_capturing()
46+
// Capture the entry-page pageview now that the user has opted in.
47+
// PostHog's init-time $pageview was suppressed by opt_out_capturing_by_default,
48+
// and subsequent pushState events only fire after this call, so we must
49+
// manually send one for the page the user is currently on.
50+
if (import.meta.client) {
51+
const url = new URL(window.location.href)
52+
url.search = ''
53+
url.hash = ''
54+
ph?.capture('$pageview', { $current_url: url.toString() })
55+
}
4656
}
4757

4858
function declineAnalytics() {

app/composables/useInterviews.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export interface Interview {
2+
id: string
3+
scheduledAt: string
4+
duration: number
5+
type: 'phone' | 'video' | 'in_person' | 'panel' | 'technical' | 'take_home'
6+
status: 'scheduled' | 'completed' | 'cancelled' | 'no_show'
7+
title: string
8+
candidateFirstName?: string
9+
candidateLastName?: string
10+
}

app/plugins/posthog-identity.client.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ const SENSITIVE_URL_PROPS = ['$current_url', '$initial_current_url', '$referrer'
2121

2222
export default defineNuxtPlugin({
2323
name: 'posthog-identity',
24-
parallel: true,
2524
setup() {
2625
// usePostHog() is auto-imported by @posthog/nuxt, but the module is
2726
// conditionally loaded (only when POSTHOG_PUBLIC_KEY is set). In CI and

nuxt.config.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ export default defineNuxtConfig({
5757
publicKey: process.env.POSTHOG_PUBLIC_KEY || '',
5858
host: process.env.POSTHOG_HOST || 'https://eu.i.posthog.com',
5959
clientConfig: {
60+
// ── Reverse proxy: route PostHog through reqcore.com to bypass ad blockers ──
61+
// Requests to /ingest/** are proxied by Nitro to eu.i.posthog.com
62+
api_host: '/ingest',
63+
ui_host: 'https://eu.posthog.com',
6064
// ── Privacy: disable invasive features ──
6165
autocapture: false,
6266
disable_session_recording: true,
@@ -117,11 +121,8 @@ export default defineNuxtConfig({
117121

118122
runtimeConfig: {
119123
public: {
120-
/** PostHog public key and host for server-side event capture */
121-
posthog: {
122-
publicKey: process.env.POSTHOG_PUBLIC_KEY || '',
123-
host: process.env.POSTHOG_HOST || 'https://eu.i.posthog.com',
124-
},
124+
// PostHog runtimeConfig is managed by @posthog/nuxt via posthogConfig above.
125+
// Override at runtime with NUXT_PUBLIC_POSTHOG_PUBLIC_KEY / NUXT_PUBLIC_POSTHOG_HOST.
125126
/** When set, the dashboard shows a read-only demo banner for this org slug */
126127
demoOrgSlug: process.env.DEMO_ORG_SLUG || (isRailwayPreview ? 'reqcore-demo' : ''),
127128
/** Public live-demo account email used to prefill sign-in */
@@ -202,6 +203,12 @@ export default defineNuxtConfig({
202203
// Route rules — prerender/ISR for public pages
203204
// ─────────────────────────────────────────────
204205
routeRules: {
206+
// ── PostHog reverse proxy — bypasses ad blockers by routing through reqcore.com ──
207+
// NOTE: Targets are hardcoded to the EU data center (eu.i.posthog.com).
208+
// If you use the US data center, set POSTHOG_HOST=https://us.i.posthog.com
209+
// and update these two proxy targets to us-assets.i.posthog.com / us.i.posthog.com.
210+
'/ingest/static/**': { proxy: 'https://eu-assets.i.posthog.com/static/**' },
211+
'/ingest/**': { proxy: 'https://eu.i.posthog.com/**' },
205212
'/': { prerender: true },
206213
'/roadmap': { prerender: true },
207214
'/blog': { prerender: true },

server/utils/posthog.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ let client: PostHog | null = null
1212
export function useServerPostHog(): PostHog | null {
1313
if (client) return client
1414

15-
const config = useRuntimeConfig()
16-
const publicKey = config.public.posthog?.publicKey
17-
const host = config.public.posthog?.host
15+
const publicKey = process.env.POSTHOG_PUBLIC_KEY
16+
const host = process.env.POSTHOG_HOST
1817

1918
if (!publicKey) {
2019
return null

0 commit comments

Comments
 (0)