Spaces:
Build error
Build error
File size: 3,479 Bytes
ef73937 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | import axios from 'axios';
import { config } from '@config/index.js';
interface VPNAPIResponse {
security: {
is_vpn: boolean;
is_proxy: boolean;
is_tor: boolean;
is_relay: boolean;
is_datacenter: boolean;
};
network: {
asn: number;
org: string;
};
}
const VPN_CHECK_CACHE = new Map<string, { result: boolean; timestamp: number }>();
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
export async function checkVPN(ip: string): Promise<{
isVPN: boolean;
isProxy: boolean;
isDatacenter: boolean;
isTOR: boolean;
details?: string;
}> {
if (!config.VPN_DETECTION_ENABLED) {
return { isVPN: false, isProxy: false, isDatacenter: false, isTOR: false };
}
// Skip local/private IPs
if (isPrivateIP(ip)) {
return { isVPN: false, isProxy: false, isDatacenter: false, isTOR: false };
}
// Check cache
const cached = VPN_CHECK_CACHE.get(ip);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return { isVPN: cached.result, isProxy: false, isDatacenter: false, isTOR: false };
}
try {
let result: { isVPN: boolean; isProxy: boolean; isDatacenter: boolean; isTOR: boolean; details?: string };
if (config.IPAPI_KEY) {
result = await checkWithIPAPI(ip);
} else {
result = await checkWithFreeAPI(ip);
}
VPN_CHECK_CACHE.set(ip, { result: result.isVPN || result.isProxy || result.isDatacenter, timestamp: Date.now() });
return result;
} catch (error) {
// On error, allow the request (fail open)
console.warn(`VPN check failed for ${ip}:`, error);
return { isVPN: false, isProxy: false, isDatacenter: false, isTOR: false, details: 'Check failed' };
}
}
async function checkWithIPAPI(ip: string) {
const response = await axios.get(`http://ip-api.com/json/${ip}?fields=status,proxy,hosting,as,org`, {
timeout: 5000,
headers: { 'User-Agent': 'WhatsApp-Channel-Directory/1.0' },
});
if (response.data.status !== 'success') {
throw new Error('IP API request failed');
}
return {
isVPN: false,
isProxy: response.data.proxy === true,
isDatacenter: response.data.hosting === true,
isTOR: false,
details: `ASN: ${response.data.as}, Org: ${response.data.org}`,
};
}
async function checkWithFreeAPI(ip: string): Promise<{
isVPN: boolean;
isProxy: boolean;
isDatacenter: boolean;
isTOR: boolean;
details?: string;
}> {
// Use a free API or local list - for now, return safe defaults
// In production, integrate with a proper VPN detection service
return {
isVPN: false,
isProxy: false,
isDatacenter: false,
isTOR: false,
details: 'Free tier - limited detection',
};
}
function isPrivateIP(ip: string): boolean {
const privateRanges = [
/^10\./,
/^192\.168\./,
/^172\.(1[6-9]|2[0-9]|3[0-1])\./,
/^127\./,
/^::1$/,
/^fe80::/,
/^fc00:/,
/^fd00:/,
];
return privateRanges.some(range => range.test(ip));
}
export function getClientIP(request: { headers: Record<string, string | string[] | undefined>; ip?: string }): string {
// Check various headers for real IP (behind proxy)
const forwarded = request.headers['x-forwarded-for'];
if (forwarded) {
const ips = (Array.isArray(forwarded) ? forwarded[0] : forwarded).split(',').map(ip => ip.trim());
return ips[0];
}
const realIP = request.headers['x-real-ip'];
if (realIP) {
return Array.isArray(realIP) ? realIP[0] : realIP;
}
return request.ip || 'unknown';
} |