mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 21:51:56 +00:00
314 lines
10 KiB
TypeScript
314 lines
10 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
import { useEffect, useState } from "react";
|
|
import { useRouter } from "next/router";
|
|
import { faArrowRight, faCheck, faXmark } from "@fortawesome/free-solid-svg-icons";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
// TODO: This needs to be moved from public folder
|
|
import {
|
|
contextNetlifyMapping,
|
|
integrationSlugNameMapping,
|
|
reverseContextNetlifyMapping
|
|
} from "public/data/frequentConstants";
|
|
|
|
import Button from "@app/components/basic/buttons/Button";
|
|
import ListBox from "@app/components/basic/Listbox";
|
|
|
|
import deleteIntegration from "../../pages/api/integrations/DeleteIntegration";
|
|
import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps";
|
|
import updateIntegration from "../../pages/api/integrations/updateIntegration";
|
|
|
|
interface Integration {
|
|
_id: string;
|
|
isActive: boolean;
|
|
app: string | null;
|
|
appId: string | null;
|
|
path: string | null;
|
|
region: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
environment: string;
|
|
integration: string;
|
|
targetEnvironment: string;
|
|
workspace: string;
|
|
integrationAuth: string;
|
|
secretPath: string;
|
|
}
|
|
|
|
interface IntegrationApp {
|
|
name: string;
|
|
appId?: string;
|
|
owner?: string;
|
|
}
|
|
|
|
type Props = {
|
|
integration: Integration;
|
|
integrations: Integration[];
|
|
setIntegrations: any;
|
|
bot: any;
|
|
setBot: any;
|
|
environments: Array<{ name: string; slug: string }>;
|
|
handleDeleteIntegration: (args: { integration: Integration }) => void;
|
|
};
|
|
|
|
// TODO: refactor
|
|
const IntegrationTile = ({
|
|
integration,
|
|
integrations,
|
|
bot,
|
|
setBot,
|
|
setIntegrations,
|
|
environments = [],
|
|
handleDeleteIntegration
|
|
}: Props) => {
|
|
const [integrationEnvironment, setIntegrationEnvironment] = useState<Props["environments"][0]>(
|
|
environments.find(({ slug }) => slug === integration?.environment) || {
|
|
name: "",
|
|
slug: ""
|
|
}
|
|
);
|
|
const router = useRouter();
|
|
const [apps, setApps] = useState<IntegrationApp[]>([]); // integration app objects
|
|
const [integrationApp, setIntegrationApp] = useState(""); // integration app name
|
|
const [integrationTargetEnvironment, setIntegrationTargetEnvironment] = useState("");
|
|
|
|
useEffect(() => {
|
|
const loadIntegration = async () => {
|
|
const tempApps: [IntegrationApp] = await getIntegrationApps({
|
|
integrationAuthId: integration?.integrationAuth
|
|
});
|
|
|
|
setApps(tempApps);
|
|
|
|
if (integration?.app) {
|
|
setIntegrationApp(integration.app);
|
|
} else if (integration?.path && integration?.region) {
|
|
setIntegrationApp(`${integration.path} (${integration.region})`);
|
|
} else if (tempApps.length > 0) {
|
|
setIntegrationApp(tempApps[0].name);
|
|
} else {
|
|
setIntegrationApp("");
|
|
}
|
|
|
|
switch (integration.integration) {
|
|
case "vercel":
|
|
setIntegrationTargetEnvironment(
|
|
integration?.targetEnvironment
|
|
? integration.targetEnvironment.charAt(0).toUpperCase() +
|
|
integration.targetEnvironment.substring(1)
|
|
: "Development"
|
|
);
|
|
break;
|
|
case "netlify":
|
|
setIntegrationTargetEnvironment(
|
|
integration?.targetEnvironment
|
|
? contextNetlifyMapping[integration.targetEnvironment]
|
|
: "Local development"
|
|
);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
|
|
loadIntegration();
|
|
}, []);
|
|
|
|
const handleStartIntegration = async () => {
|
|
const reformatTargetEnvironment = (targetEnvironment: string) => {
|
|
switch (integration.integration) {
|
|
case "vercel":
|
|
return targetEnvironment.toLowerCase();
|
|
case "netlify":
|
|
return reverseContextNetlifyMapping[targetEnvironment];
|
|
default:
|
|
return null;
|
|
}
|
|
};
|
|
|
|
try {
|
|
const siteApp = apps.find((app) => app.name === integrationApp); // obj or undefined
|
|
const appId = siteApp?.appId ?? null;
|
|
const owner = siteApp?.owner ?? null;
|
|
|
|
// return updated integration
|
|
const updatedIntegration = await updateIntegration({
|
|
integrationId: integration._id,
|
|
environment: integrationEnvironment.slug,
|
|
isActive: true,
|
|
app: integrationApp,
|
|
appId,
|
|
targetEnvironment: reformatTargetEnvironment(integrationTargetEnvironment),
|
|
owner
|
|
});
|
|
|
|
setIntegrations(
|
|
integrations.map((i) => (i._id === updatedIntegration._id ? updatedIntegration : i))
|
|
);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
};
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
const renderIntegrationSpecificParams = (integration: Integration) => {
|
|
try {
|
|
switch (integration.integration) {
|
|
case "vercel":
|
|
return (
|
|
<div>
|
|
<div className="mb-2 w-60 text-xs font-semibold text-gray-400">ENVIRONMENT</div>
|
|
<ListBox
|
|
data={!integration.isActive ? ["Development", "Preview", "Production"] : null}
|
|
isSelected={integrationTargetEnvironment}
|
|
onChange={setIntegrationTargetEnvironment}
|
|
isFull
|
|
/>
|
|
</div>
|
|
);
|
|
case "netlify":
|
|
return (
|
|
<div>
|
|
<div className="mb-2 text-xs font-semibold text-gray-400">CONTEXT</div>
|
|
<ListBox
|
|
data={
|
|
!integration.isActive
|
|
? ["Production", "Deploy previews", "Branch deploys", "Local development"]
|
|
: null
|
|
}
|
|
isSelected={integrationTargetEnvironment}
|
|
onChange={setIntegrationTargetEnvironment}
|
|
/>
|
|
</div>
|
|
);
|
|
case "railway":
|
|
return (
|
|
<div>
|
|
<div className="mb-2 text-xs font-semibold text-gray-400">ENVIRONMENT</div>
|
|
<ListBox
|
|
data={
|
|
!integration.isActive
|
|
? ["Production", "Deploy previews", "Branch deploys", "Local development"]
|
|
: null
|
|
}
|
|
isSelected={integration.targetEnvironment}
|
|
onChange={setIntegrationTargetEnvironment}
|
|
/>
|
|
</div>
|
|
);
|
|
case "gitlab":
|
|
return (
|
|
<div>
|
|
<div className="mb-2 text-xs font-semibold text-gray-400">ENVIRONMENT</div>
|
|
<ListBox
|
|
data={null}
|
|
isSelected={integration.targetEnvironment}
|
|
onChange={setIntegrationTargetEnvironment}
|
|
/>
|
|
</div>
|
|
);
|
|
default:
|
|
return <div />;
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
|
|
return <div />;
|
|
};
|
|
|
|
if (!integrationApp && integration.integration !== "checkly") return <div />;
|
|
|
|
const isSelected =
|
|
integration.integration === "hashicorp-vault"
|
|
? `${integration.app} - path: ${integration.path}`
|
|
: integrationApp;
|
|
|
|
return (
|
|
<div className="mx-6 mb-8 flex max-w-5xl justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6">
|
|
<div className="flex">
|
|
<div>
|
|
<p className="mb-2 text-xs font-semibold text-gray-400">ENVIRONMENT</p>
|
|
<ListBox
|
|
data={!integration.isActive ? environments.map(({ name }) => name) : null}
|
|
isSelected={integrationEnvironment.name}
|
|
onChange={(envName) =>
|
|
setIntegrationEnvironment(
|
|
environments.find(({ name }) => envName === name) || {
|
|
name: "unknown",
|
|
slug: "unknown"
|
|
}
|
|
)
|
|
}
|
|
isFull
|
|
/>
|
|
</div>
|
|
<div className="ml-2">
|
|
<p className="mb-2 text-xs font-semibold text-gray-400">SECRET PATH</p>
|
|
<div className="cursor-default rounded-md bg-white/[.07] py-2.5 pl-4 pr-10 text-sm font-semibold text-gray-300">
|
|
{/* {integration.integration.charAt(0).toUpperCase() + integration.integration.slice(1)} */}
|
|
{integration.secretPath}
|
|
</div>
|
|
</div>
|
|
<div className="pt-2">
|
|
<FontAwesomeIcon icon={faArrowRight} className="mx-4 mt-8 text-gray-400" />
|
|
</div>
|
|
<div className="mr-2">
|
|
<p className="mb-2 text-xs font-semibold text-gray-400">INTEGRATION</p>
|
|
<div className="cursor-default rounded-md bg-white/[.07] py-2.5 pl-4 pr-10 text-sm font-semibold text-gray-300">
|
|
{/* {integration.integration.charAt(0).toUpperCase() + integration.integration.slice(1)} */}
|
|
{integrationSlugNameMapping[integration.integration]}
|
|
</div>
|
|
</div>
|
|
<div className="mr-2">
|
|
<div className="mb-2 text-xs font-semibold text-gray-400">APP</div>
|
|
{integrationApp ? (
|
|
<div title={integrationApp}>
|
|
<ListBox
|
|
data={!integration.isActive ? apps.map((app) => app.name) : null}
|
|
isSelected={isSelected}
|
|
onChange={(app) => {
|
|
setIntegrationApp(app);
|
|
}}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<div className="h-10 w-52 animate-pulse rounded-md bg-mineshaft-600 px-4 py-2 font-bold">
|
|
-
|
|
</div>
|
|
)}
|
|
</div>
|
|
{renderIntegrationSpecificParams(integration)}
|
|
</div>
|
|
<div className="flex cursor-default items-end">
|
|
{integration.isActive ? (
|
|
<div className="flex max-w-5xl flex-row items-center rounded-md border border-mineshaft-500 bg-mineshaft-600 p-[0.44rem] px-4">
|
|
<FontAwesomeIcon icon={faCheck} className="mr-2.5 text-lg text-primary" />
|
|
<div className="font-semibold text-gray-300">In Sync</div>
|
|
</div>
|
|
) : (
|
|
<Button
|
|
text="Start Integration"
|
|
onButtonPressed={() => handleStartIntegration()}
|
|
color="mineshaft"
|
|
size="md"
|
|
/>
|
|
)}
|
|
<div className="ml-2 opacity-80 duration-200 hover:opacity-100">
|
|
<Button
|
|
onButtonPressed={() =>
|
|
handleDeleteIntegration({
|
|
integration
|
|
})
|
|
}
|
|
color="red"
|
|
size="icon-md"
|
|
icon={faXmark}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default IntegrationTile;
|