Update GCP SM and GitLab integrations to have overwrite popup warning

This commit is contained in:
Tuan Dang
2023-09-16 12:36:29 +01:00
parent 2e07512bae
commit 14d253d01a
5 changed files with 334 additions and 188 deletions
@@ -187,7 +187,8 @@ const integrationSchema = new Schema<IIntegration>(
default: "/", default: "/",
}, },
metadata: { metadata: {
type: Schema.Types.Mixed type: Schema.Types.Mixed,
default: {}
} }
}, },
{ {
@@ -1,6 +1,7 @@
import crypto from "crypto"; import crypto from "crypto";
import { useState } from "react"; import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import Head from "next/head"; import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
@@ -8,6 +9,8 @@ import { useRouter } from "next/router";
import { faGoogle } from "@fortawesome/free-brands-svg-icons"; import { faGoogle } from "@fortawesome/free-brands-svg-icons";
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons"; import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { import {
useGetCloudIntegrations, useGetCloudIntegrations,
@@ -15,39 +18,27 @@ import {
import { Button, Card, CardTitle, FormControl, TextArea } from "../../../components/v2"; import { Button, Card, CardTitle, FormControl, TextArea } from "../../../components/v2";
const schema = yup.object({
accessToken: yup.string().required("Service Account JSON cannot be empty")
});
type FormData = yup.InferType<typeof schema>;
export default function GCPSecretManagerAuthorizeIntegrationPage() { export default function GCPSecretManagerAuthorizeIntegrationPage() {
const router = useRouter(); const router = useRouter();
const {
control,
handleSubmit
} = useForm<FormData>({
resolver: yupResolver(schema)
});
const { data: cloudIntegrations } = useGetCloudIntegrations(); const { data: cloudIntegrations } = useGetCloudIntegrations();
const { mutateAsync } = useSaveIntegrationAccessToken(); const { mutateAsync } = useSaveIntegrationAccessToken();
const [accessToken, setAccessToken] = useState("");
const [accessTokenErrorText, setAccessTokenErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const handleIntegrateWithPAT = async () => {
try {
setAccessTokenErrorText("");
if (accessToken.length === 0) {
setAccessTokenErrorText("Service account JSON cannot be blank");
return;
}
setIsLoading(true);
const integrationAuth = await mutateAsync({
workspaceId: localStorage.getItem("projectData.id"),
integration: "gcp-secret-manager",
refreshToken: accessToken
});
setIsLoading(false);
router.push(`/integrations/gcp-secret-manager/create?integrationAuthId=${integrationAuth._id}`);
} catch (err) {
console.error(err);
}
};
const handleIntegrateWithOAuth = () => { const handleIntegrateWithOAuth = () => {
if (!cloudIntegrations) return; if (!cloudIntegrations) return;
@@ -61,6 +52,26 @@ export default function GCPSecretManagerAuthorizeIntegrationPage() {
const link = `https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/cloud-platform&response_type=code&access_type=offline&state=${state}&redirect_uri=${window.location.origin}/integrations/gcp-secret-manager/oauth2/callback&client_id=${integrationOption.clientId}`; const link = `https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/cloud-platform&response_type=code&access_type=offline&state=${state}&redirect_uri=${window.location.origin}/integrations/gcp-secret-manager/oauth2/callback&client_id=${integrationOption.clientId}`;
window.location.assign(link); window.location.assign(link);
} }
const onFormSubmit = async ({
accessToken
}: FormData) => {
try {
setIsLoading(true);
const integrationAuth = await mutateAsync({
workspaceId: localStorage.getItem("projectData.id"),
integration: "gcp-secret-manager",
refreshToken: accessToken
});
setIsLoading(false);
router.push(`/integrations/gcp-secret-manager/create?integrationAuthId=${integrationAuth._id}`);
} catch (err) {
console.error(err);
setIsLoading(false);
}
}
return ( return (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
@@ -94,7 +105,7 @@ export default function GCPSecretManagerAuthorizeIntegrationPage() {
</Link> </Link>
</div> </div>
</CardTitle> </CardTitle>
<div className="px-7"> <div className="px-6">
<Button <Button
colorSchema="primary" colorSchema="primary"
variant="outline_bg" variant="outline_bg"
@@ -104,35 +115,43 @@ export default function GCPSecretManagerAuthorizeIntegrationPage() {
> >
Continue with OAuth Continue with OAuth
</Button> </Button>
<div className='w-full flex flex-row items-center my-4 py-2'>
<div className='w-full border-t border-mineshaft-400/40' />
<span className="mx-2 text-mineshaft-400 text-xs">or</span>
<div className='w-full border-t border-mineshaft-400/40' />
</div>
</div> </div>
<div className='w-full flex flex-row items-center my-4 py-2 px-7'> <form
<div className='w-full border-t border-mineshaft-400/40' /> onSubmit={handleSubmit(onFormSubmit)}
<span className="mx-2 text-mineshaft-400 text-xs">or</span> className="px-6 text-right pb-8"
<div className='w-full border-t border-mineshaft-400/40' />
</div>
<FormControl
label="GCP Service Account JSON"
errorText={accessTokenErrorText}
isError={accessTokenErrorText !== "" ?? false}
className="px-6"
> >
<TextArea <Controller
value={accessToken} control={control}
onChange={(e) => setAccessToken(e.target.value)} name="accessToken"
placeholder="" render={({ field, fieldState: { error } }) => (
className="h-48 border border-mineshaft-600 bg-bunker-900/80" <FormControl
label="GCP Service Account JSON"
errorText={error?.message}
isError={Boolean(error)}
>
<TextArea
{...field}
className="h-48 border border-mineshaft-600 bg-bunker-900/80"
/>
</FormControl>
)}
/> />
</FormControl> <Button
<Button colorSchema="primary"
onClick={handleIntegrateWithPAT} variant="outline_bg"
colorSchema="primary" className="mt-2 w-min"
variant="outline_bg" size="sm"
className="mb-8 mt-2 ml-auto mr-6 w-min" type="submit"
isLoading={isLoading} isLoading={isLoading}
> >
Connect to GCP Secret Manager Connect to GCP Secret Manager
</Button> </Button>
</form>
</Card> </Card>
</div> </div>
); );
@@ -4,13 +4,14 @@ import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { faArrowUpRightFromSquare, faBookOpen, faBugs, faCircleInfo } from "@fortawesome/free-solid-svg-icons"; import { faArrowUpRightFromSquare, faBookOpen, faBugs } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import queryString from "query-string"; import queryString from "query-string";
import * as yup from "yup"; import * as yup from "yup";
import { usePopUp } from "@app/hooks";
import { import {
useCreateIntegration useCreateIntegration
} from "@app/hooks/api"; } from "@app/hooks/api";
@@ -21,14 +22,15 @@ import {
CardTitle, CardTitle,
FormControl, FormControl,
Input, Input,
Modal,
ModalContent,
Select, Select,
SelectItem, SelectItem,
Switch, Switch,
Tab, Tab,
TabList, TabList,
TabPanel, TabPanel,
Tabs Tabs} from "../../../components/v2";
} from "../../../components/v2";
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById useGetIntegrationAuthById
@@ -44,30 +46,36 @@ const schema = yup.object({
selectedSourceEnvironment: yup.string().required("Source environment is required"), selectedSourceEnvironment: yup.string().required("Source environment is required"),
secretPath: yup.string().required("Secret path is required"), secretPath: yup.string().required("Secret path is required"),
targetAppId: yup.string().required("GCP project is required"), targetAppId: yup.string().required("GCP project is required"),
secretPrefix: yup.string(), secretPrefix: yup.string().trim(),
secretSuffix: yup.string(), secretSuffix: yup.string().trim(),
shouldLabel: yup.boolean(), shouldLabel: yup.boolean(),
labelName: yup.string(), labelName: yup.string().trim(),
labelValue: yup.string() labelValue: yup.string().trim()
}); });
type FormData = yup.InferType<typeof schema>; type FormData = yup.InferType<typeof schema>;
export default function GCPSecretManagerCreateIntegrationPage() { export default function GCPSecretManagerCreateIntegrationPage() {
const router = useRouter(); const router = useRouter();
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
"confirmIntegration"
] as const);
const { const {
control, control,
handleSubmit, handleSubmit,
setValue, setValue,
watch watch
} = useForm<FormData>({ } = useForm<FormData>({
resolver: yupResolver(schema), resolver: yupResolver(schema),
defaultValues: { defaultValues: {
secretPath: "/", secretPath: "/",
shouldLabel: false, secretPrefix: "",
labelName: "managed-by", secretSuffix: "",
labelValue: "infisical" shouldLabel: false,
} labelName: "managed-by",
labelValue: "infisical"
}
}); });
const shouldLabel = watch("shouldLabel"); const shouldLabel = watch("shouldLabel");
@@ -92,8 +100,8 @@ export default function GCPSecretManagerCreateIntegrationPage() {
return; return;
} }
setValue("labelName", undefined); setValue("labelName", "");
setValue("labelValue", undefined); setValue("labelValue", "");
}, [shouldLabel]); }, [shouldLabel]);
useEffect(() => { useEffect(() => {
@@ -135,8 +143,8 @@ export default function GCPSecretManagerCreateIntegrationPage() {
sourceEnvironment: sce, sourceEnvironment: sce,
secretPath, secretPath,
metadata: { metadata: {
secretPrefix, ...(secretPrefix ? { secretPrefix } : {}),
secretSuffix, ...(secretSuffix ? { secretSuffix } : {}),
...(sl ? { ...(sl ? {
secretGCPLabel: { secretGCPLabel: {
labelName, labelName,
@@ -159,7 +167,14 @@ export default function GCPSecretManagerCreateIntegrationPage() {
selectedSourceEnvironment && selectedSourceEnvironment &&
integrationAuthApps ? ( integrationAuthApps ? (
<form <form
onSubmit={handleSubmit(onFormSubmit)} onSubmit={handleSubmit((data: FormData) => {
if (!data.secretPrefix && !data.secretSuffix && !data.shouldLabel) {
handlePopUpOpen("confirmIntegration", data);
return;
}
onFormSubmit(data);
})}
className="flex flex-col h-full w-full items-center justify-center" className="flex flex-col h-full w-full items-center justify-center"
> >
<Card className="max-w-lg rounded-md border border-mineshaft-600"> <Card className="max-w-lg rounded-md border border-mineshaft-600">
@@ -169,7 +184,7 @@ export default function GCPSecretManagerCreateIntegrationPage() {
</Head> </Head>
<CardTitle <CardTitle
className="text-left px-6 text-xl mb-2" className="text-left px-6 text-xl mb-2"
subTitle="Select which environment or folder in Infisical you want to sync to GCP Secret Manager. Optionally, you can add suffixes/prefixes." subTitle="Select which environment or folder in Infisical you want to sync to GCP Secret Manager."
> >
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<div className="inline flex items-center pb-0.5"> <div className="inline flex items-center pb-0.5">
@@ -207,89 +222,87 @@ export default function GCPSecretManagerCreateIntegrationPage() {
animate={{ opacity: 1, translateX: 0 }} animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }} exit={{ opacity: 0, translateX: 30 }}
> >
<div> <Controller
<Controller control={control}
name="selectedSourceEnvironment"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Project Environment"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<Controller
control={control} control={control}
name="selectedSourceEnvironment" defaultValue=""
render={({ field: { onChange, ...field }, fieldState: { error } }) => ( name="secretPath"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Secrets Path"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
placeholder="/"
/>
</FormControl>
)}
/>
<Controller
control={control}
name="targetAppId"
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
return (
<FormControl <FormControl
label="Project Environment" label="GCP Project"
errorText={error?.message} errorText={error?.message}
isError={Boolean(error)} isError={Boolean(error)}
> >
<Select <Select
defaultValue={field.value}
{...field} {...field}
onValueChange={(e) => onChange(e)} onValueChange={(e) => {
if (e === "") return;
onChange(e)
}}
className="w-full" className="w-full"
> >
{workspace?.environments.map((sourceEnvironment) => ( {integrationAuthApps.length > 0 ? (
<SelectItem integrationAuthApps.map((integrationAuthApp) => (
value={sourceEnvironment.slug} <SelectItem
key={`source-environment-${sourceEnvironment.slug}`} value={String(integrationAuthApp.appId as string)}
> key={`target-app-${String(integrationAuthApp.appId)}`}
{sourceEnvironment.name} >
{integrationAuthApp.name}
</SelectItem>
))
) : (
<SelectItem value="none" key="target-app-none">
No projects found
</SelectItem> </SelectItem>
))} )}
</Select> </Select>
</FormControl> </FormControl>
)} )
/> }}
<Controller />
control={control}
defaultValue=""
name="secretPath"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Secrets Path"
isError={Boolean(error)}
errorText={error?.message}
>
<Input
{...field}
placeholder="/"
/>
</FormControl>
)}
/>
<Controller
control={control}
name="targetAppId"
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
return (
<FormControl
label="GCP Project"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
{...field}
onValueChange={(e) => {
if (e === "") return;
onChange(e)
}}
className="w-full"
>
{integrationAuthApps.length > 0 ? (
integrationAuthApps.map((integrationAuthApp) => (
<SelectItem
value={String(integrationAuthApp.appId as string)}
key={`target-app-${String(integrationAuthApp.appId)}`}
>
{integrationAuthApp.name}
</SelectItem>
))
) : (
<SelectItem value="none" key="target-app-none">
No projects found
</SelectItem>
)}
</Select>
</FormControl>
)
}}
/>
</div>
</motion.div> </motion.div>
</TabPanel> </TabPanel>
<TabPanel value={TabSections.Options}> <TabPanel value={TabSections.Options}>
@@ -398,11 +411,47 @@ export default function GCPSecretManagerCreateIntegrationPage() {
Create Integration Create Integration
</Button> </Button>
</Card> </Card>
<div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/> {/* <div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/>
<div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md"> <div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md">
<div className="flex flex-row items-center"><FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/> <span className="ml-3 text-md text-mineshaft-100">Pro Tips</span></div> <div className="flex flex-row items-center">
<span className="text-mineshaft-300 text-sm mt-4">After creating an integration, your secrets will start syncing immediately. This might cause an unexpected override of current secrets in GCP Secret Manager with secrets from Infisical.</span> <FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/>
</div> <span className="ml-3 text-md text-mineshaft-100">Pro Tip</span>
</div>
<span className="text-mineshaft-300 text-sm mt-4">
After creating an integration, your secrets will start syncing immediately.
To avoid overwriting existing secrets in GCP Secret Manager, you may consider adding a secret prefix/suffix and/or enabling labeling in the options tab.
</span>
</div> */}
<Modal
isOpen={popUp.confirmIntegration?.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("confirmIntegration", isOpen)}
>
<ModalContent
title="Heads Up"
footerContent={
<div className="flex items-center space-x-2">
<Button onClick={() => onFormSubmit(popUp.confirmIntegration?.data as FormData)}>
Continue Anyway
</Button>
<Button
onClick={() => handlePopUpClose("confirmIntegration")}
variant="outline_bg"
colorSchema="secondary"
>
Cancel
</Button>
</div>
}
>
<p>
You&apos;re about to overwrite any existing secrets in GCP Secret Manager.
</p>
<p className="mt-4">
To avoid this behavior, you may consider adding a secret prefix/suffix or enabling labeling in the options tab.
</p>
</ModalContent>
</Modal>
</form> </form>
) : ( ) : (
<div className="flex justify-center items-center w-full h-full"> <div className="flex justify-center items-center w-full h-full">
@@ -1,37 +1,55 @@
import crypto from "crypto"; import crypto from "crypto";
import { useState } from "react"; import { Controller, useForm } from "react-hook-form";
import Head from "next/head"; import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons"; import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useGetCloudIntegrations } from "@app/hooks/api"; import { useGetCloudIntegrations } from "@app/hooks/api";
import { Button, Card, CardTitle, FormControl, Input } from "../../../components/v2"; import { Button, Card, CardTitle, FormControl, Input } from "../../../components/v2";
const schema = yup.object({
gitLabURL: yup.string()
});
type FormData = yup.InferType<typeof schema>;
export default function GitLabAuthorizeIntegrationPage() { export default function GitLabAuthorizeIntegrationPage() {
const { data: cloudIntegrations } = useGetCloudIntegrations(); const {
control,
const [gitLabURL, setGitLabURL] = useState(""); handleSubmit
} = useForm<FormData>({
const handleIntegrateWithOAuth = () => { resolver: yupResolver(schema),
if (!cloudIntegrations) return; defaultValues: {
const integrationOption = cloudIntegrations.find((integration) => integration.slug === "gitlab"); gitLabURL: ""
if (!integrationOption) return;
const baseURL = gitLabURL.trim() === "" ? "https://gitlab.com" : gitLabURL.trim();
const csrfToken = crypto.randomBytes(16).toString("hex");
localStorage.setItem("latestCSRFToken", csrfToken);
const state = `${csrfToken}|${gitLabURL.trim() === "" ? "" : gitLabURL.trim()}`;
const link = `${baseURL}/oauth/authorize?client_id=${integrationOption.clientId}&redirect_uri=${window.location.origin}/integrations/gitlab/oauth2/callback&response_type=code&state=${state}`;
window.location.assign(link);
} }
});
const { data: cloudIntegrations } = useGetCloudIntegrations();
const onFormSubmit = ({
gitLabURL
}: FormData) => {
if (!cloudIntegrations) return;
const integrationOption = cloudIntegrations.find((integration) => integration.slug === "gitlab");
if (!integrationOption) return;
const baseURL = gitLabURL.trim() === "" ? "https://gitlab.com" : gitLabURL.trim();
const csrfToken = crypto.randomBytes(16).toString("hex");
localStorage.setItem("latestCSRFToken", csrfToken);
const state = `${csrfToken}|${gitLabURL.trim() === "" ? "" : gitLabURL.trim()}`;
const link = `${baseURL}/oauth/authorize?client_id=${integrationOption.clientId}&redirect_uri=${window.location.origin}/integrations/gitlab/oauth2/callback&response_type=code&state=${state}`;
window.location.assign(link);
}
return ( return (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
@@ -42,7 +60,7 @@ export default function GitLabAuthorizeIntegrationPage() {
<Card className="max-w-lg rounded-md border border-mineshaft-600 mb-12"> <Card className="max-w-lg rounded-md border border-mineshaft-600 mb-12">
<CardTitle <CardTitle
className="text-left px-6 text-xl" className="text-left px-6 text-xl"
subTitle="Authorize this integration to be able to sync secrets from Infisical to GitLab. If needed, specify the self-hosted GitLab URL." subTitle="Authorize this integration to sync secrets from Infisical to GitLab. If no self-hosted GitLab URL is specified, then Infisical will connect you to GitLab Cloud."
> >
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<div className="inline flex items-center pb-0.5"> <div className="inline flex items-center pb-0.5">
@@ -65,20 +83,36 @@ export default function GitLabAuthorizeIntegrationPage() {
</Link> </Link>
</div> </div>
</CardTitle> </CardTitle>
<FormControl label="Self-hosted URL (optional)" className="px-6"> <form
<Input onSubmit={handleSubmit(onFormSubmit)}
placeholder="https://self-hosted-gitlab.com" className="px-6 text-right pb-8"
value={gitLabURL} onChange={(e) => setGitLabURL(e.target.value)} >
/> <Controller
</FormControl> control={control}
<Button name="gitLabURL"
onClick={handleIntegrateWithOAuth} render={({ field, fieldState: { error } }) => (
<FormControl
label="Self-hosted URL (optional)"
errorText={error?.message}
isError={Boolean(error)}
>
<Input
{...field}
placeholder="https://self-hosted-gitlab.com"
/>
</FormControl>
)}
/>
<Button
colorSchema="primary" colorSchema="primary"
variant="outline_bg" variant="outline_bg"
className="mb-6 mt-2 ml-auto mr-6 w-min" className="mt-2 w-min"
> size="sm"
type="submit"
>
Continue with OAuth Continue with OAuth
</Button> </Button>
</form>
</Card> </Card>
</div> </div>
); );
@@ -4,13 +4,14 @@ import Head from "next/head";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { faArrowUpRightFromSquare, faBookOpen, faBugs, faCircleInfo } from "@fortawesome/free-solid-svg-icons"; import { faArrowUpRightFromSquare, faBookOpen, faBugs } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import queryString from "query-string"; import queryString from "query-string";
import * as yup from "yup"; import * as yup from "yup";
import { usePopUp } from "@app/hooks";
import { import {
useCreateIntegration useCreateIntegration
} from "@app/hooks/api"; } from "@app/hooks/api";
@@ -21,13 +22,14 @@ import {
CardTitle, CardTitle,
FormControl, FormControl,
Input, Input,
Modal,
ModalContent,
Select, Select,
SelectItem, SelectItem,
Tab, Tab,
TabList, TabList,
TabPanel, TabPanel,
Tabs Tabs} from "../../../components/v2";
} from "../../../components/v2";
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
useGetIntegrationAuthById, useGetIntegrationAuthById,
@@ -60,6 +62,9 @@ type FormData = yup.InferType<typeof schema>;
export default function GitLabCreateIntegrationPage() { export default function GitLabCreateIntegrationPage() {
const router = useRouter(); const router = useRouter();
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
"confirmIntegration"
] as const);
const { const {
control, control,
@@ -70,7 +75,9 @@ export default function GitLabCreateIntegrationPage() {
resolver: yupResolver(schema), resolver: yupResolver(schema),
defaultValues: { defaultValues: {
targetEntity: "individual", targetEntity: "individual",
secretPath: "/" secretPath: "/",
secretPrefix: "",
secretSuffix: ""
} }
}); });
const selectedSourceEnvironment = watch("selectedSourceEnvironment"); const selectedSourceEnvironment = watch("selectedSourceEnvironment");
@@ -167,7 +174,14 @@ export default function GitLabCreateIntegrationPage() {
integrationAuthApps && integrationAuthApps &&
integrationAuthTeams ? ( integrationAuthTeams ? (
<form <form
onSubmit={handleSubmit(onFormSubmit)} onSubmit={handleSubmit((data: FormData) => {
if (!data.secretPrefix && !data.secretSuffix) {
handlePopUpOpen("confirmIntegration", data);
return;
}
onFormSubmit(data);
})}
className="flex flex-col h-full w-full items-center justify-center" className="flex flex-col h-full w-full items-center justify-center"
> >
<Head> <Head>
@@ -431,11 +445,40 @@ export default function GitLabCreateIntegrationPage() {
Create Integration Create Integration
</Button> </Button>
</Card> </Card>
<div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/> {/* <div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/>
<div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md"> <div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md">
<div className="flex flex-row items-center"><FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/> <span className="ml-3 text-md text-mineshaft-100">Pro Tips</span></div> <div className="flex flex-row items-center"><FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/> <span className="ml-3 text-md text-mineshaft-100">Pro Tips</span></div>
<span className="text-mineshaft-300 text-sm mt-4">After creating an integration, your secrets will start syncing immediately. This might cause an unexpected override of current secrets in GitLab with secrets from Infisical.</span> <span className="text-mineshaft-300 text-sm mt-4">After creating an integration, your secrets will start syncing immediately. This might cause an unexpected override of current secrets in GitLab with secrets from Infisical.</span>
</div> </div> */}
<Modal
isOpen={popUp.confirmIntegration?.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("confirmIntegration", isOpen)}
>
<ModalContent
title="Heads Up"
footerContent={
<div className="flex items-center space-x-2">
<Button onClick={() => onFormSubmit(popUp.confirmIntegration?.data as FormData)}>
Continue Anyway
</Button>
<Button
onClick={() => handlePopUpClose("confirmIntegration")}
variant="outline_bg"
colorSchema="secondary"
>
Cancel
</Button>
</div>
}
>
<p>
You&apos;re about to overwrite any existing secrets in GitLab.
</p>
<p className="mt-4">
To avoid this behavior, you may consider adding a secret prefix/suffix in the options tab.
</p>
</ModalContent>
</Modal>
</form> </form>
) : ( ) : (
<div className="flex justify-center items-center w-full h-full"> <div className="flex justify-center items-center w-full h-full">