fix: use correct client ip header
This commit is contained in:
@@ -41,7 +41,6 @@
|
||||
"groupmq": "1.0.0-next.19",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"ramda": "^0.29.1",
|
||||
"request-ip": "^3.3.0",
|
||||
"sharp": "^0.33.5",
|
||||
"source-map-support": "^0.5.21",
|
||||
"sqlstring": "^2.3.3",
|
||||
@@ -58,7 +57,6 @@
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/jsonwebtoken": "^9.0.9",
|
||||
"@types/ramda": "^0.30.2",
|
||||
"@types/request-ip": "^0.0.41",
|
||||
"@types/source-map-support": "^0.5.10",
|
||||
"@types/sqlstring": "^2.3.2",
|
||||
"@types/uuid": "^10.0.0",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getClientIp } from '@/utils/get-client-ip';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
|
||||
import { generateDeviceId, parseUserAgent } from '@openpanel/common/server';
|
||||
@@ -21,7 +20,7 @@ export async function postEvent(
|
||||
request.timestamp,
|
||||
request.body,
|
||||
);
|
||||
const ip = getClientIp(request)!;
|
||||
const ip = request.clientIp;
|
||||
const ua = request.headers['user-agent']!;
|
||||
const projectId = request.client?.projectId;
|
||||
const headers = getStringHeaders(request.headers);
|
||||
|
||||
@@ -4,9 +4,12 @@ import { parseUrlMeta } from '@/utils/parseUrlMeta';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import sharp from 'sharp';
|
||||
|
||||
import { getClientIp } from '@/utils/get-client-ip';
|
||||
import {
|
||||
DEFAULT_HEADER_ORDER,
|
||||
getClientIpFromHeaders,
|
||||
} from '@openpanel/common/server/get-client-ip';
|
||||
import { TABLE_NAMES, ch, chQuery, formatClickhouseDate } from '@openpanel/db';
|
||||
import { getGeoLocation } from '@openpanel/geo';
|
||||
import { type GeoLocation, getGeoLocation } from '@openpanel/geo';
|
||||
import { getCache, getRedisCache } from '@openpanel/redis';
|
||||
|
||||
interface GetFaviconParams {
|
||||
@@ -394,12 +397,35 @@ export async function stats(request: FastifyRequest, reply: FastifyReply) {
|
||||
}
|
||||
|
||||
export async function getGeo(request: FastifyRequest, reply: FastifyReply) {
|
||||
const ip = getClientIp(request);
|
||||
const ip = getClientIpFromHeaders(request.headers);
|
||||
const others = await Promise.all(
|
||||
DEFAULT_HEADER_ORDER.map(async (header) => {
|
||||
const ip = getClientIpFromHeaders(request.headers, header);
|
||||
return {
|
||||
header,
|
||||
ip,
|
||||
geo: await getGeoLocation(ip),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
if (!ip) {
|
||||
return reply.status(400).send('Bad Request');
|
||||
}
|
||||
const geo = await getGeoLocation(ip);
|
||||
return reply.status(200).send(geo);
|
||||
return reply.status(200).send({
|
||||
selected: {
|
||||
geo,
|
||||
ip,
|
||||
},
|
||||
...others.reduce(
|
||||
(acc, other) => {
|
||||
acc[other.header] = other;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, { ip: string; geo: GeoLocation }>,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getOgImage(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getClientIp } from '@/utils/get-client-ip';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { assocPath, pathOr } from 'ramda';
|
||||
|
||||
@@ -22,7 +21,7 @@ export async function updateProfile(
|
||||
if (!projectId) {
|
||||
return reply.status(400).send('No projectId');
|
||||
}
|
||||
const ip = getClientIp(request)!;
|
||||
const ip = request.clientIp;
|
||||
const ua = request.headers['user-agent']!;
|
||||
const uaInfo = parseUserAgent(ua, properties);
|
||||
const geo = await getGeoLocation(ip);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getClientIp } from '@/utils/get-client-ip';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { path, assocPath, pathOr, pick } from 'ramda';
|
||||
|
||||
@@ -91,7 +90,7 @@ export async function handler(
|
||||
const timestamp = getTimestamp(request.timestamp, request.body.payload);
|
||||
const ip =
|
||||
path<string>(['properties', '__ip'], request.body.payload) ||
|
||||
getClientIp(request)!;
|
||||
request.clientIp;
|
||||
const ua = request.headers['user-agent']!;
|
||||
const projectId = request.client?.projectId;
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { getClientIp } from '@/utils/get-client-ip';
|
||||
import type {
|
||||
FastifyReply,
|
||||
FastifyRequest,
|
||||
HookHandlerDoneFunction,
|
||||
} from 'fastify';
|
||||
import { getClientIpFromHeaders } from '@openpanel/common/server/get-client-ip';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
|
||||
export async function ipHook(request: FastifyRequest) {
|
||||
const ip = getClientIp(request);
|
||||
const ip = getClientIpFromHeaders(request.headers);
|
||||
|
||||
if (ip) {
|
||||
request.clientIp = ip;
|
||||
} else {
|
||||
request.clientIp = '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ process.env.TZ = 'UTC';
|
||||
declare module 'fastify' {
|
||||
interface FastifyRequest {
|
||||
client: IServiceClientWithProject | null;
|
||||
clientIp?: string;
|
||||
clientIp: string;
|
||||
timestamp?: number;
|
||||
session: SessionValidationResult;
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import requestIp from 'request-ip';
|
||||
|
||||
const ignore = ['127.0.0.1', '::1'];
|
||||
|
||||
export function getClientIp(req: FastifyRequest) {
|
||||
return requestIp.getClientIp(req);
|
||||
}
|
||||
67
apps/public/app/api/[...op]/route.ts
Normal file
67
apps/public/app/api/[...op]/route.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { getClientIpFromHeaders } from '@openpanel/common/server/get-client-ip';
|
||||
// adding .js next/script import fixes an issues
|
||||
// with esm and nextjs (when using pages dir)
|
||||
import { NextResponse } from 'next/server.js';
|
||||
|
||||
type CreateNextRouteHandlerOptions = {
|
||||
apiUrl?: string;
|
||||
};
|
||||
|
||||
function createNextRouteHandler(options: CreateNextRouteHandlerOptions) {
|
||||
return async function POST(req: Request) {
|
||||
const apiUrl = options.apiUrl ?? 'https://api.openpanel.dev';
|
||||
const headers = new Headers(req.headers);
|
||||
const clientIp = getClientIpFromHeaders(headers);
|
||||
console.log('debug', {
|
||||
clientIp,
|
||||
userAgent: req.headers.get('user-agent'),
|
||||
});
|
||||
try {
|
||||
const res = await fetch(`${apiUrl}/track`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(await req.json()),
|
||||
});
|
||||
return NextResponse.json(await res.text(), { status: res.status });
|
||||
} catch (e) {
|
||||
return NextResponse.json(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createScriptHandler() {
|
||||
return async function GET(req: Request) {
|
||||
if (!req.url.endsWith('op1.js')) {
|
||||
return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const scriptUrl = 'https://openpanel.dev/op1.js';
|
||||
try {
|
||||
const res = await fetch(scriptUrl, {
|
||||
next: { revalidate: 86400 },
|
||||
});
|
||||
const text = await res.text();
|
||||
const etag = `"${createHash('md5').update(text).digest('hex')}"`;
|
||||
return new NextResponse(text, {
|
||||
headers: {
|
||||
'Content-Type': 'text/javascript',
|
||||
'Cache-Control':
|
||||
'public, max-age=86400, stale-while-revalidate=86400',
|
||||
ETag: etag,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Failed to fetch script',
|
||||
message: e instanceof Error ? e.message : String(e),
|
||||
},
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const POST = createNextRouteHandler({});
|
||||
export const GET = createScriptHandler();
|
||||
@@ -61,12 +61,9 @@ export default async function Layout({ children }: { children: ReactNode }) {
|
||||
<RootProvider>
|
||||
<TooltipProvider>{children}</TooltipProvider>
|
||||
</RootProvider>
|
||||
<Script
|
||||
defer
|
||||
src="http://localhost:3000/script.js"
|
||||
data-website-id="44d65df1-e9cb-4c2c-917d-4bf1c7850948"
|
||||
/>
|
||||
<OpenPanelComponent
|
||||
apiUrl="/api/op"
|
||||
cdnUrl="/api/op/op1.js"
|
||||
clientId="301c6dc1-424c-4bc3-9886-a8beab09b615"
|
||||
trackAttributes
|
||||
trackScreenViews
|
||||
|
||||
@@ -273,19 +273,21 @@ export function GET() {
|
||||
|
||||
### 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.
|
||||
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. You'll also need to either host our tracking script or you can use `createScriptHandler` function which proxies this as well.
|
||||
|
||||
```typescript title="/app/api/[...op]/route.ts"
|
||||
import { createNextRouteHandler } from '@openpanel/nextjs/server';
|
||||
import { createNextRouteHandler, createScriptHandler } from '@openpanel/nextjs/server';
|
||||
|
||||
export const POST = createNextRouteHandler();
|
||||
export const GET = createScriptHandler()
|
||||
```
|
||||
|
||||
Remember to change the `apiUrl` in the `OpenPanelComponent` to your own server.
|
||||
Remember to change the `apiUrl` and `cdnUrl` in the `OpenPanelComponent` to your own server.
|
||||
|
||||
```tsx
|
||||
<OpenPanelComponent
|
||||
apiUrl="/api/op" // [!code highlight]
|
||||
cdnUrl="/api/op/op1.js" // [!code highlight]
|
||||
clientId="your-client-id"
|
||||
trackScreenViews={true}
|
||||
/>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"dependencies": {
|
||||
"@hyperdx/node-opentelemetry": "^0.8.1",
|
||||
"@number-flow/react": "0.3.5",
|
||||
"@openpanel/common": "workspace:*",
|
||||
"@openpanel/nextjs": "^1.0.5",
|
||||
"@openpanel/payments": "workspace:^",
|
||||
"@openpanel/sdk-info": "workspace:^",
|
||||
|
||||
Reference in New Issue
Block a user