mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 13:56:43 +00:00
feat(dashboard-v3): resolved create secret issue and allow empty secret values in create secret
This commit is contained in:
@@ -45,6 +45,7 @@ import { usePopUp } from "@app/hooks";
|
||||
import { useCreateFolder, useDeleteSecretBatch } from "@app/hooks/api";
|
||||
import { DecryptedSecret, TImportedSecrets, UserWsKeyPair, WsTag } from "@app/hooks/api/types";
|
||||
|
||||
import { useSelectedSecretActions, useSelectedSecrets } from "../../SecretMainPage.store";
|
||||
import { Filter, GroupBy } from "../../SecretMainPage.types";
|
||||
import { CreateSecretForm } from "./CreateSecretForm";
|
||||
import { CreateSecretImportForm } from "./CreateSecretImportForm";
|
||||
@@ -61,14 +62,13 @@ type Props = {
|
||||
filter: Filter;
|
||||
tags?: WsTag[];
|
||||
isVisible?: boolean;
|
||||
selectedSecrets: Record<string, boolean>;
|
||||
snapshotCount: number;
|
||||
isSnapshotCountLoading?: boolean;
|
||||
autoCapitalization?: boolean;
|
||||
onGroupByChange: (opt?: GroupBy) => void;
|
||||
onSearchChange: (term: string) => void;
|
||||
onToggleTagFilter: (tagId: string) => void;
|
||||
onVisiblilityToggle: () => void;
|
||||
onResetSelectedSecret: () => void;
|
||||
onClickRollbackMode: () => void;
|
||||
};
|
||||
|
||||
@@ -82,14 +82,13 @@ export const ActionBar = ({
|
||||
filter,
|
||||
tags = [],
|
||||
isVisible,
|
||||
selectedSecrets,
|
||||
snapshotCount,
|
||||
isSnapshotCountLoading,
|
||||
autoCapitalization,
|
||||
onSearchChange,
|
||||
onToggleTagFilter,
|
||||
onGroupByChange,
|
||||
onVisiblilityToggle,
|
||||
onResetSelectedSecret,
|
||||
onClickRollbackMode
|
||||
}: Props) => {
|
||||
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
|
||||
@@ -106,6 +105,8 @@ export const ActionBar = ({
|
||||
const { mutateAsync: createFolder } = useCreateFolder();
|
||||
const { mutateAsync: deleteBatchSecretV3 } = useDeleteSecretBatch();
|
||||
|
||||
const selectedSecrets = useSelectedSecrets();
|
||||
const { reset: resetSelectedSecret } = useSelectedSecretActions();
|
||||
const isMultiSelectActive = Boolean(Object.keys(selectedSecrets).length);
|
||||
|
||||
const handleFolderCreate = async (folderName: string) => {
|
||||
@@ -170,7 +171,7 @@ export const ActionBar = ({
|
||||
environment,
|
||||
secrets: bulkDeletedSecrets.map(({ key }) => ({ secretName: key, type: "shared" }))
|
||||
});
|
||||
onResetSelectedSecret();
|
||||
resetSelectedSecret();
|
||||
handlePopUpClose("bulkDeleteSecrets");
|
||||
createNotification({
|
||||
type: "success",
|
||||
@@ -376,7 +377,7 @@ export const ActionBar = ({
|
||||
>
|
||||
<div className="text-bunker-300 flex items-center bg-mineshaft-800 mt-3.5 py-2 px-4 rounded-md border border-mineshaft-600">
|
||||
<Tooltip content="Clear">
|
||||
<IconButton variant="plain" ariaLabel="clear-selection" onClick={onResetSelectedSecret}>
|
||||
<IconButton variant="plain" ariaLabel="clear-selection" onClick={resetSelectedSecret}>
|
||||
<FontAwesomeIcon icon={faMinusSquare} size="lg" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
@@ -410,6 +411,7 @@ export const ActionBar = ({
|
||||
secrets={secrets}
|
||||
environment={environment}
|
||||
workspaceId={workspaceId}
|
||||
autoCapitalize={autoCapitalization}
|
||||
secretPath={secretPath}
|
||||
decryptFileKey={decryptFileKey!}
|
||||
isOpen={popUp.addSecret.isOpen}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { DecryptedSecret, UserWsKeyPair } from "@app/hooks/api/types";
|
||||
|
||||
const typeSchema = z.object({
|
||||
key: z.string(),
|
||||
value: z.string()
|
||||
value: z.string().optional()
|
||||
});
|
||||
|
||||
type TFormSchema = z.infer<typeof typeSchema>;
|
||||
@@ -24,6 +24,7 @@ type Props = {
|
||||
isOpen?: boolean;
|
||||
onClose: () => void;
|
||||
onTogglePopUp: (isOpen: boolean) => void;
|
||||
autoCapitalize?: boolean;
|
||||
};
|
||||
|
||||
export const CreateSecretForm = ({
|
||||
@@ -33,7 +34,8 @@ export const CreateSecretForm = ({
|
||||
secretPath = "/",
|
||||
isOpen,
|
||||
onClose,
|
||||
onTogglePopUp
|
||||
onTogglePopUp,
|
||||
autoCapitalize = true
|
||||
}: Props) => {
|
||||
const {
|
||||
register,
|
||||
@@ -54,7 +56,7 @@ export const CreateSecretForm = ({
|
||||
workspaceId,
|
||||
secretPath,
|
||||
secretName: key,
|
||||
secretValue: value,
|
||||
secretValue: value || "",
|
||||
secretComment: "",
|
||||
type: "shared",
|
||||
latestFileKey: decryptFileKey
|
||||
@@ -82,7 +84,11 @@ export const CreateSecretForm = ({
|
||||
>
|
||||
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
||||
<FormControl label="Key" isError={Boolean(errors?.key)} errorText={errors?.key?.message}>
|
||||
<Input {...register("key")} placeholder="Type your secret name" />
|
||||
<Input
|
||||
{...register("key")}
|
||||
placeholder="Type your secret name"
|
||||
autoCapitalization={autoCapitalize}
|
||||
/>
|
||||
</FormControl>
|
||||
<Controller
|
||||
control={control}
|
||||
|
||||
+52
-45
@@ -168,24 +168,31 @@ export const SecretDetailSidebar = ({
|
||||
<FormControl label="Key">
|
||||
<Input isDisabled {...register("key")} />
|
||||
</FormControl>
|
||||
<Controller
|
||||
name="value"
|
||||
key="secret-value"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl label="Value">
|
||||
<SecretInput
|
||||
isReadOnly={isReadOnly}
|
||||
key="secret-value"
|
||||
isDisabled={isOverridden}
|
||||
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-bunker-800 px-2 py-1.5"
|
||||
{...field}
|
||||
autoFocus={false}
|
||||
/>
|
||||
</FormControl>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Controller
|
||||
name="value"
|
||||
key="secret-value"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControl label="Value">
|
||||
<SecretInput
|
||||
isReadOnly={isReadOnly}
|
||||
key="secret-value"
|
||||
isDisabled={isOverridden || isAllowed}
|
||||
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-bunker-800 px-2 py-1.5"
|
||||
{...field}
|
||||
autoFocus={false}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div className="mb-2">
|
||||
</ProjectPermissionCan>
|
||||
<div className="mb-2 border-b border-mineshaft-600 pb-4">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||
@@ -217,34 +224,6 @@ export const SecretDetailSidebar = ({
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<div className="my-2 mb-6 border-t border-mineshaft-600 pt-4">
|
||||
<Controller
|
||||
control={control}
|
||||
name="skipMultilineEncoding"
|
||||
render={({ field: { value, onChange, onBlur } }) => (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="skipmultiencoding-option"
|
||||
onCheckedChange={onChange}
|
||||
isChecked={value}
|
||||
onBlur={onBlur}
|
||||
isDisabled={!isAllowed}
|
||||
className="items-center"
|
||||
>
|
||||
Disable multi line encoding
|
||||
<Tooltip content="Infisical encodes multiline secrets by escaping newlines and wrappign in quotes. To disable, enable this option">
|
||||
<FontAwesomeIcon icon={faCircleQuestion} className="ml-1" size="sm" />
|
||||
</Tooltip>
|
||||
</Switch>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<FormControl label="Tags" className="">
|
||||
<div className="overflow-hidden grid gap-2 grid-flow-col auto-cols-min pt-2">
|
||||
{fields.map(({ tagColor, id: formId, name, _id }) => (
|
||||
@@ -341,6 +320,34 @@ export const SecretDetailSidebar = ({
|
||||
rows={5}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="my-2 mb-6 border-b border-mineshaft-600 pb-4">
|
||||
<Controller
|
||||
control={control}
|
||||
name="skipMultilineEncoding"
|
||||
render={({ field: { value, onChange, onBlur } }) => (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={subject(ProjectPermissionSub.Secrets, { environment, secretPath })}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="skipmultiencoding-option"
|
||||
onCheckedChange={onChange}
|
||||
isChecked={value}
|
||||
onBlur={onBlur}
|
||||
isDisabled={!isAllowed}
|
||||
className="items-center"
|
||||
>
|
||||
Disable multi line encoding
|
||||
<Tooltip content="Infisical encodes multiline secrets by escaping newlines and wrappign in quotes. To disable, enable this option">
|
||||
<FontAwesomeIcon icon={faCircleQuestion} className="ml-1" size="sm" />
|
||||
</Tooltip>
|
||||
</Switch>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="dark mb-4 text-sm text-bunker-300 flex-grow">
|
||||
<div className="mb-2">Version History</div>
|
||||
<div className="flex h-48 flex-col space-y-2 overflow-y-auto overflow-x-hidden rounded-md border border-mineshaft-600 bg-bunker-800 p-2 dark:[color-scheme:dark]">
|
||||
|
||||
@@ -13,6 +13,7 @@ import { UserWsKeyPair, WsTag } from "@app/hooks/api/types";
|
||||
|
||||
import { secretSnapshotKeys } from "~/hooks/api/secretSnapshots/queries";
|
||||
|
||||
import { useSelectedSecretActions, useSelectedSecrets } from "../../SecretMainPage.store";
|
||||
import { Filter, GroupBy, SortDir } from "../../SecretMainPage.types";
|
||||
import { SecretDetailSidebar } from "./SecretDetaiSidebar";
|
||||
import { SecretItem } from "./SecretItem";
|
||||
@@ -27,8 +28,6 @@ type Props = {
|
||||
sortDir?: SortDir;
|
||||
tags?: WsTag[];
|
||||
isVisible?: boolean;
|
||||
selectedSecrets: Record<string, boolean>;
|
||||
onToggleSecretSelect: (id: string) => void;
|
||||
};
|
||||
|
||||
const reorderSecretGroupByUnderscore = (secrets: DecryptedSecret[], sortDir: SortDir) => {
|
||||
@@ -86,9 +85,7 @@ export const SecretListView = ({
|
||||
filter,
|
||||
sortDir = SortDir.ASC,
|
||||
tags: wsTags = [],
|
||||
isVisible,
|
||||
selectedSecrets,
|
||||
onToggleSecretSelect
|
||||
isVisible
|
||||
}: Props) => {
|
||||
const { createNotification } = useNotificationContext();
|
||||
const queryClient = useQueryClient();
|
||||
@@ -114,6 +111,8 @@ export const SecretListView = ({
|
||||
onSuccess: undefined
|
||||
}
|
||||
});
|
||||
const selectedSecrets = useSelectedSecrets();
|
||||
const { toggle: toggleSelectedSecret } = useSelectedSecretActions();
|
||||
|
||||
const handleSecretOperation = async (
|
||||
operation: "create" | "update" | "delete",
|
||||
@@ -303,7 +302,7 @@ export const SecretListView = ({
|
||||
secretPath={secretPath}
|
||||
tags={wsTags}
|
||||
isSelected={selectedSecrets?.[secret._id]}
|
||||
onToggleSecretSelect={onToggleSecretSelect}
|
||||
onToggleSecretSelect={toggleSelectedSecret}
|
||||
isVisible={isVisible}
|
||||
secret={secret}
|
||||
key={secret._id}
|
||||
|
||||
Reference in New Issue
Block a user