fix(ui): подтверждать удаление объектов IPsec
This commit is contained in:
1 parent
74e7cd257d
commit
611f31875e
6 files changed
+113
-49
No files matched your search
+22
-41
@@ -191,6 +191,9 @@ export default function IpsecPage() {
|
||||
const [peerOpen, setPeerOpen] = useState(false)
|
||||
const [editingPeer, setEditingPeer] = useState<{ serverId: string; peer: IpsecPeerDto } | null>(null)
|
||||
const [pendingDelete, setPendingDelete] = useState<PendingDelete | null>(null)
|
||||
const deleteLock = useRef(false)
|
||||
const [deleteError, setDeleteError] = useState<string | null>(null)
|
||||
const [forceAllowed, setForceAllowed] = useState(false)
|
||||
const [historyOpen, setHistoryOpen] = useState(false)
|
||||
const [revisions, setRevisions] = useState<ConfigRevisionDto[]>([])
|
||||
const [historyLoading, setHistoryLoading] = useState(false)
|
||||
@@ -476,57 +479,32 @@ export default function IpsecPage() {
|
||||
: exportIpsecUserCert(backendUrl, certClient!.serverId, certClient!.rosId, passphrase))
|
||||
}
|
||||
|
||||
const confirmDelete = async () => {
|
||||
if (!pendingDelete) return
|
||||
const confirmDelete = async (force = false) => {
|
||||
if (!pendingDelete || deleteLock.current || (force && !forceAllowed)) return
|
||||
const target = pendingDelete
|
||||
deleteLock.current = true
|
||||
setBusy(true)
|
||||
setDeleteError(null)
|
||||
try {
|
||||
if (target.kind === "user") {
|
||||
await deleteIpsecUser(backendUrl, target.client.serverId, target.client.rosId)
|
||||
toast.success(`Клиент «${target.client.name}» удалён`)
|
||||
} else if (target.kind === "server") {
|
||||
await deleteIpsecServer(backendUrl, target.summary.serverId)
|
||||
toast.success(`IKEv2-сервер на ${target.summary.serverName} удалён`)
|
||||
} else if (target.kind === "peer") {
|
||||
await deleteIpsecPeer(backendUrl, target.serverId, target.peer.rosId)
|
||||
toast.success(`Peer «${target.peer.name}» удалён`)
|
||||
await deleteIpsecPeer(backendUrl, target.serverId, target.peer.rosId, { force })
|
||||
} else {
|
||||
await deleteIpsecCert(backendUrl, target.serverId, target.cert.name)
|
||||
toast.success(`Сертификат «${target.cert.name}» удалён`)
|
||||
await deleteIpsecCert(backendUrl, target.serverId, target.cert.name, { force })
|
||||
}
|
||||
await loadLive()
|
||||
} catch (e) {
|
||||
const forceDelete = async (fn: () => Promise<unknown>) => {
|
||||
try {
|
||||
await fn()
|
||||
toast.success("Удалено")
|
||||
await loadLive()
|
||||
} catch (err) {
|
||||
toast.error("Не удалось удалить", { description: err instanceof Error ? err.message : String(err) })
|
||||
}
|
||||
}
|
||||
if (e instanceof ApiClientError && e.status === 409 && target.kind === "peer") {
|
||||
toast.error("На peer ссылаются identity", {
|
||||
description: e.message,
|
||||
action: {
|
||||
label: "Удалить принудительно",
|
||||
onClick: () => { void forceDelete(() => deleteIpsecPeer(backendUrl, target.serverId, target.peer.rosId, { force: true })) },
|
||||
},
|
||||
})
|
||||
} else if (e instanceof ApiClientError && e.status === 409 && target.kind === "cert") {
|
||||
toast.error("Сертификат используется", {
|
||||
description: e.message,
|
||||
action: {
|
||||
label: "Удалить принудительно",
|
||||
onClick: () => { void forceDelete(() => deleteIpsecCert(backendUrl, target.serverId, target.cert.name, { force: true })) },
|
||||
},
|
||||
})
|
||||
} else {
|
||||
toast.error("Ошибка удаления", { description: e instanceof Error ? e.message : String(e) })
|
||||
}
|
||||
} finally {
|
||||
setBusy(false)
|
||||
toast.success("Удалено")
|
||||
setPendingDelete(null)
|
||||
setForceAllowed(false)
|
||||
await loadLive()
|
||||
} catch (error) {
|
||||
setDeleteError(error instanceof Error ? error.message : "Не удалось удалить")
|
||||
setForceAllowed(error instanceof ApiClientError && error.status === 409 && (target.kind === "peer" || target.kind === "cert"))
|
||||
} finally {
|
||||
deleteLock.current = false
|
||||
setBusy(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -856,7 +834,7 @@ export default function IpsecPage() {
|
||||
onReexport={reexportCert}
|
||||
/>
|
||||
|
||||
<AlertDialog open={pendingDelete != null} onOpenChange={(v) => { if (!v) setPendingDelete(null) }}>
|
||||
<AlertDialog open={pendingDelete != null} onOpenChange={(v) => { if (!v && !deleteLock.current) { setPendingDelete(null); setDeleteError(null); setForceAllowed(false) } }}>
|
||||
<AlertDialogContent size="default">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogMedia className="bg-destructive/10 text-destructive">
|
||||
@@ -864,9 +842,12 @@ export default function IpsecPage() {
|
||||
</AlertDialogMedia>
|
||||
<AlertDialogTitle>{deleteDialogTitle}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{deleteDialogDescription}</AlertDialogDescription>
|
||||
{busy && <p role="status" className="text-sm text-muted-foreground">Ожидание результата удаления…</p>}
|
||||
{deleteError && <p role="alert" className="text-sm text-destructive">{deleteError}</p>}
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={busy}>Отмена</AlertDialogCancel>
|
||||
{forceAllowed && <Button variant="destructive" disabled={busy} onClick={() => void confirmDelete(true)}>Удалить принудительно</Button>}
|
||||
<AlertDialogAction
|
||||
variant="destructive"
|
||||
disabled={busy}
|
||||
|
||||
Reference in new issue
Block a user