Fix merge conflicts

This commit is contained in:
Tuan Dang
2023-09-25 13:26:09 +01:00
45 changed files with 1542 additions and 102 deletions
@@ -1,6 +1,5 @@
/* eslint-disable react/no-danger */
import { forwardRef, HTMLAttributes } from "react";
import ContentEditable from "react-contenteditable";
import sanitizeHtml, { DisallowedTagsModes } from "sanitize-html";
import { useToggle } from "@app/hooks";
@@ -35,58 +34,52 @@ const syntaxHighlight = (content?: string | null, isVisible?: boolean) => {
`<span class="ph-no-capture text-yellow">&#36;&#123;<span class="ph-no-capture text-yello-200/80">${b}</span>&#125;</span>`
);
return newContent;
// akhilmhdh: Dont remove this br. I am still clueless how this works but weirdly enough
// when break is added a line break works properly
return `${newContent}<br/>`;
};
type Props = Omit<HTMLAttributes<HTMLDivElement>, "onChange" | "onBlur"> & {
type Props = HTMLAttributes<HTMLTextAreaElement> & {
value?: string | null;
isVisible?: boolean;
isDisabled?: boolean;
onChange?: (val: string) => void;
onBlur?: () => void;
};
export const SecretInput = forwardRef<HTMLDivElement, Props>(
({ value, isVisible, onChange, onBlur, isDisabled, ...props }, ref) => {
const commonClassName = "font-mono text-sm caret-white border-none outline-none w-full break-all";
export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
({ value, isVisible, onBlur, isDisabled, onFocus, ...props }, ref) => {
const [isSecretFocused, setIsSecretFocused] = useToggle();
return (
<div
className="thin-scrollbar relative overflow-y-auto overflow-x-hidden"
style={{ maxHeight: `${21 * 7}px` }}
>
<div
dangerouslySetInnerHTML={{
__html: syntaxHighlight(value, isVisible || isSecretFocused)
}}
className={`absolute top-0 left-0 z-0 h-full w-full inline-block text-ellipsis whitespace-pre-wrap break-all ${
!value && value !== "" && "italic text-red-600/70"
}`}
ref={ref}
/>
<ContentEditable
className="relative z-10 h-full w-full text-ellipsis inline-block whitespace-pre-wrap break-all text-transparent caret-white outline-none"
role="textbox"
onChange={(evt) => {
if (onChange) onChange(evt.currentTarget.innerText.trim());
}}
onFocus={() => setIsSecretFocused.on()}
disabled={isDisabled}
spellCheck={false}
onBlur={() => {
if (onBlur) onBlur();
setIsSecretFocused.off();
}}
html={
isVisible || isSecretFocused
? sanitizeHtml(
value?.replaceAll("<", "&lt;").replaceAll(">", "&gt;") || "",
sanitizeConf
)
: syntaxHighlight(value, false)
}
{...props}
/>
<div className="overflow-auto w-full" style={{ maxHeight: `${21 * 7}px` }}>
<div className="relative overflow-hidden">
<pre aria-hidden className="m-0 ">
<code className={`inline-block w-full ${commonClassName}`}>
<span
style={{ whiteSpace: "break-spaces" }}
dangerouslySetInnerHTML={{
__html: syntaxHighlight(value, isVisible || isSecretFocused) ?? ""
}}
/>
</code>
</pre>
<textarea
style={{ whiteSpace: "break-spaces" }}
aria-label="secret value"
ref={ref}
className={`absolute inset-0 block h-full resize-none overflow-hidden bg-transparent text-transparent no-scrollbar focus:border-0 ${commonClassName}`}
onFocus={() => setIsSecretFocused.on()}
disabled={isDisabled}
spellCheck={false}
onBlur={(evt) => {
onBlur?.(evt);
setIsSecretFocused.off();
}}
value={value || ""}
{...props}
/>
</div>
</div>
);
}
@@ -9,6 +9,8 @@ import {
Environment,
IntegrationAuth,
NorthflankSecretGroup,
Org,
Project,
Service,
Team,
TeamCityBuildConfig} from "./types";
@@ -27,6 +29,31 @@ const integrationAuthKeys = {
integrationAuthId: string;
appId: string;
}) => [{ integrationAuthId, appId }, "integrationAuthVercelBranches"] as const,
getIntegrationAuthQoveryOrgs: (integrationAuthId: string) =>
[{ integrationAuthId }, "integrationAuthQoveryOrgs"] as const,
getIntegrationAuthQoveryProjects: ({
integrationAuthId,
orgId
}: {
integrationAuthId: string;
orgId: string;
}) => [{ integrationAuthId, orgId }, "integrationAuthQoveryProjects"] as const,
getIntegrationAuthQoveryEnvironments: ({
integrationAuthId,
projectId
}: {
integrationAuthId: string;
projectId: string;
}) => [{ integrationAuthId, projectId }, "integrationAuthQoveryEnvironments"] as const,
getIntegrationAuthQoveryScopes: ({
integrationAuthId,
environmentId,
scope
}: {
integrationAuthId: string;
environmentId: string;
scope: "job" | "application" | "container";
}) => [{ integrationAuthId, environmentId, scope }, "integrationAuthQoveryScopes"] as const,
getIntegrationAuthRailwayEnvironments: ({
integrationAuthId,
appId
@@ -120,6 +147,121 @@ const fetchIntegrationAuthVercelBranches = async ({
return branches;
};
const fetchIntegrationAuthQoveryOrgs = async (integrationAuthId: string) => {
const {
data: { orgs }
} = await apiRequest.get<{ orgs: Org[] }>(
`/api/v1/integration-auth/${integrationAuthId}/qovery/orgs`
);
return orgs;
};
const fetchIntegrationAuthQoveryProjects = async ({
integrationAuthId,
orgId
}: {
integrationAuthId: string;
orgId: string;
}) => {
if (orgId === "none") return [];
const {
data: { projects }
} = await apiRequest.get<{ projects: Project[] }>(
`/api/v1/integration-auth/${integrationAuthId}/qovery/projects`,
{
params: {
orgId
}
}
);
return projects;
};
const fetchIntegrationAuthQoveryEnvironments = async ({
integrationAuthId,
projectId
}: {
integrationAuthId: string;
projectId: string;
}) => {
if (projectId === "none") return [];
const {
data: { environments }
} = await apiRequest.get<{ environments: Environment[] }>(
`/api/v1/integration-auth/${integrationAuthId}/qovery/environments`,
{
params: {
projectId
}
}
);
return environments;
};
const fetchIntegrationAuthQoveryScopes = async ({
integrationAuthId,
environmentId,
scope
}: {
integrationAuthId: string;
environmentId: string;
scope: "job" | "application" | "container";
}) => {
if (environmentId === "none") return [];
if (scope === "application") {
const {
data: { apps }
} = await apiRequest.get<{ apps: App[] }>(
`/api/v1/integration-auth/${integrationAuthId}/qovery/apps`,
{
params: {
environmentId
}
}
);
return apps;
}
if (scope === "container") {
const {
data: { containers }
} = await apiRequest.get<{ containers: App[] }>(
`/api/v1/integration-auth/${integrationAuthId}/qovery/containers`,
{
params: {
environmentId
}
}
);
return containers;
}
if (scope === "job") {
const {
data: { jobs }
} = await apiRequest.get<{ jobs: App[] }>(
`/api/v1/integration-auth/${integrationAuthId}/qovery/jobs`,
{
params: {
environmentId
}
}
);
return jobs;
}
return undefined;
};
const fetchIntegrationAuthRailwayEnvironments = async ({
integrationAuthId,
appId
@@ -197,6 +339,8 @@ const fetchIntegrationAuthTeamCityBuildConfigs = async ({
integrationAuthId: string;
appId: string;
}) => {
if (appId === "") return [];
const {
data: { buildConfigs }
} = await apiRequest.get<{ buildConfigs: TeamCityBuildConfig[] }>(
@@ -269,6 +413,82 @@ export const useGetIntegrationAuthVercelBranches = ({
});
};
export const useGetIntegrationAuthQoveryOrgs = (integrationAuthId: string) => {
return useQuery({
queryKey: integrationAuthKeys.getIntegrationAuthQoveryOrgs(integrationAuthId),
queryFn: () =>
fetchIntegrationAuthQoveryOrgs(integrationAuthId),
enabled: true
});
};
export const useGetIntegrationAuthQoveryProjects = ({
integrationAuthId,
orgId
}: {
integrationAuthId: string;
orgId: string;
}) => {
return useQuery({
queryKey: integrationAuthKeys.getIntegrationAuthQoveryProjects({
integrationAuthId,
orgId
}),
queryFn: () =>
fetchIntegrationAuthQoveryProjects({
integrationAuthId,
orgId
}),
enabled: true
});
};
export const useGetIntegrationAuthQoveryEnvironments = ({
integrationAuthId,
projectId
}: {
integrationAuthId: string;
projectId: string;
}) => {
return useQuery({
queryKey: integrationAuthKeys.getIntegrationAuthQoveryEnvironments({
integrationAuthId,
projectId
}),
queryFn: () =>
fetchIntegrationAuthQoveryEnvironments({
integrationAuthId,
projectId
}),
enabled: true
});
};
export const useGetIntegrationAuthQoveryScopes = ({
integrationAuthId,
environmentId,
scope
}: {
integrationAuthId: string;
environmentId: string;
scope: "job" | "application" | "container";
}) => {
return useQuery({
queryKey: integrationAuthKeys.getIntegrationAuthQoveryScopes({
integrationAuthId,
environmentId,
scope
}),
queryFn: () =>
fetchIntegrationAuthQoveryScopes({
integrationAuthId,
environmentId,
scope
}),
enabled: true
});
};
export const useGetIntegrationAuthRailwayEnvironments = ({
integrationAuthId,
appId
@@ -26,6 +26,21 @@ export type Environment = {
environmentId: string;
};
export type Container = {
name: string;
containerId: string;
};
export type Org = {
name: string;
orgId: string;
};
export type Project = {
name: string;
projectId: string;
};
export type Service = {
name: string;
serviceId: string;
@@ -40,6 +40,7 @@ export const useCreateIntegration = () => {
owner,
path,
region,
scope,
secretPath,
metadata
}: {
@@ -56,6 +57,7 @@ export const useCreateIntegration = () => {
owner?: string;
path?: string;
region?: string;
scope?: string;
metadata?: {
secretPrefix?: string;
secretSuffix?: string;
@@ -73,6 +75,7 @@ export const useCreateIntegration = () => {
targetServiceId,
owner,
path,
scope,
region,
secretPath,
metadata
@@ -32,5 +32,9 @@ export type TIntegration = {
__v: number;
metadata?: {
secretSuffix?: string;
scope: string;
org: string;
project: string;
environment: string;
}
};
+2 -2
View File
@@ -487,7 +487,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
</MenuItem>
</a>
</Link>
{/* <Link href={`/project/${currentWorkspace?._id}/audit-logs`} passHref>
<Link href={`/project/${currentWorkspace?._id}/audit-logs`} passHref>
<a>
<MenuItem
isSelected={
@@ -498,7 +498,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
Audit Logs
</MenuItem>
</a>
</Link> */}
</Link>
<Link href={`/project/${currentWorkspace?._id}/settings`} passHref>
<a>
<MenuItem
@@ -84,6 +84,7 @@ export default function GitLabCreateIntegrationPage() {
const selectedSourceEnvironment = watch("selectedSourceEnvironment");
const targetEntity = watch("targetEntity");
const targetTeamId = watch("targetTeamId");
const targetAppIdValue = watch("targetAppId");
const { mutateAsync } = useCreateIntegration();
@@ -440,6 +441,7 @@ export default function GitLabCreateIntegrationPage() {
size="sm"
type="submit"
isLoading={isLoading}
isDisabled={targetAppIdValue === "none"}
>
Create Integration
</Button>
@@ -0,0 +1,106 @@
import { useState } from "react";
import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
useSaveIntegrationAccessToken
} from "@app/hooks/api";
import { Button, Card, CardTitle, FormControl, Input } from "../../../components/v2";
export default function QoveryCreateIntegrationPage() {
const router = useRouter();
const { mutateAsync } = useSaveIntegrationAccessToken();
const [accessToken, setAccessToken] = useState("");
const [accessTokenErrorText, setAccessTokenErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false);
const handleButtonClick = async () => {
try {
setAccessTokenErrorText("");
if (accessToken.length === 0) {
setAccessTokenErrorText("Access token cannot be blank");
return;
}
setIsLoading(true);
const integrationAuth = await mutateAsync({
workspaceId: localStorage.getItem("projectData.id"),
integration: "qovery",
accessToken
});
setIsLoading(false);
router.push(`/integrations/qovery/create?integrationAuthId=${integrationAuth._id}`);
} catch (err) {
console.error(err);
}
};
return (
<div className="flex h-full w-full items-center justify-center">
<Head>
<title>Authorize Qovery Integration</title>
<link rel='icon' href='/infisical.ico' />
</Head>
<Card className="max-w-lg rounded-md border border-mineshaft-600 mb-12">
<CardTitle
className="text-left px-6 text-xl"
subTitle="After adding your API key, you will be prompted to set up an integration for a particular Infisical project and environment."
>
<div className="flex flex-row items-center">
<div className="inline flex items-center pb-0.5">
<Image
src="/images/integrations/Qovery.png"
height={30}
width={30}
alt="Qovery logo"
/>
</div>
<span className="ml-2.5">Qovery Integration </span>
<Link href="https://infisical.com/docs/integrations/cloud/qovery" passHref>
<a target="_blank" rel="noopener noreferrer">
<div className="ml-2 mb-1 rounded-md text-yellow text-sm inline-block bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] opacity-80 hover:opacity-100 cursor-default">
<FontAwesomeIcon icon={faBookOpen} className="mr-1.5"/>
Docs
<FontAwesomeIcon icon={faArrowUpRightFromSquare} className="ml-1.5 text-xxs mb-[0.07rem]"/>
</div>
</a>
</Link>
</div>
</CardTitle>
<FormControl
label="Qovery API token"
errorText={accessTokenErrorText}
isError={accessTokenErrorText !== "" ?? false}
className="mx-6"
>
<Input
placeholder=""
value={accessToken}
onChange={(e) => setAccessToken(e.target.value)}
/>
</FormControl>
<Button
onClick={handleButtonClick}
colorSchema="primary"
variant="outline_bg"
className="mb-6 mt-2 ml-auto mr-6 w-min"
isFullWidth={false}
isLoading={isLoading}
>
Connect to Qovery
</Button>
</Card>
</div>
);
}
QoveryCreateIntegrationPage.requireAuth = true;
@@ -0,0 +1,409 @@
import { useEffect, useState } from "react";
import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { faArrowUpRightFromSquare, faBookOpen, faBugs } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { motion } from "framer-motion";
import queryString from "query-string";
import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem,
Tab,
TabList,
TabPanel,
Tabs
} from "@app/components/v2";
import {
useCreateIntegration
} from "@app/hooks/api";
import {
useGetIntegrationAuthQoveryEnvironments,
useGetIntegrationAuthQoveryOrgs,
useGetIntegrationAuthQoveryProjects,
useGetIntegrationAuthQoveryScopes
} from "@app/hooks/api/integrationAuth/queries";
import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
const qoveryScopes = [
{ label: "Application", value: "application" },
{ label: "Container", value: "container" },
{ label: "Job", value: "job" }
];
enum TabSections {
InfisicalSettings = "infisicalSettings",
QoverySettings = "qoverySettings"
}
/**
* Follow the logic:
* - Access token
* -> Get organizations (orgId) - select an organization
* -> Get projects belonging to the organization - select project
* -> Get environments belonging to the project - select environmnet
* -> Get qovery scopes / application (this is the application to sync to)
*
* Do we even need to get organizations?
*/
export default function QoveryCreateIntegrationPage() {
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 [scope, setScope] = useState("application");
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
const [secretPath, setSecretPath] = useState("/");
const { data: integrationAuthOrgs } = useGetIntegrationAuthQoveryOrgs((integrationAuthId as string) ?? "");
const [targetOrgId, setTargetOrgId] = useState("");
const { data: integrationAuthProjects } = useGetIntegrationAuthQoveryProjects({
integrationAuthId: (integrationAuthId as string) ?? "",
orgId: targetOrgId
});
const [targetProjectId, setTargetProjectId] = useState("");
const { data: integrationAuthEnvironments } = useGetIntegrationAuthQoveryEnvironments({
integrationAuthId: (integrationAuthId as string) ?? "",
projectId: targetProjectId
});
const [targetEnvironmentId, setTargetEnvironmentId] = useState("");
const { data: integrationAuthApps, isLoading: isIntegrationAuthAppsLoading } = useGetIntegrationAuthQoveryScopes({
integrationAuthId: (integrationAuthId as string) ?? "",
environmentId: targetEnvironmentId,
scope: (scope as ("job" | "application" | "container"))
});
const [targetAppId, setTargetAppId] = useState("");
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
if (workspace) {
setSelectedSourceEnvironment(workspace.environments[0].slug);
}
}, [workspace]);
useEffect(() => {
if (integrationAuthApps) {
if (integrationAuthApps.length > 0) {
setTargetAppId(String(integrationAuthApps[0].appId));
} else {
setTargetAppId("none");
}
}
}, [integrationAuthApps]);
useEffect(() => {
if (integrationAuthOrgs) {
if (integrationAuthOrgs.length > 0) {
setTargetOrgId(String(integrationAuthOrgs[0].orgId));
} else {
setTargetOrgId("none");
}
}
}, [integrationAuthOrgs]);
useEffect(() => {
if (integrationAuthProjects) {
if (integrationAuthProjects.length > 0) {
setTargetProjectId(String(integrationAuthProjects[0].projectId));
} else {
setTargetProjectId("none");
}
}
}, [integrationAuthProjects]);
useEffect(() => {
if (integrationAuthEnvironments) {
if (integrationAuthEnvironments.length > 0) {
setTargetEnvironmentId(String(integrationAuthEnvironments[0].environmentId));
} else {
setTargetEnvironmentId("none");
}
}
}, [integrationAuthEnvironments]);
const handleButtonClick = async () => {
try {
if (!integrationAuth?._id) return;
setIsLoading(true);
const targetOrg = integrationAuthOrgs?.find((integrationAuthOrg) => integrationAuthOrg.orgId === targetOrgId)?.name;
const targetProject = integrationAuthProjects?.find((integrationAuthProject) => integrationAuthProject.projectId === targetProjectId)?.name;
const targetEnvironment = integrationAuthEnvironments?.find((integrationAuthEnvironment) => integrationAuthEnvironment.environmentId === targetEnvironmentId)?.name;
const targetApp = integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId)?.name;
await mutateAsync({
integrationAuthId: integrationAuth?._id,
isActive: true,
app: targetApp,
appId: targetAppId,
scope,
sourceEnvironment: selectedSourceEnvironment,
targetEnvironment,
targetEnvironmentId,
targetService: targetProject,
targetServiceId: targetProjectId,
owner: targetOrg,
secretPath
});
setIsLoading(false);
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
} catch (err) {
console.error(err);
}
};
return integrationAuth &&
workspace &&
selectedSourceEnvironment ? (
<div className="flex flex-col h-full w-full items-center justify-center bg-gradient-to-tr from-mineshaft-900 to-bunker-900">
<Head>
<title>Set Up Qovery Integration</title>
<link rel='icon' href='/infisical.ico' />
</Head>
<Card className="max-w-lg rounded-md border border-mineshaft-600 p-0">
<CardTitle
className="text-left px-6 text-xl"
subTitle="Choose which environment in Infisical you want to sync to Checkly environment variables."
>
<div className="flex flex-row items-center">
<div className="inline flex items-center pb-0.5">
<Image
src="/images/integrations/Qovery.png"
height={30}
width={30}
alt="Qovery logo"
/>
</div>
<span className="ml-2.5">Qovery Integration </span>
<Link href="https://infisical.com/docs/integrations/cloud/qovery" passHref>
<a target="_blank" rel="noopener noreferrer">
<div className="ml-2 mb-1 rounded-md text-yellow text-sm inline-block bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] opacity-80 hover:opacity-100 cursor-default">
<FontAwesomeIcon icon={faBookOpen} className="mr-1.5"/>
Docs
<FontAwesomeIcon icon={faArrowUpRightFromSquare} className="ml-1.5 text-xxs mb-[0.07rem]"/>
</div>
</a>
</Link>
</div>
</CardTitle>
<Tabs defaultValue={TabSections.InfisicalSettings} className="px-6">
<TabList>
<div className="flex flex-row border-b border-mineshaft-600 w-full">
<Tab value={TabSections.InfisicalSettings}>Infisical Settings</Tab>
<Tab value={TabSections.QoverySettings}>Qovery Settings</Tab>
</div>
</TabList>
<TabPanel value={TabSections.InfisicalSettings}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<FormControl label="Infisical Project Environment">
<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" className="pb-[14.68rem]">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
</motion.div>
</TabPanel>
<TabPanel value={TabSections.QoverySettings}>
<motion.div
key="panel-1"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: -30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<FormControl label="Qovery Scope">
<Select
value={scope}
onValueChange={(val) => setScope(val)}
className="w-full border border-mineshaft-500"
>
{qoveryScopes.map((qoveryScope) => (
<SelectItem
value={qoveryScope.value}
key={`target-app-${qoveryScope.value}`}
>
{qoveryScope.label}
</SelectItem>
))}
</Select>
</FormControl>
{integrationAuthOrgs && <FormControl label="Qovery Organization">
<Select
value={targetOrgId}
onValueChange={(val) => setTargetOrgId(val)}
className="w-full border border-mineshaft-500"
isDisabled={integrationAuthOrgs.length === 0}
>
{integrationAuthOrgs.length > 0 ? (
integrationAuthOrgs.map((integrationAuthOrg) => (
<SelectItem
value={integrationAuthOrg.orgId}
key={`target-app-${integrationAuthOrg.orgId}`}
>
{integrationAuthOrg.name}
</SelectItem>
))
) : (
<SelectItem value="none" key="target-app-none">
No organizaztions found
</SelectItem>
)}
</Select>
</FormControl>}
{integrationAuthProjects && <FormControl label="Qovery Project">
<Select
value={targetProjectId}
onValueChange={(val) => setTargetProjectId(val)}
className="w-full border border-mineshaft-500"
isDisabled={integrationAuthProjects.length === 0}
>
{integrationAuthProjects.length > 0 ? (
integrationAuthProjects.map((integrationAuthProject) => (
<SelectItem
value={integrationAuthProject.projectId}
key={`target-app-${integrationAuthProject.projectId}`}
>
{integrationAuthProject.name}
</SelectItem>
))
) : (
<SelectItem value="none" key="target-app-none">
No projects found
</SelectItem>
)}
</Select>
</FormControl>}
{integrationAuthEnvironments && <FormControl label="Qovery Environment">
<Select
value={targetEnvironmentId}
onValueChange={(val) => setTargetEnvironmentId(val)}
className="w-full border border-mineshaft-500"
isDisabled={integrationAuthEnvironments.length === 0}
>
{integrationAuthEnvironments.length > 0 ? (
integrationAuthEnvironments.map((integrationAuthEnvironment) => (
<SelectItem
value={integrationAuthEnvironment.environmentId}
key={`target-app-${integrationAuthEnvironment.environmentId}`}
>
{integrationAuthEnvironment.name}
</SelectItem>
))
) : (
<SelectItem value="none" key="target-app-none">
No environments found
</SelectItem>
)}
</Select>
</FormControl>}
{(scope && integrationAuthApps) && <FormControl label={`Qovery ${scope.charAt(0).toUpperCase() + scope.slice(1)}`}>
<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 {scope}s found
</SelectItem>
)}
</Select>
</FormControl>}
</motion.div>
</TabPanel>
</Tabs>
<Button
onClick={handleButtonClick}
color="mineshaft"
variant="outline_bg"
className="mb-6 ml-auto mr-6"
isFullWidth={false}
isLoading={isLoading}
>
Create Integration
</Button>
</Card>
{/* <div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/>
<div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md">
<div className="flex flex-row items-center"><FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/> <span className="ml-3 text-md text-mineshaft-100">Pro Tips</span></div>
<span className="text-mineshaft-300 text-sm mt-4">After creating an integration, your secrets will start syncing immediately. This might cause an unexpected override of current secrets in Qovery with secrets from Infisical.</span>
</div> */}
</div>
) : (
<div className="flex justify-center items-center w-full h-full">
<Head>
<title>Set Up Qovery Integration</title>
<link rel='icon' href='/infisical.ico' />
</Head>
{isIntegrationAuthAppsLoading ? <img src="/images/loading/loading.gif" height={70} width={120} alt="infisical loading indicator" /> : <div className="max-w-md h-max p-6 border border-mineshaft-600 rounded-md bg-mineshaft-800 text-mineshaft-200 flex flex-col text-center">
<FontAwesomeIcon icon={faBugs} className="text-6xl my-2 inlineli"/>
<p>
Something went wrong. Please contact <a
className="inline underline underline-offset-4 decoration-primary-500 opacity-80 hover:opacity-100 text-mineshaft-100 duration-200 cursor-pointer"
target="_blank"
rel="noopener noreferrer"
href="mailto:[email protected]"
>
support@infisical.com
</a> if the issue persists.
</p>
</div>}
</div>
);
}
QoveryCreateIntegrationPage.requireAuth = true;
@@ -49,7 +49,7 @@ export default function TeamCityCreateIntegrationPage() {
integrationAuthId: (integrationAuthId as string) ?? "",
appId: targetAppId
});
useEffect(() => {
if (workspace) {
setSelectedSourceEnvironment(workspace.environments[0].slug);
@@ -270,7 +270,7 @@ export const SecretInputRow = memo(
className="flex w-full flex-grow flex-row border-r border-none border-red"
style={{ padding: "0.5rem 0 0.5rem 1rem" }}
>
<div className="w-full">
<div className="w-full flex items-center">
{isOverridden ? (
<Controller
control={control}
@@ -87,6 +87,9 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
case "checkly":
link = `${window.location.origin}/integrations/checkly/authorize`;
break;
case "qovery":
link = `${window.location.origin}/integrations/qovery/authorize`;
break;
case "railway":
link = `${window.location.origin}/integrations/railway/authorize`;
break;
@@ -96,8 +96,30 @@ export const IntegrationsSection = ({
{integrationSlugNameMapping[integration.integration]}
</div>
</div>
{(integration.integration === "qovery") && (
<div className="flex flex-row">
<div className="ml-2 flex flex-col">
<FormLabel label="Org" />
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
{integration?.owner || "-"}
</div>
</div>
<div className="ml-2 flex flex-col">
<FormLabel label="Project" />
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
{integration?.targetService || "-"}
</div>
</div>
<div className="ml-2 flex flex-col">
<FormLabel label="Env" />
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
{integration?.targetEnvironment || "-"}
</div>
</div>
</div>
)}
<div className="ml-2 flex flex-col">
<FormLabel label="App" />
<FormLabel label={integration?.metadata?.scope || "App"} />
<div className="min-w-[8rem] rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
{integration.integration === "hashicorp-vault"
? `${integration.app} - path: ${integration.path}`
@@ -131,7 +153,7 @@ export const IntegrationsSection = ({
I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.Integrations}
>
{(isAllowed) => (
{(isAllowed: boolean) => (
<div className="ml-2 opacity-80 duration-200 hover:opacity-100">
<Tooltip content="Remove Integration">
<IconButton
@@ -3,6 +3,7 @@ import { faElementor } from "@fortawesome/free-brands-svg-icons";
import {
faAnchorLock,
faArrowLeft,
faBook,
faCog,
faKey,
faLock,
@@ -82,6 +83,12 @@ const SINGLE_PERMISSION_LIST = [
icon: faTags,
formName: "tags"
},
{
title: "Audit Logs",
subtitle: "Audit log management control",
icon: faBook,
formName: "audit-logs"
},
{
title: "IP Allowlist",
subtitle: "IP allowlist management control",