234 lines
7.6 KiB
TypeScript
234 lines
7.6 KiB
TypeScript
import { Grid } from '@mui/material';
|
|
import { FC, useMemo, useRef, useState } from 'react';
|
|
|
|
import API_BASE_URL from '../../lib/consts/API_BASE_URL';
|
|
import NETWORK_TYPES from '../../lib/consts/NETWORK_TYPES';
|
|
|
|
import AddPeerDialog from './AddPeerDialog';
|
|
import api from '../../lib/api';
|
|
import ConfirmDialog from '../ConfirmDialog';
|
|
import FlexBox from '../FlexBox';
|
|
import handleAPIError from '../../lib/handleAPIError';
|
|
import List from '../List';
|
|
import MessageBox, { Message } from '../MessageBox';
|
|
import { ExpandablePanel } from '../Panels';
|
|
import periodicFetch from '../../lib/fetchers/periodicFetch';
|
|
import State from '../State';
|
|
import { BodyText, MonoText, SmallText } from '../Text';
|
|
import useProtect from '../../hooks/useProtect';
|
|
import useProtectedState from '../../hooks/useProtectedState';
|
|
|
|
const ConfigPeersForm: FC<ConfigPeerFormProps> = ({
|
|
refreshInterval = 60000,
|
|
}) => {
|
|
const { protect } = useProtect();
|
|
|
|
const addPeerDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
|
|
const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
|
|
|
|
const [apiMessage, setAPIMessage] = useProtectedState<Message | undefined>(
|
|
undefined,
|
|
protect,
|
|
);
|
|
const [confirmDialogProps, setConfirmDialogProps] =
|
|
useState<ConfirmDialogProps>({
|
|
actionProceedText: '',
|
|
content: '',
|
|
titleText: '',
|
|
});
|
|
const [inboundConnections, setInboundConnections] =
|
|
useProtectedState<InboundConnectionList>({}, protect);
|
|
const [isEditPeerConnections, setIsEditPeerConnections] =
|
|
useState<boolean>(false);
|
|
const [peerConnections, setPeerConnections] =
|
|
useProtectedState<PeerConnectionList>({}, protect);
|
|
|
|
const apiMessageElement = useMemo(
|
|
() =>
|
|
apiMessage && (
|
|
<Grid item sm={2} xs={1}>
|
|
<MessageBox {...apiMessage} />
|
|
</Grid>
|
|
),
|
|
[apiMessage],
|
|
);
|
|
|
|
const { isLoading } = periodicFetch<APIHostConnectionOverviewList>(
|
|
`${API_BASE_URL}/host/connection`,
|
|
{
|
|
refreshInterval,
|
|
onError: (error) => {
|
|
setAPIMessage({
|
|
children: `Failed to get connection data. Error: ${error}`,
|
|
type: 'error',
|
|
});
|
|
},
|
|
onSuccess: ({
|
|
local: {
|
|
inbound: { ipAddress: ipAddressList, port: dbPort, user: dbUser },
|
|
peer,
|
|
},
|
|
}) => {
|
|
setInboundConnections((previous) =>
|
|
Object.entries(ipAddressList).reduce<InboundConnectionList>(
|
|
(
|
|
nyu,
|
|
[ipAddress, { networkLinkNumber, networkNumber, networkType }],
|
|
) => {
|
|
nyu[ipAddress] = {
|
|
...previous[ipAddress],
|
|
dbPort,
|
|
dbUser,
|
|
ipAddress,
|
|
networkLinkNumber,
|
|
networkNumber,
|
|
networkType,
|
|
};
|
|
|
|
return nyu;
|
|
},
|
|
{},
|
|
),
|
|
);
|
|
|
|
setPeerConnections((previous) =>
|
|
Object.entries(peer).reduce<PeerConnectionList>(
|
|
(
|
|
nyu,
|
|
[
|
|
peerIPAddress,
|
|
{
|
|
hostUUID,
|
|
isPing: isPingTest,
|
|
port: peerDBPort,
|
|
user: peerDBUser,
|
|
},
|
|
],
|
|
) => {
|
|
const peerKey = `${peerDBUser}@${peerIPAddress}:${peerDBPort}`;
|
|
|
|
nyu[peerKey] = {
|
|
...previous[peerKey],
|
|
dbPort: peerDBPort,
|
|
dbUser: peerDBUser,
|
|
hostUUID,
|
|
ipAddress: peerIPAddress,
|
|
isPingTest,
|
|
};
|
|
|
|
return nyu;
|
|
},
|
|
{},
|
|
),
|
|
);
|
|
},
|
|
},
|
|
);
|
|
|
|
return (
|
|
<>
|
|
<ExpandablePanel
|
|
header={<BodyText>Configure striker peers</BodyText>}
|
|
loading={isLoading}
|
|
>
|
|
<Grid columns={{ xs: 1, sm: 2 }} container spacing="1em">
|
|
<Grid item xs={1}>
|
|
<List
|
|
header="Inbound connections"
|
|
listEmpty={
|
|
<BodyText align="center">
|
|
No inbound connections found.
|
|
</BodyText>
|
|
}
|
|
listItemKeyPrefix="config-peers-inbound-connection"
|
|
listItems={inboundConnections}
|
|
renderListItem={(
|
|
ipAddress,
|
|
{ dbPort, dbUser, networkNumber, networkType },
|
|
) => (
|
|
<FlexBox spacing={0} sx={{ width: '100%' }}>
|
|
<MonoText>{`${dbUser}@${ipAddress}:${dbPort}`}</MonoText>
|
|
<SmallText>{`${NETWORK_TYPES[networkType]} ${networkNumber}`}</SmallText>
|
|
</FlexBox>
|
|
)}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={1}>
|
|
<List
|
|
header="Peer connections"
|
|
allowEdit
|
|
edit={isEditPeerConnections}
|
|
listEmpty={
|
|
<BodyText align="center">No peer connections found.</BodyText>
|
|
}
|
|
listItemKeyPrefix="config-peers-peer-connection"
|
|
listItems={peerConnections}
|
|
onAdd={() => {
|
|
addPeerDialogRef.current.setOpen?.call(null, true);
|
|
}}
|
|
onDelete={() => {
|
|
const pairs = Object.entries(peerConnections);
|
|
const deleteRequestBody =
|
|
pairs.reduce<APIDeleteHostConnectionRequestBody>(
|
|
(previous, [, { hostUUID, isChecked }]) => {
|
|
if (isChecked) {
|
|
previous.local.push(hostUUID);
|
|
}
|
|
|
|
return previous;
|
|
},
|
|
{ local: [] },
|
|
);
|
|
const deleteCount = deleteRequestBody.local.length;
|
|
|
|
if (deleteCount > 0) {
|
|
setConfirmDialogProps({
|
|
actionProceedText: 'Delete',
|
|
content: `The peer relationship between this striker and the selected ${deleteCount} host(s) will terminate. The removed peer(s) can be re-added later.`,
|
|
onProceedAppend: () => {
|
|
api
|
|
.delete('/host/connection', { data: deleteRequestBody })
|
|
.catch((error) => {
|
|
const emsg = handleAPIError(error);
|
|
|
|
emsg.children = `Failed to delete peer connection(s). ${emsg.children}`;
|
|
|
|
setAPIMessage(emsg);
|
|
});
|
|
},
|
|
proceedColour: 'red',
|
|
titleText: `Delete ${deleteCount} peer(s) from this striker?`,
|
|
});
|
|
|
|
confirmDialogRef.current.setOpen?.call(null, true);
|
|
}
|
|
}}
|
|
onEdit={() => {
|
|
setIsEditPeerConnections((previous) => !previous);
|
|
}}
|
|
onItemCheckboxChange={(key, event, isChecked) => {
|
|
peerConnections[key].isChecked = isChecked;
|
|
|
|
setPeerConnections((previous) => ({ ...previous }));
|
|
}}
|
|
renderListItem={(peer, { isPingTest = false }) => (
|
|
<FlexBox row spacing={0}>
|
|
<FlexBox spacing={0}>
|
|
<MonoText>{peer}</MonoText>
|
|
<State label="Ping" state={isPingTest} />
|
|
</FlexBox>
|
|
</FlexBox>
|
|
)}
|
|
/>
|
|
</Grid>
|
|
{apiMessageElement}
|
|
</Grid>
|
|
</ExpandablePanel>
|
|
<AddPeerDialog ref={addPeerDialogRef} />
|
|
<ConfirmDialog {...confirmDialogProps} ref={confirmDialogRef} />
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ConfigPeersForm;
|