mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-09 15:38:03 -05:00
improvement: native integration legacy details and sync redirects
This commit is contained in:
@@ -31,6 +31,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
|||||||
.object({
|
.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
slug: z.string(),
|
slug: z.string(),
|
||||||
|
syncSlug: z.string().optional(),
|
||||||
clientSlug: z.string().optional(),
|
clientSlug: z.string().optional(),
|
||||||
image: z.string(),
|
image: z.string(),
|
||||||
isAvailable: z.boolean().optional(),
|
isAvailable: z.boolean().optional(),
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ export const getIntegrationOptions = async () => {
|
|||||||
{
|
{
|
||||||
name: "AWS Secrets Manager",
|
name: "AWS Secrets Manager",
|
||||||
slug: "aws-secret-manager",
|
slug: "aws-secret-manager",
|
||||||
|
syncSlug: "aws-secrets-manager",
|
||||||
image: "Amazon Web Services.png",
|
image: "Amazon Web Services.png",
|
||||||
isAvailable: true,
|
isAvailable: true,
|
||||||
type: "custom",
|
type: "custom",
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ title: "AWS Parameter Store"
|
|||||||
description: "Learn how to sync secrets from Infisical to AWS Parameter Store."
|
description: "Learn how to sync secrets from Infisical to AWS Parameter Store."
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The AWS Parameter Store Native Integration will be deprecated in 2026. Please migrate to our new [AWS Parameter Store Sync](../secret-syncs/aws-parameter-store).
|
||||||
|
</Note>
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Assume Role (Recommended)">
|
<Tab title="Assume Role (Recommended)">
|
||||||
Infisical will assume the provided role in your AWS account securely, without the need to share any credentials.
|
Infisical will assume the provided role in your AWS account securely, without the need to share any credentials.
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ title: "AWS Secrets Manager"
|
|||||||
description: "Learn how to sync secrets from Infisical to AWS Secrets Manager."
|
description: "Learn how to sync secrets from Infisical to AWS Secrets Manager."
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The AWS Secrets Manager Native Integration will be deprecated in 2026. Please migrate to our new [AWS Secrets Manager Sync](../secret-syncs/aws-secrets-manager).
|
||||||
|
</Note>
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Assume Role (Recommended)">
|
<Tab title="Assume Role (Recommended)">
|
||||||
Infisical will assume the provided role in your AWS account securely, without the need to share any credentials.
|
Infisical will assume the provided role in your AWS account securely, without the need to share any credentials.
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ title: "Azure App Configuration"
|
|||||||
description: "How to sync secrets from Infisical to Azure App Configuration"
|
description: "How to sync secrets from Infisical to Azure App Configuration"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The Azure App Configuration Native Integration will be deprecated in 2026. Please migrate to our new [Azure App Configuration Sync](../secret-syncs/azure-app-configuration).
|
||||||
|
</Note>
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Usage">
|
<Tab title="Usage">
|
||||||
**Prerequisites:**
|
**Prerequisites:**
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ title: "Azure Key Vault"
|
|||||||
description: "How to sync secrets from Infisical to Azure Key Vault"
|
description: "How to sync secrets from Infisical to Azure Key Vault"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The Azure Key Vault Native Integration will be deprecated in 2026. Please migrate to our new [Azure Key Vault Sync](../secret-syncs/azure-key-vault).
|
||||||
|
</Note>
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Usage">
|
<Tab title="Usage">
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ title: "Databricks"
|
|||||||
description: "Learn how to sync secrets from Infisical to Databricks."
|
description: "Learn how to sync secrets from Infisical to Databricks."
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The Databricks Native Integration will be deprecated in 2026. Please migrate to our new [Databricks Sync](../secret-syncs/databricks).
|
||||||
|
</Note>
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
|
||||||
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)
|
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ title: "GCP Secret Manager"
|
|||||||
description: "How to sync secrets from Infisical to GCP Secret Manager"
|
description: "How to sync secrets from Infisical to GCP Secret Manager"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The GCP Secret Manager Native Integration will be deprecated in 2026. Please migrate to our new [GCP Secret Manager Sync](../secret-syncs/gcp-secret-manager).
|
||||||
|
</Note>
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Usage">
|
<Tab title="Usage">
|
||||||
<AccordionGroup>
|
<AccordionGroup>
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ title: "Terraform Cloud"
|
|||||||
description: "How to sync secrets from Infisical to Terraform Cloud"
|
description: "How to sync secrets from Infisical to Terraform Cloud"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The Terraform Cloud Native Integration will be deprecated in 2026. Please migrate to our new [Terraform Cloud Sync](../secret-syncs/terraform-cloud).
|
||||||
|
</Note>
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
|
||||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ title: "Vercel"
|
|||||||
description: "How to sync secrets from Infisical to Vercel"
|
description: "How to sync secrets from Infisical to Vercel"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
The Vercel Native Integration will be deprecated in 2026. Please migrate to our new [Vercel Sync](../secret-syncs/vercel).
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Usage">
|
<Tab title="Usage">
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { Modal, ModalContent } from "@app/components/v2";
|
import { Modal, ModalContent } from "@app/components/v2";
|
||||||
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
|
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
|
||||||
@@ -10,6 +10,7 @@ import { SecretSyncSelect } from "./SecretSyncSelect";
|
|||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onOpenChange: (isOpen: boolean) => void;
|
onOpenChange: (isOpen: boolean) => void;
|
||||||
|
selectSync?: SecretSync | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ContentProps = {
|
type ContentProps = {
|
||||||
@@ -32,8 +33,12 @@ const Content = ({ onComplete, setSelectedSync, selectedSync }: ContentProps) =>
|
|||||||
return <SecretSyncSelect onSelect={setSelectedSync} />;
|
return <SecretSyncSelect onSelect={setSelectedSync} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CreateSecretSyncModal = ({ onOpenChange, ...props }: Props) => {
|
export const CreateSecretSyncModal = ({ onOpenChange, selectSync = null, ...props }: Props) => {
|
||||||
const [selectedSync, setSelectedSync] = useState<SecretSync | null>(null);
|
const [selectedSync, setSelectedSync] = useState<SecretSync | null>(selectSync);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedSync(selectSync);
|
||||||
|
}, [selectSync]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export type TCloudIntegration = {
|
export type TCloudIntegration = {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
syncSlug?: string;
|
||||||
image: string;
|
image: string;
|
||||||
isAvailable: boolean;
|
isAvailable: boolean;
|
||||||
type: string;
|
type: string;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { useNavigate, useSearch } from "@tanstack/react-router";
|
import { useNavigate, useSearch } from "@tanstack/react-router";
|
||||||
|
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import { Badge, PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
import { Badge, PageHeader, Tab, TabList, TabPanel, Tabs, Tooltip } from "@app/components/v2";
|
||||||
import { ROUTE_PATHS } from "@app/const/routes";
|
import { ROUTE_PATHS } from "@app/const/routes";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||||
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
|
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||||
@@ -51,46 +51,19 @@ export const IntegrationsListPage = () => {
|
|||||||
title="Integrations"
|
title="Integrations"
|
||||||
description="Manage integrations with third-party services."
|
description="Manage integrations with third-party services."
|
||||||
/>
|
/>
|
||||||
<div className="mb-4 mt-4 flex flex-col rounded-r border-l-2 border-l-primary bg-mineshaft-300/5 px-4 py-2.5">
|
|
||||||
<div className="mb-1 flex items-center text-sm">
|
|
||||||
<FontAwesomeIcon icon={faInfoCircle} size="sm" className="mr-1.5 text-primary" />
|
|
||||||
Integrations Update
|
|
||||||
</div>
|
|
||||||
<p className="mb-2 mt-1 text-sm text-bunker-300">
|
|
||||||
Infisical is excited to announce{" "}
|
|
||||||
<a
|
|
||||||
className="text-bunker-200 underline decoration-primary-700 underline-offset-4 duration-200 hover:text-mineshaft-100 hover:decoration-primary-600"
|
|
||||||
href="https://infisical.com/docs/integrations/secret-syncs/overview"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Secret Syncs
|
|
||||||
</a>
|
|
||||||
, a new way to sync your secrets to third-party services using{" "}
|
|
||||||
<a
|
|
||||||
className="text-bunker-200 underline decoration-primary-700 underline-offset-4 duration-200 hover:text-mineshaft-100 hover:decoration-primary-600"
|
|
||||||
href="https://infisical.com/docs/integrations/app-connections/overview"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
App Connections
|
|
||||||
</a>
|
|
||||||
, offering deeper customization and re-configurability.
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-bunker-300">
|
|
||||||
Existing integrations (now called Native Integrations) will continue to be supported
|
|
||||||
as we build out our Secret Sync library.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Tabs value={selectedTab} onValueChange={updateSelectedTab}>
|
<Tabs value={selectedTab} onValueChange={updateSelectedTab}>
|
||||||
<TabList>
|
<TabList>
|
||||||
<Tab value={IntegrationsListPageTabs.SecretSyncs}>
|
<Tab value={IntegrationsListPageTabs.SecretSyncs}>Secret Syncs</Tab>
|
||||||
Secret Syncs
|
<Tab value={IntegrationsListPageTabs.NativeIntegrations}>
|
||||||
<Badge variant="primary" className="ml-1 cursor-pointer text-xs">
|
Native Integrations
|
||||||
New
|
<Tooltip content="Native Integrations will be deprecated in 2026. Please migrate to Secret Syncs as they become available.">
|
||||||
</Badge>
|
<div>
|
||||||
|
<Badge variant="primary" className="ml-1 cursor-pointer text-xs">
|
||||||
|
Legacy
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab value={IntegrationsListPageTabs.NativeIntegrations}>Native Integrations</Tab>
|
|
||||||
<Tab value={IntegrationsListPageTabs.FrameworkIntegrations}>
|
<Tab value={IntegrationsListPageTabs.FrameworkIntegrations}>
|
||||||
Framework Integrations
|
Framework Integrations
|
||||||
</Tab>
|
</Tab>
|
||||||
@@ -108,6 +81,26 @@ export const IntegrationsListPage = () => {
|
|||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value={IntegrationsListPageTabs.NativeIntegrations}>
|
<TabPanel value={IntegrationsListPageTabs.NativeIntegrations}>
|
||||||
|
<div className="mb-5 flex flex-col rounded-r border-l-2 border-l-primary bg-mineshaft-300/5 px-4 py-2.5">
|
||||||
|
<div className="mb-1 flex items-center text-sm">
|
||||||
|
<FontAwesomeIcon icon={faInfoCircle} size="sm" className="mr-1.5 text-primary" />
|
||||||
|
Native Integrations Transitioning to Legacy Status
|
||||||
|
</div>
|
||||||
|
<p className="mb-2 mt-1 text-sm text-bunker-300">
|
||||||
|
Native integrations are now a legacy feature and will begin deprecation in 2026.
|
||||||
|
We recommend migrating to our new{" "}
|
||||||
|
<a
|
||||||
|
className="text-bunker-200 underline decoration-primary-700 underline-offset-4 duration-200 hover:text-mineshaft-100 hover:decoration-primary-600"
|
||||||
|
href="https://infisical.com/docs/integrations/secret-syncs/overview"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Secret Syncs
|
||||||
|
</a>{" "}
|
||||||
|
feature which offers the same functionality as Native Integrations with improved
|
||||||
|
stability, insights, re-configurability, and customization.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
renderGuardBanner
|
renderGuardBanner
|
||||||
I={ProjectPermissionActions.Read}
|
I={ProjectPermissionActions.Read}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
faXmark
|
faXmark
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
|
|
||||||
import { NoEnvironmentsBanner } from "@app/components/integrations/NoEnvironmentsBanner";
|
import { NoEnvironmentsBanner } from "@app/components/integrations/NoEnvironmentsBanner";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
@@ -19,6 +20,7 @@ import {
|
|||||||
Skeleton,
|
Skeleton,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
|
import { ROUTE_PATHS } from "@app/const/routes";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
@@ -26,7 +28,9 @@ import {
|
|||||||
useWorkspace
|
useWorkspace
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
|
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||||
import { IntegrationAuth, TCloudIntegration } from "@app/hooks/api/types";
|
import { IntegrationAuth, TCloudIntegration } from "@app/hooks/api/types";
|
||||||
|
import { IntegrationsListPageTabs } from "@app/types/integrations";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
@@ -40,6 +44,10 @@ type Props = {
|
|||||||
|
|
||||||
type TRevokeIntegrationPopUp = { provider: string };
|
type TRevokeIntegrationPopUp = { provider: string };
|
||||||
|
|
||||||
|
const SECRET_SYNCS = Object.values(SecretSync) as string[];
|
||||||
|
// this assumes sync and integration will have same name which looks like it will be the case
|
||||||
|
const isSecretSyncAvailable = (type: string) => SECRET_SYNCS.includes(type);
|
||||||
|
|
||||||
export const CloudIntegrationSection = ({
|
export const CloudIntegrationSection = ({
|
||||||
isLoading,
|
isLoading,
|
||||||
cloudIntegrations = [],
|
cloudIntegrations = [],
|
||||||
@@ -54,6 +62,7 @@ export const CloudIntegrationSection = ({
|
|||||||
] as const);
|
] as const);
|
||||||
const { permission } = useProjectPermission();
|
const { permission } = useProjectPermission();
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const isEmpty = !isLoading && !cloudIntegrations?.length;
|
const isEmpty = !isLoading && !cloudIntegrations?.length;
|
||||||
|
|
||||||
@@ -111,73 +120,104 @@ export const CloudIntegrationSection = ({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{!isLoading && filteredIntegrations.length ? (
|
{!isLoading && filteredIntegrations.length ? (
|
||||||
filteredIntegrations.map((cloudIntegration) => (
|
filteredIntegrations.map((cloudIntegration) => {
|
||||||
<div
|
const syncSlug = cloudIntegration.syncSlug ?? cloudIntegration.slug;
|
||||||
onKeyDown={() => null}
|
const isSyncAvailable = isSecretSyncAvailable(syncSlug);
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
return (
|
||||||
className={`group relative ${
|
<div
|
||||||
cloudIntegration.isAvailable
|
onKeyDown={() => null}
|
||||||
? "cursor-pointer duration-200 hover:bg-mineshaft-700"
|
role="button"
|
||||||
: "opacity-50"
|
tabIndex={0}
|
||||||
} flex h-32 flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4`}
|
className={`group relative ${
|
||||||
onClick={() => {
|
cloudIntegration.isAvailable
|
||||||
if (!cloudIntegration.isAvailable) return;
|
? "cursor-pointer duration-200 hover:bg-mineshaft-700"
|
||||||
if (
|
: "opacity-50"
|
||||||
permission.cannot(
|
} flex h-36 flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-3`}
|
||||||
ProjectPermissionActions.Create,
|
onClick={() => {
|
||||||
ProjectPermissionSub.Integrations
|
if (isSyncAvailable) {
|
||||||
)
|
navigate({
|
||||||
) {
|
to: ROUTE_PATHS.SecretManager.IntegrationsListPage.path,
|
||||||
createNotification({
|
params: {
|
||||||
type: "error",
|
projectId: currentWorkspace.id
|
||||||
text: "You do not have permission to create an integration"
|
},
|
||||||
});
|
search: {
|
||||||
return;
|
selectedTab: IntegrationsListPageTabs.SecretSyncs,
|
||||||
}
|
addSync: syncSlug as SecretSync
|
||||||
onIntegrationStart(cloudIntegration.slug);
|
}
|
||||||
}}
|
});
|
||||||
key={cloudIntegration.slug}
|
return;
|
||||||
>
|
}
|
||||||
<img
|
if (!cloudIntegration.isAvailable) return;
|
||||||
src={`/images/integrations/${cloudIntegration.image}`}
|
if (
|
||||||
height={60}
|
permission.cannot(
|
||||||
width={60}
|
ProjectPermissionActions.Create,
|
||||||
className="mt-auto"
|
ProjectPermissionSub.Integrations
|
||||||
alt="integration logo"
|
)
|
||||||
/>
|
) {
|
||||||
<div className="mt-auto max-w-xs text-center text-sm font-semibold text-gray-300 duration-200 group-hover:text-gray-200">
|
createNotification({
|
||||||
{cloudIntegration.name}
|
type: "error",
|
||||||
</div>
|
text: "You do not have permission to create an integration"
|
||||||
{cloudIntegration.isAvailable &&
|
});
|
||||||
Boolean(integrationAuths?.[cloudIntegration.slug]) && (
|
return;
|
||||||
<div className="absolute right-0 top-0 z-30 h-full">
|
}
|
||||||
<div className="relative h-full">
|
onIntegrationStart(cloudIntegration.slug);
|
||||||
<div className="absolute right-0 top-0 w-24 flex-row items-center overflow-hidden whitespace-nowrap rounded-bl-md rounded-tr-md bg-primary px-2 py-0.5 text-xs text-black opacity-80 transition-all duration-300 group-hover:w-0 group-hover:p-0">
|
}}
|
||||||
<FontAwesomeIcon icon={faCheck} className="mr-2 text-xs" />
|
key={cloudIntegration.slug}
|
||||||
Authorized
|
>
|
||||||
</div>
|
<div className="m-auto flex flex-col items-center">
|
||||||
<Tooltip content="Revoke Access">
|
<img
|
||||||
<div
|
src={`/images/integrations/${cloudIntegration.image}`}
|
||||||
onKeyDown={() => null}
|
height={60}
|
||||||
role="button"
|
width={60}
|
||||||
tabIndex={0}
|
className="mt-auto"
|
||||||
onClick={async (event) => {
|
alt="integration logo"
|
||||||
event.stopPropagation();
|
/>
|
||||||
handlePopUpOpen("deleteConfirmation", {
|
<div
|
||||||
provider: cloudIntegration.slug
|
className={`mt-2 max-w-xs text-center text-sm font-semibold text-gray-300 duration-200 group-hover:text-gray-200 ${isSyncAvailable ? "mb-4" : ""}`}
|
||||||
});
|
>
|
||||||
}}
|
{cloudIntegration.name}
|
||||||
className="absolute right-0 top-0 flex h-0 w-12 cursor-pointer items-center justify-center overflow-hidden rounded-r-md bg-red text-xs opacity-50 transition-all duration-300 hover:opacity-100 group-hover:h-full"
|
</div>
|
||||||
>
|
</div>
|
||||||
<FontAwesomeIcon icon={faXmark} size="xl" />
|
{cloudIntegration.isAvailable &&
|
||||||
|
Boolean(integrationAuths?.[cloudIntegration.slug]) && (
|
||||||
|
<div className="absolute right-0 top-0 z-30 h-full">
|
||||||
|
<div className="relative h-full">
|
||||||
|
<div className="absolute right-0 top-0 w-24 flex-row items-center overflow-hidden whitespace-nowrap rounded-bl-md rounded-tr-md bg-primary px-2 py-0.5 text-xs text-black opacity-80 transition-all duration-300 group-hover:w-0 group-hover:p-0">
|
||||||
|
<FontAwesomeIcon icon={faCheck} className="mr-2 text-xs" />
|
||||||
|
Authorized
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
<Tooltip content="Revoke Access">
|
||||||
|
<div
|
||||||
|
onKeyDown={() => null}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={async (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
handlePopUpOpen("deleteConfirmation", {
|
||||||
|
provider: cloudIntegration.slug
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="absolute right-0 top-0 flex h-0 w-12 cursor-pointer items-center justify-center overflow-hidden rounded-r-md bg-red text-xs opacity-50 transition-all duration-300 hover:opacity-100 group-hover:h-full"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} size="xl" />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isSyncAvailable && (
|
||||||
|
<div className="absolute bottom-0 left-0 z-30 h-full w-full">
|
||||||
|
<div className="relative h-full">
|
||||||
|
<div className="absolute bottom-0 left-0 w-full flex-row overflow-hidden whitespace-nowrap rounded-bl-md rounded-br-md bg-yellow/20 px-2 py-0.5 text-center text-xs text-yellow">
|
||||||
|
Secret Sync Available
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))
|
);
|
||||||
|
})
|
||||||
) : (
|
) : (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
className="col-span-full h-32 w-full rounded-md bg-transparent pt-14"
|
className="col-span-full h-32 w-full rounded-md bg-transparent pt-14"
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
import { faArrowUpRightFromSquare, faBookOpen, faPlus } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowUpRightFromSquare, faBookOpen, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { useNavigate, useSearch } from "@tanstack/react-router";
|
||||||
|
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import { CreateSecretSyncModal } from "@app/components/secret-syncs";
|
import { CreateSecretSyncModal } from "@app/components/secret-syncs";
|
||||||
import { Button, Spinner } from "@app/components/v2";
|
import { Button, Spinner } from "@app/components/v2";
|
||||||
|
import { ROUTE_PATHS } from "@app/const/routes";
|
||||||
import { ProjectPermissionSub, useWorkspace } from "@app/context";
|
import { ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||||
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
|
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
@@ -14,8 +17,27 @@ import { SecretSyncsTable } from "./SecretSyncTable";
|
|||||||
export const SecretSyncsTab = () => {
|
export const SecretSyncsTab = () => {
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["addSync"] as const);
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["addSync"] as const);
|
||||||
|
|
||||||
|
const { addSync, ...search } = useSearch({
|
||||||
|
from: ROUTE_PATHS.SecretManager.IntegrationsListPage.id
|
||||||
|
});
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!addSync) return;
|
||||||
|
|
||||||
|
handlePopUpOpen("addSync", addSync);
|
||||||
|
navigate({
|
||||||
|
to: ROUTE_PATHS.SecretManager.IntegrationsListPage.path,
|
||||||
|
params: {
|
||||||
|
projectId: currentWorkspace.id
|
||||||
|
},
|
||||||
|
search
|
||||||
|
});
|
||||||
|
}, [addSync]);
|
||||||
|
|
||||||
const { data: secretSyncs = [], isPending: isSecretSyncsPending } = useListSecretSyncs(
|
const { data: secretSyncs = [], isPending: isSecretSyncsPending } = useListSecretSyncs(
|
||||||
currentWorkspace.id,
|
currentWorkspace.id,
|
||||||
{
|
{
|
||||||
@@ -76,6 +98,7 @@ export const SecretSyncsTab = () => {
|
|||||||
<SecretSyncsTable secretSyncs={secretSyncs} />
|
<SecretSyncsTable secretSyncs={secretSyncs} />
|
||||||
</div>
|
</div>
|
||||||
<CreateSecretSyncModal
|
<CreateSecretSyncModal
|
||||||
|
selectSync={popUp.addSync.data}
|
||||||
isOpen={popUp.addSync.isOpen}
|
isOpen={popUp.addSync.isOpen}
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("addSync", isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle("addSync", isOpen)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { workspaceKeys } from "@app/hooks/api";
|
|||||||
import { TIntegration } from "@app/hooks/api/integrations/types";
|
import { TIntegration } from "@app/hooks/api/integrations/types";
|
||||||
import {
|
import {
|
||||||
fetchSecretSyncsByProjectId,
|
fetchSecretSyncsByProjectId,
|
||||||
|
SecretSync,
|
||||||
secretSyncKeys,
|
secretSyncKeys,
|
||||||
TSecretSync
|
TSecretSync
|
||||||
} from "@app/hooks/api/secretSyncs";
|
} from "@app/hooks/api/secretSyncs";
|
||||||
@@ -15,7 +16,8 @@ import { IntegrationsListPageTabs } from "@app/types/integrations";
|
|||||||
import { IntegrationsListPage } from "./IntegrationsListPage";
|
import { IntegrationsListPage } from "./IntegrationsListPage";
|
||||||
|
|
||||||
const IntegrationsListPageQuerySchema = z.object({
|
const IntegrationsListPageQuerySchema = z.object({
|
||||||
selectedTab: z.nativeEnum(IntegrationsListPageTabs).optional()
|
selectedTab: z.nativeEnum(IntegrationsListPageTabs).optional(),
|
||||||
|
addSync: z.nativeEnum(SecretSync).optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
|
|||||||
Reference in New Issue
Block a user