fix(backups): показывать частичный результат внутри формы
This commit is contained in:
1 parent
ad457b90a9
commit
98b4c39a00
18 files changed
+112
-16
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { test } from "node:test"
|
||||
import { pollBackupJob, backupJobOutcome, waitForBackupPoll } from "./backup-job-poll"
|
||||
import { pollBackupJob, backupJobOutcome, backupJobResultError, waitForBackupPoll } from "./backup-job-poll"
|
||||
import type { BackupJobStatusResponse } from "@mmapp/contracts/backups"
|
||||
const target = { serverIds: ["1"], notes: "manual" }
|
||||
const baseJob: BackupJobStatusResponse = { id: "job", status: "queued", serverIds: ["1"], notes: "manual", total: 1, completed: 0, requestedAt: "2026-10-01T00:00:00Z", created: [], failures: [] }
|
||||
@@ -30,3 +30,12 @@ test("cancellation interrupts delay and suppresses late observation", async () =
|
||||
await assert.rejects(() => pollBackupJob("base", "job", target, { signal: late.signal, onJob: () => { calls++ }, readJob: async () => { late.abort(); return baseJob } }))
|
||||
assert.equal(calls, 0)
|
||||
})
|
||||
|
||||
test("saved terminal job reconstructs partial errors without a previous hook error", () => {
|
||||
assert.equal(backupJobResultError(baseJob), null)
|
||||
const success: BackupJobStatusResponse = { ...baseJob, status: "done", completed: 1, created: [file] }
|
||||
assert.equal(backupJobResultError(success), null)
|
||||
assert.match(backupJobResultError({ ...success, created: [{ ...file, uploadError: "S3 unavailable" }] }) ?? "", /config.rsc: S3 unavailable/)
|
||||
assert.match(backupJobResultError({ ...success, created: [], failures: [{ serverId: "1", error: "offline" }] }) ?? "", /Создано 0 из 1.*1: offline/)
|
||||
assert.match(backupJobResultError({ ...baseJob, status: "failed" }) ?? "", /Задача завершилась с ошибкой/)
|
||||
})
|
||||
@@ -27,3 +27,10 @@ export function backupJobOutcome(job: BackupJobStatusResponse) {
|
||||
const uploadFailures = job.created.filter(item => item.uploadError)
|
||||
return { fullSuccess: job.status === "done" && job.created.length === job.total && !job.failures.length && !uploadFailures.length, created: job.created.length, deviceFailures: job.failures, uploadFailures }
|
||||
}
|
||||
|
||||
export function backupJobResultError(job: BackupJobStatusResponse): string | null {
|
||||
const outcome = backupJobOutcome(job)
|
||||
if (!["done", "failed"].includes(job.status) || outcome.fullSuccess) return null
|
||||
const failures = [...outcome.deviceFailures.map(failure => failure.serverId + ": " + failure.error), ...outcome.uploadFailures.map(file => file.filename + ": " + file.uploadError)]
|
||||
return "Создано " + outcome.created + " из " + job.total + ". " + (failures.join("; ") || "Задача завершилась с ошибкой")
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from "react"
|
||||
import type { CreateBackupRequest } from "@mmapp/contracts/backups"
|
||||
import { ApiClientError } from "@/shared/api/http-client"
|
||||
import { createBackupsAsync } from "@/shared/api/backups"
|
||||
import { pollBackupJob, backupJobOutcome } from "@/lib/backup-job-poll"
|
||||
import { pollBackupJob, backupJobOutcome, backupJobResultError } from "@/lib/backup-job-poll"
|
||||
import { backupJobStorageKey, createBackupJobCheckpoint, readBackupJobCheckpoint, type BackupJobCheckpoint } from "@/lib/backup-job-checkpoint"
|
||||
import { userApiError } from "@/shared/api/users"
|
||||
import { toast } from "sonner"
|
||||
@@ -50,10 +50,7 @@ export function useBackupJob(backendUrl: string, live: boolean, canRead: boolean
|
||||
if (!active()) return
|
||||
const outcome = backupJobOutcome(result)
|
||||
if (outcome.fullSuccess) toast.success("API подтвердил создание всех резервных копий", { id: "backup-job-" + result.id })
|
||||
else {
|
||||
const failures = [...outcome.deviceFailures.map(failure => failure.serverId + ": " + failure.error), ...outcome.uploadFailures.map(file => file.filename + ": " + file.uploadError)]
|
||||
setError("Создано " + outcome.created + " из " + result.total + ". " + (failures.join("; ") || "Задача завершилась с ошибкой"))
|
||||
}
|
||||
else setError(backupJobResultError(result))
|
||||
await onDone()
|
||||
} catch (reason) {
|
||||
if (!active()) return
|
||||
|
||||
Reference in new issue
Block a user