fix(ui): убрать проверку статуса бэкенда из компонентов
Docker images / prepare-release (push) Successful in 5s
Docker images / backend-image (push) Successful in 1m32s
Docker images / frontend-image (push) Successful in 1m39s
Docker images / updater-image (push) Successful in 37s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s

This commit is contained in:
Denozordec
2026-05-12 18:58:38 +07:00
parent e7a8ad0910
commit bfceb07a23
15 changed files with 57 additions and 50 deletions
+2 -2
View File
@@ -826,8 +826,8 @@ const TABS: Array<{ id: BgpTab; label: string; icon: React.ReactNode }> = [
export default function BgpPage() {
const [activeTab, setActiveTab] = useState<BgpTab>("sessions")
const { mode, backendUrl, backendStatus } = useDataSource()
const isLive = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const isLive = mode === "live"
const [liveSessions, setLiveSessions] = useState<BgpSession[]>([])
const [loading, setLoading] = useState(false)
+2 -2
View File
@@ -289,8 +289,8 @@ function ProbeSourceCell({ probe, catalog }: { probe: PingProbe; catalog: Server
export default function DashboardPage() {
const pathname = usePathname()
const { mode, backendUrl, backendStatus } = useDataSource()
const isLive = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const isLive = mode === "live"
const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])
const [liveProbes, setLiveProbes] = useState<PingProbe[] | null>(null)
+2 -2
View File
@@ -661,8 +661,8 @@ function RunRowDetail({ r }: { r: SchedulerRunRowDto }) {
}
export default function DataCollectionPage() {
const { mode, backendUrl, backendStatus } = useDataSource()
const isLive = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const isLive = mode === "live"
const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])
const [trafficCollector, setTrafficCollector] = useState<CollectorSettingsDto | null>(null)
+2 -2
View File
@@ -272,8 +272,8 @@ type PageTab = "tunnels" | "pools"
// ════════════════════════════════════════════════════════════════════════════
export default function GrePage() {
const { mode, backendUrl, backendStatus } = useDataSource()
const isLive = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const isLive = mode === "live"
const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])
const [liveServers, setLiveServers] = useState<Server[]>([])
+4 -4
View File
@@ -777,12 +777,12 @@ function ZoomControls({ zoom, onZoomIn, onZoomOut, onFit }: {
// ════════════════════════════════════════════════════════════════════════════
export default function NetworkMapPage() {
const { mode, backendUrl, backendStatus } = useDataSource()
const useLiveData = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const useLiveData = mode === "live"
const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])
const [mapServers, setMapServers] = useState<Server[]>(mockServers)
const [mapGreTunnels, setMapGreTunnels] = useState<GreTunnel[]>(mockGreTunnels)
const [mapServers, setMapServers] = useState<Server[]>([])
const [mapGreTunnels, setMapGreTunnels] = useState<GreTunnel[]>([])
const [speedProbes, setSpeedProbes] = useState<GreSpeedProbeSnapshot[]>([])
/** FQDN из GRE outer → IPv4 (ответ POST /api/network/resolve-hosts), для матчинга с WAN. */
const [greResolvedIpv4ByHost, setGreResolvedIpv4ByHost] = useState<Record<string, string>>({})
+13 -2
View File
@@ -1227,8 +1227,8 @@ export default function OspfPage() {
const [activeTab, setActiveTab] = useState<OspfTab>("interfaces")
const [filterServerId, setFilterServerId] = useState<string>("all")
const { mode, backendUrl, backendStatus } = useDataSource()
const isLive = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const isLive = mode === "live"
const [liveData, setLiveData] = useState<BackendOspfAll | null>(null)
const [loading, setLoading] = useState(false)
@@ -1291,6 +1291,16 @@ export default function OspfPage() {
const { nodes: graphNodes, edges: graphEdges } = buildLiveGraph(neighbors)
return { items, neighbors, graphNodes, graphEdges, routerIds, bfdSessions }
}
if (isLive) {
return {
items: [],
neighbors: [],
graphNodes: [],
graphEdges: [],
routerIds: {},
bfdSessions: [],
}
}
return {
items: MOCK_ITEMS,
neighbors: MOCK_NEIGHBORS,
@@ -1318,6 +1328,7 @@ export default function OspfPage() {
}
return [...seen.values()]
}
if (isLive) return []
// Mock: derive from MOCK_GRAPH_NODES that actually have interfaces
const activeIds = new Set(MOCK_ITEMS.map(i => i.routerKey))
const siteCountry: Record<string, string> = { MSK: "RU", SPB: "RU", FRA: "DE", AMS: "NL", SGP: "SG" }
+2 -4
View File
@@ -729,10 +729,8 @@ export default function ProbesPage() {
const allServers = useMemo(() => {
if (!isLive) return servers
if (liveServers.length > 0) return liveServers
if (liveLoad === "error") return servers
return []
}, [isLive, liveServers, liveLoad])
return liveServers
}, [isLive, liveServers])
useEffect(() => {
if (!isLive) {
+4 -4
View File
@@ -498,12 +498,12 @@ function TypeChip({ type }: { type: "jump-host" | "exit-node" | "home-router" })
}
export default function RecursiveRoutesPage() {
const { mode, backendUrl, backendStatus } = useDataSource()
const isLive = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const isLive = mode === "live"
const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])
const [servers, setServers] = useState<Server[]>(mockServers)
const [selectedServerId, setSelectedServerId] = useState<string>(mockServers[0]?.id ?? "")
const [servers, setServers] = useState<Server[]>([])
const [selectedServerId, setSelectedServerId] = useState<string>("")
const [rows, setRows] = useState<RecursiveRouteRow[]>([])
const [busy, setBusy] = useState<"load" | "save" | "from" | "to" | null>(null)
const [search, setSearch] = useState("")
+4 -4
View File
@@ -321,10 +321,10 @@ type SheetMode = "add" | "edit"
// ════════════════════════════════════════════════════════════════════════════
export default function ServersPage() {
const { mode, backendUrl, backendStatus } = useDataSource()
const isLive = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const isLive = mode === "live"
const [serverList, setServerList] = useState<Server[]>(initialServers)
const [serverList, setServerList] = useState<Server[]>([])
const [_backendOk, setBackendOk] = useState(false)
const [search, setSearch] = useState("")
const [typeFilter, setTypeFilter] = useState<TypeFilter>("all")
@@ -358,7 +358,7 @@ export default function ServersPage() {
})
.catch(() => {
setBackendOk(false)
setServerList(initialServers)
setServerList([])
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mode, backendUrl])
+2 -2
View File
@@ -427,8 +427,8 @@ interface BackendServer {
}
export default function TerminalPage() {
const { mode, backendUrl, backendStatus } = useDataSource()
const isLive = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const isLive = mode === "live"
// Server list state
const [liveServers, setLiveServers] = useState<TermServer[]>([])
+2 -2
View File
@@ -758,8 +758,8 @@ const SORT_FIELDS: Array<{ field: SortField; label: string; modesOnly?: GroupMod
]
export default function TrafficPage() {
const { mode, backendUrl, backendStatus } = useDataSource()
const isLive = mode === "live" && backendStatus === true
const { mode, backendUrl } = useDataSource()
const isLive = mode === "live"
const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])
const [groupMode, setGroupMode] = useState<GroupMode>("servers")
+7 -13
View File
@@ -1293,15 +1293,15 @@ function ResourcesTab({ resources, serversList, liveApi }: { resources: ServerRe
// ── page ───────────────────────────────────────────────────────────────────────
export default function UptimePage() {
const [allServers, setAllServers] = useState<Server[]>(mockServers)
const { mode, backendUrl, backendStatus, checkBackend } = useDataSource()
/** При mode=live всегда ходим на backend. Нельзя требовать backendStatus===true: до ответа /health там undefined — иначе обзор/«Обновить» молчат. */
const liveApi = mode === "live"
const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])
const [allServers, setAllServers] = useState<Server[]>([])
const [tab, setTab] = useState<"probes" | "resources" | "speed">("probes")
const [probes, setProbes] = useState<PingProbe[]>(INIT_PROBES)
const [resources, setResources] = useState<ServerResource[]>(INIT_RESOURCES)
const [probes, setProbes] = useState<PingProbe[]>([])
const [resources, setResources] = useState<ServerResource[]>([])
const [isPaused, setIsPaused] = useState(false)
const [search, setSearch] = useState("")
const [statusFilter, setStatusFilter] = useState<"all" | "up" | "warn" | "down">("all")
@@ -1334,18 +1334,12 @@ export default function UptimePage() {
enabled: true,
})
useEffect(() => {
if (!liveApi) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setAllServers(mockServers)
return
}
/** В live каталог серверов подгружается вместе с overview (см. loadLiveOverview), иначе после «Серверы» строки ресурсов пропадают из-за .filter(r => r.server). */
}, [liveApi])
useEffect(() => {
if (liveApi) return
queueMicrotask(() => setProbes(mockProbesWithSavedStars(INIT_PROBES)))
// eslint-disable-next-line react-hooks/set-state-in-effect
setAllServers(mockServers)
setProbes(mockProbesWithSavedStars(INIT_PROBES))
setResources(INIT_RESOURCES)
}, [liveApi])
const isPausedRef = useRef(isPaused)
+5 -6
View File
@@ -128,7 +128,7 @@ type LiveSidebarCounts = SidebarCountsDto & { greTunnels?: number }
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
const { resolvedTheme, setTheme } = useTheme()
const { mode, backendUrl, backendStatus } = useDataSource()
const { mode, backendUrl } = useDataSource()
const evo = useEvoBGP()
const [mounted, setMounted] = React.useState(false)
const [liveCounts, setLiveCounts] = React.useState<LiveSidebarCounts | null>(null)
@@ -142,7 +142,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
}, [])
React.useEffect(() => {
if (mode !== "live" || backendStatus !== true) {
if (mode !== "live") {
setLiveCounts(null)
return
}
@@ -176,15 +176,14 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
cancelled = true
window.clearInterval(id)
}
}, [mode, backendStatus, backendUrl])
}, [mode, backendUrl])
const navGroups = React.useMemo((): NavGroup[] => {
function badgeFor(url: string): string | undefined {
if (mode !== "live") return mockBadges[url]
if (backendStatus !== true) return undefined
if (url === "/domains" || url === "/ip-ranges" || url === "/asns") {
if (!evo.enabled) return mockBadges[url]
if (!evo.enabled) return undefined
if (!evo.snapshot) return undefined
if (url === "/domains") return formatSidebarBadgeCount(evo.snapshot.domains.length)
if (url === "/ip-ranges") return formatSidebarBadgeCount(evo.snapshot.ipRanges.length)
@@ -214,7 +213,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
badge: mounted ? badgeFor(it.url) : undefined,
})),
}))
}, [mounted, mode, backendStatus, liveCounts, mockBadges, evo.enabled, evo.snapshot])
}, [mounted, mode, liveCounts, mockBadges, evo.enabled, evo.snapshot])
const isDark = (resolvedTheme ?? "dark") === "dark"
const appVersionLabel = formatAppVersionLabel(getAppVersion())
+5
View File
@@ -14,6 +14,11 @@ import {
export type DataSourceMode = "mock" | "live"
/** Режим «Живые» в настройках: моковые данные не подставлять. */
export function isLiveDataSourceMode(mode: DataSourceMode): boolean {
return mode === "live"
}
interface DataSourceContextValue {
mode: DataSourceMode
setMode: (m: DataSourceMode) => void
+1 -1
View File
File diff suppressed because one or more lines are too long