Skip to content

Commit bfec72e

Browse files
committed
add posthog analytics
1 parent e0c3928 commit bfec72e

File tree

10 files changed

+240
-15
lines changed

10 files changed

+240
-15
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ yarn-error.log*
4141
next-env.d.ts
4242

4343
dist
44+
45+
.env
46+
.env.local

app/api/badge/[login]/route.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { NextRequest } from 'next/server';
33
import { BadgeTemplateType, BadgeType, ThemeType } from '@/badge/badge.types';
44
import { renderSmallBadge } from '@/badge/templates/small/small.render';
55
import { renderMediumBadge } from '@/badge/templates/medium/medium.render';
6+
import { posthog } from '@/lib/posthog/posthog-node-client';
67

78
type Props = { params: Promise<{ login: string }> };
89

@@ -20,9 +21,15 @@ export async function GET(req: NextRequest, { params }: Props) {
2021

2122
const searchParams = req.nextUrl.searchParams;
2223
const type = searchParams.get('type') as BadgeType;
23-
const template = searchParams.get('template') as BadgeTemplateType;
24+
const template = (searchParams.get('template') ?? 'medium') as BadgeTemplateType;
2425
const theme = (searchParams.get('theme') ?? 'light') as ThemeType;
2526

27+
posthog.capture({
28+
distinctId: login,
29+
event: 'badge_rendered',
30+
properties: { type, template, theme },
31+
});
32+
2633
const svg = await getRendererByTemplate(template)({ theme, login, type });
2734

2835
if (!svg) {

app/api/graphql/route.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
1+
import { appRouteReject } from '@/utils/app-route-reject';
12
import { NextRequest, NextResponse } from 'next/server';
23

34
export async function POST(req: NextRequest) {
4-
const isProd = process.env.NODE_ENV === 'production';
5-
6-
if (isProd) {
7-
const origin = req.headers.get('origin') || req.headers.get('referer');
8-
const host = req.headers.get('host');
9-
const protocol = req.headers.get('x-forwarded-proto') || 'https';
10-
const expectedOrigin = `${protocol}://${host}`;
11-
12-
console.log('origin', origin, expectedOrigin);
13-
14-
// if (!origin || !origin.startsWith(expectedOrigin)) {
15-
// return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
16-
// }
5+
if (appRouteReject(req)) {
6+
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
177
}
188

199
try {

app/layout.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Metadata } from 'next';
22
import { Geist, Geist_Mono } from 'next/font/google';
33
import './globals.css';
44
import { SessionProvider } from 'next-auth/react';
5+
import { PostHogProvider } from '../lib/posthog/post-hog-provider';
56

67
const geistSans = Geist({
78
variable: '--font-geist-sans',
@@ -26,7 +27,9 @@ export default function RootLayout({
2627
return (
2728
<html lang="en">
2829
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
29-
<SessionProvider>{children}</SessionProvider>
30+
<PostHogProvider>
31+
<SessionProvider>{children}</SessionProvider>
32+
</PostHogProvider>
3033
</body>
3134
</html>
3235
);

lib/posthog/post-hog-provider.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use client';
2+
3+
import posthog from 'posthog-js';
4+
import { PostHogProvider as PHProvider, usePostHog } from 'posthog-js/react';
5+
import { Suspense, useEffect } from 'react';
6+
import { usePathname, useSearchParams } from 'next/navigation';
7+
8+
export function PostHogProvider({ children }: { children: React.ReactNode }) {
9+
useEffect(() => {
10+
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
11+
api_host: '/ingest',
12+
ui_host: 'https://eu.posthog.com',
13+
capture_pageview: false, // We capture pageviews manually
14+
capture_pageleave: true, // Enable pageleave capture
15+
});
16+
}, []);
17+
18+
return (
19+
<PHProvider client={posthog}>
20+
<SuspendedPostHogPageView />
21+
{children}
22+
</PHProvider>
23+
);
24+
}
25+
26+
function PostHogPageView() {
27+
const pathname = usePathname();
28+
const searchParams = useSearchParams();
29+
const posthog = usePostHog();
30+
31+
useEffect(() => {
32+
if (pathname && posthog) {
33+
let url = window.origin + pathname;
34+
const search = searchParams.toString();
35+
if (search) {
36+
url += '?' + search;
37+
}
38+
posthog.capture('$pageview', { $current_url: url });
39+
}
40+
}, [pathname, searchParams, posthog]);
41+
42+
return null;
43+
}
44+
45+
function SuspendedPostHogPageView() {
46+
return (
47+
<Suspense fallback={null}>
48+
<PostHogPageView />
49+
</Suspense>
50+
);
51+
}

lib/posthog/posthog-node-client.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { PostHog } from 'posthog-node';
2+
3+
function postHogClient() {
4+
const posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
5+
host: 'https://eu.i.posthog.com',
6+
flushAt: 1,
7+
flushInterval: 0,
8+
});
9+
10+
return posthogClient;
11+
}
12+
13+
export const posthog = postHogClient();

next.config.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,23 @@ const nextConfig: NextConfig = {
1010
},
1111
],
1212
},
13+
async rewrites() {
14+
return [
15+
{
16+
source: "/ingest/static/:path*",
17+
destination: "https://eu-assets.i.posthog.com/static/:path*",
18+
},
19+
{
20+
source: "/ingest/:path*",
21+
destination: "https://eu.i.posthog.com/:path*",
22+
},
23+
{
24+
source: "/ingest/decide",
25+
destination: "https://eu.i.posthog.com/decide",
26+
},
27+
];
28+
},
29+
skipTrailingSlashRedirect: true,
1330
};
1431

1532
export default nextConfig;

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
"mongodb": "^6.13.0",
2222
"next": "15.1.6",
2323
"next-auth": "5.0.0-beta.25",
24+
"posthog-js": "^1.234.1",
25+
"posthog-node": "^4.11.1",
2426
"react": "^19.0.0",
2527
"react-dom": "^19.0.0",
2628
"satori": "^0.12.1",

0 commit comments

Comments
 (0)