mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 11:27:47 +00:00
feat: updated ui to reflect secret import and approval
This commit is contained in:
@@ -677,7 +677,9 @@ export const registerRoutes = async (
|
|||||||
secretApprovalPolicyService,
|
secretApprovalPolicyService,
|
||||||
secretBlindIndexDAL,
|
secretBlindIndexDAL,
|
||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
secretApprovalRequestSecretDAL
|
secretApprovalRequestSecretDAL,
|
||||||
|
secretQueueService,
|
||||||
|
snapshotService
|
||||||
});
|
});
|
||||||
const secretRotationQueue = secretRotationQueueFactory({
|
const secretRotationQueue = secretRotationQueueFactory({
|
||||||
telemetryService,
|
telemetryService,
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export const fnSecretsFromImports = async ({
|
|||||||
let secretsFromDeeperImports: TSecretImportSecrets[] = [];
|
let secretsFromDeeperImports: TSecretImportSecrets[] = [];
|
||||||
if (deeperImports.length) {
|
if (deeperImports.length) {
|
||||||
secretsFromDeeperImports = await fnSecretsFromImports({
|
secretsFromDeeperImports = await fnSecretsFromImports({
|
||||||
allowedImports: deeperImports,
|
allowedImports: deeperImports.filter(({ isReplication }) => !isReplication),
|
||||||
secretImportDAL,
|
secretImportDAL,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { TProjectDALFactory } from "../project/project-dal";
|
|||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
import { TSecretDALFactory } from "../secret/secret-dal";
|
import { TSecretDALFactory } from "../secret/secret-dal";
|
||||||
import { TSecretQueueFactory } from "../secret/secret-queue";
|
import { TSecretQueueFactory } from "../secret/secret-queue";
|
||||||
|
import { SecretOperations } from "../secret/secret-types";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretImportDALFactory } from "./secret-import-dal";
|
import { TSecretImportDALFactory } from "./secret-import-dal";
|
||||||
import { fnSecretsFromImports } from "./secret-import-fns";
|
import { fnSecretsFromImports } from "./secret-import-fns";
|
||||||
@@ -26,7 +27,7 @@ type TSecretImportServiceFactoryDep = {
|
|||||||
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">;
|
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">;
|
||||||
projectEnvDAL: TProjectEnvDALFactory;
|
projectEnvDAL: TProjectEnvDALFactory;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets">;
|
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "replicateSecrets">;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ERR_SEC_IMP_NOT_FOUND = new BadRequestError({ message: "Secret import not found" });
|
const ERR_SEC_IMP_NOT_FOUND = new BadRequestError({ message: "Secret import not found" });
|
||||||
@@ -53,7 +54,7 @@ export const secretImportServiceFactory = ({
|
|||||||
isReplication,
|
isReplication,
|
||||||
path
|
path
|
||||||
}: TCreateSecretImportDTO) => {
|
}: TCreateSecretImportDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission, membership } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -108,12 +109,27 @@ export const secretImportServiceFactory = ({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
await secretQueueService.syncSecrets({
|
if (secImport.isReplication) {
|
||||||
secretPath: secImport.importPath,
|
const importedSecrets = await secretDAL.find({ folderId: sourceFolder?.id });
|
||||||
projectId,
|
await secretQueueService.replicateSecrets({
|
||||||
environmentSlug: importEnv.slug,
|
secretPath: secImport.importPath,
|
||||||
excludeReplication: true
|
projectId,
|
||||||
});
|
environmentSlug: importEnv.slug,
|
||||||
|
pickOnlyImportIds: [secImport.id],
|
||||||
|
folderId: sourceFolder?.id as string,
|
||||||
|
secrets: importedSecrets.map(({ id, version }) => ({ operation: SecretOperations.Create, version, id })),
|
||||||
|
// TODO(akhilmhdh): approval based replication this will fail for identity
|
||||||
|
membershipId: membership?.id as string,
|
||||||
|
environmentId: importEnv.id
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await secretQueueService.syncSecrets({
|
||||||
|
secretPath: secImport.importPath,
|
||||||
|
projectId,
|
||||||
|
environmentSlug: importEnv.slug,
|
||||||
|
excludeReplication: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return { ...secImport, importEnv };
|
return { ...secImport, importEnv };
|
||||||
};
|
};
|
||||||
@@ -283,7 +299,7 @@ export const secretImportServiceFactory = ({
|
|||||||
if (!folder) return [];
|
if (!folder) return [];
|
||||||
// this will already order by position
|
// this will already order by position
|
||||||
// so anything based on this order will also be in right position
|
// so anything based on this order will also be in right position
|
||||||
const secretImports = await secretImportDAL.find({ folderId: folder.id });
|
const secretImports = await secretImportDAL.find({ folderId: folder.id, isReplication: false });
|
||||||
|
|
||||||
const allowedImports = secretImports.filter(({ importEnv, importPath }) =>
|
const allowedImports = secretImports.filter(({ importEnv, importPath }) =>
|
||||||
permission.can(
|
permission.can(
|
||||||
|
|||||||
@@ -64,12 +64,15 @@ export const secretReplicationServiceFactory = ({
|
|||||||
}: TSecretReplicationServiceFactoryDep) => {
|
}: TSecretReplicationServiceFactoryDep) => {
|
||||||
queueService.start(QueueName.SecretReplication, async (job) => {
|
queueService.start(QueueName.SecretReplication, async (job) => {
|
||||||
logger.info(job.data, "Replication started");
|
logger.info(job.data, "Replication started");
|
||||||
const { secrets, folderId, secretPath, environmentId, projectId, membershipId } = job.data;
|
const { secrets, folderId, secretPath, environmentId, projectId, membershipId, pickOnlyImportIds } = job.data;
|
||||||
const secretImports = await secretImportDAL.find({
|
let secretImports = await secretImportDAL.find({
|
||||||
importPath: secretPath,
|
importPath: secretPath,
|
||||||
importEnv: environmentId,
|
importEnv: environmentId,
|
||||||
isReplication: true
|
isReplication: true
|
||||||
});
|
});
|
||||||
|
secretImports = pickOnlyImportIds
|
||||||
|
? secretImports.filter(({ id }) => pickOnlyImportIds?.includes(id))
|
||||||
|
: secretImports;
|
||||||
if (!secretImports.length || !secrets.length) return;
|
if (!secretImports.length || !secrets.length) return;
|
||||||
|
|
||||||
// unfiltered secrets to be replicated
|
// unfiltered secrets to be replicated
|
||||||
|
|||||||
@@ -143,6 +143,18 @@ export const secretQueueFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const replicateSecrets = async (dto: TSyncSecretsDTO) => {
|
||||||
|
await queueService.queue(QueueName.SecretReplication, QueueJobs.SecretReplication, dto, {
|
||||||
|
attempts: 3,
|
||||||
|
backoff: {
|
||||||
|
type: "exponential",
|
||||||
|
delay: 2000
|
||||||
|
},
|
||||||
|
removeOnComplete: true,
|
||||||
|
removeOnFail: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const removeSecretReminder = async (dto: TRemoveSecretReminderDTO) => {
|
const removeSecretReminder = async (dto: TRemoveSecretReminderDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
await queueService.stopRepeatableJob(
|
await queueService.stopRepeatableJob(
|
||||||
@@ -361,22 +373,17 @@ export const secretQueueFactory = ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
await syncIntegrations({ secretPath, projectId, environment });
|
await syncIntegrations({ secretPath, projectId, environment });
|
||||||
if (!excludeReplication) {
|
if (!excludeReplication)
|
||||||
await queueService.queue(
|
await replicateSecrets({
|
||||||
QueueName.SecretReplication,
|
environmentId,
|
||||||
QueueJobs.SecretReplication,
|
projectId,
|
||||||
{ environmentId, projectId, secretPath, folderId, secrets, membershipId },
|
secretPath,
|
||||||
{
|
folderId,
|
||||||
attempts: 3,
|
secrets,
|
||||||
backoff: {
|
membershipId,
|
||||||
type: "exponential",
|
excludeReplication,
|
||||||
delay: 2000
|
environmentSlug: environment
|
||||||
},
|
});
|
||||||
removeOnComplete: true,
|
|
||||||
removeOnFail: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
queueService.start(QueueName.IntegrationSync, async (job) => {
|
queueService.start(QueueName.IntegrationSync, async (job) => {
|
||||||
@@ -394,7 +401,8 @@ export const secretQueueFactory = ({
|
|||||||
const linkSourceDto = {
|
const linkSourceDto = {
|
||||||
projectId,
|
projectId,
|
||||||
importEnv: folder.environment.id,
|
importEnv: folder.environment.id,
|
||||||
importPath: secretPath
|
importPath: secretPath,
|
||||||
|
isReplication: false
|
||||||
};
|
};
|
||||||
const imports = await secretImportDAL.find(linkSourceDto);
|
const imports = await secretImportDAL.find(linkSourceDto);
|
||||||
|
|
||||||
@@ -598,6 +606,7 @@ export const secretQueueFactory = ({
|
|||||||
syncIntegrations,
|
syncIntegrations,
|
||||||
addSecretReminder,
|
addSecretReminder,
|
||||||
removeSecretReminder,
|
removeSecretReminder,
|
||||||
handleSecretReminder
|
handleSecretReminder,
|
||||||
|
replicateSecrets
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -550,7 +550,8 @@ export const secretServiceFactory = ({
|
|||||||
|
|
||||||
if (includeImports) {
|
if (includeImports) {
|
||||||
const secretImports = await secretImportDAL.findByFolderIds(paths.map((p) => p.folderId));
|
const secretImports = await secretImportDAL.findByFolderIds(paths.map((p) => p.folderId));
|
||||||
const allowedImports = secretImports.filter(({ importEnv, importPath }) =>
|
const allowedImports = secretImports.filter(({ importEnv, importPath, isReplication }) =>
|
||||||
|
!isReplication &&
|
||||||
// if its service token allow full access over imported one
|
// if its service token allow full access over imported one
|
||||||
actor === ActorType.SERVICE
|
actor === ActorType.SERVICE
|
||||||
? true
|
? true
|
||||||
@@ -655,7 +656,7 @@ export const secretServiceFactory = ({
|
|||||||
// then search for imported secrets
|
// then search for imported secrets
|
||||||
// here we consider the import order also thus starting from bottom
|
// here we consider the import order also thus starting from bottom
|
||||||
if (!secret && includeImports) {
|
if (!secret && includeImports) {
|
||||||
const secretImports = await secretImportDAL.find({ folderId });
|
const secretImports = await secretImportDAL.find({ folderId, isReplication: false });
|
||||||
const allowedImports = secretImports.filter(({ importEnv, importPath }) =>
|
const allowedImports = secretImports.filter(({ importEnv, importPath }) =>
|
||||||
// if its service token allow full access over imported one
|
// if its service token allow full access over imported one
|
||||||
actor === ActorType.SERVICE
|
actor === ActorType.SERVICE
|
||||||
|
|||||||
@@ -389,6 +389,8 @@ export type TSyncSecretsDTO<T extends boolean = false> = {
|
|||||||
environmentId: string;
|
environmentId: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
membershipId: string;
|
membershipId: string;
|
||||||
|
// used for import creation to trigger replication
|
||||||
|
pickOnlyImportIds?: string[];
|
||||||
secrets: {
|
secrets: {
|
||||||
operation: SecretOperations;
|
operation: SecretOperations;
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ export const useGetSecretApprovalRequestCount = ({
|
|||||||
}) =>
|
}) =>
|
||||||
useQuery({
|
useQuery({
|
||||||
queryKey: secretApprovalRequestKeys.count({ workspaceId }),
|
queryKey: secretApprovalRequestKeys.count({ workspaceId }),
|
||||||
|
refetchInterval: 5000,
|
||||||
queryFn: () => fetchSecretApprovalRequestCount({ workspaceId }),
|
queryFn: () => fetchSecretApprovalRequestCount({ workspaceId }),
|
||||||
enabled: Boolean(workspaceId) && (options?.enabled ?? true)
|
enabled: Boolean(workspaceId) && (options?.enabled ?? true)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export type TSecretApprovalSecChange = {
|
|||||||
|
|
||||||
export type TSecretApprovalRequest<J extends unknown = EncryptedSecret> = {
|
export type TSecretApprovalRequest<J extends unknown = EncryptedSecret> = {
|
||||||
id: string;
|
id: string;
|
||||||
|
isReplication?: boolean;
|
||||||
slug: string;
|
slug: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
committerId: string;
|
committerId: string;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export type TSecretImport = {
|
|||||||
position: string;
|
position: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
isReplication?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetImportedFoldersByEnvDTO = {
|
export type TGetImportedFoldersByEnvDTO = {
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export const decryptSecrets = (
|
|||||||
env: encSecret.environment,
|
env: encSecret.environment,
|
||||||
key: secretKey,
|
key: secretKey,
|
||||||
value: secretValue,
|
value: secretValue,
|
||||||
|
isReplicated: encSecret.isReplicated,
|
||||||
tags: encSecret.tags,
|
tags: encSecret.tags,
|
||||||
comment: secretComment,
|
comment: secretComment,
|
||||||
reminderRepeatDays: encSecret.secretReminderRepeatDays,
|
reminderRepeatDays: encSecret.secretReminderRepeatDays,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export type EncryptedSecret = {
|
|||||||
secretValueIV: string;
|
secretValueIV: string;
|
||||||
secretValueTag: string;
|
secretValueTag: string;
|
||||||
__v: number;
|
__v: number;
|
||||||
|
isReplicated?: boolean;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
@@ -31,6 +32,7 @@ export type DecryptedSecret = {
|
|||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
comment: string;
|
comment: string;
|
||||||
|
isReplicated?: boolean;
|
||||||
reminderRepeatDays?: number | null;
|
reminderRepeatDays?: number | null;
|
||||||
reminderNote?: string | null;
|
reminderNote?: string | null;
|
||||||
tags: WsTag[];
|
tags: WsTag[];
|
||||||
|
|||||||
+5
-3
@@ -212,7 +212,8 @@ export const SecretApprovalRequest = () => {
|
|||||||
createdAt,
|
createdAt,
|
||||||
policy,
|
policy,
|
||||||
reviewers,
|
reviewers,
|
||||||
status
|
status,
|
||||||
|
isReplication
|
||||||
} = secretApproval;
|
} = secretApproval;
|
||||||
const isApprover = policy?.approvers?.indexOf(myMembershipId || "") !== -1;
|
const isApprover = policy?.approvers?.indexOf(myMembershipId || "") !== -1;
|
||||||
const isReviewed =
|
const isReviewed =
|
||||||
@@ -240,8 +241,9 @@ export const SecretApprovalRequest = () => {
|
|||||||
Opened {formatDistance(new Date(createdAt), new Date())} ago by{" "}
|
Opened {formatDistance(new Date(createdAt), new Date())} ago by{" "}
|
||||||
{membersGroupById?.[committerId]?.user?.firstName}{" "}
|
{membersGroupById?.[committerId]?.user?.firstName}{" "}
|
||||||
{membersGroupById?.[committerId]?.user?.lastName} (
|
{membersGroupById?.[committerId]?.user?.lastName} (
|
||||||
{membersGroupById?.[committerId]?.user?.email}){" "}
|
{membersGroupById?.[committerId]?.user?.email})
|
||||||
{isApprover && !isReviewed && status === "open" && "- Review required"}
|
{isReplication && "via replication"}
|
||||||
|
{isApprover && !isReviewed && status === "open" && " - Review required"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+1
@@ -185,6 +185,7 @@ export const SecretApprovalRequestChanges = ({
|
|||||||
<div className="flex flex-grow flex-col">
|
<div className="flex flex-grow flex-col">
|
||||||
<div className="mb-1 text-lg">
|
<div className="mb-1 text-lg">
|
||||||
{generateCommitText(secretApprovalRequestDetails.commits)}
|
{generateCommitText(secretApprovalRequestDetails.commits)}
|
||||||
|
{secretApprovalRequestDetails.isReplication && <span className="text-sm text-bunker-300"> (replication)</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center text-sm text-bunker-300">
|
<div className="flex items-center text-sm text-bunker-300">
|
||||||
{committer?.user?.firstName}
|
{committer?.user?.firstName}
|
||||||
|
|||||||
+18
-3
@@ -11,7 +11,7 @@ import {
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import { EmptyState, IconButton, SecretInput, TableContainer } from "@app/components/v2";
|
import { EmptyState, IconButton, SecretInput, TableContainer, Tag } from "@app/components/v2";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||||
import { useToggle } from "@app/hooks";
|
import { useToggle } from "@app/hooks";
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ type Props = {
|
|||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
environment: string;
|
environment: string;
|
||||||
secretPath?: string;
|
secretPath?: string;
|
||||||
|
isReplication?: boolean;
|
||||||
importEnvName: string;
|
importEnvName: string;
|
||||||
importEnvPath: string;
|
importEnvPath: string;
|
||||||
importedSecrets: { key: string; value: string; overriden: { env: string; secretPath: string } }[];
|
importedSecrets: { key: string; value: string; overriden: { env: string; secretPath: string } }[];
|
||||||
@@ -27,11 +28,20 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// to show the environment and folder icon
|
// to show the environment and folder icon
|
||||||
export const EnvFolderIcon = ({ env, secretPath }: { env: string; secretPath: string }) => (
|
export const EnvFolderIcon = ({
|
||||||
|
env,
|
||||||
|
secretPath,
|
||||||
|
isReplication
|
||||||
|
}: {
|
||||||
|
env: string;
|
||||||
|
secretPath: string;
|
||||||
|
isReplication?: boolean;
|
||||||
|
}) => (
|
||||||
<div className="inline-flex items-center space-x-2">
|
<div className="inline-flex items-center space-x-2">
|
||||||
<div style={{ minWidth: "96px" }}>{env || "-"}</div>
|
<div style={{ minWidth: "96px" }}>{env || "-"}</div>
|
||||||
{secretPath && (
|
{secretPath && (
|
||||||
<div className="inline-flex items-center space-x-2 border-l border-mineshaft-600 pl-2">
|
<div className="inline-flex items-center space-x-2 border-l border-mineshaft-600 pl-2">
|
||||||
|
{isReplication && <Tag size="xs">Replication Mode</Tag>}
|
||||||
<FontAwesomeIcon icon={faFolder} className="text-md text-green-700" />
|
<FontAwesomeIcon icon={faFolder} className="text-md text-green-700" />
|
||||||
<span>{secretPath}</span>
|
<span>{secretPath}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,6 +54,7 @@ export const SecretImportItem = ({
|
|||||||
id,
|
id,
|
||||||
importEnvName,
|
importEnvName,
|
||||||
importEnvPath,
|
importEnvPath,
|
||||||
|
isReplication,
|
||||||
importedSecrets = [],
|
importedSecrets = [],
|
||||||
searchTerm = "",
|
searchTerm = "",
|
||||||
secretPath,
|
secretPath,
|
||||||
@@ -92,7 +103,11 @@ export const SecretImportItem = ({
|
|||||||
<FontAwesomeIcon icon={faFileImport} />
|
<FontAwesomeIcon icon={faFileImport} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-grow items-center px-4 py-2">
|
<div className="flex flex-grow items-center px-4 py-2">
|
||||||
<EnvFolderIcon env={importEnvName || ""} secretPath={importEnvPath} />
|
<EnvFolderIcon
|
||||||
|
env={importEnvName || ""}
|
||||||
|
secretPath={importEnvPath}
|
||||||
|
isReplication={isReplication}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-4 border-l border-mineshaft-600 px-4 py-2">
|
<div className="flex items-center space-x-4 border-l border-mineshaft-600 px-4 py-2">
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
|
|||||||
+5
-5
@@ -90,7 +90,7 @@ export const SecretImportListView = ({
|
|||||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
"deleteSecretImport"
|
"deleteSecretImport"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(MouseSensor, {}),
|
useSensor(MouseSensor, {}),
|
||||||
useSensor(TouchSensor, {}),
|
useSensor(TouchSensor, {}),
|
||||||
@@ -159,12 +159,13 @@ export const SecretImportListView = ({
|
|||||||
>
|
>
|
||||||
<SortableContext items={items} strategy={verticalListSortingStrategy}>
|
<SortableContext items={items} strategy={verticalListSortingStrategy}>
|
||||||
{items?.map((item) => {
|
{items?.map((item) => {
|
||||||
const { importPath, importEnv, id } = item;
|
const { importPath, importEnv, id, isReplication } = item;
|
||||||
return (
|
return (
|
||||||
<SecretImportItem
|
<SecretImportItem
|
||||||
searchTerm={searchTerm}
|
searchTerm={searchTerm}
|
||||||
key={`imported-env-${id}`}
|
key={`imported-env-${id}`}
|
||||||
id={id}
|
id={id}
|
||||||
|
isReplication={isReplication}
|
||||||
importEnvPath={importPath}
|
importEnvPath={importPath}
|
||||||
importEnvName={importEnv.name}
|
importEnvName={importEnv.name}
|
||||||
importedSecrets={computeImportedSecretRows(
|
importedSecrets={computeImportedSecretRows(
|
||||||
@@ -185,9 +186,8 @@ export const SecretImportListView = ({
|
|||||||
isOpen={popUp.deleteSecretImport.isOpen}
|
isOpen={popUp.deleteSecretImport.isOpen}
|
||||||
deleteKey="unlink"
|
deleteKey="unlink"
|
||||||
title="Do you want to remove this secret import?"
|
title="Do you want to remove this secret import?"
|
||||||
subTitle={`This will unlink secrets from environment ${
|
subTitle={`This will unlink secrets from environment ${(popUp.deleteSecretImport?.data as TSecretImport)?.importEnv
|
||||||
(popUp.deleteSecretImport?.data as TSecretImport)?.importEnv
|
} of path ${(popUp.deleteSecretImport?.data as TSecretImport)?.importPath}?`}
|
||||||
} of path ${(popUp.deleteSecretImport?.data as TSecretImport)?.importPath}?`}
|
|
||||||
onChange={(isOpen) => handlePopUpToggle("deleteSecretImport", isOpen)}
|
onChange={(isOpen) => handlePopUpToggle("deleteSecretImport", isOpen)}
|
||||||
onDeleteApproved={handleSecretImportDelete}
|
onDeleteApproved={handleSecretImportDelete}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -420,9 +420,8 @@ export const SecretItem = memo(
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={
|
||||||
secretReminderRepeatDays && secretReminderRepeatDays > 0
|
secretReminderRepeatDays && secretReminderRepeatDays > 0
|
||||||
? `Every ${secretReminderRepeatDays} day${
|
? `Every ${secretReminderRepeatDays} day${Number(secretReminderRepeatDays) > 1 ? "s" : ""
|
||||||
Number(secretReminderRepeatDays) > 1 ? "s" : ""
|
}
|
||||||
}
|
|
||||||
`
|
`
|
||||||
: "Reminder"
|
: "Reminder"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user