fix(wireguard): учитывать неподтверждённую ревизию импорта

This commit is contained in:
Denozordec committed 2026-10-09 09:21:02 +07:00
1 parent 91ee424b42
commit 8eaf78b686
10 files changed
+84 -11

No files matched your search

+1 -1
View File
@@ -18,7 +18,7 @@
"test:probes": "tsx src/routes/probes.test.ts && tsx src/routes/probes.cancel.test.ts",
"test:exec": "tsx src/routes/exec.test.ts && tsx src/plugins/auth.exec.test.ts",
"test:auth": "tsx src/lib/permissions.test.ts && tsx src/plugins/auth.smoke.test.ts && tsx src/plugins/auth.ospf-optimizer.test.ts",
"test:wireguard": "npx tsx src/services/wireguard-config.test.ts && npx tsx --test src/services/wireguard-import.test.ts src/services/wireguard-import-apply.test.ts && npx tsx src/routes/wireguard-import.test.ts",
"test:wireguard": "npx tsx src/services/wireguard-config.test.ts && npx tsx --test src/services/wireguard-import.test.ts src/services/wireguard-import-apply.test.ts src/services/wireguard-import-revision.test.ts && npx tsx src/routes/wireguard-import.test.ts",
"test:traffic-rate": "tsx src/services/traffic-rate.test.ts",
"test:traffic-flow": "tsx src/services/traffic-flow-parse.test.ts && tsx src/services/traffic-flow-map-exporter.test.ts && tsx src/services/traffic-flow-ifaces.test.ts && tsx src/services/traffic-flow-ifindex.test.ts && tsx src/services/traffic-flow-dedup.test.ts && tsx src/services/traffic-flow-planes.test.ts && tsx src/services/traffic-flow-ip.test.ts && tsx src/services/traffic-flow-dest.test.ts && tsx src/services/traffic-flow-classify.test.ts && tsx src/services/traffic-flow-ripe.test.ts && tsx src/services/traffic-flow-brands.test.ts && tsx src/services/traffic-flow-ingest.test.ts && tsx src/services/traffic-flow-analytics.test.ts && tsx src/services/traffic-flow-map-hops.test.ts && tsx src/services/traffic-flow-purge.test.ts && tsx src/services/traffic-flow-geoip.test.ts && tsx src/services/traffic-flow-facts.test.ts && tsx src/services/traffic-flow-facts-filter.test.ts && tsx src/services/traffic-flow-facts-rebuild.test.ts && tsx src/services/statistics-aggregate.test.ts",
"test:users": "tsx src/modules/users/iface-type.test.ts && tsx src/modules/users/bindings.test.ts && tsx src/modules/users/interface-inventory.test.ts && tsx src/routes/users-catalog.test.ts",
+4 -5
View File
@@ -13,6 +13,7 @@ import {
import { MikrotikClient, MikrotikError } from "../services/mikrotik.js"
import { deleteWireguardInterface } from "../services/wireguard-delete.js"
import { applyWireguardImport, WireguardImportApplyError } from "../services/wireguard-import-apply.js"
import { confirmWireguardImportRevision } from "../services/wireguard-import-revision.js"
import { parseWireguardImport } from "../services/wireguard-import.js"
import {
generateMikrotikRsc,
@@ -111,7 +112,7 @@ async function recordWireguard(
server: NonNullable<Awaited<ReturnType<typeof getEnabledServerById>>>,
source: ConfigRevisionSource,
) {
await captureAndAppendRevision({
return captureAndAppendRevision({
serverId: server.id,
section: "wireguard",
source,
@@ -315,10 +316,8 @@ const wireguardRoutes: FastifyPluginAsyncZod = async (app) => {
const client = MikrotikClient.fromServer(server)
try {
const applied = await applyWireguardImport(client, config, (peer, interfaceName) => peerToRosBody({ ...peer, interfaceName }))
try {
await recordWireguard(server, "copy")
} catch {
return reply.status(502).send({ error: "Конфигурация применена, но ревизия не сохранена. Обновите данные перед повтором", state: "applied", preview })
if (!(await confirmWireguardImportRevision(() => recordWireguard(server, "copy")))) {
return reply.status(502).send({ error: "Конфигурация применена, но сохранение ревизии не подтверждено. Обновите данные перед повтором", state: "applied", preview })
}
return reply.send({ dryRun: false, preview, applied })
} catch (error) {
@@ -0,0 +1,26 @@
import assert from "node:assert/strict"
import { test } from "node:test"
import { confirmWireguardImportRevision } from "./wireguard-import-revision.js"
import { captureAndAppendRevision } from "./config-revisions.js"
test("a new or unchanged revision confirms successful persistence", async () => {
for (const created of [true, false]) {
assert.equal(await confirmWireguardImportRevision(async () => ({ created, revision: { id: "existing-or-created" } })), true)
}
})
test("null and missing receipts do not confirm a revision", async () => {
for (const receipt of [null, undefined]) assert.equal(await confirmWireguardImportRevision(async () => receipt), false)
})
test("an exception does not confirm persistence", async () => {
assert.equal(await confirmWireguardImportRevision(async () => { throw new Error("Persistence unavailable") }), false)
})
test("real snapshot helper swallowing a capture error must fail revision confirmation", async () => {
let calls = 0
const capture = async () => { calls++; throw new Error("Snapshot unavailable") }
const receipt = () => captureAndAppendRevision({ serverId: 1, section: "wireguard", source: "copy", capture })
assert.equal(await confirmWireguardImportRevision(receipt), false)
assert.equal(calls, 1)
})
@@ -0,0 +1,8 @@
/** A swallowed snapshot/persistence error returns null; it is not an audit receipt. */
export async function confirmWireguardImportRevision(save: () => Promise<{ created: boolean; revision: unknown } | null | undefined>): Promise<boolean> {
try {
return (await save()) != null
} catch {
return false
}
}