UI improvements on secret deletion warning

This commit is contained in:
carlosmonastyrski
2025-04-03 16:00:41 -03:00
parent 4d8598a019
commit a0bf03b2ae
7 changed files with 243 additions and 59 deletions
@@ -474,7 +474,12 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
importedBy: z
.object({
envName: z.string(),
folderName: z.string()
folders: z
.object({
folderName: z.string(),
secrets: z.array(z.string()).optional()
})
.array()
})
.array()
.optional()
@@ -5,6 +5,8 @@ import { TableName, TSecretImports } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify } from "@app/lib/knex";
import { EnvInfo, FolderInfo, FolderResult, SecretResult } from "./secret-import-types";
export type TSecretImportDALFactory = ReturnType<typeof secretImportDALFactory>;
export const secretImportDALFactory = (db: TDbClient) => {
@@ -169,24 +171,104 @@ export const secretImportDALFactory = (db: TDbClient) => {
}
};
const getFolderIsImportedBy = async (secretPath: string, environment: string, tx?: Knex) => {
const getFolderIsImportedBy = async (
secretPath: string,
environmentId: string,
environment: string,
projectId: string,
tx?: Knex
) => {
try {
const docs = await (tx || db.replicaNode())(TableName.SecretImport)
.where({ importPath: secretPath, importEnv: environment })
const knexInstance = tx || db.replicaNode();
const folderImports = await knexInstance(TableName.SecretImport)
.where({ importPath: secretPath, importEnv: environmentId })
.join(TableName.SecretFolder, `${TableName.SecretImport}.folderId`, `${TableName.SecretFolder}.id`)
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
.select(
db.ref("name").withSchema(TableName.Environment).as("envName"),
db.ref("name").withSchema(TableName.SecretFolder).as("folderName"),
db.ref("parentId").withSchema(TableName.SecretFolder).as("parentFolderId")
knexInstance.ref("name").withSchema(TableName.Environment).as("envName"),
knexInstance.ref("name").withSchema(TableName.SecretFolder).as("folderName"),
knexInstance.ref("parentId").withSchema(TableName.SecretFolder).as("parentFolderId")
);
return docs.map(({ envName, folderName, parentFolderId }) => ({
const secretReferences = await knexInstance(TableName.SecretReferenceV2)
.where({ secretPath, environment })
.join(TableName.SecretV2, `${TableName.SecretReferenceV2}.secretId`, `${TableName.SecretV2}.id`)
.join(TableName.SecretFolder, `${TableName.SecretV2}.folderId`, `${TableName.SecretFolder}.id`)
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
.where(`${TableName.Environment}.projectId`, projectId)
.select(
knexInstance.ref("key").withSchema(TableName.SecretV2).as("secretId"),
knexInstance.ref("name").withSchema(TableName.SecretFolder).as("folderName"),
knexInstance.ref("name").withSchema(TableName.Environment).as("envName"),
knexInstance.ref("parentId").withSchema(TableName.SecretFolder).as("parentFolderId")
);
const folderResults = folderImports.map(({ envName, folderName, parentFolderId }) => ({
envName,
folderName: parentFolderId ? folderName : "Project Root Folder"
folderName: parentFolderId ? folderName : "/"
}));
const secretResults = secretReferences.map(({ envName, secretId, folderName, parentFolderId }) => ({
envName,
secretId,
folderName: parentFolderId ? folderName : "/"
}));
type ResultItem = FolderResult | SecretResult;
const allResults: ResultItem[] = [...folderResults, ...secretResults];
type EnvFolderMap = {
[envName: string]: {
[folderName: string]: {
secrets: string[];
};
};
};
const groupedByEnv = allResults.reduce<EnvFolderMap>((acc, item) => {
const env = item.envName;
const folder = item.folderName;
const updatedAcc = { ...acc };
if (!updatedAcc[env]) {
updatedAcc[env] = {};
}
if (!updatedAcc[env][folder]) {
updatedAcc[env][folder] = { secrets: [] };
}
if ("secretId" in item && item.secretId) {
updatedAcc[env][folder].secrets = [...updatedAcc[env][folder].secrets, item.secretId];
}
return updatedAcc;
}, {});
const formattedResult: EnvInfo[] = Object.keys(groupedByEnv).map((envName) => {
const envData = groupedByEnv[envName];
const folders: FolderInfo[] = Object.keys(envData).map((folderName) => {
const folderData = envData[folderName];
const hasSecrets = folderData.secrets.length > 0;
return {
folderName,
...(hasSecrets && { secrets: folderData.secrets })
};
});
return {
envName,
folders
};
});
return formattedResult;
} catch (error) {
throw new DatabaseError({ error, name: "get secret imports count" });
throw new DatabaseError({ error, name: "get secret imports and references" });
}
};
@@ -38,6 +38,7 @@ import {
TResyncSecretImportReplicationDTO,
TUpdateSecretImportDTO
} from "./secret-import-types";
import { logger } from "@app/lib/logger";
type TSecretImportServiceFactoryDep = {
secretImportDAL: TSecretImportDALFactory;
@@ -821,8 +822,8 @@ export const secretImportServiceFactory = ({
message: `Folder with path '${secretPath}' in environment with slug '${environment}' not found`
});
const importedBy = await secretImportDAL.getFolderIsImportedBy(secretPath, folder.envId);
const importedBy = await secretImportDAL.getFolderIsImportedBy(secretPath, folder.envId, environment, projectId);
logger.info("importedBy", JSON.stringify(importedBy));
return importedBy;
};
@@ -45,3 +45,24 @@ export type TGetSecretsFromImportDTO = {
environment: string;
path: string;
} & TProjectPermission;
export type FolderResult = {
envName: string;
folderName: string;
};
export type SecretResult = {
envName: string;
secretId: string;
folderName: string;
};
export type FolderInfo = {
folderName: string;
secrets?: string[];
};
export type EnvInfo = {
envName: string;
folders: FolderInfo[];
};