fix(ui): изолировать операции восстановления Filters
This commit is contained in:
1 parent
7554f1804b
commit
1fba16e1f2
5 files changed
+145
-15
No files matched your search
+23
-13
@@ -1426,12 +1426,14 @@ export default function FiltersPage() {
|
||||
}, [isLive, apiFetch])
|
||||
|
||||
const loadLiveRules = useCallback(async (serverId: string) => {
|
||||
if (!isLive || !serverId) return
|
||||
if (!isLive || !serverId) return false
|
||||
try {
|
||||
const fresh = await apiFetch<LiveFiltersResponse>(
|
||||
`/api/filters/rules?serverId=${encodeURIComponent(serverId)}`,
|
||||
)
|
||||
const liveRules = fresh.rulesets.find((r) => r.serverId === serverId)?.rules ?? fresh.rulesets[0]?.rules ?? []
|
||||
const ruleset = fresh.rulesets.find(r => r.serverId === serverId)
|
||||
if (!ruleset || fresh.stale) throw new Error("Не удалось получить актуальные правила выбранного устройства")
|
||||
const liveRules = ruleset.rules
|
||||
setRulesets((prev) => {
|
||||
const has = prev.some((rs) => rs.serverId === serverId)
|
||||
if (!has) return [...prev, { serverId, rules: liveRules }]
|
||||
@@ -1441,9 +1443,11 @@ export default function FiltersPage() {
|
||||
if (fresh.greTunnels?.length) {
|
||||
setGreByServer((prev) => ({ ...prev, [serverId]: fresh.greTunnels }))
|
||||
}
|
||||
return true
|
||||
} catch (err) {
|
||||
setLiveStale(true)
|
||||
toast.error("Не удалось прочитать правила с роутера", { description: String(err) })
|
||||
return false
|
||||
}
|
||||
}, [isLive, apiFetch])
|
||||
|
||||
@@ -1540,7 +1544,8 @@ export default function FiltersPage() {
|
||||
}, [isLive, apiFetch, applyBusy, historyRestoring, liveStale, liveLoadState])
|
||||
|
||||
const refreshFromRouter = useCallback(async () => {
|
||||
if (!isLive || !selectedServerId || applyBusy) return
|
||||
if (!isLive || !selectedServerId || applyLock.current || applyBusy || historyRestoring) return
|
||||
applyLock.current = true
|
||||
setApplyBusy(true)
|
||||
try {
|
||||
await loadLiveRules(selectedServerId)
|
||||
@@ -1549,9 +1554,10 @@ export default function FiltersPage() {
|
||||
ensureRecursiveRoutes(selectedServerId),
|
||||
])
|
||||
} finally {
|
||||
applyLock.current = false
|
||||
setApplyBusy(false)
|
||||
}
|
||||
}, [isLive, selectedServerId, applyBusy, loadLiveRules, ensureGreTunnels, ensureRecursiveRoutes])
|
||||
}, [isLive, selectedServerId, applyBusy, historyRestoring, loadLiveRules, ensureGreTunnels, ensureRecursiveRoutes])
|
||||
|
||||
useEffect(() => () => { historyEpoch.current += 1 }, [selectedServerId, backendUrl, isLive])
|
||||
|
||||
@@ -1575,7 +1581,10 @@ export default function FiltersPage() {
|
||||
}, [isLive, selectedServerId, apiFetch])
|
||||
|
||||
const restoreRevision = useCallback(async (id: string) => {
|
||||
if (!isLive || !selectedServerId) return
|
||||
if (!isLive || !selectedServerId) throw new Error("Выберите устройство в режиме живых данных")
|
||||
if (applyLock.current || applyBusy || historyRestoring) throw new Error("Дождитесь завершения текущей операции")
|
||||
applyLock.current = true
|
||||
setApplyBusy(true)
|
||||
setHistoryRestoring(true)
|
||||
try {
|
||||
const res = await apiFetch<{ ok: boolean; rules?: FilterRule[] }>(
|
||||
@@ -1586,17 +1595,17 @@ export default function FiltersPage() {
|
||||
if (res.rules) {
|
||||
setRulesets((p) => p.map((rs) => rs.serverId === selectedServerId ? { ...rs, rules: res.rules ?? [] } : rs))
|
||||
} else {
|
||||
await loadLiveRules(selectedServerId)
|
||||
if (!await loadLiveRules(selectedServerId)) throw new Error("Версия применена, но актуальные правила не прочитаны — обновите данные")
|
||||
}
|
||||
setLiveStale(false)
|
||||
toast.success("Версия применена на роутер")
|
||||
await loadRevisions()
|
||||
} catch (err) {
|
||||
throw err
|
||||
toast.success("Версия применена на роутер")
|
||||
} finally {
|
||||
applyLock.current = false
|
||||
setApplyBusy(false)
|
||||
setHistoryRestoring(false)
|
||||
}
|
||||
}, [isLive, selectedServerId, apiFetch, loadLiveRules, loadRevisions])
|
||||
}, [isLive, selectedServerId, apiFetch, applyBusy, historyRestoring, loadLiveRules, loadRevisions])
|
||||
|
||||
const updateRules = useCallback((serverId: string, updater: (rules: FilterRule[]) => FilterRule[]) => {
|
||||
const current = rulesets.find((rs) => rs.serverId === serverId)?.rules ?? []
|
||||
@@ -1738,7 +1747,7 @@ export default function FiltersPage() {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => void refreshFromRouter()}
|
||||
disabled={applyBusy}
|
||||
disabled={applyBusy || historyRestoring}
|
||||
title="Прочитать актуальные правила с роутера"
|
||||
>
|
||||
<RefreshCwIcon className={cn("size-4", applyBusy && "animate-spin")} />
|
||||
@@ -1748,10 +1757,11 @@ export default function FiltersPage() {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (applyLock.current) return
|
||||
setHistoryOpen(true)
|
||||
void loadRevisions()
|
||||
}}
|
||||
disabled={applyBusy}
|
||||
disabled={applyBusy || historyRestoring}
|
||||
title="История версий и откат на CHR"
|
||||
>
|
||||
<HistoryIcon className="size-4" />
|
||||
@@ -2000,7 +2010,7 @@ export default function FiltersPage() {
|
||||
open={historyOpen}
|
||||
onOpenChange={v => { if (!v) historyEpoch.current += 1; setHistoryOpen(v) }}
|
||||
loadError={historyError}
|
||||
onRetry={() => { void loadRevisions() }}
|
||||
onRetry={() => { if (!applyLock.current) void loadRevisions() }}
|
||||
title="История фильтров"
|
||||
itemLabel="правил"
|
||||
revisions={revisions}
|
||||
|
||||
Reference in new issue
Block a user