gongxiangp2/lib/network.ts

202 lines
5.9 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.

// 网络管理:智能检测内外网
// 判断是否为内网IP地址
function isInternalIP(hostname: string): boolean {
// 如果是localhost
if (hostname === 'localhost' || hostname === '127.0.0.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
}
function getCurrentHost(): string {
if (typeof window === 'undefined') return ''
return window.location.host
}
function getCurrentHostname(): string {
if (typeof window === 'undefined') return ''
return window.location.hostname
}
export interface NetworkStatus {
isInternal: boolean
host: string
lastChecked?: number // 上次检测时间戳
}
let currentNetwork: NetworkStatus | null = null
let networkChangeListeners: ((status: NetworkStatus) => void)[] = []
// 网络状态缓存有效期(5分钟)
const NETWORK_CACHE_TTL = 5 * 60 * 1000
// 初始化网络检测(优先使用后端API获取准确状态)
export async function initializeNetwork(forceRefresh: boolean = false): Promise<NetworkStatus> {
if (typeof window === 'undefined') {
return { isInternal: false, host: '' }
}
// 如果已经检测过且缓存未过期,直接返回缓存结果
if (!forceRefresh && currentNetwork && currentNetwork.lastChecked) {
const cacheAge = Date.now() - currentNetwork.lastChecked
if (cacheAge < NETWORK_CACHE_TTL) {
return currentNetwork
}
}
const host = getCurrentHost()
const hostname = getCurrentHostname()
const previousStatus = currentNetwork?.isInternal
// 优先调用后端API获取准确的网络状态(后端可以通过请求头获取真实的客户端IP)
try {
const response = await fetch('/api/network-status', {
cache: 'no-store', // 不缓存,确保获取最新状态
})
if (response.ok) {
const data = await response.json()
if (data.success) {
currentNetwork = {
isInternal: data.isIntranet,
host,
lastChecked: Date.now()
}
// 不输出IP和Hostname信息,避免在浏览器控制台暴露内网信息
if (data.isIntranet) {
console.log(`✅ 检测到内网访问`)
} else {
console.log(`🌐 检测到公网访问`)
}
// 如果网络状态发生变化,通知监听器
if (previousStatus !== undefined && previousStatus !== data.isIntranet) {
console.log(`⚠️ 网络状态变化: ${previousStatus ? '内网' : '外网'} → ${data.isIntranet ? '内网' : '外网'}`)
notifyNetworkChange(currentNetwork)
}
return currentNetwork
}
}
} catch (error) {
console.warn('后端网络检测失败,使用前端判断:', error)
}
// 如果后端API调用失败,回退到前端判断(基于hostname)
const isInternal = isInternalIP(hostname)
currentNetwork = {
isInternal,
host,
lastChecked: Date.now()
}
// 不输出hostname信息,避免在浏览器控制台暴露内网信息
if (isInternal) {
console.log(`✅ 检测到内网访问`)
} else {
console.log(`🌐 检测到公网访问`)
}
// 如果网络状态发生变化,通知监听器
if (previousStatus !== undefined && previousStatus !== isInternal) {
console.log(`⚠️ 网络状态变化: ${previousStatus ? '内网' : '外网'} → ${isInternal ? '内网' : '外网'}`)
notifyNetworkChange(currentNetwork)
}
return currentNetwork
}
// 获取当前网络状态
export function getNetworkStatus(): NetworkStatus {
if (!currentNetwork) {
const host = getCurrentHost()
const hostname = getCurrentHostname()
const isInternal = isInternalIP(hostname)
return { isInternal, host }
}
return currentNetwork
}
// 获取API基础URL
export function getApiBaseUrl(): string {
if (!currentNetwork) {
return '' // 使用相对路径
}
return currentNetwork.isInternal ? `http://${currentNetwork.host}` : ''
}
// 清除缓存(用于刷新网络状态)
export function clearNetworkCache() {
currentNetwork = null
}
// 添加网络状态变化监听器
export function addNetworkChangeListener(listener: (status: NetworkStatus) => void) {
networkChangeListeners.push(listener)
return () => {
networkChangeListeners = networkChangeListeners.filter(l => l !== listener)
}
}
// 通知所有监听器网络状态变化
function notifyNetworkChange(status: NetworkStatus) {
networkChangeListeners.forEach(listener => {
try {
listener(status)
} catch (error) {
console.error('网络状态变化监听器执行失败:', error)
}
})
}
// 设置网络变化监听(监听浏览器online/offline事件)
export function setupNetworkChangeDetection() {
if (typeof window === 'undefined') return
// 监听浏览器网络状态变化
window.addEventListener('online', () => {
console.log('🌐 网络恢复连接,重新检测网络状态...')
initializeNetwork(true)
})
window.addEventListener('offline', () => {
console.log('❌ 网络断开连接')
})
// 监听页面可见性变化(用户切换回页面时重新检测)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible' && currentNetwork) {
const cacheAge = Date.now() - (currentNetwork.lastChecked || 0)
// 如果缓存超过1分钟,重新检测
if (cacheAge > 60 * 1000) {
console.log('📡 页面恢复可见,重新检测网络状态...')
initializeNetwork(true)
}
}
})
}