fix(ui): показывать свежесть опроса и изменений IPsec
This commit is contained in:
1 parent
921aba3054
commit
ce43b2d762
5 files changed
+75
-1
No files matched your search
@@ -183,6 +183,9 @@ function IpsecPage() {
|
||||
const [dataError, setDataError] = useState<string | null>(null)
|
||||
const [inventoryError, setInventoryError] = useState<string | null>(null)
|
||||
const [hasLoaded, setHasLoaded] = useState(false)
|
||||
const [readReceipt, setReadReceipt] = useState<Record<string, string>>({})
|
||||
const [responseReceipt, setResponseReceipt] = useState<string | null>(null)
|
||||
const [writeReceipt, setWriteReceipt] = useState<{ serverId: string; at: string } | null>(null)
|
||||
const mounted = useRef(true)
|
||||
const operationLock = useRef(false)
|
||||
useEffect(() => { mounted.current = true; return () => { mounted.current = false } }, [])
|
||||
@@ -239,6 +242,10 @@ function IpsecPage() {
|
||||
setSummaries(previous => [...ipsec.servers, ...previous.filter(s => failedIds.has(s.serverId))])
|
||||
setClients(previous => [...ipsec.clients, ...previous.filter(c => failedIds.has(c.serverId))])
|
||||
setFailures(ipsec.failures ?? [])
|
||||
const at = new Date().toISOString()
|
||||
setResponseReceipt(at)
|
||||
const successfulIds = new Set([...ipsec.servers.map(s => s.serverId), ...ipsec.clients.map(c => c.serverId)])
|
||||
setReadReceipt(previous => ({ ...previous, ...Object.fromEntries([...successfulIds].map(id => [id, at])) }))
|
||||
setHasLoaded(true)
|
||||
setDataError(null)
|
||||
} else {
|
||||
@@ -292,6 +299,7 @@ function IpsecPage() {
|
||||
const res = await restoreIpsecRevision(backendUrl, id, historyServerId)
|
||||
if (!mounted.current) return
|
||||
if (res?.ok !== true) throw new Error("Сервер не подтвердил восстановление")
|
||||
confirmOperation(historyServerId)
|
||||
toast.success("Версия применена на роутер")
|
||||
await loadLive(true)
|
||||
await loadRevisions()
|
||||
@@ -311,6 +319,10 @@ function IpsecPage() {
|
||||
const assertDeviceReady = (serverId: string) => {
|
||||
if (operationLock.current || !canManage(serverId)) throw new Error("Сначала подтвердите актуальное состояние устройства: повторите опрос IPsec")
|
||||
}
|
||||
const confirmOperation = (serverId: string) => setWriteReceipt({ serverId, at: new Date().toISOString() })
|
||||
const formatReceipt = (at: string) => new Date(at).toLocaleString("ru-RU", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" })
|
||||
const scopeReceipt = effectiveServerId === ALL_SERVERS_ID ? responseReceipt : readReceipt[effectiveServerId]
|
||||
const scopeWriteReceipt = writeReceipt && (effectiveServerId === ALL_SERVERS_ID || writeReceipt.serverId === effectiveServerId) ? writeReceipt : null
|
||||
const beginOperation = (serverId: string) => {
|
||||
assertDeviceReady(serverId)
|
||||
operationLock.current = true
|
||||
@@ -407,6 +419,7 @@ function IpsecPage() {
|
||||
createNatRule: form.createNatRule,
|
||||
})
|
||||
if (!mounted.current) return
|
||||
confirmOperation(form.serverId)
|
||||
toast.success("IKEv2-сервер инициализирован")
|
||||
setInitOpen(false)
|
||||
await loadLive(true)
|
||||
@@ -429,6 +442,7 @@ function IpsecPage() {
|
||||
passphrase: form.passphrase.trim() || undefined,
|
||||
})
|
||||
if (!mounted.current) return
|
||||
confirmOperation(form.serverId)
|
||||
toast.success(`Клиент «${form.name.trim()}» создан`)
|
||||
setUserOpen(false)
|
||||
setEditing(null)
|
||||
@@ -459,6 +473,7 @@ function IpsecPage() {
|
||||
psk: form.authMethod === "pre-shared-key" && form.psk.trim() ? form.psk : undefined,
|
||||
})
|
||||
if (!mounted.current) return
|
||||
confirmOperation(editing.serverId)
|
||||
toast.success("Клиент обновлён")
|
||||
setUserOpen(false)
|
||||
setEditing(null)
|
||||
@@ -535,6 +550,7 @@ function IpsecPage() {
|
||||
await deleteIpsecCert(backendUrl, target.serverId, target.cert.name, { force })
|
||||
}
|
||||
if (!mounted.current) return
|
||||
confirmOperation(serverId)
|
||||
toast.success("Удалено")
|
||||
setPendingDelete(null)
|
||||
setForceAllowed(false)
|
||||
@@ -568,6 +584,7 @@ function IpsecPage() {
|
||||
disabled: form.disabled,
|
||||
})
|
||||
if (!mounted.current) return
|
||||
confirmOperation(editingPeer.serverId)
|
||||
toast.success("Peer обновлён")
|
||||
setPeerOpen(false)
|
||||
setEditingPeer(null)
|
||||
@@ -688,6 +705,13 @@ function IpsecPage() {
|
||||
]}
|
||||
/>
|
||||
|
||||
{isLive && <div role="group" aria-label="Свежесть IPsec" className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
|
||||
<span data-source-time="read">{effectiveServerId === ALL_SERVERS_ID ? "Последний ответ IPsec получен в приложении: " : "Устройство опрошено, ответ получен в приложении: "}
|
||||
{scopeReceipt ? <time dateTime={scopeReceipt} title="Клиентское время получения ответа; не время измерения RouterOS">{formatReceipt(scopeReceipt)}</time> : "неизвестно"}
|
||||
</span>
|
||||
{scopeWriteReceipt && <span data-source-time="write">Изменение подтверждено API: <time dateTime={scopeWriteReceipt.at} title={`Устройство ${scopeWriteReceipt.serverId}; подтверждение API не заменяет опрос`}>{formatReceipt(scopeWriteReceipt.at)}</time></span>}
|
||||
</div>}
|
||||
|
||||
{isLive && (dataError || inventoryError) ? (
|
||||
<Alert variant="destructive">
|
||||
<AlertCircleIcon />
|
||||
|
||||
Reference in new issue
Block a user