Skip to content

Commit 8fbe944

Browse files
committed
fix: use correct client ip header
1 parent c1801ad commit 8fbe944

File tree

20 files changed

+255
-61
lines changed

20 files changed

+255
-61
lines changed

apps/api/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
"groupmq": "1.0.0-next.19",
4242
"jsonwebtoken": "^9.0.2",
4343
"ramda": "^0.29.1",
44-
"request-ip": "^3.3.0",
4544
"sharp": "^0.33.5",
4645
"source-map-support": "^0.5.21",
4746
"sqlstring": "^2.3.3",
@@ -58,7 +57,6 @@
5857
"@types/js-yaml": "^4.0.9",
5958
"@types/jsonwebtoken": "^9.0.9",
6059
"@types/ramda": "^0.30.2",
61-
"@types/request-ip": "^0.0.41",
6260
"@types/source-map-support": "^0.5.10",
6361
"@types/sqlstring": "^2.3.2",
6462
"@types/uuid": "^10.0.0",

apps/api/src/controllers/event.controller.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { getClientIp } from '@/utils/get-client-ip';
21
import type { FastifyReply, FastifyRequest } from 'fastify';
32

43
import { generateDeviceId, parseUserAgent } from '@openpanel/common/server';
@@ -21,7 +20,7 @@ export async function postEvent(
2120
request.timestamp,
2221
request.body,
2322
);
24-
const ip = getClientIp(request)!;
23+
const ip = request.clientIp;
2524
const ua = request.headers['user-agent']!;
2625
const projectId = request.client?.projectId;
2726
const headers = getStringHeaders(request.headers);

apps/api/src/controllers/misc.controller.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import { parseUrlMeta } from '@/utils/parseUrlMeta';
44
import type { FastifyReply, FastifyRequest } from 'fastify';
55
import sharp from 'sharp';
66

7-
import { getClientIp } from '@/utils/get-client-ip';
7+
import {
8+
DEFAULT_HEADER_ORDER,
9+
getClientIpFromHeaders,
10+
} from '@openpanel/common/server/get-client-ip';
811
import { TABLE_NAMES, ch, chQuery, formatClickhouseDate } from '@openpanel/db';
9-
import { getGeoLocation } from '@openpanel/geo';
12+
import { type GeoLocation, getGeoLocation } from '@openpanel/geo';
1013
import { getCache, getRedisCache } from '@openpanel/redis';
1114

1215
interface GetFaviconParams {
@@ -394,12 +397,35 @@ export async function stats(request: FastifyRequest, reply: FastifyReply) {
394397
}
395398

396399
export async function getGeo(request: FastifyRequest, reply: FastifyReply) {
397-
const ip = getClientIp(request);
400+
const ip = getClientIpFromHeaders(request.headers);
401+
const others = await Promise.all(
402+
DEFAULT_HEADER_ORDER.map(async (header) => {
403+
const ip = getClientIpFromHeaders(request.headers, header);
404+
return {
405+
header,
406+
ip,
407+
geo: await getGeoLocation(ip),
408+
};
409+
}),
410+
);
411+
398412
if (!ip) {
399413
return reply.status(400).send('Bad Request');
400414
}
401415
const geo = await getGeoLocation(ip);
402-
return reply.status(200).send(geo);
416+
return reply.status(200).send({
417+
selected: {
418+
geo,
419+
ip,
420+
},
421+
...others.reduce(
422+
(acc, other) => {
423+
acc[other.header] = other;
424+
return acc;
425+
},
426+
{} as Record<string, { ip: string; geo: GeoLocation }>,
427+
),
428+
});
403429
}
404430

405431
export async function getOgImage(

apps/api/src/controllers/profile.controller.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { getClientIp } from '@/utils/get-client-ip';
21
import type { FastifyReply, FastifyRequest } from 'fastify';
32
import { assocPath, pathOr } from 'ramda';
43

@@ -22,7 +21,7 @@ export async function updateProfile(
2221
if (!projectId) {
2322
return reply.status(400).send('No projectId');
2423
}
25-
const ip = getClientIp(request)!;
24+
const ip = request.clientIp;
2625
const ua = request.headers['user-agent']!;
2726
const uaInfo = parseUserAgent(ua, properties);
2827
const geo = await getGeoLocation(ip);

apps/api/src/controllers/track.controller.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { getClientIp } from '@/utils/get-client-ip';
21
import type { FastifyReply, FastifyRequest } from 'fastify';
32
import { path, assocPath, pathOr, pick } from 'ramda';
43

@@ -91,7 +90,7 @@ export async function handler(
9190
const timestamp = getTimestamp(request.timestamp, request.body.payload);
9291
const ip =
9392
path<string>(['properties', '__ip'], request.body.payload) ||
94-
getClientIp(request)!;
93+
request.clientIp;
9594
const ua = request.headers['user-agent']!;
9695
const projectId = request.client?.projectId;
9796

apps/api/src/hooks/ip.hook.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import { getClientIp } from '@/utils/get-client-ip';
2-
import type {
3-
FastifyReply,
4-
FastifyRequest,
5-
HookHandlerDoneFunction,
6-
} from 'fastify';
1+
import { getClientIpFromHeaders } from '@openpanel/common/server/get-client-ip';
2+
import type { FastifyRequest } from 'fastify';
73

84
export async function ipHook(request: FastifyRequest) {
9-
const ip = getClientIp(request);
5+
const ip = getClientIpFromHeaders(request.headers);
6+
107
if (ip) {
118
request.clientIp = ip;
9+
} else {
10+
request.clientIp = '';
1211
}
1312
}

apps/api/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ process.env.TZ = 'UTC';
5555
declare module 'fastify' {
5656
interface FastifyRequest {
5757
client: IServiceClientWithProject | null;
58-
clientIp?: string;
58+
clientIp: string;
5959
timestamp?: number;
6060
session: SessionValidationResult;
6161
}

apps/api/src/utils/get-client-ip.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { createHash } from 'node:crypto';
2+
import { getClientIpFromHeaders } from '@openpanel/common/server/get-client-ip';
3+
// adding .js next/script import fixes an issues
4+
// with esm and nextjs (when using pages dir)
5+
import { NextResponse } from 'next/server.js';
6+
7+
type CreateNextRouteHandlerOptions = {
8+
apiUrl?: string;
9+
};
10+
11+
function createNextRouteHandler(options: CreateNextRouteHandlerOptions) {
12+
return async function POST(req: Request) {
13+
const apiUrl = options.apiUrl ?? 'https://api.openpanel.dev';
14+
const headers = new Headers(req.headers);
15+
const clientIp = getClientIpFromHeaders(headers);
16+
console.log('debug', {
17+
clientIp,
18+
userAgent: req.headers.get('user-agent'),
19+
});
20+
try {
21+
const res = await fetch(`${apiUrl}/track`, {
22+
method: 'POST',
23+
headers,
24+
body: JSON.stringify(await req.json()),
25+
});
26+
return NextResponse.json(await res.text(), { status: res.status });
27+
} catch (e) {
28+
return NextResponse.json(e);
29+
}
30+
};
31+
}
32+
33+
function createScriptHandler() {
34+
return async function GET(req: Request) {
35+
if (!req.url.endsWith('op1.js')) {
36+
return NextResponse.json({ error: 'Not found' }, { status: 404 });
37+
}
38+
39+
const scriptUrl = 'https://openpanel.dev/op1.js';
40+
try {
41+
const res = await fetch(scriptUrl, {
42+
next: { revalidate: 86400 },
43+
});
44+
const text = await res.text();
45+
const etag = `"${createHash('md5').update(text).digest('hex')}"`;
46+
return new NextResponse(text, {
47+
headers: {
48+
'Content-Type': 'text/javascript',
49+
'Cache-Control':
50+
'public, max-age=86400, stale-while-revalidate=86400',
51+
ETag: etag,
52+
},
53+
});
54+
} catch (e) {
55+
return NextResponse.json(
56+
{
57+
error: 'Failed to fetch script',
58+
message: e instanceof Error ? e.message : String(e),
59+
},
60+
{ status: 500 },
61+
);
62+
}
63+
};
64+
}
65+
66+
export const POST = createNextRouteHandler({});
67+
export const GET = createScriptHandler();

apps/public/app/layout.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,9 @@ export default async function Layout({ children }: { children: ReactNode }) {
6161
<RootProvider>
6262
<TooltipProvider>{children}</TooltipProvider>
6363
</RootProvider>
64-
<Script
65-
defer
66-
src="http://localhost:3000/script.js"
67-
data-website-id="44d65df1-e9cb-4c2c-917d-4bf1c7850948"
68-
/>
6964
<OpenPanelComponent
65+
apiUrl="/api/op"
66+
cdnUrl="/api/op/op1.js"
7067
clientId="301c6dc1-424c-4bc3-9886-a8beab09b615"
7168
trackAttributes
7269
trackScreenViews

0 commit comments

Comments
 (0)