47 lines
2.2 KiB
TypeScript
47 lines
2.2 KiB
TypeScript
import type { MikrotikClient } from "./mikrotik.js"
|
|
import { MikrotikError } from "./mikrotik.js"
|
|
import { parseRecursiveCatalog } from "./recursive-route-catalog.js"
|
|
|
|
export class RecursiveApplyError extends Error {
|
|
constructor(message: string, public readonly receipt: {
|
|
state: "partial" | "unknown" | "rejected" | "applied"
|
|
confirmedWrites: number
|
|
createdIds: string[]
|
|
deletedIds: string[]
|
|
needsReview: boolean
|
|
metadataRecorded: boolean
|
|
}) { super(message); this.name = "RecursiveApplyError" }
|
|
}
|
|
|
|
export async function executeRecursiveApply(
|
|
client: Pick<MikrotikClient, "get" | "put" | "delete">,
|
|
payloads: Record<string, string>[],
|
|
persist: () => Promise<void>,
|
|
) {
|
|
// The editor replaces every owned row: reject unsupported ownership before any write.
|
|
const existing = parseRecursiveCatalog(await client.get<unknown>("/ip/route"))
|
|
const createdIds: string[] = [], deletedIds: string[] = []
|
|
let nativeComplete = false
|
|
try {
|
|
for (const payload of payloads) {
|
|
const result = await client.put<unknown>("/ip/route", payload)
|
|
if (!result || typeof result !== "object" || !(".id" in result) || typeof result[".id"] !== "string" || !result[".id"].trim()) throw new Error("RouterOS не подтвердил ID созданного маршрута")
|
|
createdIds.push(result[".id"])
|
|
}
|
|
for (const route of existing) {
|
|
await client.delete("/ip/route/" + encodeURIComponent(route.id))
|
|
deletedIds.push(route.id)
|
|
}
|
|
nativeComplete = true
|
|
await persist()
|
|
return { pushed: createdIds.length, deleted: deletedIds.length }
|
|
} catch (error) {
|
|
const rejected = error instanceof MikrotikError && error.statusCode >= 400 && error.statusCode < 500
|
|
const confirmedWrites = createdIds.length + deletedIds.length
|
|
const state = nativeComplete ? "applied" : rejected ? (confirmedWrites ? "partial" : "rejected") : "unknown"
|
|
throw new RecursiveApplyError(`Подтверждено записей RouterOS: ${confirmedWrites}. Обновите каталог перед повторной попыткой. ${String(error)}`, {
|
|
state, confirmedWrites, createdIds, deletedIds, needsReview: state !== "rejected", metadataRecorded: false,
|
|
})
|
|
}
|
|
}
|