diff --git a/apps/docs/src/pages/docs/nextjs.mdx b/apps/docs/src/pages/docs/nextjs.mdx index 4f07750e..3b38acc2 100644 --- a/apps/docs/src/pages/docs/nextjs.mdx +++ b/apps/docs/src/pages/docs/nextjs.mdx @@ -208,3 +208,16 @@ opServer.event('my_server_event', { ok: '✅' }); // Pass `profileId` to track events for a specific user opServer.event('my_server_event', { profileId: '123', ok: '✅' }); ``` + +### Proxy events + +With `createNextRouteHandler` you can proxy your events through your server, this will ensure all events are tracked since there is a lot of adblockers that block requests to third party domains. + +```typescript +import { createNextRouteHandler } from '@openpanel/nextjs'; + +export const { POST } = createNextRouteHandler({ + clientId: '{YOUR_CLIENT_ID}', + clientSecret: '{YOUR_CLIENT_SECRET}', +}); +``` diff --git a/packages/sdks/nextjs/createNextRouteHandler.ts b/packages/sdks/nextjs/createNextRouteHandler.ts new file mode 100644 index 00000000..f65d0612 --- /dev/null +++ b/packages/sdks/nextjs/createNextRouteHandler.ts @@ -0,0 +1,68 @@ +import { NextResponse } from 'next/server'; + +const VALID_PATHS = [ + '/profile', + '/profile/increment', + '/profile/decrement', + '/event', +]; + +function getIp(req: Request) { + if (req.headers.get('X-Forwarded-For')) { + return req.headers.get('X-Forwarded-For')?.split(',')[0]; + } + return req.headers.get('x-real-ip') ?? '0.0.0.0'; +} + +function getPath(params?: Record) { + const segments = params?.op; + if (segments && Array.isArray(segments)) { + const path = `/${segments.join('/')}`; + if (VALID_PATHS.includes(path)) { + return path; + } + } + + return null; +} + +export function createNextRouteHandler({ + clientId, + clientSecret, + url = 'https://api.openpanel.dev', +}: { + clientId: string; + clientSecret: string; + url?: string; +}) { + return { + POST: async function POST( + req: Request, + { params }: { params: Record } + ) { + const path = getPath(params); + if (!path) { + return NextResponse.json('Invalid path'); + } + + const headers = { + 'user-agent': req.headers.get('user-agent')!, + 'Content-Type': req.headers.get('Content-Type')!, + 'openpanel-client-id': clientId, + 'openpanel-client-secret': clientSecret, + 'x-client-ip': getIp(req)!, + }; + + try { + const res = await fetch(`${url}${path}`, { + method: 'POST', + headers, + body: JSON.stringify(await req.json()), + }); + return NextResponse.json(await res.text()); + } catch (e) { + return NextResponse.json(e); + } + }, + }; +} diff --git a/packages/sdks/nextjs/index.tsx b/packages/sdks/nextjs/index.tsx index 204e4f54..da6fea09 100644 --- a/packages/sdks/nextjs/index.tsx +++ b/packages/sdks/nextjs/index.tsx @@ -9,6 +9,7 @@ import type { } from '@openpanel/web'; export * from '@openpanel/web'; +export { createNextRouteHandler } from './createNextRouteHandler'; const CDN_URL = 'https://openpanel.dev/op.js';