Merge pull request #4374 from Infisical/ENG-3518

Disable environments edit when a project has more environment than the allowed by the org plan
This commit is contained in:
carlosmonastyrski
2025-08-14 12:24:27 -07:00
committed by GitHub
2 changed files with 49 additions and 12 deletions
@@ -177,6 +177,18 @@ export const projectEnvServiceFactory = ({
} }
} }
const envs = await projectEnvDAL.find({ projectId });
const project = await projectDAL.findById(projectId);
const plan = await licenseService.getPlan(project.orgId);
if (plan.environmentLimit !== null && envs.length > plan.environmentLimit) {
// case: limit imposed on number of environments allowed
// case: number of environments used exceeds the number of environments allowed
throw new BadRequestError({
message:
"Failed to update environment due to environment limit exceeded. To update an environment, please upgrade your plan or remove unused environments."
});
}
const env = await projectEnvDAL.transaction(async (tx) => { const env = await projectEnvDAL.transaction(async (tx) => {
if (position) { if (position) {
const existingEnvWithPosition = await projectEnvDAL.findOne({ projectId, position }, tx); const existingEnvWithPosition = await projectEnvDAL.findOne({ projectId, position }, tx);
@@ -12,9 +12,15 @@ import {
Td, Td,
Th, Th,
THead, THead,
Tooltip,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import {
ProjectPermissionActions,
ProjectPermissionSub,
useSubscription,
useWorkspace
} from "@app/context";
import { useUpdateWsEnvironment } from "@app/hooks/api"; import { useUpdateWsEnvironment } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -35,6 +41,7 @@ type Props = {
export const EnvironmentTable = ({ handlePopUpOpen }: Props) => { export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const { subscription } = useSubscription();
const updateEnvironment = useUpdateWsEnvironment(); const updateEnvironment = useUpdateWsEnvironment();
@@ -61,6 +68,16 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
} }
}; };
const isMoreEnvironmentsAllowed =
subscription?.environmentLimit && currentWorkspace?.environments
? currentWorkspace.environments.length <= subscription.environmentLimit
: true;
const environmentsOverPlanLimit =
subscription?.environmentLimit && currentWorkspace?.environments
? Math.max(0, currentWorkspace.environments.length - subscription.environmentLimit)
: 0;
return ( return (
<TableContainer> <TableContainer>
<Table> <Table>
@@ -122,18 +139,26 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
a={ProjectPermissionSub.Environments} a={ProjectPermissionSub.Environments}
> >
{(isAllowed) => ( {(isAllowed) => (
<Tooltip
content={
isMoreEnvironmentsAllowed
? ""
: `You have exceeded the number of environments allowed by your plan. To edit an existing environment, either upgrade your plan or remove at least ${environmentsOverPlanLimit} environment${environmentsOverPlanLimit === 1 ? "" : "s"}.`
}
>
<IconButton <IconButton
className="mr-3 py-2" className="mr-3 py-2"
onClick={() => { onClick={() => {
handlePopUpOpen("updateEnv", { name, slug, id }); handlePopUpOpen("updateEnv", { name, slug, id });
}} }}
isDisabled={!isAllowed} isDisabled={!isAllowed || !isMoreEnvironmentsAllowed}
colorSchema="primary" colorSchema="primary"
variant="plain" variant="plain"
ariaLabel="update" ariaLabel="update"
> >
<FontAwesomeIcon icon={faPencil} /> <FontAwesomeIcon icon={faPencil} />
</IconButton> </IconButton>
</Tooltip>
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>
<ProjectPermissionCan <ProjectPermissionCan