mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Refactoring integrations frontend (cleanup)
This commit is contained in:
113
frontend/components/integrations/CloudIntegration.tsx
Normal file
113
frontend/components/integrations/CloudIntegration.tsx
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import {
|
||||||
|
faCheck,
|
||||||
|
faX,
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
interface CloudIntegration {
|
||||||
|
integration: IntegrationOption;
|
||||||
|
setSelectedIntegrationOption: () => void;
|
||||||
|
integrationOptionPress: () => void;
|
||||||
|
deleteIntegrationAuth: () => void;
|
||||||
|
authorizations: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IntegrationOption {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
clientId: string;
|
||||||
|
docsLink: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CloudIntegration = ({
|
||||||
|
integration,
|
||||||
|
setSelectedIntegrationOption,
|
||||||
|
integrationOptionPress,
|
||||||
|
deleteIntegrationAuth,
|
||||||
|
authorizations
|
||||||
|
}: CloudIntegration) => {
|
||||||
|
console.log('cio', integration);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative ${
|
||||||
|
["Heroku"].includes(integration.name)
|
||||||
|
? "hover:bg-white/10 duration-200 cursor-pointer"
|
||||||
|
: "opacity-50"
|
||||||
|
} flex flex-row bg-white/5 h-32 rounded-md p-4 items-center`}
|
||||||
|
onClick={() => {
|
||||||
|
if (!["Heroku"].includes(integration.name)) return;
|
||||||
|
setSelectedIntegrationOption(integration);
|
||||||
|
integrationOptionPress({
|
||||||
|
integrationOption: integration
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
key={integration.name}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={`/images/integrations/${integration.name}.png`}
|
||||||
|
height={70}
|
||||||
|
width={70}
|
||||||
|
alt="integration logo"
|
||||||
|
/>
|
||||||
|
{integration.name.split(" ").length > 2 ? (
|
||||||
|
<div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-3xl ml-4 max-w-xs">
|
||||||
|
<div>{integration.name.split(" ")[0]}</div>
|
||||||
|
<div className="text-base">
|
||||||
|
{integration.name.split(" ")[1]}{" "}
|
||||||
|
{integration.name.split(" ")[2]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-xl ml-4 max-w-xs">
|
||||||
|
{integration.name}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{["Heroku"].includes(integration.name) &&
|
||||||
|
authorizations
|
||||||
|
.map((authorization) => authorization.integration)
|
||||||
|
.includes(integration.name.toLowerCase()) && (
|
||||||
|
<div className="absolute group z-50 top-0 right-0 flex flex-row">
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
deleteIntegrationAuth({
|
||||||
|
integrationAuthId: authorizations
|
||||||
|
.filter(
|
||||||
|
(authorization) =>
|
||||||
|
authorization.integration ==
|
||||||
|
integration.name.toLowerCase()
|
||||||
|
)
|
||||||
|
.map((authorization) => authorization._id)[0],
|
||||||
|
});
|
||||||
|
router.reload();
|
||||||
|
}}
|
||||||
|
className="cursor-pointer w-max bg-red py-0.5 px-2 rounded-b-md text-xs flex flex-row items-center opacity-0 group-hover:opacity-100 duration-200"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faX}
|
||||||
|
className="text-xs mr-2 py-px"
|
||||||
|
/>
|
||||||
|
Revoke
|
||||||
|
</div>
|
||||||
|
<div className="w-max bg-primary py-0.5 px-2 rounded-bl-md rounded-tr-md text-xs flex flex-row items-center text-black opacity-90 group-hover:opacity-100 duration-200">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faCheck}
|
||||||
|
className="text-xs mr-2"
|
||||||
|
/>
|
||||||
|
Authorized
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!["Heroku"].includes(integration.name) && (
|
||||||
|
<div className="absolute group z-50 top-0 right-0 flex flex-row">
|
||||||
|
<div className="w-max bg-yellow py-0.5 px-2 rounded-bl-md rounded-tr-md text-xs flex flex-row items-center text-black opacity-90">
|
||||||
|
Coming Soon
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CloudIntegration;
|
||||||
37
frontend/components/integrations/FrameworkIntegration.tsx
Normal file
37
frontend/components/integrations/FrameworkIntegration.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
interface Framework {
|
||||||
|
name: string;
|
||||||
|
link: string;
|
||||||
|
image: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FrameworkIntegration = ({
|
||||||
|
framework
|
||||||
|
}: {
|
||||||
|
framework: Framework;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div key={framework.name}>
|
||||||
|
<a
|
||||||
|
href={framework.link}
|
||||||
|
rel="noopener"
|
||||||
|
className={`relative flex flex-row items-center justify-center bg-bunker-500 hover:bg-gradient-to-tr hover:from-sky-400 hover:to-primary duration-200 h-32 rounded-md p-0.5 items-center cursor-pointer`}
|
||||||
|
>
|
||||||
|
<div className={`font-semibold bg-bunker-500 flex flex-col items-center justify-center h-full w-full rounded-md text-gray-300 group-hover:text-gray-200 duration-200 ${framework?.name?.split(" ").length > 1 ? "text-sm px-1" : "text-xl px-2"} text-center w-full max-w-xs`}>
|
||||||
|
{framework?.image && <Image
|
||||||
|
src={`/images/integrations/${framework.image}.png`}
|
||||||
|
height={framework?.name ? 60 : 90}
|
||||||
|
width={framework?.name ? 60 : 90}
|
||||||
|
alt="integration logo"
|
||||||
|
></Image>}
|
||||||
|
{framework?.name && framework?.image && <div className="h-2"></div>}
|
||||||
|
{framework?.name && framework.name}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FrameworkIntegration;
|
||||||
140
frontend/components/integrations/Integration.tsx
Normal file
140
frontend/components/integrations/Integration.tsx
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import {
|
||||||
|
faArrowRight,
|
||||||
|
faRotate,
|
||||||
|
faX,
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import {
|
||||||
|
reverseEnvMapping
|
||||||
|
} from "../../public/data/frequentConstants";
|
||||||
|
import updateIntegration from "../../pages/api/integrations/updateIntegration"
|
||||||
|
import deleteIntegration from "../../pages/api/integrations/DeleteIntegration"
|
||||||
|
import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps";
|
||||||
|
import Button from "~/components/basic/buttons/Button";
|
||||||
|
import ListBox from "~/components/basic/Listbox";
|
||||||
|
|
||||||
|
interface ProjectIntegration {
|
||||||
|
app?: string;
|
||||||
|
environment: string;
|
||||||
|
integration: string;
|
||||||
|
integrationAuth: string;
|
||||||
|
isActive: Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Integration = ({
|
||||||
|
projectIntegration
|
||||||
|
}: {
|
||||||
|
projectIntegration: ProjectIntegration;
|
||||||
|
}) => {
|
||||||
|
const [integrationEnvironment, setIntegrationEnvironment] = useState(
|
||||||
|
reverseEnvMapping[projectIntegration.environment]
|
||||||
|
);
|
||||||
|
const [fileState, setFileState] = useState([]);
|
||||||
|
const router = useRouter();
|
||||||
|
const [apps, setApps] = useState([]);
|
||||||
|
const [integrationApp, setIntegrationApp] = useState(
|
||||||
|
projectIntegration.app ? projectIntegration.app : apps[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(async () => {
|
||||||
|
const tempHerokuApps = await getIntegrationApps({
|
||||||
|
integrationAuthId: projectIntegration.integrationAuth,
|
||||||
|
});
|
||||||
|
const tempHerokuAppNames = tempHerokuApps.map((app) => app.name);
|
||||||
|
setApps(tempHerokuAppNames);
|
||||||
|
setIntegrationApp(
|
||||||
|
projectIntegration.app ? projectIntegration.app : tempHerokuAppNames[0]
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col max-w-5xl justify-center bg-white/5 p-6 rounded-md mx-6 mt-8">
|
||||||
|
<div className="relative px-4 flex flex-row items-center justify-between mb-4">
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<div>
|
||||||
|
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||||
|
ENVIRONMENT
|
||||||
|
</div>
|
||||||
|
<ListBox
|
||||||
|
data={
|
||||||
|
!projectIntegration.isActive && [
|
||||||
|
"Development",
|
||||||
|
"Staging",
|
||||||
|
"Production",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
selected={integrationEnvironment}
|
||||||
|
onChange={setIntegrationEnvironment}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faArrowRight}
|
||||||
|
className="mx-4 text-gray-400 mt-8"
|
||||||
|
/>
|
||||||
|
<div className="mr-2">
|
||||||
|
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||||
|
INTEGRATION
|
||||||
|
</div>
|
||||||
|
<div className="py-2.5 bg-white/[.07] rounded-md pl-4 pr-20 text-sm font-semibold text-gray-300">
|
||||||
|
{projectIntegration.integration.charAt(0).toUpperCase() +
|
||||||
|
projectIntegration.integration.slice(1)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||||
|
HEROKU APP
|
||||||
|
</div>
|
||||||
|
<ListBox
|
||||||
|
data={!projectIntegration.isActive && apps}
|
||||||
|
selected={integrationApp}
|
||||||
|
onChange={setIntegrationApp}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row mt-6">
|
||||||
|
{projectIntegration.isActive ? (
|
||||||
|
<div className="max-w-5xl flex flex-row items-center bg-white/5 p-2 rounded-md px-4">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faRotate}
|
||||||
|
className="text-lg mr-2.5 text-primary animate-spin"
|
||||||
|
/>
|
||||||
|
<div className="text-gray-300 font-semibold">In Sync</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
text="Start Integration"
|
||||||
|
onButtonPressed={async () => {
|
||||||
|
const result = await updateIntegration({
|
||||||
|
integrationId: projectIntegration._id,
|
||||||
|
environment: envMapping[integrationEnvironment],
|
||||||
|
app: integrationApp,
|
||||||
|
isActive: true
|
||||||
|
});
|
||||||
|
router.reload();
|
||||||
|
}}
|
||||||
|
color="mineshaft"
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="opacity-50 hover:opacity-100 duration-200 ml-2">
|
||||||
|
<Button
|
||||||
|
onButtonPressed={async () => {
|
||||||
|
await deleteIntegration({
|
||||||
|
integrationId: projectIntegration._id,
|
||||||
|
});
|
||||||
|
router.reload();
|
||||||
|
}}
|
||||||
|
color="red"
|
||||||
|
size="icon-md"
|
||||||
|
icon={faX}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Integration;
|
||||||
@@ -2,208 +2,85 @@ import React, { useEffect, useState } from "react";
|
|||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import {
|
|
||||||
faArrowRight,
|
|
||||||
faCheck,
|
|
||||||
faRotate,
|
|
||||||
faX,
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
|
|
||||||
import Button from "~/components/basic/buttons/Button";
|
|
||||||
import ListBox from "~/components/basic/Listbox";
|
|
||||||
import NavHeader from "~/components/navigation/NavHeader";
|
import NavHeader from "~/components/navigation/NavHeader";
|
||||||
import getSecretsForProject from "~/components/utilities/secrets/getSecretsForProject";
|
import Integration from "~/components/integrations/Integration";
|
||||||
|
import FrameworkIntegration from "~/components/integrations/FrameworkIntegration";
|
||||||
|
import CloudIntegration from "~/components/integrations/CloudIntegration";
|
||||||
import guidGenerator from "~/utilities/randomId";
|
import guidGenerator from "~/utilities/randomId";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
envMapping,
|
frameworks
|
||||||
frameworks,
|
|
||||||
reverseEnvMapping
|
|
||||||
} from "../../public/data/frequentConstants";
|
} from "../../public/data/frequentConstants";
|
||||||
import deleteIntegration from "../api/integrations/DeleteIntegration";
|
|
||||||
import deleteIntegrationAuth from "../api/integrations/DeleteIntegrationAuth";
|
import deleteIntegrationAuth from "../api/integrations/DeleteIntegrationAuth";
|
||||||
import getIntegrationApps from "../api/integrations/GetIntegrationApps";
|
|
||||||
import getIntegrations from "../api/integrations/GetIntegrations";
|
import getIntegrations from "../api/integrations/GetIntegrations";
|
||||||
import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
|
import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
|
||||||
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
|
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
|
||||||
import updateIntegration from "../api/integrations/updateIntegration";
|
|
||||||
import getBot from "../api/bot/getBot";
|
import getBot from "../api/bot/getBot";
|
||||||
import setBotActiveStatus from "../api/bot/setBotActiveStatus";
|
import setBotActiveStatus from "../api/bot/setBotActiveStatus";
|
||||||
import getLatestFileKey from "../api/workspace/getLatestFileKey";
|
import getLatestFileKey from "../api/workspace/getLatestFileKey";
|
||||||
import ActivateBotDialog from "~/components/basic/dialog/ActivateBotDialog";
|
import ActivateBotDialog from "~/components/basic/dialog/ActivateBotDialog";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
decryptAssymmetric,
|
decryptAssymmetric,
|
||||||
encryptAssymmetric
|
encryptAssymmetric
|
||||||
} = require('../../components/utilities/cryptography/crypto');
|
} = require('../../components/utilities/cryptography/crypto');
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
|
|
||||||
const Integration = ({ projectIntegration }) => {
|
|
||||||
const [integrationEnvironment, setIntegrationEnvironment] = useState(
|
|
||||||
reverseEnvMapping[projectIntegration.environment]
|
|
||||||
);
|
|
||||||
const [fileState, setFileState] = useState([]);
|
|
||||||
const [data, setData] = useState();
|
|
||||||
const [isKeyAvailable, setIsKeyAvailable] = useState(true);
|
|
||||||
const router = useRouter();
|
|
||||||
const [apps, setApps] = useState([]);
|
|
||||||
const [integrationApp, setIntegrationApp] = useState(
|
|
||||||
projectIntegration.app ? projectIntegration.app : apps[0]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(async () => {
|
|
||||||
const tempHerokuApps = await getIntegrationApps({
|
|
||||||
integrationAuthId: projectIntegration.integrationAuth,
|
|
||||||
});
|
|
||||||
const tempHerokuAppNames = tempHerokuApps.map((app) => app.name);
|
|
||||||
setApps(tempHerokuAppNames);
|
|
||||||
setIntegrationApp(
|
|
||||||
projectIntegration.app ? projectIntegration.app : tempHerokuAppNames[0]
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col max-w-5xl justify-center bg-white/5 p-6 rounded-md mx-6 mt-8">
|
|
||||||
<div className="relative px-4 flex flex-row items-center justify-between mb-4">
|
|
||||||
<div className="flex flex-row">
|
|
||||||
<div>
|
|
||||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
|
||||||
ENVIRONMENT
|
|
||||||
</div>
|
|
||||||
<ListBox
|
|
||||||
data={
|
|
||||||
!projectIntegration.isActive && [
|
|
||||||
"Development",
|
|
||||||
"Staging",
|
|
||||||
"Production",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
selected={integrationEnvironment}
|
|
||||||
onChange={setIntegrationEnvironment}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faArrowRight}
|
|
||||||
className="mx-4 text-gray-400 mt-8"
|
|
||||||
/>
|
|
||||||
<div className="mr-2">
|
|
||||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
|
||||||
INTEGRATION
|
|
||||||
</div>
|
|
||||||
<div className="py-2.5 bg-white/[.07] rounded-md pl-4 pr-20 text-sm font-semibold text-gray-300">
|
|
||||||
{projectIntegration.integration.charAt(0).toUpperCase() +
|
|
||||||
projectIntegration.integration.slice(1)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
|
||||||
HEROKU APP
|
|
||||||
</div>
|
|
||||||
<ListBox
|
|
||||||
data={!projectIntegration.isActive && apps}
|
|
||||||
selected={integrationApp}
|
|
||||||
onChange={setIntegrationApp}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-row mt-6">
|
|
||||||
{projectIntegration.isActive ? (
|
|
||||||
<div className="max-w-5xl flex flex-row items-center bg-white/5 p-2 rounded-md px-4">
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faRotate}
|
|
||||||
className="text-lg mr-2.5 text-primary animate-spin"
|
|
||||||
/>
|
|
||||||
<div className="text-gray-300 font-semibold">In Sync</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
text="Start Integration"
|
|
||||||
onButtonPressed={async () => {
|
|
||||||
const result = await updateIntegration({
|
|
||||||
integrationId: projectIntegration._id,
|
|
||||||
environment: envMapping[integrationEnvironment],
|
|
||||||
app: integrationApp,
|
|
||||||
isActive: true
|
|
||||||
});
|
|
||||||
router.reload();
|
|
||||||
}}
|
|
||||||
color="mineshaft"
|
|
||||||
size="md"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="opacity-50 hover:opacity-100 duration-200 ml-2">
|
|
||||||
<Button
|
|
||||||
onButtonPressed={async () => {
|
|
||||||
await deleteIntegration({
|
|
||||||
integrationId: projectIntegration._id,
|
|
||||||
});
|
|
||||||
router.reload();
|
|
||||||
}}
|
|
||||||
color="red"
|
|
||||||
size="icon-md"
|
|
||||||
icon={faX}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function Integrations() {
|
export default function Integrations() {
|
||||||
const [integrations, setIntegrations] = useState({});
|
const [integrations, setIntegrations] = useState([]);
|
||||||
const [projectIntegrations, setProjectIntegrations] = useState([]);
|
const [projectIntegrations, setProjectIntegrations] = useState([]);
|
||||||
const [authorizations, setAuthorizations] = useState();
|
const [authorizations, setAuthorizations] = useState();
|
||||||
const router = useRouter();
|
|
||||||
const [csrfToken, setCsrfToken] = useState("");
|
|
||||||
const [bot, setBot] = useState(null);
|
const [bot, setBot] = useState(null);
|
||||||
const [isActivateBotOpen, setIsActivateBotOpen] = useState(false);
|
const [isActivateBotOpen, setIsActivateBotOpen] = useState(false);
|
||||||
const [selectedIntegrationOption, setSelectedIntegrationOption] = useState(null);
|
const [selectedIntegrationOption, setSelectedIntegrationOption] = useState(null);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(async () => {
|
useEffect(async () => {
|
||||||
try {
|
try {
|
||||||
// generate CSRF token for OAuth2 code-token exchange integrations
|
// get integrations authorized for project
|
||||||
const tempCSRFToken = crypto.randomBytes(16).toString("hex");
|
|
||||||
setCsrfToken(tempCSRFToken);
|
|
||||||
localStorage.setItem("latestCSRFToken", tempCSRFToken);
|
|
||||||
|
|
||||||
let projectAuthorizations = await getWorkspaceAuthorizations({
|
let projectAuthorizations = await getWorkspaceAuthorizations({
|
||||||
workspaceId: router.query.id,
|
workspaceId: router.query.id,
|
||||||
});
|
});
|
||||||
setAuthorizations(projectAuthorizations);
|
setAuthorizations(projectAuthorizations);
|
||||||
|
|
||||||
|
// get active/inactive (cloud) integrations for project
|
||||||
const projectIntegrations = await getWorkspaceIntegrations({
|
const projectIntegrations = await getWorkspaceIntegrations({
|
||||||
workspaceId: router.query.id,
|
workspaceId: router.query.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
setProjectIntegrations(projectIntegrations);
|
setProjectIntegrations(projectIntegrations);
|
||||||
|
|
||||||
|
// get bot for project
|
||||||
const bot = await getBot({
|
const bot = await getBot({
|
||||||
workspaceId: router.query.id
|
workspaceId: router.query.id
|
||||||
});
|
});
|
||||||
|
|
||||||
setBot(bot.bot);
|
setBot(bot.bot);
|
||||||
|
|
||||||
const integrationsData = await getIntegrations();
|
// get cloud integration options
|
||||||
setIntegrations(integrationsData);
|
let integrationOptions = await getIntegrations();
|
||||||
|
integrationOptions = Object
|
||||||
|
.keys(integrationOptions)
|
||||||
|
.map(integrationOption => integrationOptions[integrationOption]);
|
||||||
|
|
||||||
|
setIntegrations(integrationOptions);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle activate/deactivate bot
|
* Activate bot for project by performing the following steps:
|
||||||
|
* 1. Get the (encrypted) project key
|
||||||
|
* 2. Decrypt project key with user's private key
|
||||||
|
* 3. Encrypt project key with bot's public key
|
||||||
|
* 4. Send encrypted project key to backend and set bot status to active
|
||||||
*/
|
*/
|
||||||
const handleBotActivate = async () => {
|
const handleBotActivate = async () => {
|
||||||
const key = await getLatestFileKey({ workspaceId: router.query.id });
|
|
||||||
try {
|
try {
|
||||||
if (bot) {
|
const key = await getLatestFileKey({ workspaceId: router.query.id });
|
||||||
let botKey;
|
|
||||||
if (!bot.isActive) {
|
|
||||||
// case: bot is active -> deactivate
|
|
||||||
|
|
||||||
|
if (bot) {
|
||||||
|
// case: there is a bot
|
||||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
||||||
|
|
||||||
const WORKSPACE_KEY = decryptAssymmetric({
|
const WORKSPACE_KEY = decryptAssymmetric({
|
||||||
ciphertext: key.latestKey.encryptedKey,
|
ciphertext: key.latestKey.encryptedKey,
|
||||||
nonce: key.latestKey.nonce,
|
nonce: key.latestKey.nonce,
|
||||||
@@ -221,16 +98,14 @@ export default function Integrations() {
|
|||||||
encryptedKey: ciphertext,
|
encryptedKey: ciphertext,
|
||||||
nonce
|
nonce
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// case: bot is not active
|
// case: bot is not active
|
||||||
const bot2 = await setBotActiveStatus({
|
setBot((await setBotActiveStatus({
|
||||||
botId: bot._id,
|
botId: bot._id,
|
||||||
isActive: bot.isActive ? false : true,
|
isActive: bot.isActive ? false : true,
|
||||||
botKey
|
botKey
|
||||||
});
|
})).bot);
|
||||||
setBot(bot2.bot);
|
}
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@@ -244,6 +119,10 @@ export default function Integrations() {
|
|||||||
*/
|
*/
|
||||||
const handleIntegrationOption = async ({ integrationOption }) => {
|
const handleIntegrationOption = async ({ integrationOption }) => {
|
||||||
// TODO: modularize
|
// TODO: modularize
|
||||||
|
|
||||||
|
// generate CSRF token for OAuth2 code-token exchange integrations
|
||||||
|
const csrfToken = crypto.randomBytes(16).toString("hex");
|
||||||
|
|
||||||
switch (integrationOption.name) {
|
switch (integrationOption.name) {
|
||||||
case 'Heroku':
|
case 'Heroku':
|
||||||
window.location = `https://id.heroku.com/oauth/authorize?client_id=7b1311a1-1cb2-4938-8adf-f37a399ec41b&response_type=code&scope=write-protected&state=${csrfToken}`;
|
window.location = `https://id.heroku.com/oauth/authorize?client_id=7b1311a1-1cb2-4938-8adf-f37a399ec41b&response_type=code&scope=write-protected&state=${csrfToken}`;
|
||||||
@@ -286,152 +165,58 @@ export default function Integrations() {
|
|||||||
content="Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files."
|
content="Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files."
|
||||||
/>
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<div className="flex flex-row">
|
<div className="w-full max-h-96 pb-2 h-screen max-h-[calc(100vh-10px)] overflow-y-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
||||||
<div className="w-full max-h-96 pb-2 h-screen max-h-[calc(100vh-10px)] overflow-y-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
<NavHeader pageName="Project Integrations" isProjectRelated={true} />
|
||||||
<NavHeader pageName="Project Integrations" isProjectRelated={true} />
|
<ActivateBotDialog
|
||||||
<ActivateBotDialog
|
isOpen={isActivateBotOpen}
|
||||||
isOpen={isActivateBotOpen}
|
closeModal={() => setIsActivateBotOpen(false)}
|
||||||
closeModal={() => setIsActivateBotOpen(false)}
|
selectedIntegrationOption={selectedIntegrationOption}
|
||||||
selectedIntegrationOption={selectedIntegrationOption}
|
handleBotActivate={handleBotActivate}
|
||||||
handleBotActivate={handleBotActivate}
|
handleIntegrationOption={handleIntegrationOption}
|
||||||
handleIntegrationOption={handleIntegrationOption}
|
/>
|
||||||
/>
|
{projectIntegrations.length > 0 && (
|
||||||
{projectIntegrations.length > 0 && (
|
<>
|
||||||
<>
|
<div className="flex flex-col justify-between items-start mx-4 mb-4 mt-6 text-xl max-w-5xl px-2">
|
||||||
<div className="flex flex-col justify-between items-start mx-4 mb-4 mt-6 text-xl max-w-5xl px-2">
|
<p className="font-semibold text-3xl">Current Project Integrations</p>
|
||||||
<div className="flex flex-row justify-start items-center text-3xl">
|
<p className="text-base text-gray-400">
|
||||||
<p className="font-semibold mr-4">Current Project Integrations</p>
|
Manage your integrations of Infisical with third-party services.
|
||||||
</div>
|
</p>
|
||||||
<p className="text-base text-gray-400">
|
|
||||||
Manage your integrations of Infisical with third-party services.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{projectIntegrations.map((projectIntegration) => (
|
|
||||||
<Integration
|
|
||||||
key={guidGenerator()}
|
|
||||||
projectIntegration={projectIntegration}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div className={`flex flex-col justify-between items-start mx-4 ${projectIntegrations.length > 0 ? 'mt-12' : 'mt-6'} mb-4 text-xl max-w-5xl px-2`}>
|
|
||||||
<div className="flex flex-row justify-start items-center text-3xl">
|
|
||||||
<p className="font-semibold mr-4">Cloud Integrations</p>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="mr-4 text-base text-gray-400">
|
{projectIntegrations.map((projectIntegration) => (
|
||||||
Click on an integration to begin syncing secrets to it.
|
<Integration
|
||||||
</p>
|
key={guidGenerator()}
|
||||||
</div>
|
projectIntegration={projectIntegration}
|
||||||
<div className="grid gap-4 grid-cols-4 grid-rows-2 mx-6 mt-4 max-w-5xl">
|
/>
|
||||||
{Object.keys(integrations).map((integration) => (
|
|
||||||
<div
|
|
||||||
className={`relative ${
|
|
||||||
["Heroku"].includes(integrations[integration].name)
|
|
||||||
? "hover:bg-white/10 duration-200 cursor-pointer"
|
|
||||||
: "opacity-50"
|
|
||||||
} flex flex-row bg-white/5 h-32 rounded-md p-4 items-center`}
|
|
||||||
onClick={() => {
|
|
||||||
if (!["Heroku"].includes(integrations[integration].name)) return;
|
|
||||||
setSelectedIntegrationOption(integrations[integration]);
|
|
||||||
integrationOptionPress({
|
|
||||||
integrationOption: integrations[integration]
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
key={integrations[integration].name}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src={`/images/integrations/${integrations[integration].name}.png`}
|
|
||||||
height={70}
|
|
||||||
width={70}
|
|
||||||
alt="integration logo"
|
|
||||||
/>
|
|
||||||
{integrations[integration].name.split(" ").length > 2 ? (
|
|
||||||
<div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-3xl ml-4 max-w-xs">
|
|
||||||
<div>{integrations[integration].name.split(" ")[0]}</div>
|
|
||||||
<div className="text-base">
|
|
||||||
{integrations[integration].name.split(" ")[1]}{" "}
|
|
||||||
{integrations[integration].name.split(" ")[2]}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="font-semibold text-gray-300 group-hover:text-gray-200 duration-200 text-xl ml-4 max-w-xs">
|
|
||||||
{integrations[integration].name}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{["Heroku"].includes(integrations[integration].name) &&
|
|
||||||
authorizations
|
|
||||||
.map((authorization) => authorization.integration)
|
|
||||||
.includes(integrations[integration].name.toLowerCase()) && (
|
|
||||||
<div className="absolute group z-50 top-0 right-0 flex flex-row">
|
|
||||||
<div
|
|
||||||
onClick={() => {
|
|
||||||
deleteIntegrationAuth({
|
|
||||||
integrationAuthId: authorizations
|
|
||||||
.filter(
|
|
||||||
(authorization) =>
|
|
||||||
authorization.integration ==
|
|
||||||
integrations[integration].name.toLowerCase()
|
|
||||||
)
|
|
||||||
.map((authorization) => authorization._id)[0],
|
|
||||||
});
|
|
||||||
router.reload();
|
|
||||||
}}
|
|
||||||
className="cursor-pointer w-max bg-red py-0.5 px-2 rounded-b-md text-xs flex flex-row items-center opacity-0 group-hover:opacity-100 duration-200"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faX}
|
|
||||||
className="text-xs mr-2 py-px"
|
|
||||||
/>
|
|
||||||
Revoke
|
|
||||||
</div>
|
|
||||||
<div className="w-max bg-primary py-0.5 px-2 rounded-bl-md rounded-tr-md text-xs flex flex-row items-center text-black opacity-90 group-hover:opacity-100 duration-200">
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={faCheck}
|
|
||||||
className="text-xs mr-2"
|
|
||||||
/>
|
|
||||||
Authorized
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!["Heroku"].includes(integrations[integration].name) && (
|
|
||||||
<div className="absolute group z-50 top-0 right-0 flex flex-row">
|
|
||||||
<div className="w-max bg-yellow py-0.5 px-2 rounded-bl-md rounded-tr-md text-xs flex flex-row items-center text-black opacity-90">
|
|
||||||
Coming Soon
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</>
|
||||||
<div className="flex flex-col justify-between items-start mx-4 mt-12 mb-4 text-xl max-w-5xl px-2">
|
)}
|
||||||
<div className="flex flex-row justify-start items-center text-3xl">
|
<div className={`flex flex-col justify-between items-start m-4 ${projectIntegrations.length > 0 ? 'mt-12' : 'mt-6'} text-xl max-w-5xl px-2`}>
|
||||||
<p className="font-semibold mr-4">Framework Integrations</p>
|
<p className="font-semibold text-3xl">Cloud Integrations</p>
|
||||||
</div>
|
<p className="text-base text-gray-400">
|
||||||
<p className="mr-4 text-base text-gray-400">
|
Click on an integration to begin syncing secrets to it.
|
||||||
Click on a framework to get the setup instructions.
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div><div className="grid gap-4 grid-cols-7 grid-rows-2 mx-6 mt-4 max-w-5xl">
|
<div className="grid gap-4 grid-cols-4 grid-rows-2 mx-6 max-w-5xl">
|
||||||
{frameworks.map((framework) => (
|
{integrations.map((integration) => (
|
||||||
<div key={framework.name}>
|
<CloudIntegration
|
||||||
<a
|
integration={integration}
|
||||||
href={framework.link}
|
setSelectedIntegrationOption={setSelectedIntegrationOption}
|
||||||
rel="noopener"
|
integrationOptionPress={integrationOptionPress}
|
||||||
className={`relative flex flex-row items-center justify-center bg-bunker-500 hover:bg-gradient-to-tr hover:from-sky-400 hover:to-primary duration-200 h-32 rounded-md p-0.5 items-center cursor-pointer`}
|
deleteIntegrationAuth={deleteIntegrationAuth}
|
||||||
>
|
authorizations={authorizations}
|
||||||
<div className={`font-semibold bg-bunker-500 flex flex-col items-center justify-center h-full w-full rounded-md text-gray-300 group-hover:text-gray-200 duration-200 ${framework?.name?.split(" ").length > 1 ? "text-sm px-1" : "text-xl px-2"} text-center w-full max-w-xs`}>
|
/>
|
||||||
{framework?.image && <Image
|
))}
|
||||||
src={`/images/integrations/${framework.image}.png`}
|
</div>
|
||||||
height={framework?.name ? 60 : 90}
|
<div className="flex flex-col justify-between items-start mx-4 mt-12 mb-4 text-xl max-w-5xl px-2">
|
||||||
width={framework?.name ? 60 : 90}
|
<p className="font-semibold text-3xl">Framework Integrations</p>
|
||||||
alt="integration logo"
|
<p className="text-base text-gray-400">
|
||||||
></Image>}
|
Click on a framework to get the setup instructions.
|
||||||
{framework?.name && framework?.image && <div className="h-2"></div>}
|
</p>
|
||||||
{framework?.name && framework.name}
|
</div>
|
||||||
</div>
|
<div className="grid gap-4 grid-cols-7 grid-rows-2 mx-6 mt-4 max-w-5xl">
|
||||||
</a>
|
{frameworks.map((framework) => (
|
||||||
</div>
|
<FrameworkIntegration framework={framework} />
|
||||||
))}
|
))}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user