149 lines
4.8 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 { isInternalIP, detectIsIntranet, extractClientIpFromForwarded } from '@/lib/network-detection'
// 判断是否为内网IP地址(保留向后兼容,用于判断hostname)
function isInternalIPLegacy(hostname: string): boolean {
if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1') {
return true
}
// 判断是否为内网IP段
// 10.0.0.0 - 10.255.255.255
// 172.16.0.0 - 172.31.255.255
// 192.168.0.0 - 192.168.255.255
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
const match = hostname.match(ipv4Regex)
if (match) {
const [, a, b] = match.map(Number)
// 10.x.x.x
if (a === 10) return true
// 172.16.x.x - 172.31.x.x
if (a === 172 && b >= 16 && b <= 31) return true
// 192.168.x.x
if (a === 192 && b === 168) return true
}
return false
}
// 网络状态检测API
// GET: 检测用户是否在内网,以及是否访问公网域名
export async function GET(request: NextRequest) {
try {
// 获取当前访问的域名
const host = request.headers.get('host') || ''
const hostname = host.split(':')[0] // 移除端口号
// 使用统一的网络检测函数(已包含严谨的检测逻辑)
const isIntranet = detectIsIntranet(request)
// 获取客户端IP(用于调试和日志,使用与 detectIsIntranet 相同的逻辑)
const forwarded = request.headers.get('x-forwarded-for')
const realIp = request.headers.get('x-real-ip')
const cfConnectingIp = request.headers.get('cf-connecting-ip')
const xClientIp = request.headers.get('x-client-ip')
let clientIp: string | null = null
let clientIpSource: string = 'unknown'
if (forwarded) {
clientIp = extractClientIpFromForwarded(forwarded)
clientIpSource = 'x-forwarded-for'
}
if (!clientIp && realIp) {
clientIp = realIp
clientIpSource = 'x-real-ip'
}
if (!clientIp && cfConnectingIp) {
clientIp = cfConnectingIp
clientIpSource = 'cf-connecting-ip'
}
if (!clientIp && xClientIp) {
clientIp = xClientIp
clientIpSource = 'x-client-ip'
}
const clientIpDisplay = clientIp || 'unknown'
// 检查 X-Forwarded-For 中是否有内网IP
let hasInternalIpInForwarded = false
let forwardedIpList: string[] = []
if (forwarded) {
forwardedIpList = forwarded.split(',').map(ip => ip.trim()).filter(ip => ip)
hasInternalIpInForwarded = forwardedIpList.some(ip => isInternalIP(ip))
}
// 判断是否为公网域名(非内网IP的域名,且不是localhost)
const hostnameIsIntranet = isInternalIPLegacy(hostname)
const isPublicDomain = !hostnameIsIntranet &&
hostname !== 'localhost' &&
hostname !== '127.0.0.1' &&
!hostname.match(/^192\.168\./) &&
!hostname.match(/^10\./) &&
!hostname.match(/^172\.(1[6-9]|2[0-9]|3[01])\./) &&
// 确保hostname不是IP格式(是域名)
!hostname.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)
const response = NextResponse.json({
success: true,
isIntranet,
isPublicDomain,
clientIp: clientIpDisplay,
clientIpSource,
hostname,
// 诊断信息(帮助排查问题)
debug: {
forwardedIpList,
hasInternalIpInForwarded,
realIp: realIp || null,
cfConnectingIp: cfConnectingIp || null,
xClientIp: xClientIp || null,
clientIpIsInternal: clientIp ? isInternalIP(clientIp) : null,
},
timestamp: new Date().toISOString()
})
// 添加CORS头,允许从任何来源访问(用于客户端直接检测内网访问)
const origin = request.headers.get('origin')
if (origin) {
response.headers.set('Access-Control-Allow-Origin', origin)
response.headers.set('Access-Control-Allow-Credentials', 'true')
response.headers.set('Access-Control-Allow-Methods', 'GET, OPTIONS')
response.headers.set('Access-Control-Allow-Headers', 'Content-Type')
}
return response
} catch (error) {
console.error('[Network Status] Error:', error)
return NextResponse.json(
{
success: false,
error: '网络检测失败'
},
{ status: 500 }
)
}
}
export async function POST(request: NextRequest) {
try {
// 接收测试数据并立即返回,用于测速
const data = await request.arrayBuffer()
return NextResponse.json({
status: 'ok',
receivedBytes: data.byteLength,
timestamp: new Date().toISOString()
})
} catch (error) {
return NextResponse.json(
{ error: '网络测试失败' },
{ status: 500 }
)
}
}