fix(ipsec): подтвердить достоверность операций и приёмку раздела
This commit is contained in:
1 parent
e2d81f9657
commit
f80d9b7637
28 files changed
+969
-170
No files matched your search
+30
-24
@@ -1,7 +1,8 @@
|
||||
"use client"
|
||||
|
||||
import { z } from "zod"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { useSearchParams } from "next/navigation"
|
||||
import { PageHeader } from "@/components/page-header"
|
||||
import { servers as mockServers } from "@/lib/data"
|
||||
import type { Server } from "@/lib/data"
|
||||
@@ -174,7 +175,7 @@ const ipsecInventorySchema = z.array(z.object({
|
||||
latency: z.number().nullable().optional(), asn: z.string().optional(),
|
||||
})).refine(rows => new Set(rows.map(row => row.id)).size === rows.length)
|
||||
|
||||
function IpsecPage() {
|
||||
function IpsecPage({ selectedServerId, setSelectedServerId, workspaceTab, setWorkspaceTab }: { selectedServerId: string; setSelectedServerId: (id: string) => void; workspaceTab: IpsecWorkspaceTab; setWorkspaceTab: (tab: IpsecWorkspaceTab) => void }) {
|
||||
const { mode, backendUrl } = useDataSource()
|
||||
const isLive = mode === "live"
|
||||
|
||||
@@ -201,10 +202,9 @@ function IpsecPage() {
|
||||
const certPending = useRef(false)
|
||||
useEffect(() => () => { certEpoch.current += 1 }, [])
|
||||
|
||||
const [selectedServerId, setSelectedServerId] = useState(ALL_SERVERS_ID)
|
||||
const [workspaceTab, setWorkspaceTab] = useState<IpsecWorkspaceTab>("clients")
|
||||
const [search, setSearch] = useState("")
|
||||
const [initOpen, setInitOpen] = useState(false)
|
||||
const [initTargetId, setInitTargetId] = useState<string | undefined>()
|
||||
const [userOpen, setUserOpen] = useState(false)
|
||||
const [editing, setEditing] = useState<IpsecClientDto | null>(null)
|
||||
const [certOpen, setCertOpen] = useState(false)
|
||||
@@ -265,10 +265,8 @@ function IpsecPage() {
|
||||
const displaySummaries = isLive ? summaries : MOCK_IPSEC_SERVERS
|
||||
const displayClients = isLive ? clients : MOCK_IPSEC_CLIENTS
|
||||
|
||||
const effectiveServerId =
|
||||
selectedServerId === ALL_SERVERS_ID || displayServers.some((s) => s.id === selectedServerId)
|
||||
? selectedServerId
|
||||
: ALL_SERVERS_ID
|
||||
const effectiveServerId = selectedServerId
|
||||
const missingSelectedServer = selectedServerId !== ALL_SERVERS_ID && hasLoaded && !displayServers.some(s => s.id === selectedServerId)
|
||||
|
||||
const historyServerId = effectiveServerId === ALL_SERVERS_ID ? null : effectiveServerId
|
||||
|
||||
@@ -315,7 +313,7 @@ function IpsecPage() {
|
||||
}
|
||||
|
||||
const failedServerIds = new Set(failures.map(f => f.serverId))
|
||||
const scopeUnknown = isLive && (!hasLoaded || !!dataError || (effectiveServerId === ALL_SERVERS_ID ? failures.length > 0 : failedServerIds.has(effectiveServerId)))
|
||||
const scopeUnknown = missingSelectedServer || isLive && (!hasLoaded || !!dataError || (effectiveServerId === ALL_SERVERS_ID ? failures.length > 0 : failedServerIds.has(effectiveServerId)))
|
||||
|
||||
const canManage = (serverId: string) => isLive && hasLoaded && !dataError && !inventoryError &&
|
||||
!loading && !busy && !historyRestoring && !failedServerIds.has(serverId) && liveServers.some(s => s.id === serverId)
|
||||
@@ -337,7 +335,7 @@ function IpsecPage() {
|
||||
operationLock.current = false
|
||||
if (mounted.current) setBusy(false)
|
||||
}
|
||||
const hasReadyDevice = displayServers.some(s => canManage(s.id))
|
||||
const hasReadyDevice = !missingSelectedServer && displayServers.some(s => (effectiveServerId === ALL_SERVERS_ID || s.id === effectiveServerId) && canManage(s.id))
|
||||
|
||||
const scopedClients = useMemo(() => {
|
||||
if (effectiveServerId === ALL_SERVERS_ID) return displayClients
|
||||
@@ -366,10 +364,6 @@ function IpsecPage() {
|
||||
const caOk = scopedSummaries.some((s) => s.caCert)
|
||||
const compactServer = effectiveServerId !== ALL_SERVERS_ID
|
||||
const sheetServerId = compactServer ? effectiveServerId : undefined
|
||||
const certNames = useMemo(
|
||||
() => Array.from(new Set(scopedSummaries.flatMap((s) => (s.certs ?? []).map((c) => c.name)))).sort(),
|
||||
[scopedSummaries],
|
||||
)
|
||||
const peerOptions = useMemo(
|
||||
() => scopedSummaries.flatMap((s) => s.peers.map((p) => ({ serverId: s.serverId, name: p.name, managed: p.managed }))),
|
||||
[scopedSummaries],
|
||||
@@ -583,7 +577,6 @@ function IpsecPage() {
|
||||
address: form.address.trim() || undefined,
|
||||
exchangeMode: form.exchangeMode.trim() || undefined,
|
||||
passive: form.passive,
|
||||
certificate: form.certificate.trim() || undefined,
|
||||
profile: form.profile.trim() || undefined,
|
||||
disabled: form.disabled,
|
||||
})
|
||||
@@ -626,6 +619,7 @@ function IpsecPage() {
|
||||
<>
|
||||
<ServerRailLayout
|
||||
items={railItems}
|
||||
preserveSelection
|
||||
selectedId={effectiveServerId}
|
||||
onSelect={setSelectedServerId}
|
||||
showAll
|
||||
@@ -675,6 +669,7 @@ function IpsecPage() {
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
{missingSelectedServer && <Alert variant="destructive" role="alert"><AlertTitle>Устройство из ссылки не найдено</AlertTitle><AlertDescription>Выберите доступный сервер. Данные других устройств не подставляются в этот срез.</AlertDescription></Alert>}
|
||||
<KpiStatGrid
|
||||
aria-label="Сводка IPsec"
|
||||
items={[
|
||||
@@ -799,7 +794,7 @@ function IpsecPage() {
|
||||
{isLive && !hasLoaded ? <p role="status" className="p-5 text-sm text-muted-foreground">Состояние серверов IPsec пока неизвестно</p> : <IpsecServerGrid
|
||||
servers={scopedSummaries}
|
||||
canManage={canManage}
|
||||
onInit={isLive ? (s) => { setSelectedServerId(s.serverId); setInitOpen(true) } : undefined}
|
||||
onInit={isLive ? (s) => { setInitTargetId(s.serverId); setSelectedServerId(s.serverId); setInitOpen(true) } : undefined}
|
||||
onRemove={isLive ? (s) => setPendingDelete({ kind: "server", summary: s }) : undefined}
|
||||
onEditPeer={isLive ? openEditPeer : undefined}
|
||||
onDeletePeer={isLive ? (serverId, peer) => setPendingDelete({ kind: "peer", serverId, peer }) : undefined}
|
||||
@@ -831,12 +826,10 @@ function IpsecPage() {
|
||||
lines: [
|
||||
"/ip ipsec peer add \\",
|
||||
" name=ipsec-vpn address=0.0.0.0/0 \\",
|
||||
" exchange-mode=ike2 passive=yes \\",
|
||||
" certificate=ipsec-server send-cert=always",
|
||||
" exchange-mode=ike2 passive=yes profile=ipsec-vpn",
|
||||
"/ip ipsec identity add \\",
|
||||
" peer=ipsec-vpn auth-method=rsa-key \\",
|
||||
" peer=ipsec-vpn auth-method=digital-signature \\",
|
||||
" certificate=ipsec-server \\",
|
||||
" match-by=certificate \\",
|
||||
" generate-policy=port-strict \\",
|
||||
" mode-config=ipsec-vpn",
|
||||
],
|
||||
@@ -892,7 +885,6 @@ function IpsecPage() {
|
||||
onOpenChange={(v) => { setPeerOpen(v); if (!v) setEditingPeer(null) }}
|
||||
busy={busy}
|
||||
editing={editingPeer?.peer ?? null}
|
||||
certificates={certNames}
|
||||
onSubmit={handleEditPeer}
|
||||
/>
|
||||
|
||||
@@ -901,7 +893,7 @@ function IpsecPage() {
|
||||
onOpenChange={setInitOpen}
|
||||
servers={serverOptions.filter(s => canManage(s.id) || busy)}
|
||||
busy={busy}
|
||||
defaultServerId={sheetServerId}
|
||||
defaultServerId={initTargetId ?? sheetServerId}
|
||||
onSubmit={handleInit}
|
||||
/>
|
||||
|
||||
@@ -959,7 +951,21 @@ function IpsecPage() {
|
||||
)
|
||||
}
|
||||
|
||||
export default function IpsecSource() {
|
||||
function IpsecRoute() {
|
||||
const { mode, backendUrl } = useDataSource()
|
||||
return <IpsecPage key={JSON.stringify([mode, backendUrl])} />
|
||||
const params = useSearchParams()
|
||||
const selectedServerId = params.get("server") ?? ALL_SERVERS_ID
|
||||
const rawTab = params.get("tab")
|
||||
const workspaceTab: IpsecWorkspaceTab = rawTab === "server" || rawTab === "cli" ? rawTab : "clients"
|
||||
const navigate = (key: string, value: string, defaultValue: string) => {
|
||||
const next = new URLSearchParams(params.toString())
|
||||
if (value === defaultValue) next.delete(key)
|
||||
else next.set(key, value)
|
||||
window.history.pushState(null, "", "/ipsec" + (next.size ? "?" + next.toString() : ""))
|
||||
}
|
||||
return <IpsecPage key={JSON.stringify([mode, backendUrl])} selectedServerId={selectedServerId} setSelectedServerId={id => navigate("server", id, ALL_SERVERS_ID)} workspaceTab={workspaceTab} setWorkspaceTab={tab => navigate("tab", tab, "clients")} />
|
||||
}
|
||||
|
||||
export default function IpsecSource() {
|
||||
return <Suspense fallback={<div role="status">Загрузка IPsec…</div>}><IpsecRoute /></Suspense>
|
||||
}
|
||||
Reference in new issue
Block a user