Merge remote-tracking branch 'origin/main' into feat/addHerokuSecretSync

This commit is contained in:
carlosmonastyrski
2025-06-19 08:47:03 -03:00
205 changed files with 5151 additions and 759 deletions
@@ -1,4 +1,5 @@
import { useCallback } from "react";
import { subject } from "@casl/ability";
import {
faBan,
faCheck,
@@ -63,7 +64,7 @@ export const SecretSyncActionTriggers = ({ secretSync }: Props) => {
const triggerSyncSecrets = useTriggerSecretSyncSyncSecrets();
const updateSync = useUpdateSecretSync();
const { destination } = secretSync;
const { destination, environment, folder } = secretSync;
const destinationName = SECRET_SYNC_MAP[destination].name;
const { syncOption } = useSecretSyncOption(destination);
@@ -128,6 +129,14 @@ export const SecretSyncActionTriggers = ({ secretSync }: Props) => {
}
};
const permissionSubject =
environment && folder
? subject(ProjectPermissionSub.SecretSyncs, {
environment: environment.slug,
secretPath: folder.path
})
: ProjectPermissionSub.SecretSyncs;
return (
<>
<div className="ml-auto mt-4 flex flex-wrap items-center justify-end gap-2">
@@ -157,7 +166,7 @@ export const SecretSyncActionTriggers = ({ secretSync }: Props) => {
<div>
<ProjectPermissionCan
I={ProjectPermissionSecretSyncActions.SyncSecrets}
a={ProjectPermissionSub.SecretSyncs}
a={permissionSubject}
>
{(isAllowed: boolean) => (
<Button
@@ -194,7 +203,7 @@ export const SecretSyncActionTriggers = ({ secretSync }: Props) => {
{syncOption?.canImportSecrets && (
<ProjectPermissionCan
I={ProjectPermissionSecretSyncActions.ImportSecrets}
a={ProjectPermissionSub.SecretSyncs}
a={permissionSubject}
>
{(isAllowed: boolean) => (
<DropdownMenuItem
@@ -222,7 +231,7 @@ export const SecretSyncActionTriggers = ({ secretSync }: Props) => {
)}
<ProjectPermissionCan
I={ProjectPermissionSecretSyncActions.RemoveSecrets}
a={ProjectPermissionSub.SecretSyncs}
a={permissionSubject}
>
{(isAllowed: boolean) => (
<DropdownMenuItem
@@ -249,7 +258,7 @@ export const SecretSyncActionTriggers = ({ secretSync }: Props) => {
</ProjectPermissionCan>
<ProjectPermissionCan
I={ProjectPermissionSecretSyncActions.Edit}
a={ProjectPermissionSub.SecretSyncs}
a={permissionSubject}
>
{(isAllowed: boolean) => (
<DropdownMenuItem
@@ -267,7 +276,7 @@ export const SecretSyncActionTriggers = ({ secretSync }: Props) => {
</ProjectPermissionCan>
<ProjectPermissionCan
I={ProjectPermissionSecretSyncActions.Delete}
a={ProjectPermissionSub.SecretSyncs}
a={permissionSubject}
>
{(isAllowed: boolean) => (
<DropdownMenuItem
@@ -0,0 +1,14 @@
import { GenericFieldLabel } from "@app/components/secret-syncs";
import { TFlyioSync } from "@app/hooks/api/secretSyncs/types/flyio-sync";
type Props = {
secretSync: TFlyioSync;
};
export const FlyioSyncDestinationSection = ({ secretSync }: Props) => {
const {
destinationConfig: { appId }
} = secretSync;
return <GenericFieldLabel label="App ID">{appId}</GenericFieldLabel>;
};
@@ -0,0 +1,23 @@
import { GenericFieldLabel } from "@app/components/secret-syncs";
import { useRenderConnectionListServices } from "@app/hooks/api/appConnections/render";
import { TRenderSync } from "@app/hooks/api/secretSyncs/render-sync";
type Props = {
secretSync: TRenderSync;
};
export const RenderSyncDestinationSection = ({ secretSync }: Props) => {
const { data: services = [], isPending } = useRenderConnectionListServices(
secretSync.connectionId
);
const {
destinationConfig: { serviceId }
} = secretSync;
if (isPending) {
return <GenericFieldLabel label="Service">Loading...</GenericFieldLabel>;
}
const serviceName = services.find((service) => service.id === serviceId)?.name;
return <GenericFieldLabel label="Service">{serviceName ?? serviceId}</GenericFieldLabel>;
};
@@ -1,4 +1,5 @@
import { ReactNode } from "react";
import { subject } from "@casl/ability";
import { faEdit } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -18,12 +19,14 @@ import { AzureDevOpsSyncDestinationSection } from "./AzureDevOpsSyncDestinationS
import { AzureKeyVaultSyncDestinationSection } from "./AzureKeyVaultSyncDestinationSection";
import { CamundaSyncDestinationSection } from "./CamundaSyncDestinationSection";
import { DatabricksSyncDestinationSection } from "./DatabricksSyncDestinationSection";
import { FlyioSyncDestinationSection } from "./FlyioSyncDestinationSection";
import { GcpSyncDestinationSection } from "./GcpSyncDestinationSection";
import { GitHubSyncDestinationSection } from "./GitHubSyncDestinationSection";
import { HCVaultSyncDestinationSection } from "./HCVaultSyncDestinationSection";
import { HerokuSyncDestinationSection } from "./HerokuSyncDestinationSection";
import { HumanitecSyncDestinationSection } from "./HumanitecSyncDestinationSection";
import { OCIVaultSyncDestinationSection } from "./OCIVaultSyncDestinationSection";
import { RenderSyncDestinationSection } from "./RenderSyncDestinationSection";
import { TeamCitySyncDestinationSection } from "./TeamCitySyncDestinationSection";
import { TerraformCloudSyncDestinationSection } from "./TerraformCloudSyncDestinationSection";
import { VercelSyncDestinationSection } from "./VercelSyncDestinationSection";
@@ -35,7 +38,7 @@ type Props = {
};
export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }: Props) => {
const { destination, connection } = secretSync;
const { destination, connection, folder, environment } = secretSync;
const app = APP_CONNECTION_MAP[connection.app].name;
@@ -97,18 +100,29 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
case SecretSync.Heroku:
DestinationComponents = <HerokuSyncDestinationSection secretSync={secretSync} />;
break;
case SecretSync.Render:
DestinationComponents = <RenderSyncDestinationSection secretSync={secretSync} />;
break;
case SecretSync.Flyio:
DestinationComponents = <FlyioSyncDestinationSection secretSync={secretSync} />;
break;
default:
throw new Error(`Unhandled Destination Section components: ${destination}`);
}
const permissionSubject =
environment && folder
? subject(ProjectPermissionSub.SecretSyncs, {
environment: environment.slug,
secretPath: folder.path
})
: ProjectPermissionSub.SecretSyncs;
return (
<div className="flex w-full flex-col gap-3 rounded-lg border border-mineshaft-600 bg-mineshaft-900 px-4 py-3">
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-2">
<h3 className="font-semibold text-mineshaft-100">Destination Configuration</h3>
<ProjectPermissionCan
I={ProjectPermissionSecretSyncActions.Edit}
a={ProjectPermissionSub.SecretSyncs}
>
<ProjectPermissionCan I={ProjectPermissionSecretSyncActions.Edit} a={permissionSubject}>
{(isAllowed) => (
<IconButton
variant="plain"
@@ -1,4 +1,5 @@
import { useMemo } from "react";
import { subject } from "@casl/ability";
import { faEdit } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns";
@@ -16,7 +17,8 @@ type Props = {
};
export const SecretSyncDetailsSection = ({ secretSync, onEditDetails }: Props) => {
const { syncStatus, lastSyncMessage, lastSyncedAt, name, description } = secretSync;
const { syncStatus, lastSyncMessage, lastSyncedAt, name, description, environment, folder } =
secretSync;
const failureMessage = useMemo(() => {
if (syncStatus === SecretSyncStatus.Failed) {
@@ -32,14 +34,19 @@ export const SecretSyncDetailsSection = ({ secretSync, onEditDetails }: Props) =
return null;
}, [syncStatus, lastSyncMessage]);
const permissionSubject =
environment && folder
? subject(ProjectPermissionSub.SecretSyncs, {
environment: environment.slug,
secretPath: folder.path
})
: ProjectPermissionSub.SecretSyncs;
return (
<div className="flex w-full flex-col gap-3 rounded-lg border border-mineshaft-600 bg-mineshaft-900 px-4 py-3">
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-2">
<h3 className="font-semibold text-mineshaft-100">Details</h3>
<ProjectPermissionCan
I={ProjectPermissionSecretSyncActions.Edit}
a={ProjectPermissionSub.SecretSyncs}
>
<ProjectPermissionCan I={ProjectPermissionSecretSyncActions.Edit} a={permissionSubject}>
{(isAllowed) => (
<IconButton
variant="plain"
@@ -1,4 +1,5 @@
import { ReactNode } from "react";
import { subject } from "@casl/ability";
import { faEdit } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -21,7 +22,9 @@ type Props = {
export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) => {
const {
destination,
syncOptions: { initialSyncBehavior, disableSecretDeletion, keySchema }
syncOptions: { initialSyncBehavior, disableSecretDeletion, keySchema },
environment,
folder
} = secretSync;
let AdditionalSyncOptionsComponent: ReactNode;
@@ -53,21 +56,28 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
case SecretSync.OCIVault:
case SecretSync.OnePass:
case SecretSync.Heroku:
case SecretSync.Render:
case SecretSync.Flyio:
AdditionalSyncOptionsComponent = null;
break;
default:
throw new Error(`Unhandled Destination Review Fields: ${destination}`);
}
const permissionSubject =
environment && folder
? subject(ProjectPermissionSub.SecretSyncs, {
environment: environment.slug,
secretPath: folder.path
})
: ProjectPermissionSub.SecretSyncs;
return (
<div>
<div className="flex w-full flex-col gap-3 rounded-lg border border-mineshaft-600 bg-mineshaft-900 px-4 py-3">
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-2">
<h3 className="font-semibold text-mineshaft-100">Sync Options</h3>
<ProjectPermissionCan
I={ProjectPermissionSecretSyncActions.Edit}
a={ProjectPermissionSub.SecretSyncs}
>
<ProjectPermissionCan I={ProjectPermissionSecretSyncActions.Edit} a={permissionSubject}>
{(isAllowed) => (
<IconButton
variant="plain"
@@ -1,3 +1,4 @@
import { subject } from "@casl/ability";
import { faEdit, faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -16,6 +17,14 @@ type Props = {
export const SecretSyncSourceSection = ({ secretSync, onEditSource }: Props) => {
const { folder, environment } = secretSync;
const permissionSubject =
environment && folder
? subject(ProjectPermissionSub.SecretSyncs, {
environment: environment.slug,
secretPath: folder.path
})
: ProjectPermissionSub.SecretSyncs;
return (
<div>
<div className="flex w-full flex-col gap-3 rounded-lg border border-mineshaft-600 bg-mineshaft-900 px-4 py-3">
@@ -35,10 +44,7 @@ export const SecretSyncSourceSection = ({ secretSync, onEditSource }: Props) =>
</div>
</Tooltip>
)}
<ProjectPermissionCan
I={ProjectPermissionSecretSyncActions.Edit}
a={ProjectPermissionSub.SecretSyncs}
>
<ProjectPermissionCan I={ProjectPermissionSecretSyncActions.Edit} a={permissionSubject}>
{(isAllowed) => (
<IconButton
variant="plain"