Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,11 @@ NUXT_PUBLIC_SITE_URL=http://localhost:3000
# GITHUB_FEEDBACK_TOKEN=ghp_...
# GitHub repo in "owner/repo" format
# GITHUB_FEEDBACK_REPO=reqcore/reqcore

# ─── Optional: Analytics (PostHog) ──────────────────────────────────────────
# Privacy-focused product analytics & feature flags powered by PostHog.
# Get your project API key from https://posthog.com → Project settings.
# Users must accept the consent banner before any events are captured (GDPR).
# POSTHOG_PUBLIC_KEY=phc_...
# EU data center (default). Use https://us.i.posthog.com for US.
# POSTHOG_HOST=https://eu.i.posthog.com
8 changes: 8 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ COPY . .
ARG NUXT_PUBLIC_SITE_URL=http://localhost:3000
ENV NUXT_PUBLIC_SITE_URL=${NUXT_PUBLIC_SITE_URL}

# PostHog — the @posthog/nuxt module is conditionally loaded at build time.
# Pass your project API key so the module is included in the production bundle.
# Railway auto-passes service variables as Docker build args.
ARG POSTHOG_PUBLIC_KEY
ENV POSTHOG_PUBLIC_KEY=${POSTHOG_PUBLIC_KEY}
ARG POSTHOG_HOST
ENV POSTHOG_HOST=${POSTHOG_HOST}

RUN npm run build

# ─── Stage 2: Run ────────────────────────────────────────────────────────────
Expand Down
12 changes: 11 additions & 1 deletion app/composables/useAnalyticsConsent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function useAnalyticsConsent() {
// usePostHog() is auto-imported by @posthog/nuxt, but the module is
// conditionally loaded. Replicate the safe accessor so this composable
// works even when PostHog is not configured (CI, self-hosted without key).
const posthog = (useNuxtApp() as Record<string, unknown>).$posthog as ((() => { opt_in_capturing: () => void, opt_out_capturing: () => void }) | undefined)
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)
const ph = posthog?.()

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

function declineAnalytics() {
Expand Down
10 changes: 10 additions & 0 deletions app/composables/useInterviews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface Interview {
id: string
scheduledAt: string
duration: number
type: 'phone' | 'video' | 'in_person' | 'panel' | 'technical' | 'take_home'
status: 'scheduled' | 'completed' | 'cancelled' | 'no_show'
title: string
candidateFirstName?: string
candidateLastName?: string
}
1 change: 0 additions & 1 deletion app/plugins/posthog-identity.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const SENSITIVE_URL_PROPS = ['$current_url', '$initial_current_url', '$referrer'

export default defineNuxtPlugin({
name: 'posthog-identity',
parallel: true,
setup() {
// usePostHog() is auto-imported by @posthog/nuxt, but the module is
// conditionally loaded (only when POSTHOG_PUBLIC_KEY is set). In CI and
Expand Down
17 changes: 12 additions & 5 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ export default defineNuxtConfig({
publicKey: process.env.POSTHOG_PUBLIC_KEY || '',
host: process.env.POSTHOG_HOST || 'https://eu.i.posthog.com',
clientConfig: {
// ── Reverse proxy: route PostHog through reqcore.com to bypass ad blockers ──
// Requests to /ingest/** are proxied by Nitro to eu.i.posthog.com
api_host: '/ingest',
ui_host: 'https://eu.posthog.com',
// ── Privacy: disable invasive features ──
autocapture: false,
disable_session_recording: true,
Expand Down Expand Up @@ -117,11 +121,8 @@ export default defineNuxtConfig({

runtimeConfig: {
public: {
/** PostHog public key and host for server-side event capture */
posthog: {
publicKey: process.env.POSTHOG_PUBLIC_KEY || '',
host: process.env.POSTHOG_HOST || 'https://eu.i.posthog.com',
},
// PostHog runtimeConfig is managed by @posthog/nuxt via posthogConfig above.
// Override at runtime with NUXT_PUBLIC_POSTHOG_PUBLIC_KEY / NUXT_PUBLIC_POSTHOG_HOST.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
/** When set, the dashboard shows a read-only demo banner for this org slug */
demoOrgSlug: process.env.DEMO_ORG_SLUG || (isRailwayPreview ? 'reqcore-demo' : ''),
/** Public live-demo account email used to prefill sign-in */
Expand Down Expand Up @@ -202,6 +203,12 @@ export default defineNuxtConfig({
// Route rules — prerender/ISR for public pages
// ─────────────────────────────────────────────
routeRules: {
// ── PostHog reverse proxy — bypasses ad blockers by routing through reqcore.com ──
// NOTE: Targets are hardcoded to the EU data center (eu.i.posthog.com).
// If you use the US data center, set POSTHOG_HOST=https://us.i.posthog.com
// and update these two proxy targets to us-assets.i.posthog.com / us.i.posthog.com.
'/ingest/static/**': { proxy: 'https://eu-assets.i.posthog.com/static/**' },
'/ingest/**': { proxy: 'https://eu.i.posthog.com/**' },
Comment on lines +206 to +211
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded EU proxy endpoints may conflict with US data center configuration.

The proxy routes are hardcoded to EU endpoints (eu.i.posthog.com, eu-assets.i.posthog.com), but POSTHOG_HOST can be set to the US data center (us.i.posthog.com). If a user configures US hosting, the proxy will still route to EU servers.

Consider documenting this limitation or making the proxy targets configurable if US support is needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nuxt.config.ts` around lines 206 - 208, The proxy entries in nuxt.config.ts
mapping '/ingest/**' and '/ingest/static/**' are hardcoded to EU endpoints
(eu.i.posthog.com / eu-assets.i.posthog.com) which will conflict when
POSTHOG_HOST is set to a US host; update the proxy configuration to derive
targets from the POSTHOG_HOST environment variable (or add a new
POSTHOG_PROXY_HOST config) and use that value for the '/ingest/**' and
'/ingest/static/**' proxy targets, falling back to the existing EU endpoints if
the env var is not set; alternatively add a comment in nuxt.config.ts
documenting the limitation and instructing users to set the new env var if they
use the US data center so the mapping for '/ingest/**' and '/ingest/static/**'
points to the correct host.

'/': { prerender: true },
'/roadmap': { prerender: true },
'/blog': { prerender: true },
Expand Down
5 changes: 2 additions & 3 deletions server/utils/posthog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ let client: PostHog | null = null
export function useServerPostHog(): PostHog | null {
if (client) return client

const config = useRuntimeConfig()
const publicKey = config.public.posthog?.publicKey
const host = config.public.posthog?.host
const publicKey = process.env.POSTHOG_PUBLIC_KEY
const host = process.env.POSTHOG_HOST

if (!publicKey) {
return null
Expand Down
Loading