mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 14:27:30 +00:00
feat: added support for version limit update
This commit is contained in:
@@ -334,6 +334,44 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PUT",
|
||||||
|
url: "/:workspaceSlug/version-limit",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
workspaceSlug: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
pitVersionLimit: z.number().min(1).max(100)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string(),
|
||||||
|
workspace: ProjectsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const workspace = await server.services.project.updateVersionLimit({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
pitVersionLimit: req.body.pitVersionLimit,
|
||||||
|
workspaceSlug: req.params.workspaceSlug
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: "Successfully changed workspace version limit",
|
||||||
|
workspace
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:workspaceId/integrations",
|
url: "/:workspaceId/integrations",
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import {
|
|||||||
TToggleProjectAutoCapitalizationDTO,
|
TToggleProjectAutoCapitalizationDTO,
|
||||||
TUpdateProjectDTO,
|
TUpdateProjectDTO,
|
||||||
TUpdateProjectNameDTO,
|
TUpdateProjectNameDTO,
|
||||||
|
TUpdateProjectVersionLimitDTO,
|
||||||
TUpgradeProjectDTO
|
TUpgradeProjectDTO
|
||||||
} from "./project-types";
|
} from "./project-types";
|
||||||
|
|
||||||
@@ -407,6 +408,35 @@ export const projectServiceFactory = ({
|
|||||||
return updatedProject;
|
return updatedProject;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateVersionLimit = async ({
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
pitVersionLimit,
|
||||||
|
workspaceSlug
|
||||||
|
}: TUpdateProjectVersionLimitDTO) => {
|
||||||
|
const project = await projectDAL.findProjectBySlug(workspaceSlug, actorOrgId);
|
||||||
|
if (!project) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Project not found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { hasRole } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
project.id,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasRole(ProjectMembershipRole.Admin))
|
||||||
|
throw new BadRequestError({ message: "Only admins are allowed to take this action" });
|
||||||
|
|
||||||
|
return projectDAL.updateById(project.id, { pitVersionLimit });
|
||||||
|
};
|
||||||
|
|
||||||
const updateName = async ({
|
const updateName = async ({
|
||||||
projectId,
|
projectId,
|
||||||
actor,
|
actor,
|
||||||
@@ -502,6 +532,7 @@ export const projectServiceFactory = ({
|
|||||||
getAProject,
|
getAProject,
|
||||||
toggleAutoCapitalization,
|
toggleAutoCapitalization,
|
||||||
updateName,
|
updateName,
|
||||||
upgradeProject
|
upgradeProject,
|
||||||
|
updateVersionLimit
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ export type TToggleProjectAutoCapitalizationDTO = {
|
|||||||
autoCapitalization: boolean;
|
autoCapitalization: boolean;
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
|
export type TUpdateProjectVersionLimitDTO = {
|
||||||
|
pitVersionLimit: number;
|
||||||
|
workspaceSlug: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TUpdateProjectNameDTO = {
|
export type TUpdateProjectNameDTO = {
|
||||||
name: string;
|
name: string;
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
TUpdateWorkspaceIdentityRoleDTO,
|
TUpdateWorkspaceIdentityRoleDTO,
|
||||||
TUpdateWorkspaceUserRoleDTO,
|
TUpdateWorkspaceUserRoleDTO,
|
||||||
UpdateEnvironmentDTO,
|
UpdateEnvironmentDTO,
|
||||||
|
UpdatePitVersionLimitDTO,
|
||||||
Workspace
|
Workspace
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
@@ -249,6 +250,21 @@ export const useToggleAutoCapitalization = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUpdateWorkspaceVersionLimit = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<{}, {}, UpdatePitVersionLimitDTO>({
|
||||||
|
mutationFn: ({ projectSlug, pitVersionLimit }) => {
|
||||||
|
return apiRequest.put(`/api/v1/workspace/${projectSlug}/version-limit`, {
|
||||||
|
pitVersionLimit
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useDeleteWorkspace = () => {
|
export const useDeleteWorkspace = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type Workspace = {
|
|||||||
upgradeStatus: string | null;
|
upgradeStatus: string | null;
|
||||||
autoCapitalization: boolean;
|
autoCapitalization: boolean;
|
||||||
environments: WorkspaceEnv[];
|
environments: WorkspaceEnv[];
|
||||||
|
pitVersionLimit: number;
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ export type CreateWorkspaceDTO = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type RenameWorkspaceDTO = { workspaceID: string; newWorkspaceName: string };
|
export type RenameWorkspaceDTO = { workspaceID: string; newWorkspaceName: string };
|
||||||
|
export type UpdatePitVersionLimitDTO = { projectSlug: string; pitVersionLimit: number };
|
||||||
export type ToggleAutoCapitalizationDTO = { workspaceID: string; state: boolean };
|
export type ToggleAutoCapitalizationDTO = { workspaceID: string; state: boolean };
|
||||||
|
|
||||||
export type DeleteWorkspaceDTO = { workspaceID: string };
|
export type DeleteWorkspaceDTO = { workspaceID: string };
|
||||||
|
|||||||
+88
@@ -0,0 +1,88 @@
|
|||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { Button, FormControl, Input } from "@app/components/v2";
|
||||||
|
import { useProjectPermission, useWorkspace } from "@app/context";
|
||||||
|
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||||
|
import { useUpdateWorkspaceVersionLimit } from "@app/hooks/api/workspace/queries";
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
pitVersionLimit: z.coerce.number().min(1).max(100)
|
||||||
|
});
|
||||||
|
|
||||||
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
export const PointInTimeVersionLimitSection = () => {
|
||||||
|
const { mutateAsync: updatePitVersion } = useUpdateWorkspaceVersionLimit();
|
||||||
|
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { membership } = useProjectPermission();
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
formState: { isSubmitting },
|
||||||
|
handleSubmit
|
||||||
|
} = useForm<TForm>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
values: {
|
||||||
|
pitVersionLimit: currentWorkspace?.pitVersionLimit || 10
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!currentWorkspace) return null;
|
||||||
|
|
||||||
|
const handleVersionLimitSubmit = async ({ pitVersionLimit }: TForm) => {
|
||||||
|
try {
|
||||||
|
await updatePitVersion({
|
||||||
|
pitVersionLimit,
|
||||||
|
projectSlug: currentWorkspace.slug
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully updated version limit",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
createNotification({
|
||||||
|
text: "Failed updating project's version limit",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAdmin = membership.roles.includes(ProjectMembershipRole.Admin);
|
||||||
|
return (
|
||||||
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<div className="flex w-full items-center justify-between">
|
||||||
|
<p className="text-xl font-semibold">Point in Time Recovery</p>
|
||||||
|
</div>
|
||||||
|
<p className="mb-4 mt-2 max-w-2xl text-sm text-gray-400">
|
||||||
|
This defines the maximum number of folder snapshots, secret versions, and folder versions
|
||||||
|
that are retained by the system
|
||||||
|
</p>
|
||||||
|
<form onSubmit={handleSubmit(handleVersionLimitSubmit)} autoComplete="off">
|
||||||
|
<div className="max-w-xs">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue={0}
|
||||||
|
name="pitVersionLimit"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
label="Version Limit"
|
||||||
|
>
|
||||||
|
<Input {...field} type="number" min={1} step={1} isDisabled={!isAdmin} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button colorSchema="secondary" type="submit" isLoading={isSubmitting} disabled={!isAdmin}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export { PointInTimeVersionLimitSection } from "./PointInTimeVersionLimitSection";
|
||||||
+2
@@ -3,6 +3,7 @@ import { BackfillSecretReferenceSecretion } from "../BackfillSecretReferenceSect
|
|||||||
import { DeleteProjectSection } from "../DeleteProjectSection";
|
import { DeleteProjectSection } from "../DeleteProjectSection";
|
||||||
import { E2EESection } from "../E2EESection";
|
import { E2EESection } from "../E2EESection";
|
||||||
import { EnvironmentSection } from "../EnvironmentSection";
|
import { EnvironmentSection } from "../EnvironmentSection";
|
||||||
|
import { PointInTimeVersionLimitSection } from "../PointInTimeVersionLimitSection";
|
||||||
import { ProjectNameChangeSection } from "../ProjectNameChangeSection";
|
import { ProjectNameChangeSection } from "../ProjectNameChangeSection";
|
||||||
import { SecretTagsSection } from "../SecretTagsSection";
|
import { SecretTagsSection } from "../SecretTagsSection";
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ export const ProjectGeneralTab = () => {
|
|||||||
<SecretTagsSection />
|
<SecretTagsSection />
|
||||||
<AutoCapitalizationSection />
|
<AutoCapitalizationSection />
|
||||||
<E2EESection />
|
<E2EESection />
|
||||||
|
<PointInTimeVersionLimitSection />
|
||||||
<BackfillSecretReferenceSecretion />
|
<BackfillSecretReferenceSecretion />
|
||||||
<DeleteProjectSection />
|
<DeleteProjectSection />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user