Files
linux-cloud-router/web/src/SiteToSiteStatusPanel.tsx
T

64 lines
3.0 KiB
TypeScript

import Refresh from '@mui/icons-material/Refresh'
import { Alert, Box, Button, Stack } from '@mui/material'
import { Grid, IPSEC_HEADS, Section, duration, ipsecRows, text } from './RouterPanel.tsx'
import Toolbar from './Toolbar.tsx'
import { DETAILS_SHAPE, request, type RouterDetails } from './api.ts'
import { useRead } from './useRead.ts'
/** Reads the router's /config. */
const readDetails = () => request<RouterDetails>('GET', '/config', DETAILS_SHAPE)
/**
* How long a site-to-site connection has been up: the age of its oldest established IKE SA; "down" without one, and
* "unknown" when the router could not list its SAs.
*/
function uptimeOf(sas: RouterDetails['ipsec']['sas'], name: string) {
if (sas === null) {
return 'unknown'
}
const ages = sas.filter((sa) => sa.name === name && sa.state === 'ESTABLISHED' && sa.established !== undefined).map((sa) => Number(sa.established))
return ages.length === 0 ? 'down' : duration(Math.max(...ages))
}
/**
* The state of the router's site-to-site connections, from its /config: each with the identities and subnets the
* router resolved for it and how long it has been up, and their IPsec security associations, child SAs, connections
* and connection children.
*/
export default function SiteToSiteStatusPanel() {
const { data: details, error, busy, refresh } = useRead(readDetails)
const names = new Set((details?.network.site_to_site ?? []).map((connection) => connection.name))
const { sas, childSas, connections, children } = ipsecRows(details, (name) => names.has(name))
return (
<Box component="section">
<Toolbar description="The state of the site-to-site connections.">
<Button variant="outlined" startIcon={<Refresh />} disabled={busy} onClick={() => void refresh()}>Refresh</Button>
</Toolbar>
{error && <Alert severity="error" sx={{ mb: 2, whiteSpace: 'pre-line' }}>{error}</Alert>}
{details && (
<Stack sx={{ gap: 2 }}>
<Section title="Site-to-site connections">
<Grid
head={['Connection', 'Local identity', 'Remote identity', 'Local CIDRs', 'Remote CIDRs', 'Uptime']}
rows={(details.network.site_to_site ?? []).map((connection) => [
connection.name,
text(connection.local_identity),
text(connection.remote_identity),
text(connection.local_cidrs),
text(connection.remote_cidrs),
uptimeOf(details.ipsec.sas, connection.name),
])}
/>
</Section>
<Section title="IPsec security associations"><Grid head={IPSEC_HEADS.sas} rows={sas} /></Section>
<Section title="IPsec child SAs"><Grid head={IPSEC_HEADS.childSas} rows={childSas} /></Section>
<Section title="IPsec connections"><Grid head={IPSEC_HEADS.connections} rows={connections} /></Section>
<Section title="IPsec connection children"><Grid head={IPSEC_HEADS.children} rows={children} /></Section>
</Stack>
)}
</Box>
)
}