diff --git a/apps/api/src/controllers/misc.controller.ts b/apps/api/src/controllers/misc.controller.ts index 25a477b1..dbfc51d6 100644 --- a/apps/api/src/controllers/misc.controller.ts +++ b/apps/api/src/controllers/misc.controller.ts @@ -5,7 +5,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify'; import sharp from 'sharp'; import { - DEFAULT_HEADER_ORDER, + DEFAULT_IP_HEADER_ORDER, getClientIpFromHeaders, } from '@openpanel/common/server/get-client-ip'; import { TABLE_NAMES, ch, chQuery, formatClickhouseDate } from '@openpanel/db'; @@ -397,10 +397,10 @@ export async function stats(request: FastifyRequest, reply: FastifyReply) { } export async function getGeo(request: FastifyRequest, reply: FastifyReply) { - const ip = getClientIpFromHeaders(request.headers); + const { ip, header } = getClientIpFromHeaders(request.headers); const others = await Promise.all( - DEFAULT_HEADER_ORDER.map(async (header) => { - const ip = getClientIpFromHeaders(request.headers, header); + DEFAULT_IP_HEADER_ORDER.map(async (header) => { + const { ip } = getClientIpFromHeaders(request.headers, header); return { header, ip, @@ -417,13 +417,14 @@ export async function getGeo(request: FastifyRequest, reply: FastifyReply) { selected: { geo, ip, + header, }, ...others.reduce( (acc, other) => { acc[other.header] = other; return acc; }, - {} as Record, + {} as Record, ), }); } diff --git a/apps/api/src/hooks/ip.hook.ts b/apps/api/src/hooks/ip.hook.ts index 2e293203..fe544c89 100644 --- a/apps/api/src/hooks/ip.hook.ts +++ b/apps/api/src/hooks/ip.hook.ts @@ -2,11 +2,13 @@ import { getClientIpFromHeaders } from '@openpanel/common/server/get-client-ip'; import type { FastifyRequest } from 'fastify'; export async function ipHook(request: FastifyRequest) { - const ip = getClientIpFromHeaders(request.headers); + const { ip, header } = getClientIpFromHeaders(request.headers); if (ip) { request.clientIp = ip; + request.clientIpHeader = header; } else { request.clientIp = ''; + request.clientIpHeader = ''; } } diff --git a/apps/api/src/hooks/request-logging.hook.ts b/apps/api/src/hooks/request-logging.hook.ts index 3d2c9961..adbebac1 100644 --- a/apps/api/src/hooks/request-logging.hook.ts +++ b/apps/api/src/hooks/request-logging.hook.ts @@ -1,3 +1,4 @@ +import { DEFAULT_IP_HEADER_ORDER } from '@openpanel/common'; import type { FastifyReply, FastifyRequest } from 'fastify'; import { path, pick } from 'ramda'; @@ -37,12 +38,15 @@ export async function requestLoggingHook( url: request.url, method: request.method, elapsed: reply.elapsedTime, + clientIp: request.clientIp, + clientIpHeader: request.clientIpHeader, headers: pick( [ 'openpanel-client-id', 'openpanel-sdk-name', 'openpanel-sdk-version', 'user-agent', + ...DEFAULT_IP_HEADER_ORDER, ], request.headers, ), diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 575db890..7d3632ca 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -55,6 +55,7 @@ declare module 'fastify' { interface FastifyRequest { client: IServiceClientWithProject | null; clientIp: string; + clientIpHeader: string; timestamp?: number; session: SessionValidationResult; } diff --git a/packages/common/server/get-client-ip.ts b/packages/common/server/get-client-ip.ts index 037939c1..7928dd43 100644 --- a/packages/common/server/get-client-ip.ts +++ b/packages/common/server/get-client-ip.ts @@ -5,7 +5,7 @@ * Example: IP_HEADER_ORDER="cf-connecting-ip,x-real-ip,x-forwarded-for" */ -export const DEFAULT_HEADER_ORDER = [ +export const DEFAULT_IP_HEADER_ORDER = [ 'cf-connecting-ip', 'true-client-ip', 'x-forwarded-for', @@ -32,7 +32,7 @@ function getHeaderOrder(): string[] { if (typeof process !== 'undefined' && process.env?.IP_HEADER_ORDER) { return process.env.IP_HEADER_ORDER.split(',').map((h) => h.trim()); } - return DEFAULT_HEADER_ORDER; + return DEFAULT_IP_HEADER_ORDER; } function isValidIp(ip: string): boolean { @@ -45,7 +45,10 @@ function isValidIp(ip: string): boolean { export function getClientIpFromHeaders( headers: Record | Headers, overrideHeaderName?: string, -): string { +): { + ip: string; + header: string; +} { let headerOrder = getHeaderOrder(); if (overrideHeaderName) { @@ -73,7 +76,7 @@ export function getClientIpFromHeaders( if (headerName === 'x-forwarded-for') { const firstIp = value.split(',')[0]?.trim(); if (firstIp && isValidIp(firstIp)) { - return firstIp; + return { ip: firstIp, header: headerName }; } } // Handle forwarded header (RFC 7239) @@ -81,14 +84,14 @@ export function getClientIpFromHeaders( const match = value.match(/for=(?:"?\[?([^\]"]+)\]?"?)/i); const ip = match?.[1]; if (ip && isValidIp(ip)) { - return ip; + return { ip, header: headerName }; } } // Regular headers else if (isValidIp(value)) { - return value; + return { ip: value, header: headerName }; } } - return ''; + return { ip: '', header: '' }; } diff --git a/packages/sdks/express/index.ts b/packages/sdks/express/index.ts index 70ea4c95..55175ff1 100644 --- a/packages/sdks/express/index.ts +++ b/packages/sdks/express/index.ts @@ -22,7 +22,7 @@ export type OpenpanelOptions = OpenPanelOptions & { export default function createMiddleware(options: OpenpanelOptions) { return function middleware(req: Request, res: Response, next: NextFunction) { const sdk = new OpenPanel(options); - const ip = getClientIpFromHeaders(req.headers); + const { ip } = getClientIpFromHeaders(req.headers); if (ip) { sdk.api.addHeader('x-client-ip', ip); }