fix(ui): подтверждать удаление WireGuard и блокировать повторы

This commit is contained in:
Denozordec committed 2026-09-28 23:52:43 +07:00
1 parent 165f40acd7
commit f980619046
5 files changed
+131 -13

No files matched your search

+23 -9
View File
@@ -1,6 +1,6 @@
"use client"
import { Suspense, useCallback, useEffect, useMemo, useState } from "react"
import { Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react"
import Link from "next/link"
import { useRouter, useSearchParams } from "next/navigation"
import { PageHeader } from "@/components/page-header"
@@ -197,6 +197,9 @@ function WireGuardPageContent({ selectedServerId, selectedInterfaceId, allInterf
const [exportPeerId, setExportPeerId] = useState<string | null>(null)
const [peerIface, setPeerIface] = useState<WgIfaceWithServer | null>(null)
const [pendingDelete, setPendingDelete] = useState<PendingDelete | null>(null)
const deleteLock = useRef(false)
const [deleteBusy, setDeleteBusy] = useState(false)
const [deleteError, setDeleteError] = useState<string | null>(null)
const [historyOpen, setHistoryOpen] = useState(false)
const [revisions, setRevisions] = useState<ConfigRevisionDto[]>([])
const [historyLoading, setHistoryLoading] = useState(false)
@@ -490,29 +493,38 @@ function WireGuardPageContent({ selectedServerId, selectedInterfaceId, allInterf
}
async function confirmDelete() {
if (!pendingDelete) return
if (!pendingDelete || deleteLock.current) return
if (sourceUnavailable(pendingDelete.iface.serverId)) return
if (!isLive) {
toast.info("Доступно только в live-режиме")
setPendingDelete(null)
return
}
deleteLock.current = true
setDeleteBusy(true)
setDeleteError(null)
try {
if (pendingDelete.kind === "iface") {
if (!pendingDelete.iface.rosId) {
toast.info("Доступно только в live-режиме")
return
}
await deleteWireGuardInterface(backendUrl, pendingDelete.iface.serverId, pendingDelete.iface.rosId)
const result = await deleteWireGuardInterface(backendUrl, pendingDelete.iface.serverId, pendingDelete.iface.rosId)
if (result?.ok !== true) throw new Error("Сервер не подтвердил удаление")
toast.success("Удалено")
} else {
await deleteWireGuardPeer(backendUrl, pendingDelete.iface.serverId, pendingDelete.peerId)
const peer = pendingDelete.iface.peers.find(p => p.id === pendingDelete.peerId || p.rosId === pendingDelete.peerId)
const result = await deleteWireGuardPeer(backendUrl, pendingDelete.iface.serverId, peer?.rosId ?? pendingDelete.peerId)
if (result?.ok !== true) throw new Error("Сервер не подтвердил удаление")
toast.success("Пир удалён")
}
setPendingDelete(null)
await loadLive()
} catch (e) {
toast.error(e instanceof Error ? e.message : "Ошибка удаления")
setDeleteError(e instanceof Error ? e.message : "Ошибка удаления")
} finally {
deleteLock.current = false
setDeleteBusy(false)
}
}
@@ -887,7 +899,7 @@ function WireGuardPageContent({ selectedServerId, selectedInterfaceId, allInterf
onRequestLiveExport={isLive ? handleLiveExport : undefined}
/>
<AlertDialog open={!!pendingDelete} onOpenChange={(v) => { if (!v) setPendingDelete(null) }}>
<AlertDialog open={!!pendingDelete} onOpenChange={(v) => { if (!v && !deleteLock.current) { setPendingDelete(null); setDeleteError(null) } }}>
<AlertDialogContent size="default">
<AlertDialogHeader>
<AlertDialogMedia className="bg-destructive/10 text-destructive">
@@ -904,10 +916,12 @@ function WireGuardPageContent({ selectedServerId, selectedInterfaceId, allInterf
: null}
</AlertDialogDescription>
</AlertDialogHeader>
{deleteBusy && <p role="status" className="text-sm text-muted-foreground">Ожидание подтверждения удаления…</p>}
{deleteError && <p role="alert" className="text-sm text-destructive">{deleteError}</p>}
<AlertDialogFooter>
<AlertDialogCancel onClick={() => setPendingDelete(null)}>Отмена</AlertDialogCancel>
<AlertDialogAction variant="destructive" onClick={() => void confirmDelete()}>
Удалить
<AlertDialogCancel disabled={deleteBusy}>Отмена</AlertDialogCancel>
<AlertDialogAction type="button" variant="destructive" disabled={deleteBusy} onClick={() => void confirmDelete()}>
{deleteBusy ? "Удаление…" : "Удалить"}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>