75 lines
2.4 KiB
TypeScript
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 });
|
|
}
|
|
}
|
|
);
|
|
}
|