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

+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
}
}