WuYongJie 5f2ba48532
Some checks failed
CI Check / check (push) Has been cancelled
优化模板管理器拖拽交互体验
- 移除高亮卡片效果,避免与虚位卡片视觉重复
- 松开鼠标后立即更新布局,不再等待动画完成
- 优化拖拽动画效果,使用弹性缓动函数
- 更新 CHANGELOG.md 记录本次优化
2025-11-20 11:24:25 +08:00

109 lines
3.1 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'
export const dynamic = 'force-dynamic'
/**
* 判断IP是否为内网地址
*/
function isIntranetIP(ip: string): boolean {
if (!ip) return false
// 处理可能存在的端口号
const cleanIP = ip.split(':')[0]
// localhost
if (cleanIP === '127.0.0.1' || cleanIP === 'localhost' || cleanIP === '::1') {
return true
}
// IPv4 内网IP段判断
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
const match = cleanIP.match(ipv4Regex)
if (match) {
const [, a, b, c, d] = match.map(Number)
// 验证IP有效性
if (a > 255 || b > 255 || c > 255 || d > 255) {
return false
}
// 10.0.0.0 - 10.255.255.255 (Class A)
if (a === 10) return true
// 172.16.0.0 - 172.31.255.255 (Class B)
if (a === 172 && b >= 16 && b <= 31) return true
// 192.168.0.0 - 192.168.255.255 (Class C)
if (a === 192 && b === 168) return true
// 169.254.0.0 - 169.254.255.255 (Link-local)
if (a === 169 && b === 254) return true
}
return false
}
/**
* 获取客户端真实IP地址
*/
function getClientIP(request: NextRequest): string {
// 优先级:x-real-ip > x-forwarded-for > connection
const xRealIP = request.headers.get('x-real-ip')
if (xRealIP) return xRealIP
const xForwardedFor = request.headers.get('x-forwarded-for')
if (xForwardedFor) {
// x-forwarded-for 可能包含多个IP,取第一个(客户端真实IP)
return xForwardedFor.split(',')[0].trim()
}
const cfConnectingIP = request.headers.get('cf-connecting-ip') // Cloudflare
if (cfConnectingIP) return cfConnectingIP
const trueClientIP = request.headers.get('true-client-ip') // Akamai
if (trueClientIP) return trueClientIP
// 如果都没有,返回默认值
return 'unknown'
}
/**
* GET /api/network-status
* 返回用户的网络环境状态
*/
export async function GET(request: NextRequest) {
try {
const clientIP = getClientIP(request)
const isIntranet = isIntranetIP(clientIP)
// 获取当前访问的域名
const hostname = request.headers.get('host') || ''
const publicDomains = (process.env.PUBLIC_DOMAINS || '').split(',').map(s => s.trim()).filter(Boolean)
const isPublicDomain = publicDomains.length > 0 ? publicDomains.includes(hostname) : false
console.log(`[Network Status] IP: ${clientIP}, isIntranet: ${isIntranet}, hostname: ${hostname}`)
return NextResponse.json({
success: true,
isIntranet,
isPublicDomain,
// 不返回完整IP地址(隐私保护)
ipMasked: clientIP.split('.').slice(0, 2).join('.') + '.xxx.xxx',
message: isIntranet
? '检测到内网访问'
: '检测到公网访问',
intranetUrl: isIntranet && isPublicDomain
? (process.env.NEXT_PUBLIC_INTRANET_URL || '')
: null
})
} catch (error) {
console.error('[Network Status] Error:', error)
return NextResponse.json({
success: false,
error: '网络状态检测失败'
}, { status: 500 })
}
}