import express, { Request, Response } from 'express' import cors from 'cors' import rateLimit from 'express-rate-limit' import ipaddr from 'ipaddr.js' const app = express() const PORT = process.env.PORT || 3000 const GLOBALPING_API = 'https://api.globalping.io/v1' app.use(express.json()) app.use(cors({ origin: 'http://localhost:5173' })) app.use(rateLimit({ windowMs: 60 * 1000, limit: 10, message: { error: 'Rate limit exceeded' } })) interface GlobalPingLocation { country: string city?: string } const NODE_LOCATIONS: Record = { 'us-west': { country: 'US', city: 'San Francisco' }, 'us-east': { country: 'US', city: 'New York' }, 'europe': { country: 'DE', city: 'Frankfurt' }, 'asia': { country: 'JP', city: 'Tokyo' }, 'south-america': { country: 'BR', city: 'Sao Paulo' }, 'africa': { country: 'ZA', city: 'Cape Town' }, 'oceania': { country: 'AU', city: 'Sydney' } } function extractClientIp(req: Request): string | null { const forwarded = req.headers['x-forwarded-for'] const raw = typeof forwarded === 'string' ? forwarded.split(',')[0].trim() : req.socket.remoteAddress if (!raw) return null const normalized = raw.replace(/^::ffff:/, '') if (ipaddr.isValid(normalized)) return normalized return null } function isPublicIp(ip: string): boolean { if (!ipaddr.isValid(ip)) return false const parsed = ipaddr.parse(ip) return parsed.range() === 'unicast' } interface MeasurementResponse { id: string probesCount: number } interface MeasurementResult { id: string type: string status: 'in-progress' | 'finished' results?: Array<{ probe: { continent: string country: string city: string asn: number network: string } result: { status: string rawOutput: string stats?: { min: number max: number avg: number total: number loss: number } } }> } async function createMeasurement(target: string, location: GlobalPingLocation): Promise { const res = await fetch(`${GLOBALPING_API}/measurements`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip', 'User-Agent': 'LatencyTest/1.0.0' }, body: JSON.stringify({ type: 'ping', target, locations: [location], measurementOptions: { packets: 3 } }) }) if (!res.ok) { const error = await res.json().catch(() => ({})) as { error?: { message?: string } } throw new Error(error.error?.message || `GlobalPing API error: ${res.status}`) } const data = await res.json() as MeasurementResponse return data.id } async function getMeasurementResult(id: string, timeout = 30000): Promise<{ latency: number | null; success: boolean }> { const startTime = Date.now() while (Date.now() - startTime < timeout) { await new Promise(r => setTimeout(r, 500)) const res = await fetch(`${GLOBALPING_API}/measurements/${id}`, { headers: { 'Accept-Encoding': 'gzip', 'User-Agent': 'LatencyTest/1.0.0' } }) if (!res.ok) { throw new Error(`Failed to get measurement: ${res.status}`) } const data = await res.json() as MeasurementResult if (data.status !== 'in-progress') { const result = data.results?.[0]?.result if (result?.status === 'finished' && result.stats?.avg != null) { return { latency: Math.round(result.stats.avg), success: true } } return { latency: null, success: false } } } return { latency: null, success: false } } app.get('/api/ip', (req: Request, res: Response) => { const ip = extractClientIp(req) if (!ip) { return res.status(500).json({ error: 'Unable to determine IP' }) } res.json({ ip }) }) app.post('/api/latency', async (req: Request, res: Response) => { const { targetIp, nodeId } = req.body if (!targetIp || typeof targetIp !== 'string') { return res.status(400).json({ error: 'targetIp is required' }) } if (!nodeId || !NODE_LOCATIONS[nodeId]) { return res.status(400).json({ error: `Invalid nodeId. Available: ${Object.keys(NODE_LOCATIONS).join(', ')}` }) } if (!isPublicIp(targetIp)) { return res.status(400).json({ error: 'Invalid or private IP address' }) } try { const measurementId = await createMeasurement(targetIp, NODE_LOCATIONS[nodeId]) const { latency, success } = await getMeasurementResult(measurementId) res.json({ nodeId, latency, success }) } catch (error) { console.error('Latency test error:', error) res.status(500).json({ nodeId, latency: null, success: false }) } }) app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`) })