fleet/frontend/pages/admin/IntegrationsPage/cards/GlobalHostStatusWebhook/GlobalHostStatusWebhook.tsx
2025-09-29 12:10:41 -05:00

261 lines
8.4 KiB
TypeScript

import React, { useState, useEffect, useMemo } from "react";
import { IInputFieldParseTarget } from "interfaces/form_field";
import {
HOST_STATUS_WEBHOOK_HOST_PERCENTAGE_DROPDOWN_OPTIONS,
HOST_STATUS_WEBHOOK_WINDOW_DROPDOWN_OPTIONS,
} from "utilities/constants";
import { getCustomDropdownOptions } from "utilities/helpers";
import HostStatusWebhookPreviewModal from "pages/admin/components/HostStatusWebhookPreviewModal";
import SettingsSection from "pages/admin/components/SettingsSection";
import PageDescription from "components/PageDescription";
import Button from "components/buttons/Button";
import Checkbox from "components/forms/fields/Checkbox";
// @ts-ignore
import Dropdown from "components/forms/fields/Dropdown";
// @ts-ignore
import InputField from "components/forms/fields/InputField";
import validUrl from "components/forms/validators/valid_url";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
import { IAppConfigFormProps } from "../../../OrgSettingsPage/cards/constants";
interface IGlobalHostStatusWebhookFormData {
enableHostStatusWebhook: boolean;
destination_url?: string;
hostStatusWebhookHostPercentage: number;
hostStatusWebhookWindow: number;
}
interface IGlobalHostStatusWebhookFormErrors {
destination_url?: string;
}
const baseClass = "app-config-form";
const GlobalHostStatusWebhook = ({
appConfig,
handleSubmit,
isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled;
const [
showHostStatusWebhookPreviewModal,
setShowHostStatusWebhookPreviewModal,
] = useState(false);
const [formData, setFormData] = useState<IGlobalHostStatusWebhookFormData>({
enableHostStatusWebhook:
appConfig.webhook_settings.host_status_webhook
?.enable_host_status_webhook || false,
destination_url:
appConfig.webhook_settings.host_status_webhook?.destination_url || "",
hostStatusWebhookHostPercentage:
appConfig.webhook_settings.host_status_webhook?.host_percentage || 1,
hostStatusWebhookWindow:
appConfig.webhook_settings.host_status_webhook?.days_count || 1,
});
const {
enableHostStatusWebhook,
destination_url,
hostStatusWebhookHostPercentage,
hostStatusWebhookWindow,
} = formData;
const [
formErrors,
setFormErrors,
] = useState<IGlobalHostStatusWebhookFormErrors>({});
const onInputChange = ({ name, value }: IInputFieldParseTarget) => {
setFormData({ ...formData, [name]: value });
setFormErrors({});
};
const validateForm = () => {
const errors: IGlobalHostStatusWebhookFormErrors = {};
if (enableHostStatusWebhook) {
if (!destination_url) {
errors.destination_url = "Destination URL must be present";
} else if (!validUrl({ url: destination_url })) {
errors.destination_url = `${destination_url} is not a valid URL`;
}
}
setFormErrors(errors);
};
useEffect(() => {
validateForm();
}, [enableHostStatusWebhook]);
const toggleHostStatusWebhookPreviewModal = () => {
setShowHostStatusWebhookPreviewModal(!showHostStatusWebhookPreviewModal);
return false;
};
const onFormSubmit = (evt: React.MouseEvent<HTMLFormElement>) => {
evt.preventDefault();
// Formatting of API not UI
const formDataToSubmit = {
webhook_settings: {
host_status_webhook: {
enable_host_status_webhook: enableHostStatusWebhook,
destination_url,
host_percentage: hostStatusWebhookHostPercentage,
days_count: hostStatusWebhookWindow,
},
failing_policies_webhook:
appConfig.webhook_settings.failing_policies_webhook,
vulnerabilities_webhook:
appConfig.webhook_settings.vulnerabilities_webhook,
},
};
handleSubmit(formDataToSubmit);
};
const percentageHostsOptions = useMemo(
() =>
getCustomDropdownOptions(
HOST_STATUS_WEBHOOK_HOST_PERCENTAGE_DROPDOWN_OPTIONS,
hostStatusWebhookHostPercentage,
(val) => `${val}%`
),
// intentionally omit dependency so options only computed initially
[]
);
const windowOptions = useMemo(
() =>
getCustomDropdownOptions(
HOST_STATUS_WEBHOOK_WINDOW_DROPDOWN_OPTIONS,
hostStatusWebhookWindow,
(val) => `${val} day${val !== 1 ? "s" : ""}`
),
// intentionally omit dependency so options only computed initially
[]
);
return (
<div className={baseClass}>
<SettingsSection title="Host status webhook">
<PageDescription
variant="right-panel"
content={<>Send an alert if a portion of your hosts go offline.</>}
/>
<form className={baseClass} onSubmit={onFormSubmit} autoComplete="off">
<div
className={`form ${
gitOpsModeEnabled ? "disabled-by-gitops-mode" : ""
}`}
>
<Checkbox
onChange={onInputChange}
name="enableHostStatusWebhook"
value={enableHostStatusWebhook}
parseTarget
>
Enable host status webhook
</Checkbox>
<p className={`${baseClass}__section-description`}>
A request will be sent to your configured <b>Destination URL</b>{" "}
if the configured <b>Percentage of hosts</b> have not checked into
Fleet for the configured <b>Number of days</b>.
</p>
<Button
type="button"
variant="inverse"
onClick={toggleHostStatusWebhookPreviewModal}
>
Preview request
</Button>
{enableHostStatusWebhook && (
<>
<InputField
placeholder="https://server.com/example"
label="Destination URL"
onChange={onInputChange}
name="destination_url"
value={destination_url}
parseTarget
onBlur={validateForm}
error={formErrors.destination_url}
tooltip={
<>
Provide a URL to deliver <br />
the webhook request to.
</>
}
/>
<Dropdown
label="Percentage of hosts"
options={percentageHostsOptions}
onChange={onInputChange}
name="hostStatusWebhookHostPercentage"
value={hostStatusWebhookHostPercentage}
parseTarget
searchable={false}
onBlur={validateForm}
tooltip={
<>
Select the minimum percentage of hosts that
<br />
must fail to check into Fleet in order to trigger
<br />
the webhook request.
</>
}
/>
<Dropdown
label="Number of days"
options={windowOptions}
onChange={onInputChange}
name="hostStatusWebhookWindow"
value={hostStatusWebhookWindow}
parseTarget
searchable={false}
onBlur={validateForm}
tooltip={
<>
Select the minimum number of days that the
<br />
configured <b>Percentage of hosts</b> must fail to
<br />
check into Fleet in order to trigger the
<br />
webhook request.
</>
}
/>
</>
)}
</div>
<GitOpsModeTooltipWrapper
tipOffset={-8}
renderChildren={(disableChildren) => (
<Button
type="submit"
disabled={Object.keys(formErrors).length > 0 || disableChildren}
className="button-wrap"
isLoading={isUpdatingSettings}
>
Save
</Button>
)}
/>
</form>
</SettingsSection>
{showHostStatusWebhookPreviewModal && (
<HostStatusWebhookPreviewModal
toggleModal={toggleHostStatusWebhookPreviewModal}
/>
)}
</div>
);
};
export default GlobalHostStatusWebhook;