Skip to content

Commit 39b32da

Browse files
Merge pull request #79 from prisma/claim-db-worker/update-posthog-and-env-vars
claim db worker: update posthog and env vars
2 parents 6660ab8 + 831a53a commit 39b32da

9 files changed

Lines changed: 185 additions & 135 deletions

File tree

claim-db-worker/__tests__/callback-api.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ vi.mock("@/lib/env", () => ({
99
limit: vi.fn(() => Promise.resolve({ success: true })),
1010
},
1111
POSTHOG_API_KEY: "test-key",
12-
POSTHOG_API_HOST: "https://app.posthog.com",
12+
POSTHOG_PROXY_HOST: "https://proxyhog.prisma-data.net",
1313
})),
1414
}));
1515

claim-db-worker/app/api/analytics/route.ts

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { NextRequest, NextResponse } from "next/server";
22
import { getEnv } from "@/lib/env";
3+
import { sendAnalyticsEvent } from "@/lib/analytics";
34
import { buildRateLimitKey } from "@/lib/server/ratelimit";
45

56
export async function POST(request: NextRequest) {
67
const env = getEnv();
7-
88
const url = new URL(request.url);
99
const key = buildRateLimitKey(request);
1010

11-
// --- Simple rate limiting ---
1211
const { success } = await env.CLAIM_DB_RATE_LIMITER.limit({ key });
1312
if (!success) {
1413
return NextResponse.json(
@@ -21,16 +20,13 @@ export async function POST(request: NextRequest) {
2120
);
2221
}
2322

24-
if (!env.POSTHOG_API_KEY || !env.POSTHOG_API_HOST) {
25-
return NextResponse.json({ success: true });
26-
}
27-
2823
try {
29-
const {
30-
event,
31-
properties,
32-
}: { event: string; properties: Record<string, any> } =
33-
await request.json();
24+
const body = (await request.json()) as {
25+
event?: string;
26+
properties?: Record<string, unknown>;
27+
};
28+
29+
const event = body.event?.trim();
3430

3531
if (!event) {
3632
return NextResponse.json(
@@ -39,19 +35,14 @@ export async function POST(request: NextRequest) {
3935
);
4036
}
4137

42-
await fetch(`${env.POSTHOG_API_HOST}/e`, {
43-
method: "POST",
44-
headers: {
45-
"Content-Type": "application/json",
46-
Authorization: `Bearer ${env.POSTHOG_API_KEY}`,
47-
},
48-
body: JSON.stringify({
49-
api_key: env.POSTHOG_API_KEY,
50-
event,
51-
properties: properties || {},
52-
distinct_id: "web-claim",
53-
}),
54-
});
38+
if (!event.startsWith("create_db:")) {
39+
return NextResponse.json(
40+
{ error: "Event must start with create_db:" },
41+
{ status: 400 }
42+
);
43+
}
44+
45+
await sendAnalyticsEvent(event, body.properties || {});
5546

5647
return NextResponse.json({ success: true });
5748
} catch (error) {

claim-db-worker/app/api/auth/callback/route.ts

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { NextRequest, NextResponse } from "next/server";
22
import { getEnv } from "@/lib/env";
33
import { exchangeCodeForToken, validateProject } from "@/lib/auth-utils";
4+
import { sendAnalyticsEvent } from "@/lib/analytics";
45
import {
56
redirectToError,
67
redirectToSuccess,
@@ -9,41 +10,6 @@ import {
910
import { transferProject } from "@/lib/project-transfer";
1011
import { buildRateLimitKey } from "@/lib/server/ratelimit";
1112

12-
async function sendServerAnalyticsEvent(
13-
event: string,
14-
properties: Record<string, any>,
15-
request: NextRequest
16-
) {
17-
const env = getEnv();
18-
19-
if (!env.POSTHOG_API_KEY || !env.POSTHOG_API_HOST) {
20-
return;
21-
}
22-
23-
try {
24-
await fetch(`${env.POSTHOG_API_HOST}/e`, {
25-
method: "POST",
26-
headers: {
27-
"Content-Type": "application/json",
28-
Authorization: `Bearer ${env.POSTHOG_API_KEY}`,
29-
},
30-
body: JSON.stringify({
31-
api_key: env.POSTHOG_API_KEY,
32-
event,
33-
properties: {
34-
...properties,
35-
$current_url: request.url,
36-
$user_agent: request.headers.get("user-agent"),
37-
},
38-
distinct_id: "server-claim",
39-
timestamp: new Date().toISOString(),
40-
}),
41-
});
42-
} catch (error) {
43-
console.error("Failed to send server analytics event:", error);
44-
}
45-
}
46-
4713
export async function GET(request: NextRequest) {
4814
try {
4915
const env = getEnv();
@@ -98,7 +64,7 @@ export async function GET(request: NextRequest) {
9864
} catch (error) {
9965
const errorMessage =
10066
error instanceof Error ? error.message : "Unknown error";
101-
await sendServerAnalyticsEvent(
67+
await sendAnalyticsEvent(
10268
"create_db:claim_failed",
10369
{
10470
"project-id": projectID,
@@ -115,13 +81,12 @@ export async function GET(request: NextRequest) {
11581
}
11682

11783
// Validate project exists and get project data
118-
let projectData;
11984
try {
120-
projectData = await validateProject(projectID);
85+
await validateProject(projectID);
12186
} catch (error) {
12287
const errorMessage =
12388
error instanceof Error ? error.message : "Unknown error";
124-
await sendServerAnalyticsEvent(
89+
await sendAnalyticsEvent(
12590
"create_db:claim_failed",
12691
{
12792
"project-id": projectID,
@@ -176,7 +141,7 @@ export async function GET(request: NextRequest) {
176141
};
177142
const databaseId = (databases.data?.[0]?.id ?? "").replace(/^db_/, "");
178143

179-
await sendServerAnalyticsEvent(
144+
await sendAnalyticsEvent(
180145
"create_db:claim_successful",
181146
{
182147
"project-id": projectID,
@@ -194,7 +159,7 @@ export async function GET(request: NextRequest) {
194159
databaseId
195160
);
196161
} else {
197-
await sendServerAnalyticsEvent(
162+
await sendAnalyticsEvent(
198163
"create_db:claim_failed",
199164
{
200165
"project-id": projectID,

claim-db-worker/app/api/claim/route.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,22 @@ export async function GET(request: NextRequest) {
2323
}
2424

2525
const projectID = url.searchParams.get("projectID");
26+
const utmSource = url.searchParams.get("utm_source");
27+
const utmMedium = url.searchParams.get("utm_medium");
2628

2729
if (!projectID || projectID === "undefined") {
2830
return NextResponse.json({ error: "Missing project ID" }, { status: 400 });
2931
}
3032

31-
await sendAnalyticsEvent("create_db:claim_viewed", {
32-
"project-id": projectID,
33-
});
33+
await sendAnalyticsEvent(
34+
"create_db:claim_viewed",
35+
{
36+
"project-id": projectID,
37+
utm_source: utmSource || undefined,
38+
utm_medium: utmMedium || undefined,
39+
},
40+
request
41+
);
3442

3543
return NextResponse.json({ success: true });
3644
}

claim-db-worker/components/PageViewTracker.tsx

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,25 @@
22

33
import { Suspense, useEffect } from "react";
44
import { usePathname, useSearchParams } from "next/navigation";
5-
import { sendAnalyticsEvent } from "@/lib/analytics";
5+
import { sendAnalyticsEvent } from "@/lib/analytics-client";
66

77
function PageViewTrackerContent() {
88
const pathname = usePathname();
99
const searchParams = useSearchParams();
10+
const search = searchParams?.toString() ?? "";
1011

1112
useEffect(() => {
12-
if (typeof window === "undefined") return;
13+
if (!pathname || typeof window === "undefined") return;
1314

14-
if (pathname) {
15-
const url = window.location.href;
16-
const search = searchParams?.toString();
17-
const fullPath = search ? `${pathname}?${search}` : pathname;
18-
19-
sendAnalyticsEvent("create_db:claim_page_viewed", {
20-
path: pathname,
21-
full_path: fullPath,
22-
url: url,
23-
referrer: document.referrer || "",
24-
timestamp: new Date().toISOString(),
25-
});
26-
}
27-
}, [pathname, searchParams]);
15+
const fullPath = search ? `${pathname}?${search}` : pathname;
16+
void sendAnalyticsEvent("create_db:claim_page_viewed", {
17+
path: pathname,
18+
full_path: fullPath,
19+
url: window.location.href,
20+
referrer: document.referrer || "",
21+
timestamp: new Date().toISOString(),
22+
});
23+
}, [pathname, search]);
2824

2925
return null;
3026
}
Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,12 @@
11
"use client";
22

3-
import posthog, { PostHog } from "posthog-js";
4-
import { PostHogProvider as PHProvider } from "posthog-js/react";
5-
6-
declare global {
7-
interface Window {
8-
posthog: PostHog;
9-
}
10-
}
3+
import { PageViewTracker } from "@/components/PageViewTracker";
114

125
export function PostHogProvider({ children }: { children: React.ReactNode }) {
13-
if (
14-
typeof window !== "undefined" &&
15-
process.env.NEXT_PUBLIC_POSTHOG_API_KEY
16-
) {
17-
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY || "", {
18-
api_host:
19-
process.env.NEXT_PUBLIC_POSTHOG_API_HOST ||
20-
"https://proxyhog.prisma-data.net",
21-
person_profiles: "identified_only",
22-
capture_pageview: true,
23-
capture_pageleave: true,
24-
autocapture: true,
25-
debug: process.env.NODE_ENV === "development", // Enable debug in development
26-
loaded: (posthog) => {
27-
if (process.env.NODE_ENV === "development") {
28-
console.log("PostHog initialized in debug mode");
29-
// Make posthog available globally for debugging
30-
window.posthog = posthog;
31-
}
32-
},
33-
});
34-
}
35-
36-
return <PHProvider client={posthog}>{children}</PHProvider>;
6+
return (
7+
<>
8+
<PageViewTracker />
9+
{children}
10+
</>
11+
);
3712
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"use client";
2+
3+
const DISTINCT_ID_STORAGE_KEY = "create_db:distinct_id";
4+
5+
function getDistinctId(): string {
6+
if (typeof window === "undefined") {
7+
return "claim-db-worker";
8+
}
9+
10+
try {
11+
const existing = window.localStorage.getItem(DISTINCT_ID_STORAGE_KEY);
12+
if (existing) {
13+
return existing;
14+
}
15+
16+
const nextId = crypto.randomUUID();
17+
window.localStorage.setItem(DISTINCT_ID_STORAGE_KEY, nextId);
18+
return nextId;
19+
} catch {
20+
return "claim-db-worker";
21+
}
22+
}
23+
24+
export async function sendAnalyticsEvent(
25+
event: string,
26+
properties: Record<string, unknown> = {}
27+
): Promise<void> {
28+
if (!event.startsWith("create_db:")) {
29+
return;
30+
}
31+
32+
try {
33+
const response = await fetch("/api/analytics", {
34+
method: "POST",
35+
headers: {
36+
"Content-Type": "application/json",
37+
},
38+
keepalive: true,
39+
body: JSON.stringify({
40+
event,
41+
properties: {
42+
...properties,
43+
distinct_id: getDistinctId(),
44+
},
45+
}),
46+
});
47+
48+
if (!response.ok && process.env.NODE_ENV === "development") {
49+
console.error("Failed to send analytics event:", response.status);
50+
}
51+
} catch (error) {
52+
if (process.env.NODE_ENV === "development") {
53+
console.error("Failed to send analytics event:", error);
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)