mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 05:27:48 +00:00
feat: move to react query
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable react/no-danger */
|
/* eslint-disable react/no-danger */
|
||||||
import React, { forwardRef, TextareaHTMLAttributes, useRef, useState } from "react";
|
import React, { forwardRef, TextareaHTMLAttributes, useEffect, useRef, useState } from "react";
|
||||||
import { faChevronRight, faFolder, faKey, faRecycle } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronRight, faFolder, faKey, faRecycle } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
@@ -7,8 +7,8 @@ import { twMerge } from "tailwind-merge";
|
|||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
import { useToggle } from "@app/hooks";
|
import { useToggle } from "@app/hooks";
|
||||||
import { useGetUserWsKey } from "@app/hooks/api";
|
import { useGetUserWsKey } from "@app/hooks/api";
|
||||||
import { fetchProjectFolders } from "@app/hooks/api/secretFolders/queries";
|
import { useGetFoldersByEnv } from "@app/hooks/api/secretFolders/queries";
|
||||||
import { decryptSecrets, fetchProjectEncryptedSecrets } from "@app/hooks/api/secrets/queries";
|
import { useGetProjectSecrets } from "@app/hooks/api/secrets/queries";
|
||||||
|
|
||||||
const REGEX_REFERENCE = /(\${([^}]*)})/g;
|
const REGEX_REFERENCE = /(\${([^}]*)})/g;
|
||||||
const replaceContentWithDot = (str: string) => {
|
const replaceContentWithDot = (str: string) => {
|
||||||
@@ -26,7 +26,7 @@ const syntaxHighlight = (content?: string | null, isVisible?: boolean) => {
|
|||||||
if (!isVisible) return replaceContentWithDot(content);
|
if (!isVisible) return replaceContentWithDot(content);
|
||||||
|
|
||||||
let skipNext = false;
|
let skipNext = false;
|
||||||
const formatedContent = content.split(REGEX_REFERENCE).flatMap((el, i) => {
|
const formattedContent = content.split(REGEX_REFERENCE).flatMap((el, i) => {
|
||||||
const isInterpolationSyntax = el.startsWith("${") && el.endsWith("}");
|
const isInterpolationSyntax = el.startsWith("${") && el.endsWith("}");
|
||||||
if (isInterpolationSyntax) {
|
if (isInterpolationSyntax) {
|
||||||
skipNext = true;
|
skipNext = true;
|
||||||
@@ -46,7 +46,7 @@ const syntaxHighlight = (content?: string | null, isVisible?: boolean) => {
|
|||||||
|
|
||||||
// akhilmhdh: Dont remove this br. I am still clueless how this works but weirdly enough
|
// akhilmhdh: Dont remove this br. I am still clueless how this works but weirdly enough
|
||||||
// when break is added a line break works properly
|
// when break is added a line break works properly
|
||||||
return formatedContent.concat(<br />);
|
return formattedContent.concat(<br />);
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
||||||
@@ -59,7 +59,7 @@ type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
|||||||
secretPath?: string;
|
secretPath?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type VariableType = {
|
type ReferenceType = {
|
||||||
name: string;
|
name: string;
|
||||||
type: "folder" | "secret";
|
type: "folder" | "secret";
|
||||||
slug?: string;
|
slug?: string;
|
||||||
@@ -77,66 +77,90 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
|||||||
isDisabled,
|
isDisabled,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
onFocus,
|
onFocus,
|
||||||
secretPath,
|
secretPath: propSecretPath,
|
||||||
environment,
|
environment: propEnvironment,
|
||||||
onChange,
|
onChange,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
|
const childRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
const [isSecretFocused, setIsSecretFocused] = useToggle();
|
const [isSecretFocused, setIsSecretFocused] = useToggle();
|
||||||
const [showReferencePopup, setShowReferencePopup] = useState<boolean>(false);
|
|
||||||
const [value, setValue] = useState<string>(propValue || "");
|
const [value, setValue] = useState<string>(propValue || "");
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const [listVariables, setListVariables] = useState<VariableType[]>([]);
|
const [listReference, setListReference] = useState<ReferenceType[]>([]);
|
||||||
|
const [showReferencePopup, setShowReferencePopup] = useState<boolean>(false);
|
||||||
|
const [referenceKey, setReferenceKey] = useState<string>();
|
||||||
const [lastSelectionIndex, setLastSelectionIndex] = useState<number>(0);
|
const [lastSelectionIndex, setLastSelectionIndex] = useState<number>(0);
|
||||||
const childRef = useRef<HTMLTextAreaElement>(null);
|
const [secretPath, setSecretPath] = useState<string>(
|
||||||
|
propSecretPath || currentWorkspace?.environments?.[0].slug!
|
||||||
|
);
|
||||||
|
const [environment, setEnvironment] = useState<string>(
|
||||||
|
propEnvironment || currentWorkspace?.environments?.[0].slug!
|
||||||
|
);
|
||||||
|
|
||||||
const workspaceId = currentWorkspace?.id || "";
|
const workspaceId = currentWorkspace?.id || "";
|
||||||
const { data: decryptFileKey } = useGetUserWsKey(workspaceId);
|
const { data: decryptFileKey } = useGetUserWsKey(workspaceId);
|
||||||
|
const { data: secrets } = useGetProjectSecrets({
|
||||||
|
decryptFileKey: decryptFileKey!,
|
||||||
|
environment,
|
||||||
|
secretPath,
|
||||||
|
workspaceId
|
||||||
|
});
|
||||||
|
const { folderNames: folders } = useGetFoldersByEnv({
|
||||||
|
path: secretPath,
|
||||||
|
environments: [environment],
|
||||||
|
projectId: workspaceId
|
||||||
|
});
|
||||||
|
|
||||||
async function extractReference(refValue: string) {
|
useEffect(() => {
|
||||||
const isNested = refValue.includes(".");
|
|
||||||
const currentListVariable: VariableType[] = [];
|
|
||||||
|
|
||||||
let currentEnvironment = environment;
|
let currentEnvironment = propEnvironment;
|
||||||
let currentSecretPath = secretPath || "/";
|
let currentSecretPath = propSecretPath || "/";
|
||||||
|
|
||||||
|
if (!referenceKey) {
|
||||||
|
setSecretPath(currentSecretPath);
|
||||||
|
setEnvironment(currentEnvironment!);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isNested = referenceKey.includes(".");
|
||||||
|
const currentListReference: ReferenceType[] = [];
|
||||||
|
|
||||||
if (isNested) {
|
if (isNested) {
|
||||||
const [envSlug, ...folderPaths] = refValue.split(".");
|
const [envSlug, ...folderPaths] = referenceKey.split(".");
|
||||||
currentEnvironment = envSlug;
|
currentEnvironment = envSlug;
|
||||||
currentSecretPath = `/${folderPaths?.join("/")}` || "/";
|
currentSecretPath = `/${folderPaths?.join("/")}` || "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentEnvironment || !decryptFileKey || !currentSecretPath || !currentWorkspace) {
|
if (!currentEnvironment || !decryptFileKey || !currentSecretPath || !currentWorkspace || !referenceKey) {
|
||||||
setListVariables(currentListVariable);
|
// this need to clean up?
|
||||||
|
setListReference(currentListReference);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setSecretPath(currentSecretPath);
|
||||||
|
setEnvironment(currentEnvironment);
|
||||||
|
setShowReferencePopup(true);
|
||||||
|
|
||||||
|
}, [referenceKey])
|
||||||
|
|
||||||
// Todo: Move to react query
|
useEffect(() => {
|
||||||
const [encryptSecrets, folders] = await Promise.all([
|
const currentListReference: ReferenceType[] = [];
|
||||||
fetchProjectEncryptedSecrets({
|
const isNested = referenceKey?.includes(".");
|
||||||
workspaceId,
|
|
||||||
environment: currentEnvironment,
|
|
||||||
secretPath: currentSecretPath
|
|
||||||
}),
|
|
||||||
// secret reference based on folder only support for nested reference that start with envs
|
|
||||||
isNested ? fetchProjectFolders(workspaceId, currentEnvironment, currentSecretPath) : []
|
|
||||||
]);
|
|
||||||
|
|
||||||
folders?.forEach((folder) => {
|
if (isNested) {
|
||||||
currentListVariable.unshift({ name: folder.name, type: "folder" });
|
folders?.forEach((folder) => {
|
||||||
});
|
currentListReference.unshift({ name: folder, type: "folder" });
|
||||||
|
});
|
||||||
const secrets = decryptSecrets(encryptSecrets, decryptFileKey);
|
}
|
||||||
|
|
||||||
secrets?.forEach((secret) => {
|
secrets?.forEach((secret) => {
|
||||||
currentListVariable.unshift({ name: secret.key, type: "secret" });
|
currentListReference.unshift({ name: secret.key, type: "secret" });
|
||||||
});
|
});
|
||||||
|
|
||||||
setListVariables(currentListVariable);
|
setListReference(currentListReference);
|
||||||
setShowReferencePopup(true);
|
}, [secrets, referenceKey]);
|
||||||
}
|
|
||||||
|
|
||||||
function findMatch(str: string, start: number) {
|
function findMatch(str: string, start: number) {
|
||||||
const matches = [...str.matchAll(REGEX_REFERENCE)];
|
const matches = [...str.matchAll(REGEX_REFERENCE)];
|
||||||
@@ -169,7 +193,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
|||||||
const match = findMatch(text, pos);
|
const match = findMatch(text, pos);
|
||||||
if (match && typeof match.index !== "undefined") {
|
if (match && typeof match.index !== "undefined") {
|
||||||
setLastSelectionIndex(pos);
|
setLastSelectionIndex(pos);
|
||||||
extractReference(match?.[2]);
|
setReferenceKey(match?.[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
setShowReferencePopup(!!match);
|
setShowReferencePopup(!!match);
|
||||||
@@ -252,7 +276,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
|||||||
value.slice(referenceEndIndex)
|
value.slice(referenceEndIndex)
|
||||||
];
|
];
|
||||||
|
|
||||||
let oldReferenceStr = oldReference.slice(2, oldReference.length - 1);
|
let oldReferenceStr = oldReference.slice(2, -1);
|
||||||
|
|
||||||
let replaceReference = "";
|
let replaceReference = "";
|
||||||
let offset = 3;
|
let offset = 3;
|
||||||
@@ -277,7 +301,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
|||||||
// TODO: there should be a better way to do
|
// TODO: there should be a better way to do
|
||||||
onChange?.({ target: { value: newValue } } as any);
|
onChange?.({ target: { value: newValue } } as any);
|
||||||
setCaretPos(start.length + replaceReference.length + offset);
|
setCaretPos(start.length + replaceReference.length + offset);
|
||||||
if (type !== "secret") extractReference(replaceReference);
|
if (type !== "secret") setReferenceKey(replaceReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
|
function handleChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
|
||||||
@@ -305,7 +329,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="max-w-60 h-full w-full flex-col items-center justify-center rounded-md py-4 text-white">
|
<div className="max-w-60 h-full w-full flex-col items-center justify-center rounded-md py-4 text-white">
|
||||||
{listVariables.map((e, i) => {
|
{listReference.map((e, i) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className="flex items-center justify-between border-b border-mineshaft-600 px-2 py-1 text-left last:border-b-0"
|
className="flex items-center justify-between border-b border-mineshaft-600 px-2 py-1 text-left last:border-b-0"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const folderQueryKeys = {
|
|||||||
["secret-folders", { projectId, environment, path }] as const
|
["secret-folders", { projectId, environment, path }] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchProjectFolders = async (workspaceId: string, environment: string, path = "/") => {
|
const fetchProjectFolders = async (workspaceId: string, environment: string, path = "/") => {
|
||||||
const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v1/folders", {
|
const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v1/folders", {
|
||||||
params: {
|
params: {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export const decryptSecrets = (
|
|||||||
return secrets;
|
return secrets;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchProjectEncryptedSecrets = async ({
|
const fetchProjectEncryptedSecrets = async ({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
secretPath
|
secretPath
|
||||||
|
|||||||
Reference in New Issue
Block a user