Fix IPv4 display and IP detection logic

Improved IP detection and display logic to always show IPv4 format, converting IPv6-mapped IPv4 addresses (e.g., ::ffff:127.0.0.1) and IPv6 loopback (::1) to 127.0.0.1. Updated API endpoint, display components, and added dedicated test/debug pages for IP format and detection. Added documentation summarizing the fixes and new features.
This commit is contained in:
2025-08-01 15:35:15 +08:00
parent ad8676cac3
commit 2808852e9f
11 changed files with 1198 additions and 386 deletions

View File

@@ -10,6 +10,43 @@ export async function GET(request: NextRequest) {
const detailedInfo = getDetailedIpInfo(request);
let clientIp = detailedInfo.detectedIp;
// 確保返回IPv4格式的地址
function ensureIPv4Format(ip: string): string {
if (!ip) return '127.0.0.1';
// 移除空白字符
ip = ip.trim();
// 處理IPv6格式的IPv4地址
if (ip.startsWith('::ffff:')) {
return ip.substring(7);
}
// 處理純IPv6本地回環地址
if (ip === '::1') {
return '127.0.0.1';
}
// 驗證是否為有效的IPv4地址
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
if (ipv4Regex.test(ip)) {
return ip;
}
// 如果不是有效的IPv4返回默認值
return '127.0.0.1';
}
// 檢查是否為IPv6格式的IPv4地址
function isIPv6MappedIPv4(ip: string): boolean {
return ip.startsWith('::ffff:');
}
// 獲取IPv6格式的IPv4地址
function getIPv6MappedFormat(ipv4: string): string {
return `::ffff:${ipv4}`;
}
// 如果檢測到的是127.0.0.1嘗試從請求頭獲取真實IP
if (clientIp === '127.0.0.1') {
// 檢查是否有代理轉發的真實IP
@@ -17,8 +54,13 @@ export async function GET(request: NextRequest) {
if (forwardedFor) {
const ips = forwardedFor.split(',').map(ip => ip.trim());
for (const ip of ips) {
if (ip && ip !== '127.0.0.1' && ip !== '::1' && ip !== 'localhost') {
clientIp = ip;
// 處理IPv6格式的IPv4地址
let cleanIp = ip;
if (ip.startsWith('::ffff:')) {
cleanIp = ip.substring(7);
}
if (cleanIp && cleanIp !== '127.0.0.1' && cleanIp !== '::1' && cleanIp !== 'localhost') {
clientIp = cleanIp;
break;
}
}
@@ -26,13 +68,25 @@ export async function GET(request: NextRequest) {
// 檢查其他可能的IP來源
const realIp = request.headers.get('x-real-ip');
if (realIp && realIp !== '127.0.0.1') {
clientIp = realIp;
if (realIp) {
let cleanRealIp = realIp;
if (realIp.startsWith('::ffff:')) {
cleanRealIp = realIp.substring(7);
}
if (cleanRealIp !== '127.0.0.1') {
clientIp = cleanRealIp;
}
}
const clientIpHeader = request.headers.get('x-client-ip');
if (clientIpHeader && clientIpHeader !== '127.0.0.1') {
clientIp = clientIpHeader;
if (clientIpHeader) {
let cleanClientIp = clientIpHeader;
if (clientIpHeader.startsWith('::ffff:')) {
cleanClientIp = clientIpHeader.substring(7);
}
if (cleanClientIp !== '127.0.0.1') {
clientIp = cleanClientIp;
}
}
}
@@ -51,12 +105,24 @@ export async function GET(request: NextRequest) {
}
}
// 確保最終返回的IP是IPv4格式
const finalIp = ensureIPv4Format(clientIp);
const originalIp = detailedInfo.detectedIp;
const isIPv6Mapped = isIPv6MappedIPv4(originalIp);
const ipv6Format = getIPv6MappedFormat(finalIp);
return NextResponse.json({
ip: clientIp,
ip: finalIp,
isAllowed,
enableIpWhitelist,
allowedIps: enableIpWhitelist ? allowedIps.split(',').map(ip => ip.trim()) : [],
timestamp: new Date().toISOString(),
ipv6Info: {
isIPv6Mapped,
originalFormat: originalIp,
ipv6Format,
hasIPv6Support: true
},
debug: {
allIpSources: detailedInfo.ipSources,
allFoundIps: detailedInfo.allFoundIps,
@@ -67,7 +133,9 @@ export async function GET(request: NextRequest) {
referer: request.headers.get('referer'),
userAgent: request.headers.get('user-agent'),
originalDetectedIp: detailedInfo.detectedIp,
finalDetectedIp: clientIp,
finalDetectedIp: finalIp,
rawDetectedIp: clientIp, // 保留原始檢測到的IP用於調試
ipDetectionMethod: isIPv6Mapped ? 'IPv6-Mapped-IPv4' : 'Standard-IPv4'
},
location: locationInfo,
// 本地開發環境的特殊信息