mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 21:47:20 +00:00
Resolves #34525. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] QA'd all new/changed functionality manually
152 lines
4.6 KiB
TypeScript
152 lines
4.6 KiB
TypeScript
import React, { useCallback, useContext, useState } from "react";
|
|
import { InjectedRouter, Params } from "react-router/lib/Router";
|
|
import { useQuery } from "react-query";
|
|
import { useErrorHandler } from "react-error-boundary";
|
|
|
|
import { IConfig } from "interfaces/config";
|
|
import { IApiError } from "interfaces/errors";
|
|
import configAPI from "services/entities/config";
|
|
import { AppContext } from "context/app";
|
|
import { NotificationContext } from "context/notification";
|
|
import deepDifference from "utilities/deep_difference";
|
|
import Spinner from "components/Spinner";
|
|
import paths from "router/paths";
|
|
|
|
import SideNav from "../components/SideNav";
|
|
import ORG_SETTINGS_NAV_ITEMS from "./OrgSettingsNavItems";
|
|
import { DeepPartial } from "./cards/constants";
|
|
|
|
interface IOrgSettingsPageProps {
|
|
params: Params;
|
|
router: InjectedRouter; // v3
|
|
}
|
|
|
|
export const baseClass = "org-settings";
|
|
|
|
const OrgSettingsPage = ({ params, router }: IOrgSettingsPageProps) => {
|
|
const { section } = params;
|
|
const DEFAULT_SETTINGS_SECTION = ORG_SETTINGS_NAV_ITEMS[0];
|
|
|
|
const [isUpdatingSettings, setIsUpdatingSettings] = useState(false);
|
|
const { isFreeTier, isPremiumTier, setConfig, isSandboxMode } = useContext(
|
|
AppContext
|
|
);
|
|
|
|
if (isSandboxMode) {
|
|
// redirect to Integrations page in sandbox mode
|
|
router.push(paths.ADMIN_INTEGRATIONS);
|
|
}
|
|
const { renderFlash } = useContext(NotificationContext);
|
|
const handlePageError = useErrorHandler();
|
|
|
|
const {
|
|
data: appConfig,
|
|
isLoading: isLoadingAppConfig,
|
|
refetch: refetchConfig,
|
|
} = useQuery<IConfig, Error, IConfig>(["config"], () => configAPI.loadAll(), {
|
|
select: (data: IConfig) => data,
|
|
onSuccess: (data) => {
|
|
setConfig(data);
|
|
},
|
|
});
|
|
|
|
const onFormSubmit = useCallback(
|
|
async (formUpdates: DeepPartial<IConfig>) => {
|
|
if (!appConfig) {
|
|
return false;
|
|
}
|
|
|
|
setIsUpdatingSettings(true);
|
|
|
|
const diff = deepDifference(formUpdates, appConfig);
|
|
// send all formUpdates.agent_options because diff overrides all agent options
|
|
diff.agent_options = formUpdates.agent_options;
|
|
|
|
try {
|
|
await configAPI.update(diff);
|
|
renderFlash("success", "Successfully updated settings.");
|
|
refetchConfig();
|
|
return true;
|
|
} catch (response) {
|
|
const resp = response as undefined | { data: IApiError };
|
|
|
|
if (resp?.data.errors[0].reason.includes("could not dial smtp host")) {
|
|
renderFlash(
|
|
"error",
|
|
"Could not connect to SMTP server. Please try again."
|
|
);
|
|
} else if (resp?.data.errors) {
|
|
const reason = resp?.data.errors[0].reason;
|
|
const agentOptionsInvalid =
|
|
reason.includes("unsupported key provided") ||
|
|
reason.includes("invalid value type");
|
|
const isAgentOptionsError =
|
|
agentOptionsInvalid ||
|
|
reason.includes("script_execution_timeout' value exceeds limit.");
|
|
renderFlash(
|
|
"error",
|
|
<>
|
|
Couldn't update{" "}
|
|
{isAgentOptionsError ? "agent options" : "settings"}: {reason}
|
|
{agentOptionsInvalid && (
|
|
<>
|
|
<br />
|
|
If you're not using the latest osquery, use the fleetctl
|
|
apply --force command to override validation.
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
return false;
|
|
} finally {
|
|
setIsUpdatingSettings(false);
|
|
}
|
|
},
|
|
[appConfig, refetchConfig, renderFlash]
|
|
);
|
|
|
|
// filter out non-premium options
|
|
let navItems = ORG_SETTINGS_NAV_ITEMS;
|
|
if (!isPremiumTier) {
|
|
navItems = ORG_SETTINGS_NAV_ITEMS.filter(
|
|
(item) => item.urlSection !== "fleet-desktop"
|
|
);
|
|
}
|
|
|
|
const currentFormSection =
|
|
navItems.find((item) => item.urlSection === section) ??
|
|
DEFAULT_SETTINGS_SECTION;
|
|
|
|
const CurrentCard = currentFormSection.Card;
|
|
|
|
if (isFreeTier && section === "fleet-desktop") {
|
|
handlePageError({ status: 403 });
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className={`${baseClass}`}>
|
|
<SideNav
|
|
className={`${baseClass}__side-nav`}
|
|
navItems={navItems}
|
|
activeItem={currentFormSection.urlSection}
|
|
CurrentCard={
|
|
!isLoadingAppConfig && appConfig ? (
|
|
<CurrentCard
|
|
appConfig={appConfig}
|
|
handleSubmit={onFormSubmit}
|
|
isUpdatingSettings={isUpdatingSettings}
|
|
isPremiumTier={isPremiumTier}
|
|
router={router}
|
|
/>
|
|
) : (
|
|
<Spinner />
|
|
)
|
|
}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default OrgSettingsPage;
|