Restore the Cache tab in Realm Settings (#34311)

closes keycloak#17727

Signed-off-by: Christian Janker <christian.janker@gmx.at>
This commit is contained in:
Christian Ja 2024-11-05 14:09:35 +01:00 committed by GitHub
parent 6482e41cd8
commit 9851452be1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 159 additions and 5 deletions

View file

@ -3286,3 +3286,13 @@ somethingWentWrong=Something went wrong
somethingWentWrongDescription=Sorry, an unexpected error has occurred. somethingWentWrongDescription=Sorry, an unexpected error has occurred.
tryAgain=Try again tryAgain=Try again
errorSavingTranslations=Error saving translations\: '{{error}}' errorSavingTranslations=Error saving translations\: '{{error}}'
clearCachesTitle=Clear Caches
realmCache=Realm Cache
userCache=User Cache
keysCache=Keys Cache
clearButtonTitle=Clear
clearRealmCacheHelp=This will clear entries for all realms.
clearUserCacheHelp=This will clear entries for all realms.
clearKeysCacheHelp=Clears all entries from the cache of external public keys. These are keys of external clients or identity providers. This will clear all entries for all realms.
clearCacheSuccess=Cache cleared successfully
clearCacheError=Could not clear cache\: {{error}}

View file

@ -23,6 +23,9 @@ import { HelpHeader } from "./components/help-enabler/HelpHeader";
import { useRealm } from "./context/realm-context/RealmContext"; import { useRealm } from "./context/realm-context/RealmContext";
import { useWhoAmI } from "./context/whoami/WhoAmI"; import { useWhoAmI } from "./context/whoami/WhoAmI";
import { toDashboard } from "./dashboard/routes/Dashboard"; import { toDashboard } from "./dashboard/routes/Dashboard";
import useToggle from "./utils/useToggle";
import { PageHeaderClearCachesModal } from "./PageHeaderClearCachesModal";
import { useAccess } from "./context/access/Access";
const ManageAccountDropdownItem = () => { const ManageAccountDropdownItem = () => {
const { keycloak } = useEnvironment(); const { keycloak } = useEnvironment();
@ -67,6 +70,20 @@ const ServerInfoDropdownItem = () => {
); );
}; };
const ClearCachesDropdownItem = () => {
const { t } = useTranslation();
const [open, toggleModal] = useToggle();
return (
<>
<DropdownItem key="clear caches" onClick={() => toggleModal()}>
{t("clearCachesTitle")}
</DropdownItem>
{open && <PageHeaderClearCachesModal onClose={() => toggleModal()} />}
</>
);
};
const HelpDropdownItem = () => { const HelpDropdownItem = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { enabled, toggleHelp } = useHelp(); const { enabled, toggleHelp } = useHelp();
@ -81,23 +98,34 @@ const HelpDropdownItem = () => {
); );
}; };
const kebabDropdownItems = [ const kebabDropdownItems = (isMasterRealm: boolean, isManager: boolean) => [
<ManageAccountDropdownItem key="kebab Manage Account" />, <ManageAccountDropdownItem key="kebab Manage Account" />,
<ServerInfoDropdownItem key="kebab Server Info" />, <ServerInfoDropdownItem key="kebab Server Info" />,
...(isMasterRealm && isManager
? [<ClearCachesDropdownItem key="Clear Caches" />]
: []),
<HelpDropdownItem key="kebab Help" />, <HelpDropdownItem key="kebab Help" />,
<Divider component="li" key="kebab sign out separator" />, <Divider component="li" key="kebab sign out separator" />,
<SignOutDropdownItem key="kebab Sign out" />, <SignOutDropdownItem key="kebab Sign out" />,
]; ];
const userDropdownItems = [ const userDropdownItems = (isMasterRealm: boolean, isManager: boolean) => [
<ManageAccountDropdownItem key="Manage Account" />, <ManageAccountDropdownItem key="Manage Account" />,
<ServerInfoDropdownItem key="Server info" />, <ServerInfoDropdownItem key="Server info" />,
...(isMasterRealm && isManager
? [<ClearCachesDropdownItem key="Clear Caches" />]
: []),
<Divider component="li" key="sign out separator" />, <Divider component="li" key="sign out separator" />,
<SignOutDropdownItem key="Sign out" />, <SignOutDropdownItem key="Sign out" />,
]; ];
const KebabDropdown = () => { const KebabDropdown = () => {
const [isDropdownOpen, setDropdownOpen] = useState(false); const [isDropdownOpen, setDropdownOpen] = useState(false);
const { realm } = useRealm();
const { hasAccess } = useAccess();
const isMasterRealm = realm === "master";
const isManager = hasAccess("manage-realm");
return ( return (
<Dropdown <Dropdown
@ -116,7 +144,9 @@ const KebabDropdown = () => {
)} )}
isOpen={isDropdownOpen} isOpen={isDropdownOpen}
> >
<DropdownList>{kebabDropdownItems}</DropdownList> <DropdownList>
{kebabDropdownItems(isMasterRealm, isManager)}
</DropdownList>
</Dropdown> </Dropdown>
); );
}; };
@ -124,6 +154,11 @@ const KebabDropdown = () => {
const UserDropdown = () => { const UserDropdown = () => {
const { whoAmI } = useWhoAmI(); const { whoAmI } = useWhoAmI();
const [isDropdownOpen, setDropdownOpen] = useState(false); const [isDropdownOpen, setDropdownOpen] = useState(false);
const { realm } = useRealm();
const { hasAccess } = useAccess();
const isMasterRealm = realm === "master";
const isManager = hasAccess("manage-realm");
return ( return (
<Dropdown <Dropdown
@ -140,7 +175,7 @@ const UserDropdown = () => {
</MenuToggle> </MenuToggle>
)} )}
> >
<DropdownList>{userDropdownItems}</DropdownList> <DropdownList>{userDropdownItems(isMasterRealm, isManager)}</DropdownList>
</Dropdown> </Dropdown>
); );
}; };

View file

@ -0,0 +1,101 @@
import {
AlertVariant,
Button,
Flex,
FlexItem,
List,
ListItem,
Modal,
ModalVariant,
} from "@patternfly/react-core";
import { useRealm } from "./context/realm-context/RealmContext";
import { useAdminClient } from "./admin-client";
import { useTranslation } from "react-i18next";
import { HelpItem, useAlerts } from "@keycloak/keycloak-ui-shared";
export type ClearCachesModalProps = {
onClose: () => void;
};
export const PageHeaderClearCachesModal = ({
onClose,
}: ClearCachesModalProps) => {
const { realm: realmName } = useRealm();
const { t } = useTranslation();
const { adminClient } = useAdminClient();
const { addError, addAlert } = useAlerts();
const clearCache =
(clearCacheFn: typeof adminClient.cache.clearRealmCache) =>
async (realm: string) => {
try {
await clearCacheFn({ realm });
addAlert(t("clearCacheSuccess"), AlertVariant.success);
} catch (error) {
addError("clearCacheError", error);
}
};
const clearRealmCache = clearCache(adminClient.cache.clearRealmCache);
const clearUserCache = clearCache(adminClient.cache.clearUserCache);
const clearKeysCache = clearCache(adminClient.cache.clearKeysCache);
return (
<Modal
title={t("clearCachesTitle")}
variant={ModalVariant.small}
isOpen
onClose={onClose}
onClick={(e) => e.stopPropagation()}
>
<List isPlain isBordered>
<ListItem>
<Flex justifyContent={{ default: "justifyContentSpaceBetween" }}>
<FlexItem>
{t("realmCache")}{" "}
<HelpItem
helpText={t("clearRealmCacheHelp")}
fieldLabelId="clearRealmCacheHelp"
/>
</FlexItem>
<FlexItem>
<Button onClick={() => clearRealmCache(realmName)}>
{t("clearButtonTitle")}
</Button>
</FlexItem>
</Flex>
</ListItem>
<ListItem>
<Flex justifyContent={{ default: "justifyContentSpaceBetween" }}>
<FlexItem>
{t("userCache")}{" "}
<HelpItem
helpText={t("clearUserCacheHelp")}
fieldLabelId="clearUserCacheHelp"
/>
</FlexItem>
<FlexItem>
<Button onClick={() => clearUserCache(realmName)}>
{t("clearButtonTitle")}
</Button>
</FlexItem>
</Flex>
</ListItem>
<ListItem>
<Flex justifyContent={{ default: "justifyContentSpaceBetween" }}>
<FlexItem>
{t("keysCache")}{" "}
<HelpItem
helpText={t("clearKeysCacheHelp")}
fieldLabelId="clearKeysCacheHelp"
/>
</FlexItem>
<FlexItem>
<Button onClick={() => clearKeysCache(realmName)}>
{t("clearButtonTitle")}
</Button>
</FlexItem>
</Flex>
</ListItem>
</List>
</Modal>
);
};

View file

@ -35,7 +35,7 @@ Or if you just want to clear the data so you can start fresh without downloading
pnpm delete-data pnpm delete-data
``` ```
If you want to run with a local Quarkus distribution of Keycloak for development purposes, you can do so by running this command instead: If you want to run with a local Quarkus distribution of Keycloak for development purposes, you can do so by running this command instead:
```sh ```sh
pnpm start --local pnpm start --local

View file

@ -6,6 +6,14 @@ export class Cache extends Resource<{ realm?: string }> {
method: "POST", method: "POST",
path: "/clear-user-cache", path: "/clear-user-cache",
}); });
public clearKeysCache = this.makeRequest<{}, void>({
method: "POST",
path: "/clear-keys-cache",
});
public clearRealmCache = this.makeRequest<{}, void>({
method: "POST",
path: "/clear-realm-cache",
});
constructor(client: KeycloakAdminClient) { constructor(client: KeycloakAdminClient) {
super(client, { super(client, {