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';
}