Files
MikrotikManager/backend/src/services/wireguard-delete.ts
T

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 и связанных объектов")
}
}