mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 22:46:24 +00:00
feature: deep search for secrets dashboard
This commit is contained in:
@@ -1 +1,5 @@
|
||||
export { useGetProjectSecretsDetails } from "./queries";
|
||||
export {
|
||||
useGetProjectSecretsDetails,
|
||||
useGetProjectSecretsOverview,
|
||||
useGetProjectSecretsQuickSearch
|
||||
} from "./queries";
|
||||
|
||||
@@ -10,12 +10,15 @@ import {
|
||||
DashboardProjectSecretsOverview,
|
||||
DashboardProjectSecretsOverviewResponse,
|
||||
DashboardSecretsOrderBy,
|
||||
TDashboardProjectSecretsQuickSearch,
|
||||
TDashboardProjectSecretsQuickSearchResponse,
|
||||
TGetDashboardProjectSecretsDetailsDTO,
|
||||
TGetDashboardProjectSecretsOverviewDTO
|
||||
TGetDashboardProjectSecretsOverviewDTO,
|
||||
TGetDashboardProjectSecretsQuickSearchDTO
|
||||
} from "@app/hooks/api/dashboard/types";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { mergePersonalSecrets } from "@app/hooks/api/secrets/queries";
|
||||
import { unique } from "@app/lib/fn/array";
|
||||
import { groupBy, unique } from "@app/lib/fn/array";
|
||||
|
||||
export const dashboardKeys = {
|
||||
all: () => ["dashboard"] as const,
|
||||
@@ -42,8 +45,18 @@ export const dashboardKeys = {
|
||||
}: TGetDashboardProjectSecretsDetailsDTO) =>
|
||||
[
|
||||
...dashboardKeys.getDashboardSecrets({ projectId, secretPath }),
|
||||
environment,
|
||||
"secrets-details",
|
||||
environment,
|
||||
params
|
||||
] as const,
|
||||
getProjectSecretsQuickSearch: ({
|
||||
projectId,
|
||||
secretPath,
|
||||
...params
|
||||
}: TGetDashboardProjectSecretsQuickSearchDTO) =>
|
||||
[
|
||||
...dashboardKeys.getDashboardSecrets({ projectId, secretPath }),
|
||||
"quick-search",
|
||||
params
|
||||
] as const
|
||||
};
|
||||
@@ -256,3 +269,101 @@ export const useGetProjectSecretsDetails = (
|
||||
keepPreviousData: true
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchProjectSecretsQuickSearch = async ({
|
||||
environments,
|
||||
tags,
|
||||
...params
|
||||
}: TGetDashboardProjectSecretsQuickSearchDTO) => {
|
||||
const { data } = await apiRequest.get<TDashboardProjectSecretsQuickSearchResponse>(
|
||||
"/api/v1/dashboard/secrets-deep-search",
|
||||
{
|
||||
params: {
|
||||
...params,
|
||||
environments: encodeURIComponent(environments.join(",")),
|
||||
tags: encodeURIComponent(
|
||||
Object.entries(tags)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
.filter(([_, enabled]) => enabled)
|
||||
.map(([tag]) => tag)
|
||||
.join(",")
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export const useGetProjectSecretsQuickSearch = (
|
||||
{
|
||||
projectId,
|
||||
secretPath,
|
||||
search = "",
|
||||
environments,
|
||||
tags
|
||||
}: TGetDashboardProjectSecretsQuickSearchDTO,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TDashboardProjectSecretsQuickSearchResponse,
|
||||
unknown,
|
||||
TDashboardProjectSecretsQuickSearch,
|
||||
ReturnType<typeof dashboardKeys.getProjectSecretsQuickSearch>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
...options,
|
||||
enabled:
|
||||
Boolean(search?.trim() || Object.values(tags).length) &&
|
||||
(options?.enabled ?? true) &&
|
||||
Boolean(environments.length),
|
||||
queryKey: dashboardKeys.getProjectSecretsQuickSearch({
|
||||
secretPath,
|
||||
search,
|
||||
projectId,
|
||||
environments,
|
||||
tags
|
||||
}),
|
||||
queryFn: () =>
|
||||
fetchProjectSecretsQuickSearch({
|
||||
secretPath,
|
||||
search,
|
||||
projectId,
|
||||
environments,
|
||||
tags
|
||||
}),
|
||||
onError: (error) => {
|
||||
if (axios.isAxiosError(error)) {
|
||||
const serverResponse = error.response?.data as { message: string };
|
||||
createNotification({
|
||||
title: "Error fetching secrets deep search",
|
||||
type: "error",
|
||||
text: serverResponse.message
|
||||
});
|
||||
}
|
||||
},
|
||||
select: useCallback((data: Awaited<ReturnType<typeof fetchProjectSecretsQuickSearch>>) => {
|
||||
const { secrets, folders, dynamicSecrets } = data;
|
||||
|
||||
const groupedFolders = groupBy(folders, (folder) => folder.path);
|
||||
const groupedSecrets = groupBy(
|
||||
mergePersonalSecrets(secrets),
|
||||
(secret) => `${secret.path === "/" ? "" : secret.path}/${secret.key}`
|
||||
);
|
||||
const groupedDynamicSecrets = groupBy(
|
||||
dynamicSecrets,
|
||||
(dynamicSecret) =>
|
||||
`${dynamicSecret.path === "/" ? "" : dynamicSecret.path}/${dynamicSecret.name}`
|
||||
);
|
||||
|
||||
return {
|
||||
folders: groupedFolders,
|
||||
secrets: groupedSecrets,
|
||||
dynamicSecrets: groupedDynamicSecrets
|
||||
};
|
||||
}, []),
|
||||
keepPreviousData: true
|
||||
});
|
||||
};
|
||||
|
||||
@@ -69,3 +69,23 @@ export type TGetDashboardProjectSecretsDetailsDTO = Omit<
|
||||
includeImports?: boolean;
|
||||
tags: Record<string, boolean>;
|
||||
};
|
||||
|
||||
export type TDashboardProjectSecretsQuickSearchResponse = {
|
||||
folders: (TSecretFolder & { environment: string; path: string })[];
|
||||
dynamicSecrets: (TDynamicSecret & { environment: string; path: string })[];
|
||||
secrets: SecretV3Raw[];
|
||||
};
|
||||
|
||||
export type TDashboardProjectSecretsQuickSearch = {
|
||||
folders: Record<string, TDashboardProjectSecretsQuickSearchResponse["folders"]>;
|
||||
secrets: Record<string, SecretV3RawSanitized[]>;
|
||||
dynamicSecrets: Record<string, TDashboardProjectSecretsQuickSearchResponse["folders"]>;
|
||||
};
|
||||
|
||||
export type TGetDashboardProjectSecretsQuickSearchDTO = {
|
||||
projectId: string;
|
||||
secretPath: string;
|
||||
tags: Record<string, boolean>;
|
||||
search: string;
|
||||
environments: string[];
|
||||
};
|
||||
|
||||
@@ -66,7 +66,8 @@ export const mergePersonalSecrets = (rawSecrets: SecretV3Raw[]) => {
|
||||
createdAt: el.createdAt,
|
||||
updatedAt: el.updatedAt,
|
||||
version: el.version,
|
||||
skipMultilineEncoding: el.skipMultilineEncoding
|
||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||
path: el.secretPath
|
||||
};
|
||||
|
||||
if (el.type === SecretType.Personal) {
|
||||
|
||||
@@ -29,7 +29,7 @@ export type EncryptedSecret = {
|
||||
tags: WsTag[];
|
||||
};
|
||||
|
||||
// both personal and shared secret stitiched together for dashboard
|
||||
// both personal and shared secret stitched together for dashboard
|
||||
export type SecretV3RawSanitized = {
|
||||
id: string;
|
||||
version: number;
|
||||
@@ -42,6 +42,7 @@ export type SecretV3RawSanitized = {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
env: string;
|
||||
path?: string;
|
||||
valueOverride?: string;
|
||||
idOverride?: string;
|
||||
overrideAction?: string;
|
||||
@@ -57,6 +58,7 @@ export type SecretV3Raw = {
|
||||
version: number;
|
||||
type: string;
|
||||
secretKey: string;
|
||||
secretPath: string;
|
||||
secretValue?: string;
|
||||
secretComment?: string;
|
||||
secretReminderNote?: string;
|
||||
|
||||
Reference in New Issue
Block a user