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'; const ConfigPeersForm: FC = ({ refreshInterval = 60000, }) => { const addPeerDialogRef = useRef({}); const confirmDialogRef = useRef({}); const [apiMessage, setApiMessage] = useState(undefined); const [confirmDialogProps, setConfirmDialogProps] = useState({ actionProceedText: '', content: '', titleText: '', }); const [inboundConnections, setInboundConnections] = useState({}); const [isEditPeerConnections, setIsEditPeerConnections] = useState(false); const [peerConnections, setPeerConnections] = useState( {}, ); const apiMessageElement = useMemo( () => apiMessage && ( ), [apiMessage], ); const { isLoading } = periodicFetch( `${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( ( nyu, [ipAddress, { networkLinkNumber, networkNumber, networkType }], ) => { nyu[ipAddress] = { ...previous[ipAddress], dbPort, dbUser, ipAddress, networkLinkNumber, networkNumber, networkType, }; return nyu; }, {}, ), ); setPeerConnections((previous) => Object.entries(peer).reduce( ( 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 ( <> No inbound connections found. } listItemKeyPrefix="config-peers-inbound-connection" listItems={inboundConnections} renderListItem={( ipAddress, { dbPort, dbUser, networkNumber, networkType }, ) => ( {`${dbUser}@${ipAddress}:${dbPort}`} {`${NETWORK_TYPES[networkType]} ${networkNumber}`} )} /> No peer connections found. } listItemKeyPrefix="config-peers-peer-connection" listItems={peerConnections} onAdd={() => { addPeerDialogRef.current.setOpen?.call(null, true); }} onDelete={() => { const pairs = Object.entries(peerConnections); const deleteRequestBody = pairs.reduce( (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 }) => ( {peer} )} /> {apiMessageElement} ); }; export default ConfigPeersForm;