fix(backups): сверять сервер и файл подтверждения импорта

This commit is contained in:
Denozordec committed 2026-10-01 16:18:37 +07:00
1 parent af86d2c3cd
commit 99ff5aebaf
11 files changed
+51 -13

No files matched your search

+12 -1
View File
@@ -75,7 +75,7 @@ test("restore, S3 test and sync do not accept empty or malformed success", async
const original = globalThis.fetch
try {
globalThis.fetch = async () => json({}); await assert.rejects(() => restoreBackup(base, "backup-1")); await assert.rejects(() => testBackupStorage(base)); await assert.rejects(() => syncBackupsFromStorage(base))
globalThis.fetch = async () => json({ filename: "config.rsc", serverName: "router" }); assert.equal((await restoreBackup(base, "backup-1")).filename, "config.rsc")
globalThis.fetch = async () => json({ backupId: "backup-1", serverId: 1, filename: "config.rsc", serverName: "router" }); assert.equal((await restoreBackup(base, "backup-1")).filename, "config.rsc")
globalThis.fetch = async () => json(storage); assert.equal((await testBackupStorage(base)).secretConfigured, false)
globalThis.fetch = async () => json({ imported: 0, skipped: 0 }); assert.equal((await syncBackupsFromStorage(base)).imported, 0)
globalThis.fetch = async () => json({ imported: -1, skipped: 0 }); await assert.rejects(() => syncBackupsFromStorage(base))
@@ -105,3 +105,14 @@ test("download encodes identity and refuses HTML/JSON success bodies", async ()
globalThis.fetch = async () => new Response("config", { headers: { "Content-Type": "text/plain; charset=utf-8" } }); assert.equal(await (await downloadBackup(base, "backup-1")).text(), "config")
} finally { globalThis.fetch = original }
})
test("restore receipt matches backup and device identity", async () => {
const original = globalThis.fetch
try {
const receipt = { backupId: "backup-1", serverId: 1, filename: "config.rsc", serverName: "renamed router" }
for (const patch of [{ backupId: "other" }, { serverId: 2 }, { backupId: undefined }, { serverId: undefined }, { serverId: "1" }]) {
globalThis.fetch = async () => json({ ...receipt, ...patch }); await assert.rejects(() => restoreBackup(base, "backup-1", undefined, 1))
}
globalThis.fetch = async () => json(receipt); assert.equal((await restoreBackup(base, "backup-1", undefined, 1)).serverName, "renamed router")
} finally { globalThis.fetch = original }
})
+4 -2
View File
@@ -42,8 +42,10 @@ export async function deleteBackup(baseUrl: string, id: string, options?: Reques
const response = await requestBlob(baseUrl, "/api/backups/" + encodeURIComponent(id), backupOptions({ ...options, method: "DELETE" }))
if (response.status !== 204) throw new Error("Удаление резервной копии не подтверждено: ожидается HTTP 204")
}
export async function restoreBackup(baseUrl: string, id: string, options?: RequestInit) {
return restoreBackupResponseSchema.parse(await backupJson(baseUrl, "/api/backups/" + encodeURIComponent(id) + "/restore", { ...options, method: "POST" }))
export async function restoreBackup(baseUrl: string, id: string, options?: RequestInit, expectedServerId?: number) {
const result = restoreBackupResponseSchema.parse(await backupJson(baseUrl, "/api/backups/" + encodeURIComponent(id) + "/restore", { ...options, method: "POST" }))
if (result.backupId !== id || (expectedServerId !== undefined && result.serverId !== expectedServerId)) throw new Error("Ответ восстановления относится к другой резервной копии или серверу")
return result
}
export async function getBackupScheduleSettings(baseUrl: string, options?: RequestInit) {
return backupScheduleSettingsDtoSchema.parse(await backupJson(baseUrl, "/api/backups/schedule", { ...options, method: "GET" }))