Skip to content

Commit 6cc6b71

Browse files
committed
Improve client IP detection and replace 'unknown' with 'local'
- Add getClientIp() utility function to extract client IP from multiple sources - Update recorder middleware to use getClientIp() for better IP logging - Update limiter middleware to use getClientIp() for rate limiting keys - Replace 'unknown' with 'local' as a more friendly fallback identifier - Handle x-forwarded-for header with multiple IPs (take first one) - Attempt to get IP from raw request object for Bun runtime
1 parent 5aa7c10 commit 6cc6b71

3 files changed

Lines changed: 46 additions & 6 deletions

File tree

server/middleware/limiter.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { RateLimiterMemory } from 'rate-limiter-flexible';
2-
import { HonoContext } from '../types/hono';
2+
import type { HonoContext } from '../types/hono';
3+
import { getClientIp } from '../utils/main';
34

45
// Configure the rate limiter
56
const limiter = new RateLimiterMemory({
@@ -10,9 +11,7 @@ const limiter = new RateLimiterMemory({
1011
// Create rate limiting middleware function with enhanced features
1112
export const rateLimiterMiddleware = async (c: HonoContext, next: () => Promise<void>) => {
1213
const user = c.get('user');
13-
const key = user
14-
? user.id
15-
: c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown';
14+
const key = user ? user.id : getClientIp(c);
1615

1716
try {
1817
await limiter.consume(key);

server/middleware/recorder.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import moment from 'moment';
2-
import { HonoContext } from '../types/hono';
2+
import type { HonoContext } from '../types/hono';
3+
import { getClientIp } from '../utils/main';
34

45
// Request middleware, record IP and time
56
export const recorderIpAndTime = async (c: HonoContext, next: () => Promise<void>) => {
@@ -12,7 +13,7 @@ export const recorderIpAndTime = async (c: HonoContext, next: () => Promise<void
1213
return;
1314
}
1415

15-
const ipAddress = c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown';
16+
const ipAddress = getClientIp(c);
1617

1718
const logMessage = `[${moment().format(
1819
'YYYY/MM/DD HH:mm:ss'

server/utils/main.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,46 @@ export function getApiUrl(c: HonoContext): string {
1414
return `${protocol}://${host}`;
1515
}
1616

17+
/**
18+
* 获取客户端 IP 地址
19+
* 按优先级尝试从多个来源获取:x-forwarded-for -> x-real-ip -> 原始连接
20+
* @param c Hono 上下文对象
21+
* @returns 客户端 IP 地址,如果无法获取则返回 'local'
22+
*/
23+
export function getClientIp(c: HonoContext): string {
24+
// 1. 尝试从 x-forwarded-for 获取(可能包含多个 IP,取第一个)
25+
const forwardedFor = c.req.header('x-forwarded-for');
26+
if (forwardedFor) {
27+
// x-forwarded-for 可能包含多个 IP,用逗号分隔,取第一个
28+
const firstIp = forwardedFor.split(',')[0].trim();
29+
if (firstIp) {
30+
return firstIp;
31+
}
32+
}
33+
34+
// 2. 尝试从 x-real-ip 获取
35+
const realIp = c.req.header('x-real-ip');
36+
if (realIp) {
37+
return realIp.trim();
38+
}
39+
40+
// 3. 尝试从原始请求对象获取(Bun 运行时)
41+
try {
42+
const raw = c.req.raw;
43+
if (raw && 'remoteAddress' in raw) {
44+
const remoteAddress = (raw as any).remoteAddress;
45+
if (remoteAddress) {
46+
return remoteAddress;
47+
}
48+
}
49+
} catch {
50+
// 忽略错误,继续尝试其他方式
51+
}
52+
53+
// 4. 如果都获取不到,返回 'local' 作为标识(比 'unknown' 更友好)
54+
return 'local';
55+
}
56+
1757
// UUID 格式验证函数
1858
export function isValidUUID(id: string): boolean {
1959
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

0 commit comments

Comments
 (0)