mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 13:26:16 +00:00
improvement: add secret tag/metadata search functionality to single env view dashboard
This commit is contained in:
@@ -904,7 +904,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
projectId,
|
projectId,
|
||||||
path: secretPath,
|
path: secretPath,
|
||||||
search,
|
search,
|
||||||
tagSlugs: tags
|
tagSlugs: tags,
|
||||||
|
includeTagsInSearch: true,
|
||||||
|
includeMetadataInSearch: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (remainingLimit > 0 && totalSecretCount > adjustedOffset) {
|
if (remainingLimit > 0 && totalSecretCount > adjustedOffset) {
|
||||||
@@ -924,7 +926,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
search,
|
search,
|
||||||
limit: remainingLimit,
|
limit: remainingLimit,
|
||||||
offset: adjustedOffset,
|
offset: adjustedOffset,
|
||||||
tagSlugs: tags
|
tagSlugs: tags,
|
||||||
|
includeTagsInSearch: true,
|
||||||
|
includeMetadataInSearch: true
|
||||||
})
|
})
|
||||||
).secrets;
|
).secrets;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -415,6 +415,8 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
|||||||
filters?: {
|
filters?: {
|
||||||
search?: string;
|
search?: string;
|
||||||
tagSlugs?: string[];
|
tagSlugs?: string[];
|
||||||
|
includeTagsInSearch?: boolean;
|
||||||
|
includeMetadataInSearch?: boolean;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
@@ -433,17 +435,27 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
|||||||
.whereIn("folderId", folderIds)
|
.whereIn("folderId", folderIds)
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
if (filters?.search) {
|
if (filters?.search) {
|
||||||
void bd.whereILike("key", `%${filters?.search}%`);
|
void bd.whereILike(`${TableName.SecretV2}.key`, `%${filters?.search}%`);
|
||||||
|
if (filters?.includeTagsInSearch) {
|
||||||
|
void bd.orWhereILike(`${TableName.SecretTag}.slug`, `%${filters?.search}%`);
|
||||||
|
}
|
||||||
|
if (filters?.includeMetadataInSearch) {
|
||||||
|
void bd
|
||||||
|
.orWhereILike(`${TableName.ResourceMetadata}.key`, `%${filters?.search}%`)
|
||||||
|
.orWhereILike(`${TableName.ResourceMetadata}.value`, `%${filters?.search}%`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
void bd.whereNull("userId").orWhere({ userId: userId || null });
|
void bd
|
||||||
|
.whereNull(`${TableName.SecretV2}.userId`)
|
||||||
|
.orWhere({ [`${TableName.SecretV2}.userId` as "userId"]: userId || null });
|
||||||
})
|
})
|
||||||
.countDistinct("key");
|
.countDistinct(`${TableName.SecretV2}.key`);
|
||||||
|
|
||||||
// only need to join tags if filtering by tag slugs
|
// only need to join tags if filtering by tag slugs
|
||||||
const slugs = filters?.tagSlugs?.filter(Boolean);
|
const slugs = filters?.tagSlugs?.filter(Boolean);
|
||||||
if (slugs && slugs.length > 0) {
|
if ((slugs && slugs.length > 0) || filters?.includeTagsInSearch) {
|
||||||
void query
|
void query
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.SecretV2JnTag,
|
TableName.SecretV2JnTag,
|
||||||
@@ -454,12 +466,24 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
|||||||
TableName.SecretTag,
|
TableName.SecretTag,
|
||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
);
|
||||||
.whereIn("slug", slugs);
|
|
||||||
|
if (slugs?.length) {
|
||||||
|
void query.whereIn("slug", slugs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters?.includeMetadataInSearch) {
|
||||||
|
void query.leftJoin(
|
||||||
|
TableName.ResourceMetadata,
|
||||||
|
`${TableName.SecretV2}.id`,
|
||||||
|
`${TableName.ResourceMetadata}.secretId`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const secrets = await query;
|
const secrets = await query;
|
||||||
|
|
||||||
|
// @ts-expect-error not inferred by knex
|
||||||
return Number(secrets[0]?.count ?? 0);
|
return Number(secrets[0]?.count ?? 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "get folder secret count" });
|
throw new DatabaseError({ error, name: "get folder secret count" });
|
||||||
@@ -485,12 +509,14 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
|||||||
.whereIn(`${TableName.SecretV2}.folderId`, folderIds)
|
.whereIn(`${TableName.SecretV2}.folderId`, folderIds)
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
if (filters?.search) {
|
if (filters?.search) {
|
||||||
|
void bd.whereILike(`${TableName.SecretV2}.key`, `%${filters?.search}%`);
|
||||||
if (filters?.includeTagsInSearch) {
|
if (filters?.includeTagsInSearch) {
|
||||||
|
void bd.orWhereILike(`${TableName.SecretTag}.slug`, `%${filters?.search}%`);
|
||||||
|
}
|
||||||
|
if (filters?.includeMetadataInSearch) {
|
||||||
void bd
|
void bd
|
||||||
.whereILike(`${TableName.SecretV2}.key`, `%${filters?.search}%`)
|
.orWhereILike(`${TableName.ResourceMetadata}.key`, `%${filters?.search}%`)
|
||||||
.orWhereILike(`${TableName.SecretTag}.slug`, `%${filters?.search}%`);
|
.orWhereILike(`${TableName.ResourceMetadata}.value`, `%${filters?.search}%`);
|
||||||
} else {
|
|
||||||
void bd.whereILike(`${TableName.SecretV2}.key`, `%${filters?.search}%`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -355,6 +355,7 @@ export type TFindSecretsByFolderIdsFilter = {
|
|||||||
tagSlugs?: string[];
|
tagSlugs?: string[];
|
||||||
metadataFilter?: { key?: string; value?: string }[];
|
metadataFilter?: { key?: string; value?: string }[];
|
||||||
includeTagsInSearch?: boolean;
|
includeTagsInSearch?: boolean;
|
||||||
|
includeMetadataInSearch?: boolean;
|
||||||
keys?: string[];
|
keys?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1128,6 +1128,8 @@ export const secretServiceFactory = ({
|
|||||||
| "environment"
|
| "environment"
|
||||||
| "tagSlugs"
|
| "tagSlugs"
|
||||||
| "search"
|
| "search"
|
||||||
|
| "includeTagsInSearch"
|
||||||
|
| "includeMetadataInSearch"
|
||||||
>) => {
|
>) => {
|
||||||
const { shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
const { shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,8 @@ export type TGetSecretsRawDTO = {
|
|||||||
limit?: number;
|
limit?: number;
|
||||||
search?: string;
|
search?: string;
|
||||||
keys?: string[];
|
keys?: string[];
|
||||||
|
includeTagsInSearch?: boolean;
|
||||||
|
includeMetadataInSearch?: boolean;
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
export type TGetSecretAccessListDTO = {
|
export type TGetSecretAccessListDTO = {
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ export const SecretSearchInput = ({
|
|||||||
}}
|
}}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className="input text-md h-[2.3rem] w-full rounded-md rounded-l-none bg-mineshaft-800 py-[0.375rem] pl-2.5 pr-8 text-gray-400 placeholder-mineshaft-50 placeholder-opacity-50 outline-none duration-200 placeholder:text-sm hover:ring-bunker-400/60 focus:bg-mineshaft-700/80 focus:ring-1 focus:ring-primary-400/50"
|
className="input text-md h-[2.3rem] w-full rounded-md rounded-l-none bg-mineshaft-800 py-[0.375rem] pl-2.5 pr-8 text-gray-400 placeholder-mineshaft-50 placeholder-opacity-50 outline-none duration-200 placeholder:text-sm hover:ring-bunker-400/60 focus:bg-mineshaft-700/80 focus:ring-1 focus:ring-primary-400/50"
|
||||||
placeholder="Search by secret/folder name..."
|
placeholder="Search by secret, folder, tag or metadata..."
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user