mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 22:28:25 +00:00
Wired frontend to use the batch stucture
This commit is contained in:
@@ -3,8 +3,8 @@ import { Switch } from '@headlessui/react';
|
|||||||
interface ToggleProps {
|
interface ToggleProps {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
setEnabled: (value: boolean) => void;
|
setEnabled: (value: boolean) => void;
|
||||||
addOverride: (value: string | undefined, pos: number) => void;
|
addOverride: (value: string | undefined, id: string) => void;
|
||||||
pos: number;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,18 +13,18 @@ interface ToggleProps {
|
|||||||
* @param {boolean} obj.enabled - whether the toggle is turned on or off
|
* @param {boolean} obj.enabled - whether the toggle is turned on or off
|
||||||
* @param {function} obj.setEnabled - change the state of the toggle
|
* @param {function} obj.setEnabled - change the state of the toggle
|
||||||
* @param {function} obj.addOverride - a function that adds an override to a certain secret
|
* @param {function} obj.addOverride - a function that adds an override to a certain secret
|
||||||
* @param {number} obj.pos - position of a certain secret
|
* @param {number} obj.id - id of a certain secret
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const Toggle = ({ enabled, setEnabled, addOverride, pos }: ToggleProps): JSX.Element => {
|
const Toggle = ({ enabled, setEnabled, addOverride, id }: ToggleProps): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Switch
|
<Switch
|
||||||
checked={enabled}
|
checked={enabled}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
if (enabled === false) {
|
if (enabled === false) {
|
||||||
addOverride('', pos);
|
addOverride('', id);
|
||||||
} else {
|
} else {
|
||||||
addOverride(undefined, pos);
|
addOverride(undefined, id);
|
||||||
}
|
}
|
||||||
setEnabled(!enabled);
|
setEnabled(!enabled);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -45,19 +45,19 @@ export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
|||||||
leaveFrom="opacity-100 scale-100"
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo="opacity-0 scale-95"
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-grey border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker border border-mineshaft-600 p-6 text-left align-middle shadow-xl transition-all">
|
||||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-bunker-200">
|
||||||
{t('dashboard:sidebar.delete-key-dialog.title')}
|
{t('dashboard:sidebar.delete-key-dialog.title')}
|
||||||
</Dialog.Title>
|
</Dialog.Title>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-bunker-300">
|
||||||
{t('dashboard:sidebar.delete-key-dialog.confirm-delete-message')}
|
{t('dashboard:sidebar.delete-key-dialog.confirm-delete-message')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 flex justify-start">
|
<div className="mt-6 flex justify-start">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex justify-center rounded-md border border-transparent bg-red-700 hover:bg-red-600 px-4 py-2 text-sm font-medium text-bunker-200 hover:text-white text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
className="inline-flex justify-center rounded-md border border-transparent bg-red-500 opacity-80 hover:opacity-100 px-4 py-2 text-sm font-medium text-bunker-100 text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { Tag } from 'public/data/frequentInterfaces';
|
|||||||
* @param {function} obj.modifyTags - modify tags for a certain secret
|
* @param {function} obj.modifyTags - modify tags for a certain secret
|
||||||
* @param {Tag[]} obj.position - currently selected tags for a certain secret
|
* @param {Tag[]} obj.position - currently selected tags for a certain secret
|
||||||
*/
|
*/
|
||||||
const AddTagsMenu = ({ allTags, currentTags, modifyTags, position }: { allTags: Tag[]; currentTags: Tag[]; modifyTags: (value: Tag[], position: number) => void; position: number; }) => {
|
const AddTagsMenu = ({ allTags, currentTags, modifyTags, id }: { allTags: Tag[]; currentTags: Tag[]; modifyTags: (value: Tag[], id: string) => void; id: string; }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
return (
|
return (
|
||||||
<Menu as="div" className="ml-2 relative inline-block text-left">
|
<Menu as="div" className="ml-2 relative inline-block text-left">
|
||||||
@@ -41,7 +41,7 @@ const AddTagsMenu = ({ allTags, currentTags, modifyTags, position }: { allTags:
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`${currentTags?.map(currentTag => currentTag.name).includes(tag.name) ? "opacity-30 cursor-default" : "hover:bg-mineshaft-700"} w-full text-left bg-mineshaft-800 px-2 py-0.5 text-bunker-200 rounded-sm flex items-center`}
|
className={`${currentTags?.map(currentTag => currentTag.name).includes(tag.name) ? "opacity-30 cursor-default" : "hover:bg-mineshaft-700"} w-full text-left bg-mineshaft-800 px-2 py-0.5 text-bunker-200 rounded-sm flex items-center`}
|
||||||
onClick={() => {if (!currentTags?.map(currentTag => currentTag.name).includes(tag.name)) {modifyTags(currentTags.concat([tag]), position)}}}
|
onClick={() => {if (!currentTags?.map(currentTag => currentTag.name).includes(tag.name)) {modifyTags(currentTags.concat([tag]), id)}}}
|
||||||
>
|
>
|
||||||
{currentTags?.map(currentTag => currentTag.name).includes(tag.name) ? <FontAwesomeIcon icon={faCheckSquare} className="text-xs mr-2 text-primary"/> : <FontAwesomeIcon icon={faSquare} className="text-xs mr-2"/>} {tag.name}
|
{currentTags?.map(currentTag => currentTag.name).includes(tag.name) ? <FontAwesomeIcon icon={faCheckSquare} className="text-xs mr-2 text-primary"/> : <FontAwesomeIcon icon={faSquare} className="text-xs mr-2"/>} {tag.name}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import { useTranslation } from 'next-i18next';
|
|||||||
const CommentField = ({
|
const CommentField = ({
|
||||||
comment,
|
comment,
|
||||||
modifyComment,
|
modifyComment,
|
||||||
position
|
id
|
||||||
}: {
|
}: {
|
||||||
comment: string;
|
comment: string;
|
||||||
modifyComment: (value: string, posistion: number) => void;
|
modifyComment: (value: string, id: string) => void;
|
||||||
position: number;
|
id: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ const CommentField = ({
|
|||||||
<textarea
|
<textarea
|
||||||
className="placeholder:text-bunker-400 dark:[color-scheme:dark] h-32 w-full bg-bunker-800 px-2 py-1.5 rounded-md border border-mineshaft-500 text-sm text-bunker-300 outline-none focus:ring-2 ring-primary-800 ring-opacity-70"
|
className="placeholder:text-bunker-400 dark:[color-scheme:dark] h-32 w-full bg-bunker-800 px-2 py-1.5 rounded-md border border-mineshaft-500 text-sm text-bunker-300 outline-none focus:ring-2 ring-primary-800 ring-opacity-70"
|
||||||
value={comment}
|
value={comment}
|
||||||
onChange={(e) => modifyComment(e.target.value, position)}
|
onChange={(e) => modifyComment(e.target.value, id)}
|
||||||
placeholder="Leave any comments here..."
|
placeholder="Leave any comments here..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ import { PopoverObject } from '../v2/Popover/Popover';
|
|||||||
const REGEX = /([$]{.*?})/g;
|
const REGEX = /([$]{.*?})/g;
|
||||||
|
|
||||||
interface DashboardInputFieldProps {
|
interface DashboardInputFieldProps {
|
||||||
position: number;
|
id: string;
|
||||||
onChangeHandler: (value: string, position: number) => void;
|
onChangeHandler: (value: string, id: string) => void;
|
||||||
value: string | undefined;
|
value: string | undefined;
|
||||||
type: 'varName' | 'value' | 'comment';
|
type: 'varName' | 'value' | 'comment';
|
||||||
blurred?: boolean;
|
blurred?: boolean;
|
||||||
isDuplicate?: boolean;
|
isDuplicate?: boolean;
|
||||||
isCapitalized?: boolean;
|
isCapitalized?: boolean;
|
||||||
overrideEnabled?: boolean;
|
overrideEnabled?: boolean;
|
||||||
modifyValueOverride?: (value: string | undefined, position: number) => void;
|
modifyValueOverride?: (value: string | undefined, id: string) => void;
|
||||||
isSideBarOpen?: boolean;
|
isSideBarOpen?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ interface DashboardInputFieldProps {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const DashboardInputField = ({
|
const DashboardInputField = ({
|
||||||
position,
|
id,
|
||||||
onChangeHandler,
|
onChangeHandler,
|
||||||
type,
|
type,
|
||||||
value,
|
value,
|
||||||
@@ -72,7 +72,7 @@ const DashboardInputField = ({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
onChange={(e) => onChangeHandler(isCapitalized ? e.target.value.toUpperCase() : e.target.value, position)}
|
onChange={(e) => onChangeHandler(isCapitalized ? e.target.value.toUpperCase() : e.target.value, id)}
|
||||||
type={type}
|
type={type}
|
||||||
value={value}
|
value={value}
|
||||||
className={`z-10 peer font-mono ph-no-capture bg-transparent h-full caret-bunker-200 text-sm px-2 w-full min-w-16 outline-none ${
|
className={`z-10 peer font-mono ph-no-capture bg-transparent h-full caret-bunker-200 text-sm px-2 w-full min-w-16 outline-none ${
|
||||||
@@ -105,9 +105,9 @@ const DashboardInputField = ({
|
|||||||
<button type="button" onClick={() => {
|
<button type="button" onClick={() => {
|
||||||
if (modifyValueOverride) {
|
if (modifyValueOverride) {
|
||||||
if (overrideEnabled === false) {
|
if (overrideEnabled === false) {
|
||||||
modifyValueOverride('', position);
|
modifyValueOverride('', id);
|
||||||
} else {
|
} else {
|
||||||
modifyValueOverride(undefined, position);
|
modifyValueOverride(undefined, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
@@ -126,7 +126,7 @@ const DashboardInputField = ({
|
|||||||
const error = startsWithNumber || isDuplicate;
|
const error = startsWithNumber || isDuplicate;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopoverObject text={value || ''} onChangeHandler={onChangeHandler} position={position}>
|
<PopoverObject text={value || ''} onChangeHandler={onChangeHandler} id={id}>
|
||||||
<div title={value} className={`relative flex-col w-full h-10 overflow-hidden ${
|
<div title={value} className={`relative flex-col w-full h-10 overflow-hidden ${
|
||||||
isSideBarOpen && 'bg-mineshaft-700 duration-200'
|
isSideBarOpen && 'bg-mineshaft-700 duration-200'
|
||||||
}`}>
|
}`}>
|
||||||
@@ -157,7 +157,7 @@ const DashboardInputField = ({
|
|||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChangeHandler(e.target.value, position)}
|
onChange={(e) => onChangeHandler(e.target.value, id)}
|
||||||
onScroll={syncScroll}
|
onScroll={syncScroll}
|
||||||
className={`${
|
className={`${
|
||||||
blurred
|
blurred
|
||||||
@@ -175,10 +175,10 @@ const DashboardInputField = ({
|
|||||||
} ${overrideEnabled ? 'text-primary-300' : 'text-gray-400'}
|
} ${overrideEnabled ? 'text-primary-300' : 'text-gray-400'}
|
||||||
absolute flex flex-row whitespace-pre font-mono z-0 ${blurred ? 'invisible' : 'visible'} peer-focus:visible mt-0.5 ph-no-capture overflow-x-scroll bg-transparent h-10 text-sm px-2 py-2 w-full min-w-16 outline-none duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
absolute flex flex-row whitespace-pre font-mono z-0 ${blurred ? 'invisible' : 'visible'} peer-focus:visible mt-0.5 ph-no-capture overflow-x-scroll bg-transparent h-10 text-sm px-2 py-2 w-full min-w-16 outline-none duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
||||||
>
|
>
|
||||||
{value?.split(REGEX).map((word, id) => {
|
{value?.split(REGEX).map((word) => {
|
||||||
if (word.match(REGEX) !== null) {
|
if (word.match(REGEX) !== null) {
|
||||||
return (
|
return (
|
||||||
<span className="ph-no-capture text-yellow" key={`${word}.${id + 1}`}>
|
<span className="ph-no-capture text-yellow" key={id}>
|
||||||
{word.slice(0, 2)}
|
{word.slice(0, 2)}
|
||||||
<span className="ph-no-capture text-yellow-200/80">
|
<span className="ph-no-capture text-yellow-200/80">
|
||||||
{word.slice(2, word.length - 1)}
|
{word.slice(2, word.length - 1)}
|
||||||
@@ -231,7 +231,7 @@ function inputPropsAreEqual(prev: DashboardInputFieldProps, next: DashboardInput
|
|||||||
return (
|
return (
|
||||||
prev.value === next.value &&
|
prev.value === next.value &&
|
||||||
prev.type === next.type &&
|
prev.type === next.type &&
|
||||||
prev.position === next.position &&
|
prev.id === next.id &&
|
||||||
prev.blurred === next.blurred &&
|
prev.blurred === next.blurred &&
|
||||||
prev.isCapitalized === next.isCapitalized &&
|
prev.isCapitalized === next.isCapitalized &&
|
||||||
prev.overrideEnabled === next.overrideEnabled &&
|
prev.overrideEnabled === next.overrideEnabled &&
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ import { Menu, Transition } from '@headlessui/react';
|
|||||||
*/
|
*/
|
||||||
const GenerateSecretMenu = ({
|
const GenerateSecretMenu = ({
|
||||||
modifyValue,
|
modifyValue,
|
||||||
position
|
id
|
||||||
}: {
|
}: {
|
||||||
modifyValue: (value: string, position: number) => void;
|
modifyValue: (value: string, id: string) => void;
|
||||||
position: number;
|
id: string;
|
||||||
}) => {
|
}) => {
|
||||||
const [randomStringLength, setRandomStringLength] = useState(32);
|
const [randomStringLength, setRandomStringLength] = useState(32);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -51,7 +51,7 @@ const GenerateSecretMenu = ({
|
|||||||
[...Array(randomStringLength)]
|
[...Array(randomStringLength)]
|
||||||
.map(() => Math.floor(Math.random() * 16).toString(16))
|
.map(() => Math.floor(Math.random() * 16).toString(16))
|
||||||
.join(''),
|
.join(''),
|
||||||
position
|
id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import { DeleteActionButton } from './DeleteActionButton';
|
|||||||
|
|
||||||
interface KeyPairProps {
|
interface KeyPairProps {
|
||||||
keyPair: SecretDataProps;
|
keyPair: SecretDataProps;
|
||||||
modifyKey: (value: string, position: number) => void;
|
modifyKey: (value: string, id: string) => void;
|
||||||
modifyValue: (value: string, position: number) => void;
|
modifyValue: (value: string, id: string) => void;
|
||||||
modifyValueOverride: (value: string | undefined, position: number) => void;
|
modifyValueOverride: (value: string | undefined, id: string) => void;
|
||||||
modifyComment: (value: string, position: number) => void;
|
modifyComment: (value: string, id: string) => void;
|
||||||
modifyTags: (value: Tag[], position: number) => void;
|
modifyTags: (value: Tag[], id: string) => void;
|
||||||
isBlurred: boolean;
|
isBlurred: boolean;
|
||||||
isDuplicate: boolean;
|
isDuplicate: boolean;
|
||||||
toggleSidebar: (id: string) => void;
|
toggleSidebar: (id: string) => void;
|
||||||
@@ -108,7 +108,7 @@ const KeyPair = ({
|
|||||||
isCapitalized = {isCapitalized}
|
isCapitalized = {isCapitalized}
|
||||||
onChangeHandler={modifyKey}
|
onChangeHandler={modifyKey}
|
||||||
type="varName"
|
type="varName"
|
||||||
position={keyPair.pos}
|
id={keyPair.id}
|
||||||
value={keyPair.key}
|
value={keyPair.key}
|
||||||
isDuplicate={isDuplicate}
|
isDuplicate={isDuplicate}
|
||||||
overrideEnabled={keyPair.valueOverride !== undefined}
|
overrideEnabled={keyPair.valueOverride !== undefined}
|
||||||
@@ -124,7 +124,7 @@ const KeyPair = ({
|
|||||||
<DashboardInputField
|
<DashboardInputField
|
||||||
onChangeHandler={keyPair.valueOverride !== undefined ? modifyValueOverride : modifyValue}
|
onChangeHandler={keyPair.valueOverride !== undefined ? modifyValueOverride : modifyValue}
|
||||||
type="value"
|
type="value"
|
||||||
position={keyPair.pos}
|
id={keyPair.id}
|
||||||
value={keyPair.valueOverride !== undefined ? keyPair.valueOverride : keyPair.value}
|
value={keyPair.valueOverride !== undefined ? keyPair.valueOverride : keyPair.value}
|
||||||
blurred={isBlurred}
|
blurred={isBlurred}
|
||||||
overrideEnabled={keyPair.valueOverride !== undefined}
|
overrideEnabled={keyPair.valueOverride !== undefined}
|
||||||
@@ -137,7 +137,7 @@ const KeyPair = ({
|
|||||||
<DashboardInputField
|
<DashboardInputField
|
||||||
onChangeHandler={modifyComment}
|
onChangeHandler={modifyComment}
|
||||||
type="comment"
|
type="comment"
|
||||||
position={keyPair.pos}
|
id={keyPair.id}
|
||||||
value={keyPair.comment}
|
value={keyPair.comment}
|
||||||
isDuplicate={isDuplicate}
|
isDuplicate={isDuplicate}
|
||||||
isSideBarOpen={keyPair.id === sidebarSecretId}
|
isSideBarOpen={keyPair.id === sidebarSecretId}
|
||||||
@@ -149,11 +149,11 @@ const KeyPair = ({
|
|||||||
{keyPair.tags?.map((tag, index) => (
|
{keyPair.tags?.map((tag, index) => (
|
||||||
index < 2 && <div key={keyPair.pos} className={`ml-2 px-1.5 ${tagData.filter(tagDp => tagDp._id === tag._id)[0]?.color} rounded-sm text-sm ${tagData.filter(tagDp => tagDp._id === tag._id)[0]?.colorText} flex items-center`}>
|
index < 2 && <div key={keyPair.pos} className={`ml-2 px-1.5 ${tagData.filter(tagDp => tagDp._id === tag._id)[0]?.color} rounded-sm text-sm ${tagData.filter(tagDp => tagDp._id === tag._id)[0]?.colorText} flex items-center`}>
|
||||||
<span className='mb-0.5 cursor-default'>{tag.name}</span>
|
<span className='mb-0.5 cursor-default'>{tag.name}</span>
|
||||||
<FontAwesomeIcon icon={faXmark} className="ml-1 cursor-pointer p-1" onClick={() => modifyTags(keyPair.tags.filter(ttag => ttag._id !== tag._id), keyPair.pos)}/>
|
<FontAwesomeIcon icon={faXmark} className="ml-1 cursor-pointer p-1" onClick={() => modifyTags(keyPair.tags.filter(ttag => ttag._id !== tag._id), keyPair.id)}/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<AddTagsMenu allTags={tags} currentTags={keyPair.tags} modifyTags={modifyTags} position={keyPair.pos} />
|
<AddTagsMenu allTags={tags} currentTags={keyPair.tags} modifyTags={modifyTags} id={keyPair.id} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ export interface DeleteRowFunctionProps {
|
|||||||
interface SideBarProps {
|
interface SideBarProps {
|
||||||
toggleSidebar: (value: string) => void;
|
toggleSidebar: (value: string) => void;
|
||||||
data: SecretProps[];
|
data: SecretProps[];
|
||||||
modifyKey: (value: string, position: number) => void;
|
modifyKey: (value: string, id: string) => void;
|
||||||
modifyValue: (value: string, position: number) => void;
|
modifyValue: (value: string, id: string) => void;
|
||||||
modifyValueOverride: (value: string | undefined, position: number) => void;
|
modifyValueOverride: (value: string | undefined, id: string) => void;
|
||||||
modifyComment: (value: string, position: number) => void;
|
modifyComment: (value: string, id: string) => void;
|
||||||
buttonReady: boolean;
|
buttonReady: boolean;
|
||||||
savePush: () => void;
|
savePush: () => void;
|
||||||
sharedToHide: string[];
|
sharedToHide: string[];
|
||||||
@@ -110,7 +110,7 @@ const SideBar = ({
|
|||||||
<DashboardInputField
|
<DashboardInputField
|
||||||
onChangeHandler={modifyKey}
|
onChangeHandler={modifyKey}
|
||||||
type="varName"
|
type="varName"
|
||||||
position={data[0]?.pos}
|
id={data[0]?.id}
|
||||||
value={data[0]?.key}
|
value={data[0]?.key}
|
||||||
isDuplicate={false}
|
isDuplicate={false}
|
||||||
blurred={false}
|
blurred={false}
|
||||||
@@ -128,14 +128,14 @@ const SideBar = ({
|
|||||||
<DashboardInputField
|
<DashboardInputField
|
||||||
onChangeHandler={modifyValue}
|
onChangeHandler={modifyValue}
|
||||||
type="value"
|
type="value"
|
||||||
position={data[0].pos}
|
id={data[0].id}
|
||||||
value={data[0]?.value}
|
value={data[0]?.value}
|
||||||
isDuplicate={false}
|
isDuplicate={false}
|
||||||
blurred
|
blurred
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bg-bunker-800 right-[1.07rem] top-[1.6rem] z-50">
|
<div className="absolute bg-bunker-800 right-[1.07rem] top-[1.6rem] z-50">
|
||||||
<GenerateSecretMenu modifyValue={modifyValue} position={data[0]?.pos} />
|
<GenerateSecretMenu modifyValue={modifyValue} id={data[0]?.id} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -154,7 +154,7 @@ const SideBar = ({
|
|||||||
enabled={overrideEnabled}
|
enabled={overrideEnabled}
|
||||||
setEnabled={setOverrideEnabled}
|
setEnabled={setOverrideEnabled}
|
||||||
addOverride={modifyValueOverride}
|
addOverride={modifyValueOverride}
|
||||||
pos={data[0]?.pos}
|
id={data[0]?.id}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -167,14 +167,14 @@ const SideBar = ({
|
|||||||
<DashboardInputField
|
<DashboardInputField
|
||||||
onChangeHandler={modifyValueOverride}
|
onChangeHandler={modifyValueOverride}
|
||||||
type="value"
|
type="value"
|
||||||
position={data[0]?.pos}
|
id={data[0]?.id}
|
||||||
value={overrideEnabled ? data[0]?.valueOverride : data[0]?.value}
|
value={overrideEnabled ? data[0]?.valueOverride : data[0]?.value}
|
||||||
isDuplicate={false}
|
isDuplicate={false}
|
||||||
blurred
|
blurred
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute right-[0.57rem] top-[0.3rem] z-50">
|
<div className="absolute right-[0.57rem] top-[0.3rem] z-50">
|
||||||
<GenerateSecretMenu modifyValue={modifyValueOverride} position={data[0]?.pos} />
|
<GenerateSecretMenu modifyValue={modifyValueOverride} id={data[0]?.id} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -182,7 +182,7 @@ const SideBar = ({
|
|||||||
<CommentField
|
<CommentField
|
||||||
comment={data[0]?.comment}
|
comment={data[0]?.comment}
|
||||||
modifyComment={modifyComment}
|
modifyComment={modifyComment}
|
||||||
position={data[0]?.pos}
|
id={data[0]?.id}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import * as Popover from '@radix-ui/react-popover';
|
|||||||
type Props = {
|
type Props = {
|
||||||
children: any;
|
children: any;
|
||||||
text: string;
|
text: string;
|
||||||
onChangeHandler: (value: string, position: number) => void;
|
onChangeHandler: (value: string, id: string) => void;
|
||||||
position: number;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PopoverProps = Props;
|
export type PopoverProps = Props;
|
||||||
|
|
||||||
export const PopoverObject = ({children, text, onChangeHandler, position}: Props) => (
|
export const PopoverObject = ({children, text, onChangeHandler, id}: Props) => (
|
||||||
<Popover.Root>
|
<Popover.Root>
|
||||||
<Popover.Trigger asChild className='data-[state=open]:outline data-[state=open]:outline-primary data-[state=closed]:hover:outline data-[state=closed]:hover:outline-mineshaft-400'>
|
<Popover.Trigger asChild className='data-[state=open]:outline data-[state=open]:outline-primary data-[state=closed]:hover:outline data-[state=closed]:hover:outline-mineshaft-400'>
|
||||||
{children}
|
{children}
|
||||||
@@ -26,7 +26,7 @@ export const PopoverObject = ({children, text, onChangeHandler, position}: Props
|
|||||||
<div className="flex flex-col pt-2 dark">
|
<div className="flex flex-col pt-2 dark">
|
||||||
<p className="text-bunker-200 text-[15px] leading-[0px] font-medium mb-5">Comment</p>
|
<p className="text-bunker-200 text-[15px] leading-[0px] font-medium mb-5">Comment</p>
|
||||||
<textarea
|
<textarea
|
||||||
onChange={(e) => onChangeHandler(e.target.value, position)}
|
onChange={(e) => onChangeHandler(e.target.value, id)}
|
||||||
// type={type}
|
// type={type}
|
||||||
value={text}
|
value={text}
|
||||||
className='z-10 dark:[color-scheme:dark] peer h-[20rem] ph-no-capture bg-bunker-600 border border-mineshaft-500 rounded-md py-2.5 caret-bunker-200 text-sm px-2 w-full outline-none text-bunker-300 focus:text-bunker-100 placeholder:text-bunker-400 placeholder:focus:text-transparent placeholder duration-200'
|
className='z-10 dark:[color-scheme:dark] peer h-[20rem] ph-no-capture bg-bunker-600 border border-mineshaft-500 rounded-md py-2.5 caret-bunker-200 text-sm px-2 w-full outline-none text-bunker-300 focus:text-bunker-100 placeholder:text-bunker-400 placeholder:focus:text-transparent placeholder duration-200'
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import DropZone from '@app/components/dashboard/DropZone';
|
|||||||
import KeyPair from '@app/components/dashboard/KeyPair';
|
import KeyPair from '@app/components/dashboard/KeyPair';
|
||||||
import SideBar from '@app/components/dashboard/SideBar';
|
import SideBar from '@app/components/dashboard/SideBar';
|
||||||
import NavHeader from '@app/components/navigation/NavHeader';
|
import NavHeader from '@app/components/navigation/NavHeader';
|
||||||
|
import { decryptAssymmetric, decryptSymmetric } from '@app/components/utilities/cryptography/crypto';
|
||||||
import guidGenerator from '@app/components/utilities/randomId';
|
import guidGenerator from '@app/components/utilities/randomId';
|
||||||
import encryptSecrets from '@app/components/utilities/secrets/encryptSecrets';
|
import encryptSecrets from '@app/components/utilities/secrets/encryptSecrets';
|
||||||
import getSecretsForProject from '@app/components/utilities/secrets/getSecretsForProject';
|
import getSecretsForProject from '@app/components/utilities/secrets/getSecretsForProject';
|
||||||
@@ -48,6 +49,7 @@ import batchSecrets from '../api/files/batchSecrets';
|
|||||||
import getUser from '../api/user/getUser';
|
import getUser from '../api/user/getUser';
|
||||||
import checkUserAction from '../api/userActions/checkUserAction';
|
import checkUserAction from '../api/userActions/checkUserAction';
|
||||||
import registerUserAction from '../api/userActions/registerUserAction';
|
import registerUserAction from '../api/userActions/registerUserAction';
|
||||||
|
import getLatestFileKey from '../api/workspace/getLatestFileKey';
|
||||||
import getWorkspaceEnvironments from '../api/workspace/getWorkspaceEnvironments';
|
import getWorkspaceEnvironments from '../api/workspace/getWorkspaceEnvironments';
|
||||||
import getWorkspaces from '../api/workspace/getWorkspaces';
|
import getWorkspaces from '../api/workspace/getWorkspaces';
|
||||||
import getWorkspaceTags from '../api/workspace/getWorkspaceTags';
|
import getWorkspaceTags from '../api/workspace/getWorkspaceTags';
|
||||||
@@ -95,6 +97,32 @@ interface SnapshotProps {
|
|||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EncryptedSecretProps {
|
||||||
|
_id: string;
|
||||||
|
createdAt: string;
|
||||||
|
environment: string;
|
||||||
|
secretCommentCiphertext: string;
|
||||||
|
secretCommentIV: string;
|
||||||
|
secretCommentTag: string;
|
||||||
|
secretKeyCiphertext: string;
|
||||||
|
secretKeyIV: string;
|
||||||
|
secretKeyTag: string;
|
||||||
|
secretValueCiphertext: string;
|
||||||
|
secretValueIV: string;
|
||||||
|
secretValueTag: string;
|
||||||
|
type: 'personal' | 'shared';
|
||||||
|
tags: Tag[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SecretProps {
|
||||||
|
key: string;
|
||||||
|
value: string | undefined;
|
||||||
|
type: 'personal' | 'shared';
|
||||||
|
comment: string;
|
||||||
|
id: string;
|
||||||
|
tags: Tag[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this function finds the teh duplicates in an array
|
* this function finds the teh duplicates in an array
|
||||||
* @param arr - array of anything (e.g., with secret keys and types (personal/shared))
|
* @param arr - array of anything (e.g., with secret keys and types (personal/shared))
|
||||||
@@ -303,50 +331,50 @@ export default function Dashboard() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifyValue = (value: string, pos: number) => {
|
const modifyValue = (value: string, id: string) => {
|
||||||
setData((oldData) => oldData?.map((e) => (e.pos === pos ? { ...e, value } : e)));
|
setData((oldData) => oldData?.map((e) => (e.id === id ? { ...e, value } : e)));
|
||||||
setHasUnsavedChanges(true);
|
setHasUnsavedChanges(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifyValueOverride = (valueOverride: string | undefined, pos: number) => {
|
const modifyValueOverride = (valueOverride: string | undefined, id: string) => {
|
||||||
setData((oldData) => oldData?.map((e) => (e.pos === pos ? { ...e, valueOverride } : e)));
|
setData((oldData) => oldData?.map((e) => (e.id === id ? { ...e, valueOverride } : e)));
|
||||||
setHasUnsavedChanges(true);
|
setHasUnsavedChanges(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifyKey = (key: string, pos: number) => {
|
const modifyKey = (key: string, id: string) => {
|
||||||
setData((oldData) => oldData?.map((e) => (e.pos === pos ? { ...e, key } : e)));
|
setData((oldData) => oldData?.map((e) => (e.id === id ? { ...e, key } : e)));
|
||||||
setHasUnsavedChanges(true);
|
setHasUnsavedChanges(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifyComment = (comment: string, pos: number) => {
|
const modifyComment = (comment: string, id: string) => {
|
||||||
setData((oldData) => oldData?.map((e) => (e.pos === pos ? { ...e, comment } : e)));
|
setData((oldData) => oldData?.map((e) => (e.id === id ? { ...e, comment } : e)));
|
||||||
setHasUnsavedChanges(true);
|
setHasUnsavedChanges(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifyTags = (tags: Tag[], pos: number) => {
|
const modifyTags = (tags: Tag[], id: string) => {
|
||||||
setData((oldData) => oldData?.map((e) => (e.pos === pos ? { ...e, tags } : e)));
|
setData((oldData) => oldData?.map((e) => (e.id === id ? { ...e, tags } : e)));
|
||||||
setHasUnsavedChanges(true);
|
setHasUnsavedChanges(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// For speed purposes and better perforamance, we are using useCallback
|
// For speed purposes and better perforamance, we are using useCallback
|
||||||
const listenChangeValue = useCallback((value: string, pos: number) => {
|
const listenChangeValue = useCallback((value: string, id: string) => {
|
||||||
modifyValue(value, pos);
|
modifyValue(value, id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const listenChangeValueOverride = useCallback((value: string | undefined, pos: number) => {
|
const listenChangeValueOverride = useCallback((value: string | undefined, id: string) => {
|
||||||
modifyValueOverride(value, pos);
|
modifyValueOverride(value, id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const listenChangeKey = useCallback((value: string, pos: number) => {
|
const listenChangeKey = useCallback((value: string, id: string) => {
|
||||||
modifyKey(value, pos);
|
modifyKey(value, id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const listenChangeComment = useCallback((value: string, pos: number) => {
|
const listenChangeComment = useCallback((value: string, id: string) => {
|
||||||
modifyComment(value, pos);
|
modifyComment(value, id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const listenChangeTags = useCallback((value: Tag[], pos: number) => {
|
const listenChangeTags = useCallback((value: Tag[], id: string) => {
|
||||||
modifyTags(value, pos);
|
modifyTags(value, id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -354,238 +382,331 @@ export default function Dashboard() {
|
|||||||
*/
|
*/
|
||||||
// TODO(akhilmhdh): split and make it small
|
// TODO(akhilmhdh): split and make it small
|
||||||
const savePush = async (dataToPush?: SecretDataProps[]) => {
|
const savePush = async (dataToPush?: SecretDataProps[]) => {
|
||||||
setSaveLoading(true);
|
try {
|
||||||
let newData: SecretDataProps[] | null | undefined;
|
setSaveLoading(true);
|
||||||
// dataToPush is mostly used for rollbacks, otherwise we always take the current state data
|
let newData: SecretDataProps[] | null | undefined;
|
||||||
if ((dataToPush ?? [])?.length > 0) {
|
// dataToPush is mostly used for rollbacks, otherwise we always take the current state data
|
||||||
newData = dataToPush;
|
if ((dataToPush ?? [])?.length > 0) {
|
||||||
} else {
|
newData = dataToPush;
|
||||||
newData = data;
|
} else {
|
||||||
}
|
newData = data;
|
||||||
|
}
|
||||||
|
|
||||||
// Checking if any of the secret keys start with a number - if so, don't do anything
|
// Checking if any of the secret keys start with a number - if so, don't do anything
|
||||||
const nameErrors = !newData!
|
const nameErrors = !newData!
|
||||||
.map((secret) => !Number.isNaN(Number(secret.key.charAt(0))))
|
.map((secret) => !Number.isNaN(Number(secret.key.charAt(0))))
|
||||||
.every((v) => v === false);
|
.every((v) => v === false);
|
||||||
const duplicatesExist =
|
const duplicatesExist =
|
||||||
findDuplicates(data!.map((item: SecretDataProps) => item.key)).length > 0;
|
findDuplicates(data!.map((item: SecretDataProps) => item.key)).length > 0;
|
||||||
|
|
||||||
if (nameErrors) {
|
if (nameErrors) {
|
||||||
setSaveLoading(false);
|
setSaveLoading(false);
|
||||||
return createNotification({
|
return createNotification({
|
||||||
text: 'Solve all name errors before saving secrets.',
|
text: 'Solve all name errors before saving secrets.',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (duplicatesExist) {
|
if (duplicatesExist) {
|
||||||
setSaveLoading(false);
|
setSaveLoading(false);
|
||||||
return createNotification({
|
return createNotification({
|
||||||
text: 'Remove duplicated secret names before saving.',
|
text: 'Remove duplicated secret names before saving.',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedEnv?.isWriteDenied) {
|
if (selectedEnv?.isWriteDenied) {
|
||||||
setSaveLoading(false);
|
setSaveLoading(false);
|
||||||
return createNotification({
|
return createNotification({
|
||||||
text: 'You are not allowed to edit this environment',
|
text: 'You are not allowed to edit this environment',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once "Save changes" is clicked, disable that button
|
// Once "Save changes" is clicked, disable that button
|
||||||
setHasUnsavedChanges(false);
|
setHasUnsavedChanges(false);
|
||||||
|
|
||||||
const secretsToBeDeleted = initialData!
|
const secretsToBeDeleted = initialData!
|
||||||
.filter(
|
|
||||||
(initDataPoint) =>
|
|
||||||
!newData!.map((newDataPoint) => newDataPoint.id).includes(initDataPoint.id)
|
|
||||||
)
|
|
||||||
.map((secret) => secret.id);
|
|
||||||
console.log('delete', secretsToBeDeleted.length);
|
|
||||||
|
|
||||||
const secretsToBeAdded = newData!.filter(
|
|
||||||
(newDataPoint) =>
|
|
||||||
!initialData!.map((initDataPoint) => initDataPoint.id).includes(newDataPoint.id)
|
|
||||||
);
|
|
||||||
console.log('add', secretsToBeAdded.length);
|
|
||||||
|
|
||||||
const secretsToBeUpdated = newData!.filter((newDataPoint) =>
|
|
||||||
initialData!
|
|
||||||
.filter(
|
|
||||||
(initDataPoint) =>
|
|
||||||
newData!.map((dataPoint) => dataPoint.id).includes(initDataPoint.id) &&
|
|
||||||
(newData!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0].value !==
|
|
||||||
initDataPoint.value ||
|
|
||||||
newData!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0].key !==
|
|
||||||
initDataPoint.key ||
|
|
||||||
newData!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0].comment !==
|
|
||||||
initDataPoint.comment ||
|
|
||||||
newData!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0]?.tags !==
|
|
||||||
initDataPoint?.tags)
|
|
||||||
)
|
|
||||||
.map((secret) => secret.id)
|
|
||||||
.includes(newDataPoint.id)
|
|
||||||
);
|
|
||||||
console.log('update', secretsToBeUpdated.length);
|
|
||||||
|
|
||||||
const newOverrides = newData!.filter(
|
|
||||||
(newDataPoint) => newDataPoint.valueOverride !== undefined
|
|
||||||
);
|
|
||||||
const initOverrides = initialData!.filter(
|
|
||||||
(initDataPoint) => initDataPoint.valueOverride !== undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
const overridesToBeDeleted = initOverrides
|
|
||||||
.filter(
|
|
||||||
(initDataPoint) =>
|
|
||||||
!newOverrides!.map((newDataPoint) => newDataPoint.id).includes(initDataPoint.id)
|
|
||||||
)
|
|
||||||
.map((secret) => String(secret.idOverride));
|
|
||||||
console.log('override delete', overridesToBeDeleted.length);
|
|
||||||
|
|
||||||
const overridesToBeAdded = newOverrides!
|
|
||||||
.filter(
|
|
||||||
(newDataPoint) =>
|
|
||||||
!initOverrides.map((initDataPoint) => initDataPoint.id).includes(newDataPoint.id)
|
|
||||||
)
|
|
||||||
.map((override) => ({
|
|
||||||
pos: override.pos,
|
|
||||||
key: override.key,
|
|
||||||
value: String(override.valueOverride),
|
|
||||||
valueOverride: override.valueOverride,
|
|
||||||
comment: '',
|
|
||||||
id: String(override.idOverride),
|
|
||||||
idOverride: String(override.idOverride),
|
|
||||||
tags: override.tags
|
|
||||||
}));
|
|
||||||
console.log('override add', overridesToBeAdded.length);
|
|
||||||
|
|
||||||
const overridesToBeUpdated = newOverrides!
|
|
||||||
.filter((newDataPoint) =>
|
|
||||||
initOverrides
|
|
||||||
.filter(
|
.filter(
|
||||||
(initDataPoint) =>
|
(initDataPoint) =>
|
||||||
newOverrides!.map((dataPoint) => dataPoint.id).includes(initDataPoint.id) &&
|
!newData!.map((newDataPoint) => newDataPoint.id).includes(initDataPoint.id)
|
||||||
(newOverrides!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0]
|
|
||||||
.valueOverride !== initDataPoint.valueOverride ||
|
|
||||||
newOverrides!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0].key !==
|
|
||||||
initDataPoint.key ||
|
|
||||||
newOverrides!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0]
|
|
||||||
.comment !== initDataPoint.comment ||
|
|
||||||
newOverrides!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0]?.tags !== initDataPoint?.tags)
|
|
||||||
)
|
)
|
||||||
.map((secret) => secret.id)
|
.map((secret) => secret.id);
|
||||||
.includes(newDataPoint.id)
|
console.log('delete', secretsToBeDeleted.length);
|
||||||
)
|
|
||||||
.map((override) => ({
|
|
||||||
pos: override.pos,
|
|
||||||
key: override.key,
|
|
||||||
value: String(override.valueOverride),
|
|
||||||
valueOverride: override.valueOverride,
|
|
||||||
comment: '',
|
|
||||||
id: String(override.idOverride),
|
|
||||||
idOverride: String(override.idOverride),
|
|
||||||
tags: override.tags
|
|
||||||
}));
|
|
||||||
console.log('override update', overridesToBeUpdated.length);
|
|
||||||
|
|
||||||
const requests: any = []; // TODO: fix any
|
const secretsToBeAdded = newData!.filter(
|
||||||
if (secretsToBeDeleted.concat(overridesToBeDeleted).length > 0) {
|
(newDataPoint) =>
|
||||||
console.log('DELETE: ', secretsToBeDeleted.concat(overridesToBeDeleted));
|
!initialData!.map((initDataPoint) => initDataPoint.id).includes(newDataPoint.id)
|
||||||
// await deleteSecrets({ secretIds: secretsToBeDeleted.concat(overridesToBeDeleted) });
|
);
|
||||||
secretsToBeDeleted.concat(overridesToBeDeleted).forEach((_id: string) => {
|
console.log('add', secretsToBeAdded.length);
|
||||||
requests.push({
|
|
||||||
method: 'DELETE',
|
const secretsToBeUpdated = newData!.filter((newDataPoint) =>
|
||||||
secret: {
|
initialData!
|
||||||
_id
|
.filter(
|
||||||
}
|
(initDataPoint) =>
|
||||||
});
|
newData!.map((dataPoint) => dataPoint.id).includes(initDataPoint.id) &&
|
||||||
});
|
(newData!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0].value !==
|
||||||
}
|
initDataPoint.value ||
|
||||||
if (selectedEnv && secretsToBeAdded.concat(overridesToBeAdded).length > 0) {
|
newData!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0].key !==
|
||||||
const secrets = await encryptSecrets({
|
initDataPoint.key ||
|
||||||
secretsToEncrypt: secretsToBeAdded.concat(overridesToBeAdded),
|
newData!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0].comment !==
|
||||||
workspaceId,
|
initDataPoint.comment ||
|
||||||
env: selectedEnv.slug
|
JSON.stringify(newData!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0]?.tags) !==
|
||||||
});
|
JSON.stringify(initDataPoint?.tags))
|
||||||
if (secrets) {
|
)
|
||||||
console.log('ADD: ', secrets);
|
.map((secret) => secret.id)
|
||||||
// await addSecrets({ secrets, env: selectedEnv.slug, workspaceId });
|
.includes(newDataPoint.id)
|
||||||
secrets.forEach((secret) => {
|
);
|
||||||
requests.push({
|
console.log('update', secretsToBeUpdated.length);
|
||||||
method: 'POST',
|
|
||||||
secret: {
|
const newOverrides = newData!.filter(
|
||||||
type: secret.type,
|
(newDataPoint) => newDataPoint.valueOverride !== undefined
|
||||||
secretKeyCiphertext: secret.secretKeyCiphertext,
|
);
|
||||||
secretKeyIV: secret.secretKeyIV,
|
const initOverrides = initialData!.filter(
|
||||||
secretKeyTag: secret.secretKeyTag,
|
(initDataPoint) => initDataPoint.valueOverride !== undefined
|
||||||
secretValueCiphertext: secret.secretValueCiphertext,
|
);
|
||||||
secretValueIV: secret.secretValueIV,
|
|
||||||
secretValueTag: secret.secretValueTag,
|
const overridesToBeDeleted = initOverrides
|
||||||
secretCommentCiphertext: secret.secretCommentCiphertext,
|
.filter(
|
||||||
secretCommentIV: secret.secretCommentIV,
|
(initDataPoint) =>
|
||||||
secretCommentTag: secret.secretCommentTag,
|
!newOverrides!.map((newDataPoint) => newDataPoint.id).includes(initDataPoint.id)
|
||||||
tags: secret.tags
|
)
|
||||||
}
|
.map((secret) => String(secret.idOverride));
|
||||||
})
|
console.log('override delete', overridesToBeDeleted.length);
|
||||||
});
|
|
||||||
}
|
const overridesToBeAdded = newOverrides!
|
||||||
}
|
.filter(
|
||||||
if (selectedEnv && !selectedEnv.isReadDenied && secretsToBeUpdated.concat(overridesToBeUpdated).length > 0) {
|
(newDataPoint) =>
|
||||||
const secrets = await encryptSecrets({
|
!initOverrides.map((initDataPoint) => initDataPoint.id).includes(newDataPoint.id)
|
||||||
secretsToEncrypt: secretsToBeUpdated.concat(overridesToBeUpdated),
|
)
|
||||||
workspaceId,
|
.map((override) => ({
|
||||||
env: selectedEnv.slug
|
pos: override.pos,
|
||||||
});
|
key: override.key,
|
||||||
if (secrets) {
|
value: String(override.valueOverride),
|
||||||
console.log('UPDATE: ', secrets);
|
valueOverride: override.valueOverride,
|
||||||
// await updateSecrets({ secrets });
|
comment: '',
|
||||||
secrets.forEach((secret) => {
|
id: String(override.idOverride),
|
||||||
requests.push({
|
idOverride: String(override.idOverride),
|
||||||
method: 'PATCH',
|
tags: override.tags
|
||||||
secret: {
|
}));
|
||||||
_id: secret.id,
|
console.log('override add', overridesToBeAdded.length);
|
||||||
type: secret.type,
|
|
||||||
secretKeyCiphertext: secret.secretKeyCiphertext,
|
const overridesToBeUpdated = newOverrides!
|
||||||
secretKeyIV: secret.secretKeyIV,
|
.filter((newDataPoint) =>
|
||||||
secretKeyTag: secret.secretKeyTag,
|
initOverrides
|
||||||
secretValueCiphertext: secret.secretValueCiphertext,
|
.filter(
|
||||||
secretValueIV: secret.secretValueIV,
|
(initDataPoint) =>
|
||||||
secretValueTag: secret.secretValueTag,
|
newOverrides!.map((dataPoint) => dataPoint.id).includes(initDataPoint.id) &&
|
||||||
secretCommentCiphertext: secret.secretCommentCiphertext,
|
(newOverrides!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0]
|
||||||
secretCommentIV: secret.secretCommentIV,
|
.valueOverride !== initDataPoint.valueOverride ||
|
||||||
secretCommentTag: secret.secretCommentTag,
|
newOverrides!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0].key !==
|
||||||
tags: secret.tags
|
initDataPoint.key ||
|
||||||
}
|
newOverrides!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0]
|
||||||
|
.comment !== initDataPoint.comment ||
|
||||||
|
JSON.stringify(newOverrides!.filter((dataPoint) => dataPoint.id === initDataPoint.id)[0]?.tags) !==
|
||||||
|
JSON.stringify(initDataPoint?.tags))
|
||||||
|
)
|
||||||
|
.map((secret) => secret.id)
|
||||||
|
.includes(newDataPoint.id)
|
||||||
|
)
|
||||||
|
.map((override) => ({
|
||||||
|
pos: override.pos,
|
||||||
|
key: override.key,
|
||||||
|
value: String(override.valueOverride),
|
||||||
|
valueOverride: override.valueOverride,
|
||||||
|
comment: '',
|
||||||
|
id: String(override.idOverride),
|
||||||
|
idOverride: String(override.idOverride),
|
||||||
|
tags: override.tags
|
||||||
|
}));
|
||||||
|
console.log('override update', overridesToBeUpdated.length);
|
||||||
|
|
||||||
|
const requests: any = []; // TODO: fix any
|
||||||
|
if (secretsToBeDeleted.concat(overridesToBeDeleted).length > 0) {
|
||||||
|
secretsToBeDeleted.concat(overridesToBeDeleted).forEach((_id: string) => {
|
||||||
|
requests.push({
|
||||||
|
method: 'DELETE',
|
||||||
|
secret: {
|
||||||
|
_id
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
if (selectedEnv && secretsToBeAdded.concat(overridesToBeAdded).length > 0) {
|
||||||
|
const secrets = await encryptSecrets({
|
||||||
|
secretsToEncrypt: secretsToBeAdded.concat(overridesToBeAdded),
|
||||||
|
workspaceId,
|
||||||
|
env: selectedEnv.slug
|
||||||
|
});
|
||||||
|
if (secrets) {
|
||||||
|
secrets.forEach((secret) => {
|
||||||
|
requests.push({
|
||||||
|
method: 'POST',
|
||||||
|
secret: {
|
||||||
|
type: secret.type,
|
||||||
|
secretKeyCiphertext: secret.secretKeyCiphertext,
|
||||||
|
secretKeyIV: secret.secretKeyIV,
|
||||||
|
secretKeyTag: secret.secretKeyTag,
|
||||||
|
secretValueCiphertext: secret.secretValueCiphertext,
|
||||||
|
secretValueIV: secret.secretValueIV,
|
||||||
|
secretValueTag: secret.secretValueTag,
|
||||||
|
secretCommentCiphertext: secret.secretCommentCiphertext,
|
||||||
|
secretCommentIV: secret.secretCommentIV,
|
||||||
|
secretCommentTag: secret.secretCommentTag,
|
||||||
|
tags: secret.tags
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectedEnv && !selectedEnv.isReadDenied && secretsToBeUpdated.concat(overridesToBeUpdated).length > 0) {
|
||||||
|
const secrets = await encryptSecrets({
|
||||||
|
secretsToEncrypt: secretsToBeUpdated.concat(overridesToBeUpdated),
|
||||||
|
workspaceId,
|
||||||
|
env: selectedEnv.slug
|
||||||
|
});
|
||||||
|
if (secrets) {
|
||||||
|
secrets.forEach((secret) => {
|
||||||
|
requests.push({
|
||||||
|
method: 'PATCH',
|
||||||
|
secret: {
|
||||||
|
_id: secret.id,
|
||||||
|
type: secret.type,
|
||||||
|
secretKeyCiphertext: secret.secretKeyCiphertext,
|
||||||
|
secretKeyIV: secret.secretKeyIV,
|
||||||
|
secretKeyTag: secret.secretKeyTag,
|
||||||
|
secretValueCiphertext: secret.secretValueCiphertext,
|
||||||
|
secretValueIV: secret.secretValueIV,
|
||||||
|
secretValueTag: secret.secretValueTag,
|
||||||
|
secretCommentCiphertext: secret.secretCommentCiphertext,
|
||||||
|
secretCommentIV: secret.secretCommentIV,
|
||||||
|
secretCommentTag: secret.secretCommentTag,
|
||||||
|
tags: secret.tags
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let newSecrets;
|
||||||
|
if (selectedEnv && requests.length > 0) {
|
||||||
|
newSecrets = await batchSecrets({
|
||||||
|
workspaceId,
|
||||||
|
environment: selectedEnv.slug,
|
||||||
|
requests
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let formattedNewDecryptedKeys;
|
||||||
|
if (newSecrets.createdSecrets) {
|
||||||
|
const latestKey = await getLatestFileKey({ workspaceId });
|
||||||
|
|
||||||
|
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||||
|
|
||||||
|
const tempDecryptedSecrets: SecretProps[] = [];
|
||||||
|
if (latestKey) {
|
||||||
|
// assymmetrically decrypt symmetric key with local private key
|
||||||
|
const key = decryptAssymmetric({
|
||||||
|
ciphertext: latestKey.latestKey.encryptedKey,
|
||||||
|
nonce: latestKey.latestKey.nonce,
|
||||||
|
publicKey: latestKey.latestKey.sender.publicKey,
|
||||||
|
privateKey: PRIVATE_KEY
|
||||||
|
});
|
||||||
|
|
||||||
|
// decrypt secret keys, values, and comments
|
||||||
|
newSecrets.createdSecrets.forEach((secret: EncryptedSecretProps) => {
|
||||||
|
const plainTextKey = decryptSymmetric({
|
||||||
|
ciphertext: secret.secretKeyCiphertext,
|
||||||
|
iv: secret.secretKeyIV,
|
||||||
|
tag: secret.secretKeyTag,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
|
||||||
|
let plainTextValue;
|
||||||
|
if (secret.secretValueCiphertext !== undefined) {
|
||||||
|
plainTextValue = decryptSymmetric({
|
||||||
|
ciphertext: secret.secretValueCiphertext,
|
||||||
|
iv: secret.secretValueIV,
|
||||||
|
tag: secret.secretValueTag,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
plainTextValue = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let plainTextComment;
|
||||||
|
if (secret.secretCommentCiphertext) {
|
||||||
|
plainTextComment = decryptSymmetric({
|
||||||
|
ciphertext: secret.secretCommentCiphertext,
|
||||||
|
iv: secret.secretCommentIV,
|
||||||
|
tag: secret.secretCommentTag,
|
||||||
|
key
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
plainTextComment = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDecryptedSecrets.push({
|
||||||
|
id: secret._id,
|
||||||
|
key: plainTextKey,
|
||||||
|
value: plainTextValue,
|
||||||
|
type: secret.type,
|
||||||
|
comment: plainTextComment,
|
||||||
|
tags: secret.tags
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretKeys = [...new Set(tempDecryptedSecrets.map((secret) => secret.key))];
|
||||||
|
|
||||||
|
formattedNewDecryptedKeys = secretKeys.map((key, index) => ({
|
||||||
|
id: tempDecryptedSecrets.filter((secret) => secret.key === key && secret.type === 'shared')[0]
|
||||||
|
?.id,
|
||||||
|
idOverride: tempDecryptedSecrets.filter(
|
||||||
|
(secret) => secret.key === key && secret.type === 'personal'
|
||||||
|
)[0]?.id,
|
||||||
|
pos: (newData?.filter(dp => !dp.id.includes('-'))?.length ?? 0) + index,
|
||||||
|
key,
|
||||||
|
value: tempDecryptedSecrets.filter(
|
||||||
|
(secret) => secret.key === key && secret.type === 'shared'
|
||||||
|
)[0]?.value,
|
||||||
|
valueOverride: tempDecryptedSecrets.filter(
|
||||||
|
(secret) => secret.key === key && secret.type === 'personal'
|
||||||
|
)[0]?.value,
|
||||||
|
comment: tempDecryptedSecrets.filter(
|
||||||
|
(secret) => secret.key === key && secret.type === 'shared'
|
||||||
|
)[0]?.comment,
|
||||||
|
tags: tempDecryptedSecrets.filter(
|
||||||
|
(secret) => secret.key === key && secret.type === 'shared'
|
||||||
|
)[0]?.tags
|
||||||
|
}));
|
||||||
|
|
||||||
|
setInitialData(structuredClone(newData?.filter(dp => !dp.id.includes('-')).concat(formattedNewDecryptedKeys.filter(dk => dk.id))));
|
||||||
|
setData(structuredClone(newData?.filter(dp => !dp.id.includes('-')).concat(formattedNewDecryptedKeys.filter(dk => dk.id))))
|
||||||
|
} else {
|
||||||
|
setInitialData(structuredClone(newData));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this user has never saved environment variables before, show them a prompt to read docs
|
||||||
|
if (!hasUserEverPushed) {
|
||||||
|
setCheckDocsPopUpVisible(true);
|
||||||
|
await registerUserAction({ action: 'first_time_secrets_pushed' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// increasing the number of project commits
|
||||||
|
setNumSnapshots((numSnapshots ?? 0) + 1);
|
||||||
|
setSaveLoading(false);
|
||||||
|
createNotification({
|
||||||
|
text: `Successfully saved secrets.`,
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Something went wrong while saving secrets: ", error)
|
||||||
|
createNotification({
|
||||||
|
text: `Something went wrong while saving secrets.`,
|
||||||
|
type: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedEnv && requests.length > 0) {
|
|
||||||
console.log('make batch secret request: ');
|
|
||||||
const result = await batchSecrets({
|
|
||||||
workspaceId,
|
|
||||||
environment: selectedEnv.slug,
|
|
||||||
requests
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('result of batchSecrets', result);
|
|
||||||
}
|
|
||||||
|
|
||||||
setInitialData(structuredClone(newData));
|
|
||||||
|
|
||||||
// If this user has never saved environment variables before, show them a prompt to read docs
|
|
||||||
if (!hasUserEverPushed) {
|
|
||||||
setCheckDocsPopUpVisible(true);
|
|
||||||
await registerUserAction({ action: 'first_time_secrets_pushed' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// increasing the number of project commits
|
|
||||||
setNumSnapshots((numSnapshots ?? 0) + 1);
|
|
||||||
setSaveLoading(false);
|
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -885,7 +1006,7 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[calc(10%)] border-r border-mineshaft-600">
|
<div className="w-[calc(10%)] border-r border-mineshaft-600">
|
||||||
<div className="flex items-center max-h-16">
|
<div className="flex items-center max-h-16 overflow-hidden">
|
||||||
<div className='text-bunker-300 px-2 font-semibold h-10 flex items-center w-3/12'>Comment</div>
|
<div className='text-bunker-300 px-2 font-semibold h-10 flex items-center w-3/12'>Comment</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user