Files
BYW-VoiceHub/VoiceHub-main/server/utils/ip-utils.ts
T
Henry_Du 4ce4649aaf
Build VoiceHub FPK / build-fpk (push) Failing after 56m5s
Build and Push Docker Image / build-and-push (push) Canceled after 1h9m35s
初次提交
2026-09-06 08:19:02 +08:00

98 lines
2.2 KiB
TypeScript

import type { H3Event } from 'h3'
import { getHeaders } from 'h3'
/**
* 获取客户端IP地址
* @param event H3Event对象
* @returns 客户端IP地址
*/
export function getClientIP(event: H3Event): string {
// 尝试从各种可能的头部获取真实IP
const headers = getHeaders(event)
// 按优先级检查各种IP头部
const ipHeaders = [
'x-forwarded-for',
'x-real-ip',
'x-client-ip',
'cf-connecting-ip', // Cloudflare
'x-forwarded',
'forwarded-for',
'forwarded'
]
for (const header of ipHeaders) {
const value = headers[header]
if (value) {
// x-forwarded-for 可能包含多个IP,取第一个
const ip = Array.isArray(value) ? value[0] : value
const firstIP = ip.split(',')[0].trim()
if (firstIP && isValidIP(firstIP)) {
return firstIP
}
}
}
// 如果没有找到,直接从连接信息获取
// 从连接信息获取IP(备用方法)
const remoteAddress = event.node.req.socket?.remoteAddress
if (remoteAddress) {
// 移除IPv6映射的IPv4前缀
const cleanIP = remoteAddress.replace(/^::ffff:/, '')
if (isValidIP(cleanIP)) {
return cleanIP
}
}
return 'unknown'
}
/**
* 验证IP地址格式是否有效
* @param ip IP地址字符串
* @returns 是否为有效IP
*/
function isValidIP(ip: string): boolean {
// 简单的IP格式验证
const ipv4Regex =
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/
// 基本格式验证
if (!ipv4Regex.test(ip) && !ipv6Regex.test(ip)) {
return false
}
// 排除明显无效的IP
if (ip === '0.0.0.0' || ip === '::') {
return false
}
return true
}
/**
* 格式化IP地址用于邮件显示
* @param ip IP地址
* @returns 格式化后的IP地址字符串
*/
export function formatIPForEmail(ip: string): string {
if (!ip || ip === 'unknown') {
return '未知'
}
// 如果是本地IP,显示为本地访问
if (
ip === '127.0.0.1' ||
ip === '::1' ||
ip.startsWith('192.168.') ||
ip.startsWith('10.') ||
ip.startsWith('172.')
) {
return `${ip} (本地网络)`
}
return ip
}