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:
parent
6482e41cd8
commit
9851452be1
5 changed files with 159 additions and 5 deletions
|
@ -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}}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
101
js/apps/admin-ui/src/PageHeaderClearCachesModal.tsx
Normal file
101
js/apps/admin-ui/src/PageHeaderClearCachesModal.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
|
@ -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
|
||||||
|
|
|
@ -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, {
|
||||||
|
|
Loading…
Reference in a new issue