Files
MikrotikManager/backend/src/services/ospf-template-flags.ts
T

52 lines
3.9 KiB
TypeScript

import { observedBoolean } from "./ospf-observed-values.js"
/** Resolve flags with native getters or a complete complementary switch-query partition; never invent defaults. */
export async function readOspfTemplateFlags<T extends { ".id": string; disabled?: string; passive?: string }>(payload: unknown, readProperty: (id: string, key: "disabled" | "passive") => Promise<unknown>, beforeRead: () => Promise<unknown> = async () => {}, readPassiveMembership?: () => Promise<{ present: unknown; absent: unknown }>) {
if (!Array.isArray(payload) || payload.length > 10000 || Buffer.byteLength(JSON.stringify(payload)) > 1048576 || payload.some(row => !row || typeof row !== "object" || Array.isArray(row) || typeof row[".id"] !== "string" || !row[".id"])) throw new Error("Недостоверный каталог флагов OSPF")
if (new Set(payload.map(row => row[".id"])).size !== payload.length) throw new Error("Повторяющиеся ID шаблонов OSPF")
const rows: T[] = payload.map(row => ({ ...row }))
const missing: { row: T; key: "disabled" | "passive" }[] = []
const unsetPassive: T[] = []
for (const row of rows) for (const key of ["disabled", "passive"] as const) {
const raw = row[key], value = observedBoolean(raw)
if (value !== null) row[key] = String(value)
else {
if (raw !== undefined && raw !== "" || !/^\*[a-z0-9]+$/i.test(row[".id"])) throw new Error("Не подтверждён флаг шаблона OSPF")
if (key === "passive" && raw === undefined && readPassiveMembership) { unsetPassive.push(row); continue }
missing.push({ row, key })
}
}
if (missing.length > 128) throw new Error("Превышен бюджет уточнения флагов OSPF")
let cursor = 0, failed = false
await Promise.all(Array.from({ length: Math.min(4, missing.length) }, async () => {
while (!failed && cursor < missing.length) {
const { row, key } = missing[cursor++]
try {
await beforeRead()
if (failed) return
const response = await readProperty(row[".id"], key)
const value = response && typeof response === "object" && "ret" in response && typeof response.ret === "string" ? observedBoolean(response.ret) : null
if (value === null) throw new Error("Не подтверждён effective флаг шаблона OSPF")
row[key] = String(value)
} catch (error) { failed = true; throw error }
}
}))
if (unsetPassive.length) {
if (!readPassiveMembership || missing.length + 2 > 128) throw new Error("Не подтверждён unset passive шаблона OSPF")
await beforeRead()
const membership = await readPassiveMembership()
const ids = (value: unknown) => {
if (!Array.isArray(value) || value.length > 10000 || Buffer.byteLength(JSON.stringify(value)) > 1048576 || value.some(row => !row || typeof row !== "object" || typeof row[".id"] !== "string" || !/^\*[a-z0-9]+$/i.test(row[".id"]))) throw new Error("Недостоверный ответ native switch query OSPF")
const result = new Set<string>(value.map(row => row[".id"]))
if (result.size !== value.length) throw new Error("Повторяющиеся ID native switch query OSPF")
return result
}
const present = ids(membership.present), absent = ids(membership.absent), all = new Set([...present, ...absent])
if ([...present].some(id => absent.has(id)) || all.size !== rows.length || rows.some(row => !all.has(row[".id"]))) throw new Error("Native switch query OSPF неполон, противоречив или каталог изменился")
if (rows.some(row => row.passive === "true" && !present.has(row[".id"]))) throw new Error("Native switch query противоречит подтверждённому passive")
for (const row of unsetPassive) row.passive = present.has(row[".id"]) ? "true" : "false"
await beforeRead()
}
return rows
}