Files
MikrotikManager/backend/src/services/evobgp-client.ts
T

42 lines
2.6 KiB
TypeScript

/** Fixed GET, no redirects or response-body errors containing credentials.
* Both headers and the entire response body share one deadline and byte bound. */
type EvoRequestOptions = { timeoutMs?: number; maxBytes?: number; signal?: AbortSignal }
export function fetchBoundedEvoCatalog(root: string, key: string, fetcher: typeof fetch = fetch, options: EvoRequestOptions = {}): Promise<unknown> {
return fetchBoundedEvoJson(root, "/v1/router-lists/catalog", key, fetcher, options)
}
export function fetchBoundedEvoModules(root: string, key: string, fetcher: typeof fetch = fetch, options: EvoRequestOptions = {}): Promise<unknown> {
return fetchBoundedEvoJson(root, "/v1/modules?limit=1", key, fetcher, { ...options, maxBytes: Math.min(options.maxBytes ?? 1024 * 1024, 1024 * 1024) })
}
async function fetchBoundedEvoJson(root: string, path: string, key: string, fetcher: typeof fetch, options: EvoRequestOptions): Promise<unknown> {
const controller = new AbortController(), timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 15000)
const cancel = () => controller.abort()
options.signal?.addEventListener("abort", cancel, { once: true })
try {
if (options.signal?.aborted) throw new Error("Запрос отменён")
const response = await fetcher(`${root}${path}`, { method: "GET", redirect: "error", headers: { Authorization: `Bearer ${key}`, Accept: "application/json" }, signal: controller.signal })
if (!response.ok || !response.body) {
await response.body?.cancel().catch(() => {})
throw new Error("Ответ EvoBGP не подтверждён")
}
const maxBytes = options.maxBytes ?? 8 * 1024 * 1024
if (Number(response.headers.get("content-length")) > maxBytes) {
await response.body.cancel().catch(() => {})
throw new Error("Ответ EvoBGP превышает предел размера")
}
const reader = response.body.getReader(), chunks: Buffer[] = []
let received = 0
try {
for (;;) {
const { done, value } = await reader.read()
if (done) break
received += value.byteLength
if (received > maxBytes) { await reader.cancel().catch(() => {}); throw new Error("Ответ EvoBGP превышает предел размера") }
chunks.push(Buffer.from(value))
}
} finally { reader.releaseLock() }
return JSON.parse(Buffer.concat(chunks, received).toString("utf8"))
} catch {
throw new Error("Каталог EvoBGP недоступен, повреждён или превышает ограничения запроса")
} finally { clearTimeout(timer); options.signal?.removeEventListener("abort", cancel) }
}