Files
wish-pool/app/api/ip/route.ts
2025-08-01 13:57:22 +08:00

108 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextRequest, NextResponse } from 'next/server'
import { getClientIp, isIpAllowed, getIpLocation, getDetailedIpInfo } from '@/lib/ip-utils'
// 強制動態渲染
export const dynamic = 'force-dynamic'
export async function GET(request: NextRequest) {
try {
// 使用詳細的IP檢測功能
const detailedInfo = getDetailedIpInfo(request);
let clientIp = detailedInfo.detectedIp;
// 如果檢測到的是127.0.0.1嘗試從請求頭獲取真實IP
if (clientIp === '127.0.0.1') {
// 檢查是否有代理轉發的真實IP
const forwardedFor = request.headers.get('x-forwarded-for');
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;
break;
}
}
}
// 檢查其他可能的IP來源
const realIp = request.headers.get('x-real-ip');
if (realIp && realIp !== '127.0.0.1') {
clientIp = realIp;
}
const clientIpHeader = request.headers.get('x-client-ip');
if (clientIpHeader && clientIpHeader !== '127.0.0.1') {
clientIp = clientIpHeader;
}
}
const allowedIps = process.env.ALLOWED_IPS || ''
const enableIpWhitelist = process.env.ENABLE_IP_WHITELIST === 'true'
const isAllowed = enableIpWhitelist ? isIpAllowed(clientIp, allowedIps) : true
// 嘗試獲取地理位置信息
let locationInfo = null
if (clientIp && clientIp !== '127.0.0.1' && isPublicIp(clientIp)) {
try {
locationInfo = await getIpLocation(clientIp)
} catch (error) {
console.error('Error fetching location info:', error)
}
}
return NextResponse.json({
ip: clientIp,
isAllowed,
enableIpWhitelist,
allowedIps: enableIpWhitelist ? allowedIps.split(',').map(ip => ip.trim()) : [],
timestamp: new Date().toISOString(),
debug: {
allIpSources: detailedInfo.ipSources,
allFoundIps: detailedInfo.allFoundIps,
isLocalDevelopment: detailedInfo.isLocalDevelopment,
localIp: detailedInfo.localIp,
environment: process.env.NODE_ENV,
host: request.headers.get('host'),
referer: request.headers.get('referer'),
userAgent: request.headers.get('user-agent'),
originalDetectedIp: detailedInfo.detectedIp,
finalDetectedIp: clientIp,
},
location: locationInfo,
// 本地開發環境的特殊信息
development: detailedInfo.isLocalDevelopment ? {
message: '本地開發環境 - IP檢測可能受限',
suggestions: [
'在生產環境中部署後IP檢測會更準確',
'可以使用 ngrok 或類似工具進行外部測試',
'檢查防火牆和網路配置',
'確認代理伺服器設置',
'如果使用VPN請檢查VPN設置'
]
} : null
})
} catch (error) {
console.error('Error getting IP info:', error)
return NextResponse.json(
{ error: '無法獲取IP信息' },
{ status: 500 }
)
}
}
// 檢查是否為公網IP的輔助函數
function isPublicIp(ip: string): boolean {
const privateRanges = [
/^10\./, // 10.0.0.0/8
/^172\.(1[6-9]|2[0-9]|3[0-1])\./, // 172.16.0.0/12
/^192\.168\./, // 192.168.0.0/16
/^127\./, // 127.0.0.0/8
/^169\.254\./, // 169.254.0.0/16 (Link-local)
/^0\./, // 0.0.0.0/8
/^224\./, // 224.0.0.0/4 (Multicast)
/^240\./, // 240.0.0.0/4 (Reserved)
];
return !privateRanges.some(range => range.test(ip));
}