Docker images / prepare-release (push) Successful in 9s
Docker images / backend-test (push) Successful in 2m18s
Docker images / frontend-image (push) Successful in 4m37s
Docker images / updater-image (push) Successful in 46s
Docker images / backend-image (push) Successful in 3m6s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 16s
Сохранять снимки в S3-compatible бакет, скачивать и удалять их вместе с локальными файлами. Привести /backups к DNA /servers: Frame, KPI, фильтры и реальное восстановление. Co-authored-by: Cursor <[email protected]>
98 lines
3.5 KiB
TypeScript
98 lines
3.5 KiB
TypeScript
"use client"
|
||
|
||
import {
|
||
AlertDialog,
|
||
AlertDialogAction,
|
||
AlertDialogCancel,
|
||
AlertDialogContent,
|
||
AlertDialogDescription,
|
||
AlertDialogFooter,
|
||
AlertDialogHeader,
|
||
AlertDialogMedia,
|
||
AlertDialogTitle,
|
||
} from "@/components/ui/alert-dialog"
|
||
import { Alert, AlertDescription, AlertTitle } from "@/components/reui/alert"
|
||
import type { Backup } from "@/lib/data"
|
||
import { AlertCircleIcon, LoaderCircleIcon, TriangleAlertIcon } from "lucide-react"
|
||
|
||
export function BackupDeleteDialog({
|
||
backup,
|
||
busy,
|
||
onConfirm,
|
||
onCancel,
|
||
}: {
|
||
backup: Backup | null
|
||
busy: boolean
|
||
onConfirm: () => void
|
||
onCancel: () => void
|
||
}) {
|
||
return (
|
||
<AlertDialog open={Boolean(backup)} onOpenChange={(v) => { if (!v && !busy) onCancel() }}>
|
||
<AlertDialogContent>
|
||
<AlertDialogHeader>
|
||
<AlertDialogMedia className="bg-destructive/10 text-destructive">
|
||
<AlertCircleIcon />
|
||
</AlertDialogMedia>
|
||
<AlertDialogTitle>Удалить бэкап?</AlertDialogTitle>
|
||
<AlertDialogDescription>
|
||
Файл <span className="font-mono text-foreground">{backup?.filename}</span> будет удалён
|
||
{backup?.storage === "s3" || backup?.storage === "both" ? " локально и из S3" : " с диска"}.
|
||
</AlertDialogDescription>
|
||
</AlertDialogHeader>
|
||
<AlertDialogFooter>
|
||
<AlertDialogCancel disabled={busy} onClick={onCancel}>Отмена</AlertDialogCancel>
|
||
<AlertDialogAction variant="destructive" disabled={busy} onClick={onConfirm}>
|
||
{busy ? <LoaderCircleIcon className="size-4 animate-spin" /> : null}
|
||
Удалить
|
||
</AlertDialogAction>
|
||
</AlertDialogFooter>
|
||
</AlertDialogContent>
|
||
</AlertDialog>
|
||
)
|
||
}
|
||
|
||
export function BackupRestoreDialog({
|
||
backup,
|
||
busy,
|
||
onConfirm,
|
||
onCancel,
|
||
}: {
|
||
backup: Backup | null
|
||
busy: boolean
|
||
onConfirm: () => void
|
||
onCancel: () => void
|
||
}) {
|
||
return (
|
||
<AlertDialog open={Boolean(backup)} onOpenChange={(v) => { if (!v && !busy) onCancel() }}>
|
||
<AlertDialogContent>
|
||
<AlertDialogHeader>
|
||
<AlertDialogMedia className="bg-warning/10 text-warning">
|
||
<TriangleAlertIcon />
|
||
</AlertDialogMedia>
|
||
<AlertDialogTitle>Восстановить конфигурацию?</AlertDialogTitle>
|
||
<AlertDialogDescription className="flex flex-col gap-3">
|
||
<span>
|
||
Файл <span className="font-mono text-foreground">{backup?.filename}</span> будет загружен
|
||
на <span className="text-foreground">{backup?.server}</span> и импортирован.
|
||
</span>
|
||
<Alert variant="warning">
|
||
<TriangleAlertIcon />
|
||
<AlertTitle>Это изменит рабочую конфигурацию роутера</AlertTitle>
|
||
<AlertDescription>
|
||
Сессия может оборваться. Убедитесь, что выбран именно этот сервер и этот снимок.
|
||
</AlertDescription>
|
||
</Alert>
|
||
</AlertDialogDescription>
|
||
</AlertDialogHeader>
|
||
<AlertDialogFooter>
|
||
<AlertDialogCancel disabled={busy} onClick={onCancel}>Отмена</AlertDialogCancel>
|
||
<AlertDialogAction disabled={busy} onClick={onConfirm}>
|
||
{busy ? <LoaderCircleIcon className="size-4 animate-spin" /> : null}
|
||
Восстановить
|
||
</AlertDialogAction>
|
||
</AlertDialogFooter>
|
||
</AlertDialogContent>
|
||
</AlertDialog>
|
||
)
|
||
}
|