mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 14:34:09 +00:00
Merge pull request #3528 from Infisical/ENG-2647
feat(admin): Invalidate Cache
This commit is contained in:
@@ -3,6 +3,7 @@ export {
|
||||
useAdminGrantServerAdminAccess,
|
||||
useAdminRemoveIdentitySuperAdminAccess,
|
||||
useCreateAdminUser,
|
||||
useInvalidateCache,
|
||||
useRemoveUserServerAdminAccess,
|
||||
useUpdateServerConfig,
|
||||
useUpdateServerEncryptionStrategy
|
||||
|
||||
@@ -8,6 +8,7 @@ import { adminQueryKeys, adminStandaloneKeys } from "./queries";
|
||||
import {
|
||||
RootKeyEncryptionStrategy,
|
||||
TCreateAdminUserDTO,
|
||||
TInvalidateCacheDTO,
|
||||
TServerConfig,
|
||||
TUpdateServerConfigDTO
|
||||
} from "./types";
|
||||
@@ -126,3 +127,15 @@ export const useUpdateServerEncryptionStrategy = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useInvalidateCache = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<void, object, TInvalidateCacheDTO>({
|
||||
mutationFn: async (dto) => {
|
||||
await apiRequest.post("/api/v1/admin/invalidate-cache", dto);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: adminQueryKeys.getInvalidateCache() });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
AdminGetIdentitiesFilters,
|
||||
AdminGetUsersFilters,
|
||||
AdminIntegrationsConfig,
|
||||
TGetInvalidatingCacheStatus,
|
||||
TGetServerRootKmsEncryptionDetails,
|
||||
TServerConfig
|
||||
} from "./types";
|
||||
@@ -22,8 +23,10 @@ export const adminQueryKeys = {
|
||||
getUsers: (filters: AdminGetUsersFilters) => [adminStandaloneKeys.getUsers, { filters }] as const,
|
||||
getIdentities: (filters: AdminGetIdentitiesFilters) =>
|
||||
[adminStandaloneKeys.getIdentities, { filters }] as const,
|
||||
getAdminIntegrationsConfig: () => ["admin-integrations-config"] as const,
|
||||
getServerEncryptionStrategies: () => ["server-encryption-strategies"] as const
|
||||
getAdminSlackConfig: () => ["admin-slack-config"] as const,
|
||||
getServerEncryptionStrategies: () => ["server-encryption-strategies"] as const,
|
||||
getInvalidateCache: () => ["admin-invalidate-cache"] as const,
|
||||
getAdminIntegrationsConfig: () => ["admin-integrations-config"] as const
|
||||
};
|
||||
|
||||
export const fetchServerConfig = async () => {
|
||||
@@ -118,3 +121,18 @@ export const useGetServerRootKmsEncryptionDetails = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetInvalidatingCacheStatus = (enabled = true) => {
|
||||
return useQuery({
|
||||
queryKey: adminQueryKeys.getInvalidateCache(),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TGetInvalidatingCacheStatus>(
|
||||
"/api/v1/admin/invalidating-cache-status"
|
||||
);
|
||||
|
||||
return data.invalidating;
|
||||
},
|
||||
enabled,
|
||||
refetchInterval: (data) => (data ? 3000 : false)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ export type TServerConfig = {
|
||||
enabledLoginMethods: LoginMethod[];
|
||||
authConsentContent?: string;
|
||||
pageFrameContent?: string;
|
||||
invalidatingCache: boolean;
|
||||
};
|
||||
|
||||
export type TUpdateServerConfigDTO = {
|
||||
@@ -84,3 +85,16 @@ export enum RootKeyEncryptionStrategy {
|
||||
Software = "SOFTWARE",
|
||||
HSM = "HSM"
|
||||
}
|
||||
|
||||
export enum CacheType {
|
||||
ALL = "all",
|
||||
SECRETS = "secrets"
|
||||
}
|
||||
|
||||
export type TInvalidateCacheDTO = {
|
||||
type: CacheType;
|
||||
};
|
||||
|
||||
export type TGetInvalidatingCacheStatus = {
|
||||
invalidating: boolean;
|
||||
};
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
import { IdentityPanel } from "@app/pages/admin/OverviewPage/components/IdentityPanel";
|
||||
|
||||
import { AuthPanel } from "./components/AuthPanel";
|
||||
import { CachingPanel } from "./components/CachingPanel";
|
||||
import { EncryptionPanel } from "./components/EncryptionPanel";
|
||||
import { IntegrationPanel } from "./components/IntegrationPanel";
|
||||
import { UserPanel } from "./components/UserPanel";
|
||||
@@ -42,7 +43,8 @@ enum TabSections {
|
||||
Integrations = "integrations",
|
||||
Users = "users",
|
||||
Identities = "identities",
|
||||
Kmip = "kmip"
|
||||
Kmip = "kmip",
|
||||
Caching = "caching"
|
||||
}
|
||||
|
||||
enum SignUpModes {
|
||||
@@ -164,6 +166,7 @@ export const OverviewPage = () => {
|
||||
<Tab value={TabSections.Integrations}>Integrations</Tab>
|
||||
<Tab value={TabSections.Users}>User Identities</Tab>
|
||||
<Tab value={TabSections.Identities}>Machine Identities</Tab>
|
||||
<Tab value={TabSections.Caching}>Caching</Tab>
|
||||
</div>
|
||||
</TabList>
|
||||
<TabPanel value={TabSections.Settings}>
|
||||
@@ -408,6 +411,9 @@ export const OverviewPage = () => {
|
||||
<TabPanel value={TabSections.Identities}>
|
||||
<IdentityPanel />
|
||||
</TabPanel>
|
||||
<TabPanel value={TabSections.Caching}>
|
||||
<CachingPanel />
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { faRotate } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Badge, Button, DeleteActionModal } from "@app/components/v2";
|
||||
import { useUser } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useInvalidateCache } from "@app/hooks/api";
|
||||
import { useGetInvalidatingCacheStatus } from "@app/hooks/api/admin/queries";
|
||||
import { CacheType } from "@app/hooks/api/admin/types";
|
||||
|
||||
export const CachingPanel = () => {
|
||||
const { mutateAsync: invalidateCache } = useInvalidateCache();
|
||||
const { user } = useUser();
|
||||
|
||||
const [type, setType] = useState<CacheType | null>(null);
|
||||
const [shouldPoll, setShouldPoll] = useState(false);
|
||||
|
||||
const {
|
||||
data: invalidationStatus,
|
||||
isFetching,
|
||||
refetch
|
||||
} = useGetInvalidatingCacheStatus(shouldPoll);
|
||||
const isInvalidating = Boolean(shouldPoll && (isFetching || invalidationStatus));
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"invalidateCache"
|
||||
] as const);
|
||||
|
||||
const handleInvalidateCacheSubmit = async () => {
|
||||
if (!type || isInvalidating) return;
|
||||
|
||||
try {
|
||||
await invalidateCache({ type });
|
||||
createNotification({ text: `Began invalidating ${type} cache`, type: "success" });
|
||||
setShouldPoll(true);
|
||||
handlePopUpClose("invalidateCache");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({ text: `Failed to invalidate ${type} cache`, type: "error" });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isInvalidating) return;
|
||||
|
||||
if (shouldPoll) {
|
||||
setShouldPoll(false);
|
||||
createNotification({ text: "Successfully invalidated cache", type: "success" });
|
||||
}
|
||||
}, [isInvalidating, shouldPoll]);
|
||||
|
||||
useEffect(() => {
|
||||
refetch().then((v) => setShouldPoll(v.data || false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6 flex flex-wrap items-end justify-between gap-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex flex-col">
|
||||
<div className="mb-2 flex items-center gap-3">
|
||||
<span className="text-xl font-semibold text-mineshaft-100">Secrets Cache</span>
|
||||
{isInvalidating && (
|
||||
<Badge
|
||||
variant="danger"
|
||||
className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap"
|
||||
>
|
||||
<FontAwesomeIcon icon={faRotate} className="animate-spin" />
|
||||
Invalidating Cache
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<span className="max-w-xl text-sm text-mineshaft-400">
|
||||
The encrypted secrets cache encompasses all secrets stored within the system and
|
||||
provides a temporary, secure storage location for frequently accessed credentials.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
colorSchema="danger"
|
||||
onClick={() => {
|
||||
setType(CacheType.SECRETS);
|
||||
handlePopUpOpen("invalidateCache");
|
||||
}}
|
||||
isDisabled={!user.superAdmin || isInvalidating}
|
||||
>
|
||||
Invalidate Secrets Cache
|
||||
</Button>
|
||||
</div>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.invalidateCache.isOpen}
|
||||
title={`Are you sure you want to invalidate ${type} cache?`}
|
||||
subTitle="This action is permanent and irreversible. The cache invalidation process may take several minutes to complete."
|
||||
onChange={(isOpen) => handlePopUpToggle("invalidateCache", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={handleInvalidateCacheSubmit}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user