fix(ui): изолировать источники данных Filters
This commit is contained in:
1 parent
24cc66d8d2
commit
365ab2173c
7 files changed
+146
-9
No files matched your search
@@ -1263,8 +1263,13 @@ function makeApiFetch(backendUrl: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export default function FiltersPage() {
|
||||
function FiltersPage() {
|
||||
const { mode, backendUrl } = useDataSource()
|
||||
const mounted = useRef(true)
|
||||
useEffect(() => {
|
||||
mounted.current = true
|
||||
return () => { mounted.current = false }
|
||||
}, [])
|
||||
const evo = useEvoBGP()
|
||||
/** Как на `/servers`: живые данные при режиме «Живые», без ожидания отдельного health-check */
|
||||
const isLive = mode === "live"
|
||||
@@ -1309,25 +1314,29 @@ export default function FiltersPage() {
|
||||
const [loadAttempt, setLoadAttempt] = useState(0)
|
||||
|
||||
const ensureGreTunnels = useCallback(async (serverId: string) => {
|
||||
if (!isLive) return
|
||||
if (!isLive || !mounted.current) return
|
||||
try {
|
||||
const d = await apiFetch<{ tunnels: GreTunnel[] }>(
|
||||
`/api/filters/gre-tunnels?serverId=${encodeURIComponent(serverId)}`,
|
||||
)
|
||||
if (!mounted.current) return
|
||||
setGreByServer(prev => ({ ...prev, [serverId]: d.tunnels }))
|
||||
} catch {
|
||||
if (!mounted.current) return
|
||||
setGreByServer(prev => ({ ...prev, [serverId]: [] }))
|
||||
}
|
||||
}, [isLive, apiFetch])
|
||||
|
||||
const ensureRecursiveRoutes = useCallback(async (serverId: string) => {
|
||||
if (!isLive) return
|
||||
if (!isLive || !mounted.current) return
|
||||
try {
|
||||
const d = await apiFetch<{ routes: RecursiveRouteLite[] }>(
|
||||
`/api/recursive-routes?serverId=${encodeURIComponent(serverId)}`,
|
||||
)
|
||||
if (!mounted.current) return
|
||||
setRecRoutesByServer(prev => ({ ...prev, [serverId]: d.routes }))
|
||||
} catch {
|
||||
if (!mounted.current) return
|
||||
setRecRoutesByServer(prev => ({ ...prev, [serverId]: [] }))
|
||||
}
|
||||
}, [isLive, apiFetch])
|
||||
@@ -1423,7 +1432,7 @@ export default function FiltersPage() {
|
||||
}, [isLive, apiFetch, loadAttempt])
|
||||
|
||||
const loadLiveRules = useCallback(async (serverId: string) => {
|
||||
if (!isLive || !serverId) return false
|
||||
if (!isLive || !serverId || !mounted.current) return false
|
||||
const epoch = ++readEpoch.current
|
||||
setReadyServerId(null)
|
||||
setRulesReading(true)
|
||||
@@ -1521,6 +1530,7 @@ export default function FiltersPage() {
|
||||
nextRules: FilterRule[],
|
||||
source: "apply" | "copy" = "apply",
|
||||
) => {
|
||||
if (!mounted.current) return { ok: false, error: "Источник данных изменён" }
|
||||
if (applyLock.current || applyBusy || historyRestoring) return { ok: false, error: "Дождитесь завершения текущей операции" }
|
||||
if (isLive && (liveStale || liveLoadState !== "idle" || (serverId === selectedServerId && readyServerId !== serverId))) {
|
||||
const error = "Роутер недоступен — изменения заблокированы"
|
||||
@@ -1540,6 +1550,7 @@ export default function FiltersPage() {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ serverId, rules: nextRules, source }),
|
||||
})
|
||||
if (!mounted.current) return { ok: false, error: "Источник данных изменён" }
|
||||
const parsed = filtersMutationSchema.safeParse(payload)
|
||||
if (!parsed.success) throw new Error("Сервер не подтвердил применение правил")
|
||||
const res = parsed.data
|
||||
@@ -1547,6 +1558,7 @@ export default function FiltersPage() {
|
||||
toast.success("Правила применены на роутер")
|
||||
return { ok: true }
|
||||
} catch (err) {
|
||||
if (!mounted.current) return { ok: false, error: "Источник данных изменён" }
|
||||
const error = err instanceof Error ? err.message : "Не удалось применить правила на роутер"
|
||||
toast.error("Не удалось применить правила на роутер", { description: error })
|
||||
return { ok: false, error }
|
||||
@@ -1557,7 +1569,7 @@ export default function FiltersPage() {
|
||||
}, [isLive, apiFetch, applyBusy, historyRestoring, liveStale, liveLoadState, readyServerId, selectedServerId])
|
||||
|
||||
const refreshFromRouter = useCallback(async () => {
|
||||
if (!isLive || !selectedServerId || applyLock.current || applyBusy || historyRestoring) return
|
||||
if (!isLive || !selectedServerId || !mounted.current || applyLock.current || applyBusy || historyRestoring) return
|
||||
applyLock.current = true
|
||||
setApplyBusy(true)
|
||||
try {
|
||||
@@ -1575,7 +1587,7 @@ export default function FiltersPage() {
|
||||
useEffect(() => () => { historyEpoch.current += 1; readEpoch.current += 1 }, [selectedServerId, backendUrl, isLive])
|
||||
|
||||
const loadRevisions = useCallback(async () => {
|
||||
if (!isLive || !selectedServerId) return
|
||||
if (!isLive || !selectedServerId || !mounted.current) return
|
||||
const epoch = ++historyEpoch.current
|
||||
setHistoryLoading(true)
|
||||
setHistoryError(null)
|
||||
@@ -1606,6 +1618,7 @@ export default function FiltersPage() {
|
||||
`/api/filters/revisions/${encodeURIComponent(id)}/restore`,
|
||||
{ method: "POST", body: JSON.stringify({ serverId: selectedServerId }) },
|
||||
)
|
||||
if (!mounted.current) return
|
||||
const parsed = filtersMutationSchema.safeParse(payload)
|
||||
if (!parsed.success) throw new Error("Сервер не подтвердил восстановление")
|
||||
const res = parsed.data
|
||||
@@ -1614,9 +1627,11 @@ export default function FiltersPage() {
|
||||
} else {
|
||||
if (!await loadLiveRules(selectedServerId)) throw new Error("Версия применена, но актуальные правила не прочитаны — обновите данные")
|
||||
}
|
||||
if (!mounted.current) return
|
||||
setLiveStale(false)
|
||||
setReadyServerId(selectedServerId)
|
||||
await loadRevisions()
|
||||
if (!mounted.current) return
|
||||
toast.success("Версия применена на роутер")
|
||||
} finally {
|
||||
applyLock.current = false
|
||||
@@ -2048,3 +2063,8 @@ export default function FiltersPage() {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default function FiltersSource() {
|
||||
const { mode, backendUrl } = useDataSource()
|
||||
return <FiltersPage key={JSON.stringify([mode, backendUrl])} />
|
||||
}
|
||||
Reference in new issue
Block a user