feat(secret-sync): Add Heroku Secret Sync

This commit is contained in:
carlosmonastyrski
2025-06-17 10:52:55 -03:00
parent 46f9927cf1
commit c2612f242c
86 changed files with 1772 additions and 52 deletions
@@ -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>
);
};