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