56 lines
1.7 KiB
TypeScript

import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
interface RpkiHistoryQuery {
asn?: string;
}
export async function rpkiHistoryRoutes(fastify: FastifyInstance): Promise<void> {
fastify.get<{ Querystring: RpkiHistoryQuery }>(
'/rpki-history',
async (request: FastifyRequest<{ Querystring: RpkiHistoryQuery }>, reply: FastifyReply) => {
let asnStr = request.query.asn || '';
const asn = asnStr.replace(/[^0-9]/g, '');
reply.header('Access-Control-Allow-Origin', '*');
reply.header('Cache-Control', 'public, max-age=3600');
if (!asn) {
return reply.status(400).send({ error: 'asn required' });
}
try {
const url = `https://stat.ripe.net/data/routing-history/data.json?resource=AS${asn}&max_rows=100`;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 6000);
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`RIPE Stat returned HTTP ${response.status}`);
}
const data = await response.json();
const byOrigin = (data && data.data && data.data.by_origin) || [];
const prefixes: any[] = [];
for (const orig of byOrigin) {
if (orig.prefixes) {
for (const pfx of orig.prefixes) {
prefixes.push(pfx);
}
}
}
return reply.status(200).send({
asn: asn,
prefixes: prefixes,
source: 'RIPE Stat routing-history'
});
} catch (e: any) {
return reply.status(500).send({ error: e.message });
}
}
);
}