mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 16:27:46 +00:00
Merge pull request #994 from Infisical/integration-options
Finish integration options/react form refactor for GitLab and GCP SM …
This commit is contained in:
@@ -329,6 +329,10 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
let pageToken: string | undefined;
|
let pageToken: string | undefined;
|
||||||
let hasMorePages = true;
|
let hasMorePages = true;
|
||||||
|
|
||||||
|
const filterParam = integration.metadata.secretGCPLabel
|
||||||
|
? `?filter=labels.${integration.metadata.secretGCPLabel.labelName}=${integration.metadata.secretGCPLabel.labelValue}`
|
||||||
|
: "";
|
||||||
|
|
||||||
while (hasMorePages) {
|
while (hasMorePages) {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
pageSize: String(pageSize),
|
pageSize: String(pageSize),
|
||||||
@@ -336,7 +340,7 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const res: GCPSMListSecretsRes = (await standardRequest.get(
|
const res: GCPSMListSecretsRes = (await standardRequest.get(
|
||||||
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets?filter=labels.managed-by=infisical`,
|
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets${filterParam}`,
|
||||||
{
|
{
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -347,7 +351,24 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
)).data;
|
)).data;
|
||||||
|
|
||||||
if (res.secrets) {
|
if (res.secrets) {
|
||||||
gcpSecrets = gcpSecrets.concat(res.secrets);
|
const filteredSecrets = res.secrets?.filter((gcpSecret) => {
|
||||||
|
const arr = gcpSecret.name.split("/");
|
||||||
|
const key = arr[arr.length - 1];
|
||||||
|
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
if (integration.metadata.secretPrefix && !key.startsWith(integration.metadata.secretPrefix)) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integration.metadata.secretSuffix && !key.endsWith(integration.metadata.secretSuffix)) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
});
|
||||||
|
|
||||||
|
gcpSecrets = gcpSecrets.concat(filteredSecrets);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res.nextPageToken) {
|
if (!res.nextPageToken) {
|
||||||
@@ -371,7 +392,7 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
const key = arr[arr.length - 1];
|
const key = arr[arr.length - 1];
|
||||||
|
|
||||||
const secretLatest: GCPLatestSecretVersionAccess = (await standardRequest.get(
|
const secretLatest: GCPLatestSecretVersionAccess = (await standardRequest.get(
|
||||||
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1beta1/projects/${integration.appId}/secrets/${key}/versions/latest:access`,
|
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets/${key}/versions/latest:access`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
@@ -380,6 +401,7 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
}
|
}
|
||||||
)).data;
|
)).data;
|
||||||
|
|
||||||
|
|
||||||
res[key] = Buffer.from(secretLatest.payload.data, "base64").toString("utf-8");
|
res[key] = Buffer.from(secretLatest.payload.data, "base64").toString("utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,14 +409,16 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
if (!(key in res)) {
|
if (!(key in res)) {
|
||||||
// case: create secret
|
// case: create secret
|
||||||
await standardRequest.post(
|
await standardRequest.post(
|
||||||
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1beta1/projects/${integration.appId}/secrets`,
|
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets`,
|
||||||
{
|
{
|
||||||
replication: {
|
replication: {
|
||||||
automatic: {}
|
automatic: {}
|
||||||
},
|
},
|
||||||
labels: {
|
...(integration.metadata.secretGCPLabel ? {
|
||||||
"managed-by": "infisical"
|
labels: {
|
||||||
}
|
[integration.metadata.secretGCPLabel.labelName]: integration.metadata.secretGCPLabel.labelValue
|
||||||
|
}
|
||||||
|
} : {})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
@@ -408,7 +432,7 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
await standardRequest.post(
|
await standardRequest.post(
|
||||||
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1beta1/projects/${integration.appId}/secrets/${key}:addVersion`,
|
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets/${key}:addVersion`,
|
||||||
{
|
{
|
||||||
payload: {
|
payload: {
|
||||||
data: Buffer.from(secrets[key].value).toString("base64")
|
data: Buffer.from(secrets[key].value).toString("base64")
|
||||||
@@ -428,7 +452,7 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
if (!(key in secrets)) {
|
if (!(key in secrets)) {
|
||||||
// case: delete secret
|
// case: delete secret
|
||||||
await standardRequest.delete(
|
await standardRequest.delete(
|
||||||
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1beta1/projects/${integration.appId}/secrets/${key}`,
|
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets/${key}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
@@ -440,7 +464,7 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
// case: update secret
|
// case: update secret
|
||||||
if (secrets[key].value !== res[key]) {
|
if (secrets[key].value !== res[key]) {
|
||||||
await standardRequest.post(
|
await standardRequest.post(
|
||||||
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1beta1/projects/${integration.appId}/secrets/${key}:addVersion`,
|
`${INTEGRATION_GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets/${key}:addVersion`,
|
||||||
{
|
{
|
||||||
payload: {
|
payload: {
|
||||||
data: Buffer.from(secrets[key].value).toString("base64")
|
data: Buffer.from(secrets[key].value).toString("base64")
|
||||||
@@ -1863,9 +1887,23 @@ const syncSecretsGitLab = async ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const allEnvVariables = await getAllEnvVariables(integration?.appId, accessToken);
|
const allEnvVariables = await getAllEnvVariables(integration?.appId, accessToken);
|
||||||
const getSecretsRes: GitLabSecret[] = allEnvVariables.filter(
|
const getSecretsRes: GitLabSecret[] = allEnvVariables
|
||||||
(secret: GitLabSecret) => secret.environment_scope === integration.targetEnvironment
|
.filter(
|
||||||
);
|
(secret: GitLabSecret) => secret.environment_scope === integration.targetEnvironment
|
||||||
|
)
|
||||||
|
.filter((gitLabSecret) => {
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
if (integration.metadata.secretPrefix && !gitLabSecret.key.startsWith(integration.metadata.secretPrefix)) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integration.metadata.secretSuffix && !gitLabSecret.key.endsWith(integration.metadata.secretSuffix)) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
});
|
||||||
|
|
||||||
for await (const key of Object.keys(secrets)) {
|
for await (const key of Object.keys(secrets)) {
|
||||||
const existingSecret = getSecretsRes.find((s: any) => s.key == key);
|
const existingSecret = getSecretsRes.find((s: any) => s.key == key);
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
|
||||||
|
// TODO: in the future separate metadata
|
||||||
|
// into distinct types by integration
|
||||||
export type Metadata = {
|
export type Metadata = {
|
||||||
|
secretPrefix?: string;
|
||||||
secretSuffix?: string;
|
secretSuffix?: string;
|
||||||
|
secretGCPLabel?: {
|
||||||
|
labelName: string;
|
||||||
|
labelValue: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -35,9 +35,12 @@ syncSecretsToThirdPartyServices.process(async (job: Job) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const suffixedSecrets: any = {};
|
const suffixedSecrets: any = {};
|
||||||
if (integration.metadata?.secretSuffix) {
|
if (integration.metadata) {
|
||||||
for (const key in secrets) {
|
for (const key in secrets) {
|
||||||
const newKey = key + integration.metadata?.secretSuffix;
|
const prefix = (integration.metadata?.secretPrefix || "");
|
||||||
|
const suffix = (integration.metadata?.secretSuffix || "");
|
||||||
|
const newKey = prefix + key + suffix;
|
||||||
|
|
||||||
suffixedSecrets[newKey] = secrets[key];
|
suffixedSecrets[newKey] = secrets[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,12 @@ export const CreateIntegrationV1 = z.object({
|
|||||||
path: z.string().trim().optional(),
|
path: z.string().trim().optional(),
|
||||||
region: z.string().trim().optional(),
|
region: z.string().trim().optional(),
|
||||||
metadata: z.object({
|
metadata: z.object({
|
||||||
secretSuffix: z.string().optional()
|
secretPrefix: z.string().optional(),
|
||||||
|
secretSuffix: z.string().optional(),
|
||||||
|
secretGCPLabel: z.object({
|
||||||
|
labelName: z.string(),
|
||||||
|
labelValue: z.string()
|
||||||
|
}).optional()
|
||||||
}).optional()
|
}).optional()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 1.3 MiB |
@@ -32,6 +32,16 @@ Press on the GitLab tile and grant Infisical access to your GitLab account.
|
|||||||
Select which Infisical environment secrets you want to sync to which GitLab repository and press create integration to start syncing secrets to GitLab.
|
Select which Infisical environment secrets you want to sync to which GitLab repository and press create integration to start syncing secrets to GitLab.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
Note that the GitLab integration supports a few options in the **Options** tab:
|
||||||
|
|
||||||
|
- Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced.
|
||||||
|
- Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced.
|
||||||
|
|
||||||
|
Setting a secret prefix or suffix ensures that existing secrets in GCP Secret Manager are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GitLab without the specified prefix or suffix.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||

|

|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Pipeline">
|
<Accordion title="Pipeline">
|
||||||
|
|||||||
@@ -35,14 +35,21 @@ Grant Infisical access to GCP.
|
|||||||
|
|
||||||
## Start integration
|
## Start integration
|
||||||
|
|
||||||
Select which Infisical environment secrets you want to sync to which GCP secret manager project. Lastly, press create integration to start syncing secrets to GCP secret manager.
|
In the **Connection** tab, select which Infisical environment secrets you want to sync to which GCP secret manager project. Lastly, press create integration to start syncing secrets to GCP secret manager.
|
||||||
|
|
||||||

|

|
||||||

|
|
||||||
|
|
||||||
<Note>
|
Note that the GCP Secret Manager integration supports a few options in the **Options** tab:
|
||||||
Secrets synced from Infisical to GCP Secret Manager are automatically labeled `managed-by:infisical` to avoid overwriting existing values in GCP Secret Manager.
|
|
||||||
</Note>
|
- Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced.
|
||||||
|
- Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced.
|
||||||
|
- Label in GCP Secret Manager: If selected, every secret will be labeled in GCP Secret Manager (e.g. as `managed-by:infisical`); labels can be customized.
|
||||||
|
|
||||||
|
Setting a secret prefix, suffix, or enabling the labeling option ensures that existing secrets in GCP Secret Manager are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GCP Secret Manager without the specified prefix, suffix, or attached label.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
||||||
@@ -89,14 +96,21 @@ service account in IAM & Admin > Service Accounts > Service Account > Keys).
|
|||||||
|
|
||||||
## Start integration
|
## Start integration
|
||||||
|
|
||||||
Select which Infisical environment secrets you want to sync to the GCP secret manager project. Lastly, press create integration to start syncing secrets to GCP secret manager.
|
In the **Connection** tab, select which Infisical environment secrets you want to sync to the GCP secret manager project. Lastly, press create integration to start syncing secrets to GCP secret manager.
|
||||||
|
|
||||||

|

|
||||||

|
|
||||||
|
|
||||||
<Note>
|
Note that the GCP Secret Manager integration supports a few options in the **Options** tab:
|
||||||
Secrets synced from Infisical to GCP Secret Manager are automatically labeled `managed-by:infisical` to avoid overwriting existing values in GCP Secret Manager.
|
|
||||||
</Note>
|
- Secret Prefix: If inputted, the prefix is appended to the front of every secret name prior to being synced.
|
||||||
|
- Secret Suffix: If inputted, the suffix to appended to the back of every name of every secret prior to being synced.
|
||||||
|
- Label in GCP Secret Manager: If selected, every secret will be labeled in GCP Secret Manager (e.g. as `managed-by:infisical`); labels can be customized.
|
||||||
|
|
||||||
|
Setting a secret prefix, suffix, or enabling the labeling option ensures that existing secrets in GCP Secret Manager are not overwritten during the sync. As part of this process, Infisical abstains from mutating any secrets in GCP Secret Manager without the specified prefix, suffix, or attached label.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export const useCreateIntegration = () => {
|
|||||||
path?: string;
|
path?: string;
|
||||||
region?: string;
|
region?: string;
|
||||||
metadata?: {
|
metadata?: {
|
||||||
|
secretPrefix?: string;
|
||||||
secretSuffix?: string;
|
secretSuffix?: string;
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default function GCPSecretManagerAuthorizeIntegrationPage() {
|
|||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
||||||
router.push(`/integrations/gcp-secret-manager/pat/create?integrationAuthId=${integrationAuth._id}`);
|
router.push(`/integrations/gcp-secret-manager/create?integrationAuthId=${integrationAuth._id}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
import * as yup from "yup";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useCreateIntegration
|
useCreateIntegration
|
||||||
@@ -13,7 +17,12 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
SelectItem
|
SelectItem,
|
||||||
|
Switch,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs
|
||||||
} from "../../../components/v2";
|
} from "../../../components/v2";
|
||||||
import {
|
import {
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
@@ -21,8 +30,44 @@ import {
|
|||||||
} from "../../../hooks/api/integrationAuth";
|
} from "../../../hooks/api/integrationAuth";
|
||||||
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
||||||
|
|
||||||
|
enum TabSections {
|
||||||
|
Connection = "connection",
|
||||||
|
Options = "options"
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
selectedSourceEnvironment: yup.string().required("Source environment is required"),
|
||||||
|
secretPath: yup.string().required("Secret path is required"),
|
||||||
|
targetAppId: yup.string().required("GCP project is required"),
|
||||||
|
secretPrefix: yup.string(),
|
||||||
|
secretSuffix: yup.string(),
|
||||||
|
shouldLabel: yup.boolean(),
|
||||||
|
labelName: yup.string(),
|
||||||
|
labelValue: yup.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
export default function GCPSecretManagerCreateIntegrationPage() {
|
export default function GCPSecretManagerCreateIntegrationPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
|
watch
|
||||||
|
} = useForm<FormData>({
|
||||||
|
resolver: yupResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
secretPath: "/",
|
||||||
|
shouldLabel: false,
|
||||||
|
labelName: "managed-by",
|
||||||
|
labelValue: "infisical"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const shouldLabel = watch("shouldLabel");
|
||||||
|
const selectedSourceEnvironment = watch("selectedSourceEnvironment");
|
||||||
|
|
||||||
const { mutateAsync } = useCreateIntegration();
|
const { mutateAsync } = useCreateIntegration();
|
||||||
|
|
||||||
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
||||||
@@ -33,29 +78,45 @@ export default function GCPSecretManagerCreateIntegrationPage() {
|
|||||||
integrationAuthId: (integrationAuthId as string) ?? ""
|
integrationAuthId: (integrationAuthId as string) ?? ""
|
||||||
});
|
});
|
||||||
|
|
||||||
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
|
||||||
const [targetAppId, setTargetAppId] = useState("");
|
|
||||||
const [secretPath, setSecretPath] = useState("/");
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (shouldLabel) {
|
||||||
|
setValue("labelName", "managed-by");
|
||||||
|
setValue("labelValue", "infisical");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue("labelName", undefined);
|
||||||
|
setValue("labelValue", undefined);
|
||||||
|
}, [shouldLabel]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspace) {
|
if (workspace) {
|
||||||
setSelectedSourceEnvironment(workspace.environments[0].slug);
|
setValue("selectedSourceEnvironment", workspace.environments[0].slug);
|
||||||
}
|
}
|
||||||
}, [workspace]);
|
}, [workspace]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (integrationAuthApps) {
|
if (integrationAuthApps) {
|
||||||
if (integrationAuthApps.length > 0) {
|
if (integrationAuthApps.length > 0) {
|
||||||
setTargetAppId(integrationAuthApps[0].appId as string);
|
setValue("targetAppId", integrationAuthApps[0].appId as string);
|
||||||
} else {
|
} else {
|
||||||
setTargetAppId("none");
|
setValue("targetAppId", "none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [integrationAuthApps]);
|
}, [integrationAuthApps]);
|
||||||
|
|
||||||
const handleButtonClick = async () => {
|
const onFormSubmit = async ({
|
||||||
|
selectedSourceEnvironment: sce,
|
||||||
|
secretPath,
|
||||||
|
targetAppId,
|
||||||
|
secretPrefix,
|
||||||
|
secretSuffix,
|
||||||
|
shouldLabel: sl,
|
||||||
|
labelName,
|
||||||
|
labelValue
|
||||||
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
@@ -66,82 +127,232 @@ export default function GCPSecretManagerCreateIntegrationPage() {
|
|||||||
isActive: true,
|
isActive: true,
|
||||||
app: integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId)?.name,
|
app: integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId)?.name,
|
||||||
appId: targetAppId,
|
appId: targetAppId,
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
sourceEnvironment: sce,
|
||||||
secretPath
|
secretPath,
|
||||||
|
metadata: {
|
||||||
|
secretPrefix,
|
||||||
|
secretSuffix,
|
||||||
|
...(sl ? {
|
||||||
|
secretGCPLabel: {
|
||||||
|
labelName,
|
||||||
|
labelValue
|
||||||
|
}
|
||||||
|
} : {})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
|
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return integrationAuth &&
|
return integrationAuth &&
|
||||||
workspace &&
|
workspace &&
|
||||||
selectedSourceEnvironment &&
|
selectedSourceEnvironment &&
|
||||||
integrationAuthApps &&
|
integrationAuthApps ? (
|
||||||
targetAppId ? (
|
<form
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
|
className="flex h-full w-full items-center justify-center"
|
||||||
|
>
|
||||||
<Card className="max-w-md rounded-md p-8">
|
<Card className="max-w-md rounded-md p-8">
|
||||||
<CardTitle className="text-center">GCP Secret Manager Integration</CardTitle>
|
<CardTitle className="text-center">GCP Secret Manager Integration</CardTitle>
|
||||||
<FormControl label="Project Environment" className="mt-4">
|
<Tabs defaultValue={TabSections.Connection}>
|
||||||
<Select
|
<TabList>
|
||||||
value={selectedSourceEnvironment}
|
<Tab value={TabSections.Connection}>Connection</Tab>
|
||||||
onValueChange={(val) => setSelectedSourceEnvironment(val)}
|
<Tab value={TabSections.Options}>Options</Tab>
|
||||||
className="w-full border border-mineshaft-500"
|
</TabList>
|
||||||
>
|
<TabPanel value={TabSections.Connection}>
|
||||||
{workspace?.environments.map((sourceEnvironment) => (
|
<motion.div
|
||||||
<SelectItem
|
key="panel-1"
|
||||||
value={sourceEnvironment.slug}
|
transition={{ duration: 0.15 }}
|
||||||
key={`source-environment-${sourceEnvironment.slug}`}
|
initial={{ opacity: 0, translateX: 30 }}
|
||||||
>
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
{sourceEnvironment.name}
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
</SelectItem>
|
>
|
||||||
))}
|
<div>
|
||||||
</Select>
|
<Controller
|
||||||
</FormControl>
|
control={control}
|
||||||
<FormControl label="Secrets Path">
|
name="selectedSourceEnvironment"
|
||||||
<Input
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
value={secretPath}
|
<FormControl
|
||||||
onChange={(evt) => setSecretPath(evt.target.value)}
|
label="Project Environment"
|
||||||
placeholder="Provide a path, default is /"
|
errorText={error?.message}
|
||||||
/>
|
isError={Boolean(error)}
|
||||||
</FormControl>
|
>
|
||||||
<FormControl label="GCP Project">
|
<Select
|
||||||
<Select
|
defaultValue={field.value}
|
||||||
value={targetAppId}
|
{...field}
|
||||||
onValueChange={(val) => setTargetAppId(val)}
|
onValueChange={(e) => onChange(e)}
|
||||||
className="w-full border border-mineshaft-500"
|
className="w-full"
|
||||||
isDisabled={integrationAuthApps.length === 0}
|
>
|
||||||
>
|
{workspace?.environments.map((sourceEnvironment) => (
|
||||||
{integrationAuthApps.length > 0 ? (
|
<SelectItem
|
||||||
integrationAuthApps.map((integrationAuthApp) => (
|
value={sourceEnvironment.slug}
|
||||||
<SelectItem
|
key={`source-environment-${sourceEnvironment.slug}`}
|
||||||
value={integrationAuthApp.appId as string}
|
>
|
||||||
key={`target-app-${integrationAuthApp.appId}`}
|
{sourceEnvironment.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</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>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="mt-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isLoading}
|
||||||
>
|
>
|
||||||
{integrationAuthApp.name}
|
Create Integration
|
||||||
</SelectItem>
|
</Button>
|
||||||
))
|
</div>
|
||||||
) : (
|
</motion.div>
|
||||||
<SelectItem value="none" key="target-app-none">
|
</TabPanel>
|
||||||
No projects found
|
<TabPanel value={TabSections.Options}>
|
||||||
</SelectItem>
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="secretPrefix"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Secret Prefix"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="INFISICAL_"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="secretSuffix"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Secret Suffix"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="_INFISICAL"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="mt-8">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="shouldLabel"
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<Switch
|
||||||
|
id="label-gcp"
|
||||||
|
onCheckedChange={(isChecked) => onChange(isChecked)}
|
||||||
|
isChecked={value}
|
||||||
|
>
|
||||||
|
Label in GCP Secret Manager
|
||||||
|
</Switch>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{shouldLabel && (
|
||||||
|
<div className="mt-8">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="labelName"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Label Name"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="managed-by"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="labelValue"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Label Name"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="infisical"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Select>
|
</TabPanel>
|
||||||
</FormControl>
|
</Tabs>
|
||||||
<Button
|
|
||||||
onClick={handleButtonClick}
|
|
||||||
color="mineshaft"
|
|
||||||
className="mt-4"
|
|
||||||
isLoading={isLoading}
|
|
||||||
isDisabled={integrationAuthApps.length === 0}
|
|
||||||
>
|
|
||||||
Create Integration
|
|
||||||
</Button>
|
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<div />
|
<div />
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import queryString from "query-string";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
CardTitle,
|
|
||||||
FormControl,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
SelectItem
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import {
|
|
||||||
useCreateIntegration
|
|
||||||
} from "@app/hooks/api";
|
|
||||||
import { useGetIntegrationAuthApps,useGetIntegrationAuthById } from "@app/hooks/api/integrationAuth";
|
|
||||||
import { useGetWorkspaceById } from "@app/hooks/api/workspace";
|
|
||||||
|
|
||||||
export default function GCPSecretManagerCreateIntegrationPage() {
|
|
||||||
const router = useRouter();
|
|
||||||
const { mutateAsync } = useCreateIntegration();
|
|
||||||
|
|
||||||
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
|
||||||
|
|
||||||
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
|
||||||
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? "");
|
|
||||||
const { data: integrationAuthApps } = useGetIntegrationAuthApps({
|
|
||||||
integrationAuthId: (integrationAuthId as string) ?? ""
|
|
||||||
});
|
|
||||||
|
|
||||||
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
|
||||||
const [targetAppId, setTargetAppId] = useState("");
|
|
||||||
const [secretPath, setSecretPath] = useState("/");
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (workspace) {
|
|
||||||
setSelectedSourceEnvironment(workspace.environments[0].slug);
|
|
||||||
}
|
|
||||||
}, [workspace]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (integrationAuthApps) {
|
|
||||||
if (integrationAuthApps.length > 0) {
|
|
||||||
setTargetAppId(integrationAuthApps[0].appId as string);
|
|
||||||
} else {
|
|
||||||
setTargetAppId("none");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [integrationAuthApps]);
|
|
||||||
|
|
||||||
const handleButtonClick = async () => {
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
|
|
||||||
if (!integrationAuth?._id) return;
|
|
||||||
|
|
||||||
await mutateAsync({
|
|
||||||
integrationAuthId: integrationAuth?._id,
|
|
||||||
isActive: true,
|
|
||||||
app: integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId)?.name,
|
|
||||||
appId: targetAppId,
|
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
|
||||||
secretPath
|
|
||||||
});
|
|
||||||
|
|
||||||
setIsLoading(false);
|
|
||||||
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return integrationAuth &&
|
|
||||||
workspace &&
|
|
||||||
selectedSourceEnvironment &&
|
|
||||||
integrationAuthApps
|
|
||||||
? (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Card className="max-w-md rounded-md p-8">
|
|
||||||
<CardTitle className="text-center">GCP Secret Manager Integration</CardTitle>
|
|
||||||
<FormControl label="Project Environment" className="mt-4">
|
|
||||||
<Select
|
|
||||||
value={selectedSourceEnvironment}
|
|
||||||
onValueChange={(val) => setSelectedSourceEnvironment(val)}
|
|
||||||
className="w-full border border-mineshaft-500"
|
|
||||||
>
|
|
||||||
{workspace?.environments.map((sourceEnvironment) => (
|
|
||||||
<SelectItem
|
|
||||||
value={sourceEnvironment.slug}
|
|
||||||
key={`source-environment-${sourceEnvironment.slug}`}
|
|
||||||
>
|
|
||||||
{sourceEnvironment.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl label="Secrets Path">
|
|
||||||
<Input
|
|
||||||
value={secretPath}
|
|
||||||
onChange={(evt) => setSecretPath(evt.target.value)}
|
|
||||||
placeholder="Provide a path, default is /"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl label="GCP Project">
|
|
||||||
<Select
|
|
||||||
value={targetAppId}
|
|
||||||
onValueChange={(val) => setTargetAppId(val)}
|
|
||||||
className="w-full border border-mineshaft-500"
|
|
||||||
isDisabled={integrationAuthApps.length === 0}
|
|
||||||
>
|
|
||||||
{integrationAuthApps.length > 0 ? (
|
|
||||||
integrationAuthApps.map((integrationAuthApp) => (
|
|
||||||
<SelectItem
|
|
||||||
value={integrationAuthApp.appId as string}
|
|
||||||
key={`target-app-${integrationAuthApp.appId}`}
|
|
||||||
>
|
|
||||||
{integrationAuthApp.name}
|
|
||||||
</SelectItem>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<SelectItem value="none" key="target-app-none">
|
|
||||||
No projects found
|
|
||||||
</SelectItem>
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<Button
|
|
||||||
onClick={handleButtonClick}
|
|
||||||
color="mineshaft"
|
|
||||||
className="mt-4"
|
|
||||||
isLoading={isLoading}
|
|
||||||
// isDisabled={integrationAuthApps.length === 0}
|
|
||||||
>
|
|
||||||
Create Integration
|
|
||||||
</Button>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
GCPSecretManagerCreateIntegrationPage.requireAuth = true;
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
import * as yup from "yup";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useCreateIntegration
|
useCreateIntegration
|
||||||
@@ -13,7 +17,11 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
SelectItem
|
SelectItem,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs
|
||||||
} from "../../../components/v2";
|
} from "../../../components/v2";
|
||||||
import {
|
import {
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
@@ -27,8 +35,43 @@ const gitLabEntities = [
|
|||||||
{ name: "Group", value: "group" }
|
{ name: "Group", value: "group" }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
enum TabSections {
|
||||||
|
Connection = "connection",
|
||||||
|
Options = "options"
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
targetEntity: yup.string().oneOf(gitLabEntities.map(entity => entity.value), "Invalid entity type"),
|
||||||
|
targetTeamId: yup.string(),
|
||||||
|
selectedSourceEnvironment: yup.string().required("Source environment is required"),
|
||||||
|
secretPath: yup.string().required("Secret path is required"),
|
||||||
|
targetAppId: yup.string().required("GitLab project is required"),
|
||||||
|
targetEnvironment: yup.string(),
|
||||||
|
secretPrefix: yup.string(),
|
||||||
|
secretSuffix: yup.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
export default function GitLabCreateIntegrationPage() {
|
export default function GitLabCreateIntegrationPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
|
watch
|
||||||
|
} = useForm<FormData>({
|
||||||
|
resolver: yupResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
targetEntity: "individual",
|
||||||
|
secretPath: "/"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const selectedSourceEnvironment = watch("selectedSourceEnvironment");
|
||||||
|
const targetEntity = watch("targetEntity");
|
||||||
|
const targetTeamId = watch("targetTeamId");
|
||||||
|
|
||||||
const { mutateAsync } = useCreateIntegration();
|
const { mutateAsync } = useCreateIntegration();
|
||||||
|
|
||||||
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
||||||
@@ -36,8 +79,6 @@ export default function GitLabCreateIntegrationPage() {
|
|||||||
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
||||||
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? "");
|
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? "");
|
||||||
|
|
||||||
const [targetTeamId, setTargetTeamId] = useState<string | null>(null);
|
|
||||||
|
|
||||||
const { data: integrationAuthApps } = useGetIntegrationAuthApps({
|
const { data: integrationAuthApps } = useGetIntegrationAuthApps({
|
||||||
integrationAuthId: (integrationAuthId as string) ?? "",
|
integrationAuthId: (integrationAuthId as string) ?? "",
|
||||||
...(targetTeamId ? { teamId: targetTeamId } : {})
|
...(targetTeamId ? { teamId: targetTeamId } : {})
|
||||||
@@ -46,26 +87,20 @@ export default function GitLabCreateIntegrationPage() {
|
|||||||
(integrationAuthId as string) ?? ""
|
(integrationAuthId as string) ?? ""
|
||||||
);
|
);
|
||||||
|
|
||||||
const [targetEntity, setTargetEntity] = useState(gitLabEntities[0].value);
|
|
||||||
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
|
||||||
const [secretPath, setSecretPath] = useState("/");
|
|
||||||
const [targetAppId, setTargetAppId] = useState("");
|
|
||||||
const [targetEnvironment, setTargetEnvironment] = useState("");
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspace) {
|
if (workspace) {
|
||||||
setSelectedSourceEnvironment(workspace.environments[0].slug);
|
setValue("selectedSourceEnvironment", workspace.environments[0].slug);
|
||||||
}
|
}
|
||||||
}, [workspace]);
|
}, [workspace]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (integrationAuthApps) {
|
if (integrationAuthApps) {
|
||||||
if (integrationAuthApps.length > 0) {
|
if (integrationAuthApps.length > 0) {
|
||||||
setTargetAppId(integrationAuthApps[0].appId as string);
|
setValue("targetAppId", String(integrationAuthApps[0].appId as string));
|
||||||
} else {
|
} else {
|
||||||
setTargetAppId("none");
|
setValue("targetAppId", "none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [integrationAuthApps]);
|
}, [integrationAuthApps]);
|
||||||
@@ -75,18 +110,25 @@ export default function GitLabCreateIntegrationPage() {
|
|||||||
if (integrationAuthTeams) {
|
if (integrationAuthTeams) {
|
||||||
if (integrationAuthTeams.length > 0) {
|
if (integrationAuthTeams.length > 0) {
|
||||||
// case: user is part of at least 1 group in GitLab
|
// case: user is part of at least 1 group in GitLab
|
||||||
setTargetTeamId(integrationAuthTeams[0].teamId);
|
setValue("targetTeamId", String(integrationAuthTeams[0].teamId));
|
||||||
} else {
|
} else {
|
||||||
// case: user is not part of any groups in GitLab
|
// case: user is not part of any groups in GitLab
|
||||||
setTargetTeamId("none");
|
setValue("targetTeamId", "none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (targetEntity === "individual") {
|
} else if (targetEntity === "individual") {
|
||||||
setTargetTeamId(null);
|
setValue("targetTeamId", undefined);
|
||||||
}
|
}
|
||||||
}, [targetEntity, integrationAuthTeams]);
|
}, [targetEntity, integrationAuthTeams]);
|
||||||
|
|
||||||
const handleButtonClick = async () => {
|
const onFormSubmit = async ({
|
||||||
|
selectedSourceEnvironment: sse,
|
||||||
|
secretPath,
|
||||||
|
targetAppId,
|
||||||
|
targetEnvironment,
|
||||||
|
secretPrefix,
|
||||||
|
secretSuffix
|
||||||
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
if (!integrationAuth?._id) return;
|
if (!integrationAuth?._id) return;
|
||||||
@@ -94,132 +136,257 @@ export default function GitLabCreateIntegrationPage() {
|
|||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
integrationAuthId: integrationAuth?._id,
|
integrationAuthId: integrationAuth?._id,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
app: integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId)?.name,
|
app: integrationAuthApps?.find((integrationAuthApp) => String(integrationAuthApp.appId) === targetAppId)?.name,
|
||||||
appId: String(targetAppId),
|
appId: String(targetAppId),
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
sourceEnvironment: sse,
|
||||||
targetEnvironment: targetEnvironment === "" ? "*" : targetEnvironment,
|
targetEnvironment: targetEnvironment === "" ? "*" : targetEnvironment,
|
||||||
secretPath
|
secretPath,
|
||||||
|
metadata: {
|
||||||
|
secretPrefix,
|
||||||
|
secretSuffix
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
|
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return integrationAuth &&
|
return integrationAuth &&
|
||||||
workspace &&
|
workspace &&
|
||||||
selectedSourceEnvironment &&
|
selectedSourceEnvironment &&
|
||||||
integrationAuthApps &&
|
integrationAuthApps &&
|
||||||
integrationAuthTeams &&
|
integrationAuthTeams ? (
|
||||||
targetAppId ? (
|
<form
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
|
className="flex h-full w-full items-center justify-center"
|
||||||
|
>
|
||||||
<Card className="max-w-md rounded-md p-8">
|
<Card className="max-w-md rounded-md p-8">
|
||||||
<CardTitle className="text-center">GitLab Integration</CardTitle>
|
<CardTitle className="text-center">GitLab Integration</CardTitle>
|
||||||
<FormControl label="Project Environment">
|
<Tabs defaultValue={TabSections.Connection}>
|
||||||
<Select
|
<TabList>
|
||||||
value={selectedSourceEnvironment}
|
<Tab value={TabSections.Connection}>Connection</Tab>
|
||||||
onValueChange={(val) => setSelectedSourceEnvironment(val)}
|
<Tab value={TabSections.Options}>Options</Tab>
|
||||||
className="w-full border border-mineshaft-500"
|
</TabList>
|
||||||
>
|
<TabPanel value={TabSections.Connection}>
|
||||||
{workspace?.environments.map((sourceEnvironment) => (
|
<motion.div
|
||||||
<SelectItem
|
key="panel-1"
|
||||||
value={sourceEnvironment.slug}
|
transition={{ duration: 0.15 }}
|
||||||
key={`source-environment-${sourceEnvironment.slug}`}
|
initial={{ opacity: 0, translateX: 30 }}
|
||||||
>
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
{sourceEnvironment.name}
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl label="Secrets Path">
|
|
||||||
<Input
|
|
||||||
value={secretPath}
|
|
||||||
onChange={(evt) => setSecretPath(evt.target.value)}
|
|
||||||
placeholder="Provide a path, default is /"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl label="GitLab Integration Type">
|
|
||||||
<Select
|
|
||||||
value={targetEntity}
|
|
||||||
onValueChange={(val) => setTargetEntity(val)}
|
|
||||||
className="w-full border border-mineshaft-500"
|
|
||||||
>
|
|
||||||
{gitLabEntities.map((entity) => {
|
|
||||||
return (
|
|
||||||
<SelectItem value={entity.value} key={`target-entity-${entity.value}`}>
|
|
||||||
{entity.name}
|
|
||||||
</SelectItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
{targetEntity === "group" && targetTeamId && (
|
|
||||||
<FormControl label="GitLab Group">
|
|
||||||
<Select
|
|
||||||
value={targetTeamId}
|
|
||||||
onValueChange={(val) => setTargetTeamId(val)}
|
|
||||||
className="w-full border border-mineshaft-500"
|
|
||||||
>
|
>
|
||||||
{integrationAuthTeams.length > 0 ? (
|
<div>
|
||||||
integrationAuthTeams.map((integrationAuthTeam) => (
|
<Controller
|
||||||
<SelectItem
|
control={control}
|
||||||
value={integrationAuthTeam.teamId}
|
name="selectedSourceEnvironment"
|
||||||
key={`target-team-${integrationAuthTeam.teamId}`}
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
>
|
<FormControl
|
||||||
{integrationAuthTeam.name}
|
label="Project Environment"
|
||||||
</SelectItem>
|
errorText={error?.message}
|
||||||
))
|
isError={Boolean(error)}
|
||||||
) : (
|
>
|
||||||
<SelectItem value="none" key="target-team-none">
|
<Select
|
||||||
No groups found
|
defaultValue={field.value}
|
||||||
</SelectItem>
|
{...field}
|
||||||
)}
|
onValueChange={(e) => onChange(e)}
|
||||||
</Select>
|
className="w-full"
|
||||||
</FormControl>
|
>
|
||||||
)}
|
{workspace?.environments.map((sourceEnvironment) => (
|
||||||
<FormControl label="GitLab Project">
|
<SelectItem
|
||||||
<Select
|
value={sourceEnvironment.slug}
|
||||||
value={targetAppId}
|
key={`source-environment-${sourceEnvironment.slug}`}
|
||||||
onValueChange={(val) => setTargetAppId(val)}
|
>
|
||||||
className="w-full border border-mineshaft-500"
|
{sourceEnvironment.name}
|
||||||
isDisabled={integrationAuthApps.length === 0}
|
</SelectItem>
|
||||||
>
|
))}
|
||||||
{integrationAuthApps.length > 0 ? (
|
</Select>
|
||||||
integrationAuthApps.map((integrationAuthApp) => (
|
</FormControl>
|
||||||
<SelectItem
|
)}
|
||||||
value={integrationAuthApp.appId as string}
|
/>
|
||||||
key={`target-app-${integrationAuthApp.appId}`}
|
<Controller
|
||||||
>
|
control={control}
|
||||||
{integrationAuthApp.name}
|
defaultValue=""
|
||||||
</SelectItem>
|
name="secretPath"
|
||||||
))
|
render={({ field, fieldState: { error } }) => (
|
||||||
) : (
|
<FormControl
|
||||||
<SelectItem value="none" key="target-app-none">
|
label="Secrets Path"
|
||||||
No projects found
|
isError={Boolean(error)}
|
||||||
</SelectItem>
|
errorText={error?.message}
|
||||||
)}
|
>
|
||||||
</Select>
|
<Input
|
||||||
</FormControl>
|
{...field}
|
||||||
<FormControl label="GitLab Environment Scope (Optional)">
|
placeholder="/"
|
||||||
<Input
|
/>
|
||||||
placeholder="*"
|
</FormControl>
|
||||||
value={targetEnvironment}
|
)}
|
||||||
onChange={(e) => setTargetEnvironment(e.target.value)}
|
/>
|
||||||
/>
|
<Controller
|
||||||
</FormControl>
|
control={control}
|
||||||
<Button
|
name="targetEntity"
|
||||||
onClick={handleButtonClick}
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
color="mineshaft"
|
<FormControl
|
||||||
className="mt-4"
|
label="GitLab Integration Type"
|
||||||
isLoading={isLoading}
|
errorText={error?.message}
|
||||||
isDisabled={integrationAuthApps.length === 0}
|
isError={Boolean(error)}
|
||||||
>
|
>
|
||||||
Create Integration
|
<Select
|
||||||
</Button>
|
{...field}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{gitLabEntities.map((entity) => {
|
||||||
|
return (
|
||||||
|
<SelectItem value={entity.value} key={`target-entity-${entity.value}`}>
|
||||||
|
{entity.name}
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{targetEntity === "group" && targetTeamId && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="targetTeamId"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="GitLab Group"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{integrationAuthTeams.length > 0 ? (
|
||||||
|
integrationAuthTeams.map((integrationAuthTeam) =>
|
||||||
|
(
|
||||||
|
<SelectItem
|
||||||
|
value={String(integrationAuthTeam.teamId as string)}
|
||||||
|
key={`target-team-${String(integrationAuthTeam.teamId)}`}
|
||||||
|
>
|
||||||
|
{integrationAuthTeam.name}
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<SelectItem value="none" key="target-team-none">
|
||||||
|
No groups found
|
||||||
|
</SelectItem>
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="targetAppId"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
label="GitLab 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>
|
||||||
|
)}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="targetEnvironment"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="GitLab Environment Scope (Optional)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="*"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
className="mt-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isLoading}
|
||||||
|
>
|
||||||
|
Create Integration
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={TabSections.Options}>
|
||||||
|
<div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="secretPrefix"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Secret Prefix"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="INFISICAL_"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="secretSuffix"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Secret Suffix"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="_INFISICAL"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<div />
|
<div />
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user