mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-21 07:27:58 +00:00
199 lines
8.0 KiB
TypeScript
199 lines
8.0 KiB
TypeScript
import { useEffect } from "react";
|
|
import { Controller, useForm } from "react-hook-form";
|
|
import { faTriangleExclamation, faWarning } from "@fortawesome/free-solid-svg-icons";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { useQueryClient } from "@tanstack/react-query";
|
|
import { z } from "zod";
|
|
|
|
import { createNotification } from "@app/components/notifications";
|
|
import { Button, Checkbox, Modal, ModalContent, Spinner } from "@app/components/v2";
|
|
import { useProject, useProjectPermission } from "@app/context";
|
|
import { usePopUp } from "@app/hooks";
|
|
import { projectKeys, useGetWorkspaceById, useMigrateProjectToV3 } from "@app/hooks/api";
|
|
import { ProjectVersion } from "@app/hooks/api/projects/types";
|
|
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
|
|
|
enum ProjectUpgradeStatus {
|
|
InProgress = "IN_PROGRESS",
|
|
// Completed -> Will be null if completed. So a completed status is not needed
|
|
Failed = "FAILED"
|
|
}
|
|
|
|
const formSchema = z.object({
|
|
isCLIChecked: z.literal(true),
|
|
isOperatorChecked: z.literal(true),
|
|
shouldCloseOpenApprovals: z.literal(true)
|
|
});
|
|
|
|
export const SecretV2MigrationSection = () => {
|
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["migrationInfo"] as const);
|
|
const { currentProject } = useProject();
|
|
const queryClient = useQueryClient();
|
|
const { data: workspaceDetails, refetch } = useGetWorkspaceById(
|
|
// if v3 no need to fetch
|
|
currentProject?.version === ProjectVersion.V3 ? "" : currentProject?.id || "",
|
|
{
|
|
refetchInterval:
|
|
currentProject?.upgradeStatus === ProjectUpgradeStatus.InProgress ? 2000 : false
|
|
}
|
|
);
|
|
const { hasProjectRole } = useProjectPermission();
|
|
const migrateProjectToV3 = useMigrateProjectToV3();
|
|
const { handleSubmit, control, reset } = useForm({ resolver: zodResolver(formSchema) });
|
|
useEffect(() => {
|
|
if (!popUp.migrationInfo.isOpen) {
|
|
reset();
|
|
}
|
|
}, [popUp.migrationInfo.isOpen]);
|
|
|
|
const isProjectUpgraded = workspaceDetails?.version === ProjectVersion.V3;
|
|
|
|
useEffect(() => {
|
|
if (isProjectUpgraded && migrateProjectToV3.data) {
|
|
createNotification({ type: "success", text: "Project upgrade completed successfully" });
|
|
migrateProjectToV3.reset();
|
|
queryClient.invalidateQueries({
|
|
queryKey: projectKeys.getAllUserProjects()
|
|
});
|
|
}
|
|
}, [isProjectUpgraded, Boolean(migrateProjectToV3.data)]);
|
|
|
|
if (isProjectUpgraded || currentProject?.version === ProjectVersion.V3) return null;
|
|
|
|
const isUpgrading = workspaceDetails?.upgradeStatus === ProjectUpgradeStatus.InProgress;
|
|
const didProjectUpgradeFailed = workspaceDetails?.upgradeStatus === ProjectUpgradeStatus.Failed;
|
|
|
|
const handleMigrationSecretV2 = async () => {
|
|
try {
|
|
handlePopUpToggle("migrationInfo");
|
|
await migrateProjectToV3.mutateAsync({ projectId: currentProject?.id || "" });
|
|
refetch();
|
|
createNotification({
|
|
text: "Project upgrade started",
|
|
type: "success"
|
|
});
|
|
} catch {
|
|
createNotification({
|
|
text: "Failed to upgrade project",
|
|
type: "error"
|
|
});
|
|
}
|
|
};
|
|
|
|
const isAdmin = hasProjectRole(ProjectMembershipRole.Admin);
|
|
return (
|
|
<div className="mt-4 flex max-w-2xl flex-col rounded-lg border border-primary/50 bg-primary/10 px-6 py-5">
|
|
{isUpgrading && (
|
|
<div className="bg-opacity-80 absolute top-0 left-0 z-50 flex h-screen w-screen items-center justify-center bg-bunker-500">
|
|
<Spinner size="lg" className="text-primary" />
|
|
<div className="ml-4 flex flex-col space-y-1">
|
|
<div className="text-3xl font-medium">Please wait</div>
|
|
<span className="inline-block">Upgrading secrets engine...</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<div className="mb-4 flex items-start gap-2">
|
|
<FontAwesomeIcon icon={faWarning} size="xl" className="mt-1 text-primary" />
|
|
<p className="text-xl font-medium">Upgrade secrets engine version</p>
|
|
</div>
|
|
<p className="mx-1 mb-4 leading-7 text-mineshaft-100">
|
|
Your existing workflows to fetch secrets will continue to work. However, viewing secrets on
|
|
the UI requires you to upgrade your project's secrets engine version.
|
|
</p>
|
|
<p className="mx-1 mb-4 leading-7 text-mineshaft-100">
|
|
Upgrading is free and enables the use of Infisical's new secrets engine, which is 10x
|
|
faster and allows you to encrypt secrets with your own KMS provider.
|
|
</p>
|
|
<p className="mx-1 mb-6 leading-7 text-mineshaft-100">
|
|
The upgrade takes only 1-2 minutes and will not cause any downtime.
|
|
</p>
|
|
<Button
|
|
onClick={() => handlePopUpOpen("migrationInfo")}
|
|
isDisabled={!isAdmin || isUpgrading}
|
|
isLoading={migrateProjectToV3.isPending}
|
|
className="w-full"
|
|
>
|
|
{isAdmin ? "Upgrade Secrets Engine" : "Upgrade requires admin privilege"}
|
|
</Button>
|
|
{didProjectUpgradeFailed && (
|
|
<p className="mt-2 text-sm leading-7 text-red-400">
|
|
<FontAwesomeIcon icon={faTriangleExclamation} className="mr-2" />
|
|
Secrets engine upgrade unsuccessful. For assistance, please contact the Infisical support
|
|
team.
|
|
</p>
|
|
)}
|
|
<Modal
|
|
isOpen={popUp.migrationInfo.isOpen}
|
|
onOpenChange={(isOpen) => handlePopUpToggle("migrationInfo", isOpen)}
|
|
>
|
|
<ModalContent
|
|
title="Upgrade Checklist"
|
|
subTitle="To ensure smooth transition, please ensure the following requirements are met before upgrading this project."
|
|
>
|
|
<div>
|
|
<form onSubmit={handleSubmit(handleMigrationSecretV2)}>
|
|
<div className="flex flex-col space-y-4">
|
|
<Controller
|
|
control={control}
|
|
name="isCLIChecked"
|
|
defaultValue={false}
|
|
render={({ field: { onBlur, value, onChange }, fieldState: { error } }) => (
|
|
<Checkbox
|
|
id="is-cli-checked"
|
|
isChecked={value}
|
|
onCheckedChange={onChange}
|
|
onBlur={onBlur}
|
|
isError={Boolean(error?.message)}
|
|
>
|
|
Infisical CLI version is v0.25.0 or above.
|
|
</Checkbox>
|
|
)}
|
|
/>
|
|
<Controller
|
|
control={control}
|
|
name="isOperatorChecked"
|
|
defaultValue={false}
|
|
render={({ field: { onBlur, value, onChange }, fieldState: { error } }) => (
|
|
<Checkbox
|
|
id="is-operator-checked"
|
|
isChecked={value}
|
|
onCheckedChange={onChange}
|
|
onBlur={onBlur}
|
|
isError={Boolean(error?.message)}
|
|
>
|
|
Infisical Kubernetes Operator version is v0.7.0 or above.
|
|
</Checkbox>
|
|
)}
|
|
/>
|
|
<Controller
|
|
control={control}
|
|
name="shouldCloseOpenApprovals"
|
|
defaultValue={false}
|
|
render={({ field: { onBlur, value, onChange }, fieldState: { error } }) => (
|
|
<Checkbox
|
|
id="is-approvals-checked"
|
|
isChecked={value}
|
|
onCheckedChange={onChange}
|
|
onBlur={onBlur}
|
|
isError={Boolean(error?.message)}
|
|
>
|
|
Close/merge all open approval/access requests.
|
|
</Checkbox>
|
|
)}
|
|
/>
|
|
</div>
|
|
<div className="mt-8 flex space-x-4">
|
|
<Button type="submit">Confirm Upgrade</Button>
|
|
<Button variant="outline_bg" onClick={() => handlePopUpToggle("migrationInfo")}>
|
|
Cancel
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</ModalContent>
|
|
</Modal>
|
|
</div>
|
|
);
|
|
};
|