-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
249 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,10 @@ | ||
import ClientOnly from "@/components/client-only"; | ||
import ClientPageBody from "./page-client"; | ||
|
||
export default function SessionPage() { | ||
return <ClientPageBody />; | ||
return ( | ||
<ClientOnly> | ||
<ClientPageBody /> | ||
</ClientOnly> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,10 @@ | ||
import ClientOnly from "@/components/client-only"; | ||
import ClientPageBody from "./page-client"; | ||
|
||
export default function SessionPage() { | ||
return <ClientPageBody />; | ||
return ( | ||
<ClientOnly> | ||
<ClientPageBody /> | ||
</ClientOnly> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// This API tracks user behavior by associating each user with a unique device ID. | ||
// The collected data helps enhance user experience, identify and fix bugs, | ||
// and gain insights for improving the product. | ||
// | ||
// All recorded data will be stored in the Starbase Database. | ||
|
||
import { cookies } from "next/headers"; | ||
import zod from "zod"; | ||
import { NextRequest, NextResponse } from "next/server"; | ||
import { insertTrackingRecord } from "@/lib/api/insert-tracking-record"; | ||
|
||
const eventBodySchema = zod.object({ | ||
events: zod | ||
.array( | ||
zod.object({ | ||
name: zod.string().max(255), | ||
data: zod.any().optional(), | ||
}) | ||
) | ||
.min(1), | ||
}); | ||
|
||
export const POST = async (req: NextRequest) => { | ||
// Getting the device id | ||
const cookieStore = await cookies(); | ||
|
||
let deviceId = cookieStore.get("od-id")?.value; | ||
|
||
if (!deviceId) { | ||
deviceId = crypto.randomUUID(); | ||
|
||
cookieStore.set("od-id", deviceId, { | ||
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), | ||
}); | ||
} | ||
|
||
// Get the body | ||
const body = await req.json(); | ||
const validate = eventBodySchema.safeParse(body); | ||
|
||
if (!validate.success) { | ||
return NextResponse.json({ | ||
success: false, | ||
error: validate.error.formErrors, | ||
}); | ||
} | ||
|
||
// Save the event | ||
await insertTrackingRecord(deviceId, validate.data.events.slice(0, 50)); | ||
|
||
return NextResponse.json({ | ||
success: true, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
"use client"; | ||
import { PropsWithChildren, useEffect, useState } from "react"; | ||
|
||
export default function ClientOnly(props: PropsWithChildren) { | ||
const [clientLoaded, setClientLoaded] = useState(false); | ||
|
||
useEffect(() => { | ||
setClientLoaded(typeof window !== "undefined"); | ||
}, []); | ||
|
||
if (clientLoaded) { | ||
return props.children; | ||
} | ||
|
||
return null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
"use client"; | ||
|
||
import { addTrackEvent, normalizedPathname } from "@/lib/tracking"; | ||
import { usePathname } from "next/navigation"; | ||
import { useEffect } from "react"; | ||
|
||
export default function PageTracker() { | ||
const pathname = usePathname(); | ||
|
||
// Track page views | ||
useEffect(() => { | ||
const normalized = normalizedPathname(pathname); | ||
|
||
addTrackEvent("pageview", { | ||
path: normalized, | ||
full_path: pathname === normalized ? undefined : pathname, | ||
}); | ||
}, [pathname]); | ||
|
||
// Track unhandle rejection | ||
useEffect(() => { | ||
window.addEventListener("unhandledrejection", (event) => { | ||
if (typeof event.reason === "string") { | ||
addTrackEvent("unhandledrejection", { | ||
message: event.reason, | ||
}); | ||
} else if (event.reason?.message) { | ||
addTrackEvent("unhandledrejection", { | ||
message: event.reason.message, | ||
stack: event.reason.stack, | ||
}); | ||
} else { | ||
addTrackEvent("unhandledrejection", event.toString()); | ||
} | ||
}); | ||
|
||
return () => { | ||
window.removeEventListener("unhandledrejection", () => {}); | ||
}; | ||
}, []); | ||
|
||
return null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
"use server"; | ||
|
||
import StarbaseDriver from "@/drivers/starbase-driver"; | ||
import { type TrackEventItem } from "../tracking"; | ||
import { env } from "@/env"; | ||
|
||
export async function insertTrackingRecord( | ||
deviceId: string, | ||
events: TrackEventItem[] | ||
) { | ||
if (!env.DATABASE_ANALYTIC_URL || !env.DATABASE_ANALYTIC_AUTH_TOKEN) { | ||
return { | ||
success: false, | ||
error: "Analytics database is not configured", | ||
}; | ||
} | ||
|
||
const trackingDb = new StarbaseDriver(env.DATABASE_ANALYTIC_URL, { | ||
Authorization: "Bearer " + env.DATABASE_ANALYTIC_AUTH_TOKEN, | ||
}); | ||
|
||
const sql = [ | ||
"INSERT INTO events(id, created_at, user_id, event_name, event_data) VALUES", | ||
events | ||
.map( | ||
(event) => | ||
"(" + | ||
[ | ||
crypto.randomUUID(), | ||
Date.now(), | ||
deviceId, | ||
event.name, | ||
JSON.stringify(event.data), | ||
] | ||
.map(trackingDb.escapeValue) | ||
.join(", ") + | ||
")" | ||
) | ||
.join(", "), | ||
].join(""); | ||
|
||
await trackingDb.query(sql); | ||
} |
Oops, something went wrong.