48 lines
3.0 KiB
TypeScript
48 lines
3.0 KiB
TypeScript
import type { MikrotikClient } from "./mikrotik.js"
|
|
|
|
type RosRow = { ".id"?: string; name?: string; interface?: string; dynamic?: string }
|
|
type Client = Pick<MikrotikClient, "get" | "delete">
|
|
|
|
/** RouterOS retains peer/address records when their WireGuard interface is removed. */
|
|
export async function deleteWireguardInterface(client: Client, rosId: string): Promise<void> {
|
|
const interfaces = await client.get<RosRow[]>("/interface/wireguard")
|
|
const target = interfaces.find(row => row[".id"] === rosId)
|
|
if (!target?.name) throw new Error("Интерфейс WireGuard не найден; удаление не выполнено")
|
|
const name = target.name
|
|
const belongs = (row: RosRow) => row.interface === name || row.interface === rosId
|
|
const [peers, addresses, ipv6Addresses] = await Promise.all([
|
|
client.get<RosRow[]>("/interface/wireguard/peers"),
|
|
client.get<RosRow[]>("/ip/address"),
|
|
client.get<RosRow[]>("/ipv6/address"),
|
|
])
|
|
if (![peers, addresses, ipv6Addresses].every(Array.isArray)) throw new Error("Не удалось прочитать все связанные объекты WireGuard")
|
|
const staticAddress = (row: RosRow) => row.dynamic !== "true" && row.dynamic !== "yes"
|
|
const groups = [
|
|
{ path: "/interface/wireguard/peers", rows: peers.filter(belongs) },
|
|
{ path: "/ip/address", rows: addresses.filter(belongs) },
|
|
{ path: "/ipv6/address", rows: ipv6Addresses.filter(staticAddress).filter(belongs) },
|
|
]
|
|
// Finish all reads and validate identities before the first destructive request.
|
|
if (groups.some(group => group.rows.some(row => !row[".id"]))) {
|
|
throw new Error("Не удалось определить связанные объекты WireGuard; удаление не выполнено")
|
|
}
|
|
for (const group of groups) {
|
|
for (const row of group.rows) await client.delete(`${group.path}/${encodeURIComponent(row[".id"]!)}`)
|
|
}
|
|
await client.delete(`/interface/wireguard/${encodeURIComponent(rosId)}`)
|
|
const [remainingInterfaces, remainingPeers, remainingAddresses, remainingIpv6Addresses] = await Promise.all([
|
|
client.get<RosRow[]>("/interface/wireguard"),
|
|
client.get<RosRow[]>("/interface/wireguard/peers"),
|
|
client.get<RosRow[]>("/ip/address"),
|
|
client.get<RosRow[]>("/ipv6/address"),
|
|
])
|
|
if (![remainingInterfaces, remainingPeers, remainingAddresses, remainingIpv6Addresses].every(Array.isArray)) throw new Error("RouterOS не подтвердил полное удаление WireGuard")
|
|
const hasRemaining = (rows: RosRow[], original: RosRow[]) => rows.some(row => belongs(row) || original.some(before => before[".id"] === row[".id"]))
|
|
if (remainingInterfaces.some(row => row[".id"] === rosId || row.name === name)
|
|
|| hasRemaining(remainingPeers, groups[0].rows)
|
|
|| hasRemaining(remainingAddresses, groups[1].rows)
|
|
|| hasRemaining(remainingIpv6Addresses.filter(staticAddress), groups[2].rows)) {
|
|
throw new Error("RouterOS не подтвердил полное удаление интерфейса WireGuard и связанных объектов")
|
|
}
|
|
}
|