75 lines
2.4 KiB
TypeScript

import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
interface IxMatrixQuery {
ix_id?: string;
}
async function fetchWithRetry(url: string, retries = 1, timeout = 10000): Promise<any> {
for (let i = 0; i <= retries; i++) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok) {
if (i === retries) throw new Error(`HTTP ${response.status}`);
continue;
}
return await response.json();
} catch (e) {
clearTimeout(timeoutId);
if (i === retries) throw e;
await new Promise(r => setTimeout(r, 1500));
}
}
}
export async function ixMatrixRoutes(fastify: FastifyInstance): Promise<void> {
fastify.get<{ Querystring: IxMatrixQuery }>(
'/ix-matrix',
async (request: FastifyRequest<{ Querystring: IxMatrixQuery }>, reply: FastifyReply) => {
let ixIdStr = request.query.ix_id || '';
const ixId = ixIdStr.replace(/[^0-9]/g, '');
reply.header('Access-Control-Allow-Origin', '*');
reply.header('Cache-Control', 'public, max-age=3600');
if (!ixId) {
return reply.status(400).send({ error: 'ix_id required' });
}
try {
const PEERINGDB_API_URL = 'https://www.peeringdb.com/api';
const [netixData, ixData] = await Promise.all([
fetchWithRetry(`${PEERINGDB_API_URL}/netixlan?ix_id=${ixId}&depth=1&limit=200`, 1, 15000),
fetchWithRetry(`${PEERINGDB_API_URL}/ix/${ixId}`, 1, 10000),
]);
const ix = ixData && ixData.data && ixData.data[0];
const members = ((netixData && netixData.data) || []).map((m: any) => ({
asn: m.asn,
name: m.name,
speed: m.speed,
ipaddr4: m.ipaddr4,
ipaddr6: m.ipaddr6,
policy: m.policy_general
}));
members.sort((a: any, b: any) => (b.speed || 0) - (a.speed || 0));
return reply.status(200).send({
ix_id: ixId,
ix_name: ix && ix.name,
ix_city: ix && ix.city,
members,
member_count: members.length
});
} catch (e: any) {
return reply.status(500).send({ error: e.message });
}
}
);
}