diff --git a/graylog2-web-interface/src/components/datanode/DataNodeList/DataNodeActions.tsx b/graylog2-web-interface/src/components/datanode/DataNodeList/DataNodeActions.tsx index 56aa564849648..5c726e8fd4700 100644 --- a/graylog2-web-interface/src/components/datanode/DataNodeList/DataNodeActions.tsx +++ b/graylog2-web-interface/src/components/datanode/DataNodeList/DataNodeActions.tsx @@ -26,7 +26,7 @@ import OverlayDropdownButton from 'components/common/OverlayDropdownButton'; import { MORE_ACTIONS_TITLE, MORE_ACTIONS_HOVER_TITLE } from 'components/common/EntityDataTable/Constants'; import Routes from 'routing/Routes'; -import { rejoinDataNode, removeDataNode } from '../hooks/useDataNodes'; +import { rejoinDataNode, removeDataNode, renewDatanodeCertificate } from '../hooks/useDataNodes'; type Props = { dataNode: DataNode, @@ -34,6 +34,7 @@ type Props = { const DIALOG_TYPES = { REJOIN: 'rejoin', REMOVE: 'remove', + RENEW_CERT: 'renew', }; const DIALOG_TEXT = { [DIALOG_TYPES.REJOIN]: { @@ -101,7 +102,7 @@ const DataNodeActions = ({ dataNode }: Props) => { disabled={false} dropdownZIndex={1000}> Routes.SYSTEM.DATANODES.SHOW(dataNode.node_id)}>Edit - {}}>Renew certificate + renewDatanodeCertificate(dataNode.node_id)}>Renew certificate {}}>Restart handleAction(DIALOG_TYPES.REJOIN)}>Rejoin handleAction(DIALOG_TYPES.REMOVE)}>Remove diff --git a/graylog2-web-interface/src/components/datanode/hooks/useDataNodes.ts b/graylog2-web-interface/src/components/datanode/hooks/useDataNodes.ts index a30275cad4457..e924d573ee622 100644 --- a/graylog2-web-interface/src/components/datanode/hooks/useDataNodes.ts +++ b/graylog2-web-interface/src/components/datanode/hooks/useDataNodes.ts @@ -19,7 +19,7 @@ import { useQuery } from '@tanstack/react-query'; import { qualifyUrl } from 'util/URLUtils'; import PaginationURL from 'util/PaginationURL'; import type { DataNode } from 'preflight/types'; -import UserNotification from 'preflight/util/UserNotification'; +import UserNotification from 'util/UserNotification'; import fetch from 'logic/rest/FetchProvider'; import type { Attribute, PaginatedListJSON, SearchParams } from 'stores/PaginationTypes'; @@ -47,6 +47,14 @@ type Options = { enabled: boolean, } +export const renewDatanodeCertificate = (nodeId: string) => fetch('POST', qualifyUrl(`/certrenewal/${nodeId}`)) + .then(() => { + UserNotification.success('Certificate renewed successfully'); + }) + .catch((error) => { + UserNotification.error(`Certificate renewal failed with error: ${error}`); + }); + const fetchDataNodes = async (params?: SearchParams) => { const url = PaginationURL('/system/cluster/datanodes', params?.page, params?.pageSize, params?.query, { sort: params?.sort?.attributeId, order: params?.sort?.direction }); diff --git a/graylog2-web-interface/src/pages/DataNodePage.tsx b/graylog2-web-interface/src/pages/DataNodePage.tsx index 9e79b94fd2cd7..3fdfbc0d1bc25 100644 --- a/graylog2-web-interface/src/pages/DataNodePage.tsx +++ b/graylog2-web-interface/src/pages/DataNodePage.tsx @@ -27,6 +27,7 @@ import type { SearchParams } from 'stores/PaginationTypes'; const StyledHorizontalDl = styled.dl(({ theme }) => css` margin: ${theme.spacings.md} 0; + > dt { clear: left; float: left; @@ -36,6 +37,7 @@ const StyledHorizontalDl = styled.dl(({ theme }) => css` white-space: nowrap; width: 150px; } + > *:not(dt) { margin-bottom: ${theme.spacings.md}; margin-left: 140px; @@ -85,7 +87,7 @@ const DataNodePage = () => { title={datanode.data_node_status} aria-label={datanode.data_node_status} role="button"> - {datanode.status || 'N/A'} + {datanode.data_node_status || 'N/A'}