mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 22:46:24 +00:00
feat(secret-sync): Add Heroku Secret Sync
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
||||
import { THerokuSync } from "@app/hooks/api/secretSyncs/types/heroku-sync";
|
||||
|
||||
import { getSecretSyncDestinationColValues } from "../helpers";
|
||||
import { SecretSyncTableCell } from "../SecretSyncTableCell";
|
||||
|
||||
type Props = {
|
||||
secretSync: THerokuSync;
|
||||
};
|
||||
|
||||
export const HerokuSyncDestinationCol = ({ secretSync }: Props) => {
|
||||
const { primaryText, secondaryText } = getSecretSyncDestinationColValues(secretSync);
|
||||
|
||||
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
|
||||
};
|
||||
+3
@@ -11,6 +11,7 @@ import { DatabricksSyncDestinationCol } from "./DatabricksSyncDestinationCol";
|
||||
import { GcpSyncDestinationCol } from "./GcpSyncDestinationCol";
|
||||
import { GitHubSyncDestinationCol } from "./GitHubSyncDestinationCol";
|
||||
import { HCVaultSyncDestinationCol } from "./HCVaultSyncDestinationCol";
|
||||
import { HerokuSyncDestinationCol } from "./HerokuSyncDestinationCol";
|
||||
import { HumanitecSyncDestinationCol } from "./HumanitecSyncDestinationCol";
|
||||
import { OCIVaultSyncDestinationCol } from "./OCIVaultSyncDestinationCol";
|
||||
import { TeamCitySyncDestinationCol } from "./TeamCitySyncDestinationCol";
|
||||
@@ -58,6 +59,8 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
|
||||
return <OnePassSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.AzureDevOps:
|
||||
return <AzureDevOpsSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.Heroku:
|
||||
return <HerokuSyncDestinationCol secretSync={secretSync} />;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled Secret Sync Destination Col: ${(secretSync as TSecretSync).destination}`
|
||||
|
||||
+4
@@ -116,6 +116,10 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
|
||||
primaryText = destinationConfig.devopsProjectName;
|
||||
secondaryText = destinationConfig.devopsProjectId;
|
||||
break;
|
||||
case SecretSync.Heroku:
|
||||
primaryText = destinationConfig.appName;
|
||||
secondaryText = destinationConfig.app;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Col Values ${destination}`);
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { THerokuSync } from "@app/hooks/api/secretSyncs/types/heroku-sync";
|
||||
|
||||
type Props = {
|
||||
secretSync: THerokuSync;
|
||||
};
|
||||
|
||||
export const HerokuSyncDestinationSection = ({ secretSync }: Props) => {
|
||||
const {
|
||||
destinationConfig: { app, appName }
|
||||
} = secretSync;
|
||||
|
||||
return (
|
||||
<>
|
||||
<GenericFieldLabel label="App name">{appName}</GenericFieldLabel>
|
||||
<GenericFieldLabel label="App Id">{app}</GenericFieldLabel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+4
@@ -21,6 +21,7 @@ import { DatabricksSyncDestinationSection } from "./DatabricksSyncDestinationSec
|
||||
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 { TeamCitySyncDestinationSection } from "./TeamCitySyncDestinationSection";
|
||||
@@ -93,6 +94,9 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
|
||||
case SecretSync.AzureDevOps:
|
||||
DestinationComponents = <AzureDevOpsSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
case SecretSync.Heroku:
|
||||
DestinationComponents = <HerokuSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Section components: ${destination}`);
|
||||
}
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
case SecretSync.TeamCity:
|
||||
case SecretSync.OCIVault:
|
||||
case SecretSync.OnePass:
|
||||
case SecretSync.Heroku:
|
||||
AdditionalSyncOptionsComponent = null;
|
||||
break;
|
||||
default:
|
||||
|
||||
+74
-18
@@ -3,43 +3,99 @@ import { useNavigate, useSearch } from "@tanstack/react-router";
|
||||
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useAuthorizeIntegration } from "@app/hooks/api";
|
||||
import { useCreateAppConnection, useUpdateAppConnection } from "@app/hooks/api/appConnections";
|
||||
import { HerokuConnectionMethod } from "@app/hooks/api/appConnections/types/heroku-connection";
|
||||
|
||||
export const HerokuOAuthCallbackPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const { mutateAsync } = useAuthorizeIntegration();
|
||||
const { mutateAsync: createAppConnection } = useCreateAppConnection();
|
||||
const { mutateAsync: updateAppConnection } = useUpdateAppConnection();
|
||||
|
||||
const { code, state } = useSearch({
|
||||
from: ROUTE_PATHS.SecretManager.Integratons.HerokuOauthCallbackPage.id
|
||||
});
|
||||
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
// validate state
|
||||
if (state !== localStorage.getItem("latestCSRFToken")) return;
|
||||
localStorage.removeItem("latestCSRFToken");
|
||||
const integrationAuth = await mutateAsync({
|
||||
workspaceId: currentWorkspace.id,
|
||||
code: code as string,
|
||||
integration: "heroku"
|
||||
});
|
||||
// Validate CSRF state token
|
||||
const storedState = localStorage.getItem("latestCSRFToken");
|
||||
if (state !== storedState) {
|
||||
console.error("CSRF token mismatch");
|
||||
navigate({
|
||||
to: "/organization/app-connections",
|
||||
search: { error: "invalid_state" }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up CSRF token
|
||||
localStorage.removeItem("latestCSRFToken");
|
||||
|
||||
// Retrieve stored form data
|
||||
const storedFormData = localStorage.getItem("herokuConnectionFormData");
|
||||
if (!storedFormData) {
|
||||
console.error("No stored form data found");
|
||||
navigate({
|
||||
to: "/organization/app-connections",
|
||||
search: { error: "missing_form_data" }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = JSON.parse(storedFormData);
|
||||
localStorage.removeItem("herokuConnectionFormData");
|
||||
|
||||
// Prepare app connection data with OAuth credentials
|
||||
const connectionData = {
|
||||
...formData,
|
||||
method: HerokuConnectionMethod.OAuth,
|
||||
credentials: {
|
||||
code: code as string
|
||||
}
|
||||
};
|
||||
|
||||
let appConnection;
|
||||
|
||||
// Create or update app connection
|
||||
if (formData.isUpdate && formData.connectionId) {
|
||||
appConnection = await updateAppConnection({
|
||||
connectionId: formData.connectionId,
|
||||
...connectionData
|
||||
});
|
||||
} else {
|
||||
appConnection = await createAppConnection({
|
||||
workspaceId: currentWorkspace.id,
|
||||
...connectionData
|
||||
});
|
||||
}
|
||||
|
||||
// Navigate to success page or app connections list
|
||||
navigate({
|
||||
to: "/secret-manager/$projectId/integrations/heroku/create",
|
||||
params: {
|
||||
projectId: currentWorkspace.id
|
||||
},
|
||||
to: "/organization/app-connections",
|
||||
search: {
|
||||
integrationAuthId: integrationAuth.id
|
||||
success: formData.isUpdate ? "connection_updated" : "connection_created",
|
||||
connectionId: appConnection.id
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error("Error handling Heroku OAuth callback:", err);
|
||||
navigate({
|
||||
to: "/organization/app-connections",
|
||||
search: { error: "connection_failed" }
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
}, [code, state, navigate, createAppConnection, updateAppConnection, currentWorkspace.id]);
|
||||
|
||||
return <div />;
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-b-2 border-primary" />
|
||||
<p className="text-gray-600">Connecting to Heroku...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user