fix(backups): подтверждать настройки и последнюю проверку S3

This commit is contained in:
Denozordec committed 2026-10-01 15:37:00 +07:00
1 parent 8e01666e0e
commit 30bd479a6f
14 files changed
+208 -64

No files matched your search

+16 -6
View File
@@ -5,7 +5,7 @@ import { PageHeader } from "@/components/page-header"
import { backups as mockBackups, servers as mockServers, type Backup, type Server } from "@/lib/data"
import { createFilterQuery } from "@/components/reui/filters/filters-query"
import type { FilterQuery } from "@/components/reui/filters/filters-types"
import type { BackupScheduleSettingsDto, BackupStorageSettingsDto } from "@mmapp/contracts/backups"
import { backupStorageSettingsDtoSchema, type BackupScheduleSettingsDto, type BackupStorageSettingsDto } from "@mmapp/contracts/backups"
import { KpiStatGrid } from "@/components/reui-kit/kpi-stat-grid"
import { Alert, AlertAction, AlertDescription, AlertTitle } from "@/components/reui/alert"
import { Button } from "@/components/ui/button"
@@ -36,7 +36,7 @@ import {
testBackupStorage,
type BackupItem,
} from "@/shared/api/backups"
import { requestJson, requestBlob } from "@/shared/api/http-client"
import { ApiClientError, requestJson, requestBlob } from "@/shared/api/http-client"
import { toast } from "sonner"
import { backupSchedulePayload, backupStoragePayload } from "@/lib/backup-settings"
import { BackupsHistory } from "@/components/backups/backups-history"
@@ -95,6 +95,7 @@ function BackupsContent() {
const [syncBusy, setSyncBusy] = useState(false)
const [schedule, setSchedule] = useState<BackupScheduleForm>(defaultSchedule)
const [storage, setStorage] = useState<BackupStorageForm>(defaultStorageForm)
const [storageIsDirty, setStorageIsDirty] = useState(false)
const [savedSchedule, setSavedSchedule] = useState<BackupScheduleSettingsDto | null>(null)
const [savedStorage, setSavedStorage] = useState<BackupStorageSettingsDto | null>(null)
const [scheduleLastError, setScheduleLastError] = useState<string | null>(null)
@@ -108,7 +109,7 @@ function BackupsContent() {
const [restoreTarget, setRestoreTarget] = useState<Backup | null>(null)
const setSched = <K extends keyof BackupScheduleForm>(k: K, v: BackupScheduleForm[K]) => { scheduleDirty.current = true; setSchedule(s => ({ ...s, [k]: v })) }
const setStore = <K extends keyof BackupStorageForm>(k: K, v: BackupStorageForm[K]) => { storageDirty.current = true; setStorage(s => ({ ...s, [k]: v })) }
const setStore = <K extends keyof BackupStorageForm>(k: K, v: BackupStorageForm[K]) => { if (k !== "showPassword") { storageDirty.current = true; setStorageIsDirty(true) } setStorage(s => ({ ...s, [k]: v })) }
const mapApiBackupToUi = useCallback((b: BackupItem): Backup => {
const kb = Math.round(b.sizeBytes / 1024)
@@ -199,9 +200,9 @@ function BackupsContent() {
if (!canWrite || !current("storage")) return
const abort = startSettingsMutation(); if (!abort) return
try {
const result = await putBackupStorageSettings(backendUrl, backupStoragePayload(storage), { signal: abort.signal })
const result = await putBackupStorageSettings(backendUrl, backupStoragePayload(storage, savedStorage?.secretConfigured), { signal: abort.signal })
if (!alive.current || abort.signal.aborted) return
setSavedStorage(result); setStorage(prev => ({ ...prev, ...result, s3SecretAccessKey: "" })); storageDirty.current = false; settingsConfirmed("storage")
setSavedStorage(result); setStorage(prev => ({ ...prev, ...result, s3SecretAccessKey: "" })); storageDirty.current = false; setStorageIsDirty(false); settingsConfirmed("storage")
toast.success("Хранилище сохранено")
} catch (error) { if (alive.current && !abort.signal.aborted) setOpError(userApiError(error)) }
finally { finishSettingsMutation(abort) }
@@ -218,7 +219,15 @@ function BackupsContent() {
setSavedStorage(result); settingsConfirmed("storage")
if (result.lastTestError || !result.lastTestAt || result.provider !== "s3") throw new Error(result.lastTestError || "Проверка S3 не подтверждена")
toast.success("Доступ к S3 подтверждён")
} catch (error) { if (alive.current && !abort.signal.aborted) setOpError(userApiError(error)) }
} catch (error) {
if (alive.current && !abort.signal.aborted) {
if (error instanceof ApiClientError && error.payload && typeof error.payload === "object" && "settings" in error.payload) {
const parsed = backupStorageSettingsDtoSchema.safeParse(error.payload.settings)
if (parsed.success) setSavedStorage(parsed.data)
}
setOpError(userApiError(error))
}
}
finally { finishSettingsMutation(abort) }
}
async function handleSyncStorage() {
@@ -522,6 +531,7 @@ function BackupsContent() {
onSaveStorage={handleSaveStorage}
scheduleKnown={known("schedule")}
storageKnown={known("storage")}
storageDirty={storageIsDirty}
scheduleWritable={canWrite && canReadServers && current("schedule") && current("servers")}
storageWritable={canWrite && current("storage")}
allEnabled={allEnabled}