diff --git a/backend/src/ee/services/license/license-fns.ts b/backend/src/ee/services/license/license-fns.ts index b7ae6f7ee..8ef91c6f8 100644 --- a/backend/src/ee/services/license/license-fns.ts +++ b/backend/src/ee/services/license/license-fns.ts @@ -2,6 +2,7 @@ import axios, { AxiosError } from "axios"; import { getConfig } from "@app/lib/config/env"; import { request } from "@app/lib/config/request"; +import { logger } from "@app/lib/logger"; import { TFeatureSet } from "./license-types"; @@ -98,9 +99,10 @@ export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string (response) => response, async (err) => { const originalRequest = (err as AxiosError).config; - + const errStatusCode = Number((err as AxiosError)?.response?.status); + logger.error((err as AxiosError)?.response?.data, "License server call error"); // eslint-disable-next-line - if ((err as AxiosError)?.response?.status === 401 && !(originalRequest as any)._retry) { + if ((errStatusCode === 401 || errStatusCode === 403) && !(originalRequest as any)._retry) { // eslint-disable-next-line (originalRequest as any)._retry = true; // injected diff --git a/backend/src/ee/services/license/license-service.ts b/backend/src/ee/services/license/license-service.ts index cf9818658..f5b1f96ec 100644 --- a/backend/src/ee/services/license/license-service.ts +++ b/backend/src/ee/services/license/license-service.ts @@ -348,8 +348,8 @@ export const licenseServiceFactory = ({ } = await licenseServerCloudApi.request.post( `/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods`, { - success_url: `${appCfg.SITE_URL}/dashboard`, - cancel_url: `${appCfg.SITE_URL}/dashboard` + success_url: `${appCfg.SITE_URL}/organization/billing`, + cancel_url: `${appCfg.SITE_URL}/organization/billing` } ); @@ -362,7 +362,7 @@ export const licenseServiceFactory = ({ } = await licenseServerCloudApi.request.post( `/api/license-server/v1/customers/${organization.customerId}/billing-details/billing-portal`, { - return_url: `${appCfg.SITE_URL}/dashboard` + return_url: `${appCfg.SITE_URL}/organization/billing` } ); @@ -379,7 +379,7 @@ export const licenseServiceFactory = ({ message: `Organization with ID '${orgId}' not found` }); } - if (instanceType !== InstanceType.OnPrem && instanceType !== InstanceType.EnterpriseOnPremOffline) { + if (instanceType === InstanceType.Cloud) { const { data } = await licenseServerCloudApi.request.get( `/api/license-server/v1/customers/${organization.customerId}/cloud-plan/billing` ); @@ -407,11 +407,38 @@ export const licenseServiceFactory = ({ message: `Organization with ID '${orgId}' not found` }); } - if (instanceType !== InstanceType.OnPrem && instanceType !== InstanceType.EnterpriseOnPremOffline) { - const { data } = await licenseServerCloudApi.request.get( - `/api/license-server/v1/customers/${organization.customerId}/cloud-plan/table` - ); - return data; + + const orgMembersUsed = await orgDAL.countAllOrgMembers(orgId); + const identityUsed = await identityOrgMembershipDAL.countAllOrgIdentities({ orgId }); + const projects = await projectDAL.find({ orgId }); + const projectCount = projects.length; + + if (instanceType === InstanceType.Cloud) { + const { data } = await licenseServerCloudApi.request.get<{ + head: { name: string }[]; + rows: { name: string; allowed: boolean }[]; + }>(`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/table`); + + const formattedData = { + head: data.head, + rows: data.rows.map((el) => { + let used = "-"; + + if (el.name === BillingPlanRows.MemberLimit.name) { + used = orgMembersUsed.toString(); + } else if (el.name === BillingPlanRows.WorkspaceLimit.name) { + used = projectCount.toString(); + } else if (el.name === BillingPlanRows.IdentityLimit.name) { + used = (identityUsed + orgMembersUsed).toString(); + } + + return { + ...el, + used + }; + }) + }; + return formattedData; } const mappedRows = await Promise.all( @@ -420,14 +447,11 @@ export const licenseServiceFactory = ({ let used = "-"; if (field === BillingPlanRows.MemberLimit.field) { - const orgMemberships = await orgDAL.countAllOrgMembers(orgId); - used = orgMemberships.toString(); + used = orgMembersUsed.toString(); } else if (field === BillingPlanRows.WorkspaceLimit.field) { - const projects = await projectDAL.find({ orgId }); - used = projects.length.toString(); + used = projectCount.toString(); } else if (field === BillingPlanRows.IdentityLimit.field) { - const identities = await identityOrgMembershipDAL.countAllOrgIdentities({ orgId }); - used = identities.toString(); + used = identityUsed.toString(); } return { diff --git a/backend/src/services/org/org-dal.ts b/backend/src/services/org/org-dal.ts index 54b0e1b0f..32cabf444 100644 --- a/backend/src/services/org/org-dal.ts +++ b/backend/src/services/org/org-dal.ts @@ -206,7 +206,7 @@ export const orgDALFactory = (db: TDbClient) => { .where(`${TableName.OrgMembership}.orgId`, orgId) .count("*") .join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) - .where({ isGhost: false }) + .where({ isGhost: false, [`${TableName.OrgMembership}.isActive` as "isActive"]: true }) .first(); return parseInt((count as unknown as CountResult).count || "0", 10); diff --git a/frontend/src/pages/organization/BillingPage/components/BillingCloudTab/CurrentPlanSection.tsx b/frontend/src/pages/organization/BillingPage/components/BillingCloudTab/CurrentPlanSection.tsx index 1b7e84f2c..5c85f19f7 100644 --- a/frontend/src/pages/organization/BillingPage/components/BillingCloudTab/CurrentPlanSection.tsx +++ b/frontend/src/pages/organization/BillingPage/components/BillingCloudTab/CurrentPlanSection.tsx @@ -1,4 +1,9 @@ -import { faCircleCheck, faCircleXmark, faFileInvoice } from "@fortawesome/free-solid-svg-icons"; +import { + faCircleCheck, + faCircleXmark, + faFileInvoice, + faInfoCircle +} from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { @@ -10,6 +15,7 @@ import { Td, Th, THead, + Tooltip, Tr } from "@app/components/v2"; import { useOrganization } from "@app/context"; @@ -48,9 +54,26 @@ export const CurrentPlanSection = () => { data && data?.rows?.length > 0 && data.rows.map(({ name, allowed, used }) => { + let toolTipText = null; + if (name === "Organization identity limit") { + toolTipText = + "Identity count is calculated by the total number of user identities and machine identities."; + } + return ( - {name} + + {name} + {toolTipText && ( + + + + )} + {displayCell(allowed)} {used}