做 ip 防呆機制

This commit is contained in:
2025-08-01 13:17:32 +08:00
parent 2282eed9a1
commit a54ae31896
7 changed files with 1139 additions and 125 deletions

View File

@@ -1,23 +1,57 @@
import { NextRequest, NextResponse } from 'next/server'
import { getClientIp, isIpAllowed } from '@/lib/ip-utils'
import { getClientIp, isIpAllowed, getIpLocation, getDetailedIpInfo } from '@/lib/ip-utils'
// 強制動態渲染
export const dynamic = 'force-dynamic'
export async function GET(request: NextRequest) {
try {
const clientIp = getClientIp(request)
// 使用詳細的IP檢測功能
const detailedInfo = getDetailedIpInfo(request);
const clientIp = detailedInfo.detectedIp;
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()
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'),
},
location: locationInfo,
// 本地開發環境的特殊信息
development: detailedInfo.isLocalDevelopment ? {
message: '本地開發環境 - IP檢測可能受限',
suggestions: [
'在生產環境中部署後IP檢測會更準確',
'可以使用 ngrok 或類似工具進行外部測試',
'檢查防火牆和網路配置',
'確認代理伺服器設置'
]
} : null
})
} catch (error) {
console.error('Error getting IP info:', error)
@@ -26,4 +60,20 @@ export async function GET(request: NextRequest) {
{ 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));
}