improvement: native integration legacy details and sync redirects

This commit is contained in:
Scott Wilson
2025-04-11 19:34:33 -07:00
parent cdc364d44c
commit d00b34663e
16 changed files with 204 additions and 105 deletions

View File

@@ -31,6 +31,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
.object({
name: z.string(),
slug: z.string(),
syncSlug: z.string().optional(),
clientSlug: z.string().optional(),
image: z.string(),
isAvailable: z.boolean().optional(),

View File

@@ -195,6 +195,7 @@ export const getIntegrationOptions = async () => {
{
name: "AWS Secrets Manager",
slug: "aws-secret-manager",
syncSlug: "aws-secrets-manager",
image: "Amazon Web Services.png",
isAvailable: true,
type: "custom",

View File

@@ -3,6 +3,10 @@ title: "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>
<Tab title="Assume Role (Recommended)">
Infisical will assume the provided role in your AWS account securely, without the need to share any credentials.

View File

@@ -3,6 +3,10 @@ title: "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>
<Tab title="Assume Role (Recommended)">
Infisical will assume the provided role in your AWS account securely, without the need to share any credentials.

View File

@@ -3,6 +3,10 @@ title: "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>
<Tab title="Usage">
**Prerequisites:**

View File

@@ -3,6 +3,10 @@ title: "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>
<Tab title="Usage">
Prerequisites:

View File

@@ -3,6 +3,10 @@ title: "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:
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)

View File

@@ -3,6 +3,10 @@ title: "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>
<Tab title="Usage">
<AccordionGroup>

View File

@@ -3,6 +3,10 @@ title: "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:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)

View File

@@ -3,6 +3,11 @@ title: "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>
<Tab title="Usage">
Prerequisites:

View File

@@ -1,4 +1,4 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { Modal, ModalContent } from "@app/components/v2";
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
@@ -10,6 +10,7 @@ import { SecretSyncSelect } from "./SecretSyncSelect";
type Props = {
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
selectSync?: SecretSync | null;
};
type ContentProps = {
@@ -32,8 +33,12 @@ const Content = ({ onComplete, setSelectedSync, selectedSync }: ContentProps) =>
return <SecretSyncSelect onSelect={setSelectedSync} />;
};
export const CreateSecretSyncModal = ({ onOpenChange, ...props }: Props) => {
const [selectedSync, setSelectedSync] = useState<SecretSync | null>(null);
export const CreateSecretSyncModal = ({ onOpenChange, selectSync = null, ...props }: Props) => {
const [selectedSync, setSelectedSync] = useState<SecretSync | null>(selectSync);
useEffect(() => {
setSelectedSync(selectSync);
}, [selectSync]);
return (
<Modal

View File

@@ -1,6 +1,7 @@
export type TCloudIntegration = {
name: string;
slug: string;
syncSlug?: string;
image: string;
isAvailable: boolean;
type: string;

View File

@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate, useSearch } from "@tanstack/react-router";
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 { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
@@ -51,46 +51,19 @@ export const IntegrationsListPage = () => {
title="Integrations"
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}>
<TabList>
<Tab value={IntegrationsListPageTabs.SecretSyncs}>
Secret Syncs
<Badge variant="primary" className="ml-1 cursor-pointer text-xs">
New
</Badge>
<Tab value={IntegrationsListPageTabs.SecretSyncs}>Secret Syncs</Tab>
<Tab value={IntegrationsListPageTabs.NativeIntegrations}>
Native Integrations
<Tooltip content="Native Integrations will be deprecated in 2026. Please migrate to Secret Syncs as they become available.">
<div>
<Badge variant="primary" className="ml-1 cursor-pointer text-xs">
Legacy
</Badge>
</div>
</Tooltip>
</Tab>
<Tab value={IntegrationsListPageTabs.NativeIntegrations}>Native Integrations</Tab>
<Tab value={IntegrationsListPageTabs.FrameworkIntegrations}>
Framework Integrations
</Tab>
@@ -108,6 +81,26 @@ export const IntegrationsListPage = () => {
</ProjectPermissionCan>
</TabPanel>
<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
renderGuardBanner
I={ProjectPermissionActions.Read}

View File

@@ -8,6 +8,7 @@ import {
faXmark
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate } from "@tanstack/react-router";
import { NoEnvironmentsBanner } from "@app/components/integrations/NoEnvironmentsBanner";
import { createNotification } from "@app/components/notifications";
@@ -19,6 +20,7 @@ import {
Skeleton,
Tooltip
} from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import {
ProjectPermissionActions,
ProjectPermissionSub,
@@ -26,7 +28,9 @@ import {
useWorkspace
} from "@app/context";
import { usePopUp } from "@app/hooks";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { IntegrationAuth, TCloudIntegration } from "@app/hooks/api/types";
import { IntegrationsListPageTabs } from "@app/types/integrations";
type Props = {
isLoading?: boolean;
@@ -40,6 +44,10 @@ type Props = {
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 = ({
isLoading,
cloudIntegrations = [],
@@ -54,6 +62,7 @@ export const CloudIntegrationSection = ({
] as const);
const { permission } = useProjectPermission();
const { currentWorkspace } = useWorkspace();
const navigate = useNavigate();
const isEmpty = !isLoading && !cloudIntegrations?.length;
@@ -111,73 +120,104 @@ export const CloudIntegrationSection = ({
))}
{!isLoading && filteredIntegrations.length ? (
filteredIntegrations.map((cloudIntegration) => (
<div
onKeyDown={() => null}
role="button"
tabIndex={0}
className={`group relative ${
cloudIntegration.isAvailable
? "cursor-pointer duration-200 hover:bg-mineshaft-700"
: "opacity-50"
} flex h-32 flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4`}
onClick={() => {
if (!cloudIntegration.isAvailable) return;
if (
permission.cannot(
ProjectPermissionActions.Create,
ProjectPermissionSub.Integrations
)
) {
createNotification({
type: "error",
text: "You do not have permission to create an integration"
});
return;
}
onIntegrationStart(cloudIntegration.slug);
}}
key={cloudIntegration.slug}
>
<img
src={`/images/integrations/${cloudIntegration.image}`}
height={60}
width={60}
className="mt-auto"
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">
{cloudIntegration.name}
</div>
{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>
<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" />
filteredIntegrations.map((cloudIntegration) => {
const syncSlug = cloudIntegration.syncSlug ?? cloudIntegration.slug;
const isSyncAvailable = isSecretSyncAvailable(syncSlug);
return (
<div
onKeyDown={() => null}
role="button"
tabIndex={0}
className={`group relative ${
cloudIntegration.isAvailable
? "cursor-pointer duration-200 hover:bg-mineshaft-700"
: "opacity-50"
} flex h-36 flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-3`}
onClick={() => {
if (isSyncAvailable) {
navigate({
to: ROUTE_PATHS.SecretManager.IntegrationsListPage.path,
params: {
projectId: currentWorkspace.id
},
search: {
selectedTab: IntegrationsListPageTabs.SecretSyncs,
addSync: syncSlug as SecretSync
}
});
return;
}
if (!cloudIntegration.isAvailable) return;
if (
permission.cannot(
ProjectPermissionActions.Create,
ProjectPermissionSub.Integrations
)
) {
createNotification({
type: "error",
text: "You do not have permission to create an integration"
});
return;
}
onIntegrationStart(cloudIntegration.slug);
}}
key={cloudIntegration.slug}
>
<div className="m-auto flex flex-col items-center">
<img
src={`/images/integrations/${cloudIntegration.image}`}
height={60}
width={60}
className="mt-auto"
alt="integration logo"
/>
<div
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}
</div>
</div>
{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>
</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>
);
})
) : (
<EmptyState
className="col-span-full h-32 w-full rounded-md bg-transparent pt-14"

View File

@@ -1,9 +1,12 @@
import { useEffect } from "react";
import { faArrowUpRightFromSquare, faBookOpen, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate, useSearch } from "@tanstack/react-router";
import { ProjectPermissionCan } from "@app/components/permissions";
import { CreateSecretSyncModal } from "@app/components/secret-syncs";
import { Button, Spinner } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { ProjectPermissionSub, useWorkspace } from "@app/context";
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
import { usePopUp } from "@app/hooks";
@@ -14,8 +17,27 @@ import { SecretSyncsTable } from "./SecretSyncTable";
export const SecretSyncsTab = () => {
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["addSync"] as const);
const { addSync, ...search } = useSearch({
from: ROUTE_PATHS.SecretManager.IntegrationsListPage.id
});
const navigate = useNavigate();
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(
currentWorkspace.id,
{
@@ -76,6 +98,7 @@ export const SecretSyncsTab = () => {
<SecretSyncsTable secretSyncs={secretSyncs} />
</div>
<CreateSecretSyncModal
selectSync={popUp.addSync.data}
isOpen={popUp.addSync.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("addSync", isOpen)}
/>

View File

@@ -6,6 +6,7 @@ import { workspaceKeys } from "@app/hooks/api";
import { TIntegration } from "@app/hooks/api/integrations/types";
import {
fetchSecretSyncsByProjectId,
SecretSync,
secretSyncKeys,
TSecretSync
} from "@app/hooks/api/secretSyncs";
@@ -15,7 +16,8 @@ import { IntegrationsListPageTabs } from "@app/types/integrations";
import { IntegrationsListPage } from "./IntegrationsListPage";
const IntegrationsListPageQuerySchema = z.object({
selectedTab: z.nativeEnum(IntegrationsListPageTabs).optional()
selectedTab: z.nativeEnum(IntegrationsListPageTabs).optional(),
addSync: z.nativeEnum(SecretSync).optional()
});
export const Route = createFileRoute(