From 222f572d63668fa0ee24eea5dd4efca2da37a1e7 Mon Sep 17 00:00:00 2001 From: pandeymangg Date: Thu, 20 Jun 2024 16:49:29 +0530 Subject: [PATCH] fix: ee --- .../components/EnvironmentLayout.tsx | 14 ++- .../(organization)/enterprise/page.tsx | 8 +- packages/ee/lib/service.ts | 108 +++++++++--------- packages/ui/PendingDowngradeBanner/index.tsx | 45 ++++++++ 4 files changed, 108 insertions(+), 67 deletions(-) create mode 100644 packages/ui/PendingDowngradeBanner/index.tsx diff --git a/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx b/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx index 0813457b16..e2c56efbad 100644 --- a/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx @@ -1,7 +1,7 @@ import { MainNavigation } from "@/app/(app)/environments/[environmentId]/components/MainNavigation"; import { TopControlBar } from "@/app/(app)/environments/[environmentId]/components/TopControlBar"; import type { Session } from "next-auth"; -import { getEnterpriseLicense, getIsMultiOrgEnabled } from "@formbricks/ee/lib/service"; +import { getEnterpriseLicense } from "@formbricks/ee/lib/service"; import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants"; import { getEnvironment, getEnvironments } from "@formbricks/lib/environment/service"; import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service"; @@ -15,6 +15,7 @@ import { getProducts } from "@formbricks/lib/product/service"; import { DevEnvironmentBanner } from "@formbricks/ui/DevEnvironmentBanner"; import { ErrorComponent } from "@formbricks/ui/ErrorComponent"; import { LimitsReachedBanner } from "@formbricks/ui/LimitsReachedBanner"; +import { PendingDowngradeBanner } from "@formbricks/ui/PendingDowngradeBanner"; interface EnvironmentLayoutProps { environmentId: string; @@ -43,10 +44,7 @@ export const EnvironmentLayout = async ({ environmentId, session, children }: En } const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id); - // const isMultiOrgEnabled = await getIsMultiOrgEnabled(); - // const isMultiOrgEnabled = true; - const { features, message: licenseErrorMessage } = await getEnterpriseLicense(); - console.log({ features, licenseErrorMessage }); + const { features, lastChecked, isPendingDowngrade, active } = await getEnterpriseLicense(); const isMultiOrgEnabled = features?.isMultiOrgEnabled ?? false; @@ -70,7 +68,11 @@ export const EnvironmentLayout = async ({ environmentId, session, children }: En /> )} - {licenseErrorMessage &&
{licenseErrorMessage}
} +
{ notFound(); } - const isEnterpriseEdition = await getIsEnterpriseEdition(); + const { active: isEnterpriseEdition } = await getEnterpriseLicense(); const paidFeatures = [ { @@ -166,7 +166,7 @@ const Page = async ({ params }) => { ))} -

+

No call needed, no strings attached: Request a free 30-day trial license to test all features by filling out this form:

@@ -176,7 +176,7 @@ const Page = async ({ params }) => { target="_blank"> Request 30-day Trial License -

No credit card. No sales call. Just test it :)

+

No credit card. No sales call. Just test it :)

diff --git a/packages/ee/lib/service.ts b/packages/ee/lib/service.ts index 8043ea8248..73bd8feadd 100644 --- a/packages/ee/lib/service.ts +++ b/packages/ee/lib/service.ts @@ -24,7 +24,6 @@ const getPreviousResult = (): Promise<{ active: boolean | null; lastChecked: Date; features: TEnterpriseLicenseFeatures | null; - message?: string; }> => cache( async () => ({ @@ -44,17 +43,15 @@ const setPreviousResult = async (previousResult: { active: boolean | null; lastChecked: Date; features: TEnterpriseLicenseFeatures | null; - message?: string; }) => { revalidateTag(PREVIOUS_RESULTS_CACHE_TAG_KEY); - const { lastChecked, active, features, message } = previousResult; + const { lastChecked, active, features } = previousResult; await cache( async () => ({ active, lastChecked, features, - message, }), [PREVIOUS_RESULTS_CACHE_TAG_KEY], { @@ -92,70 +89,86 @@ const fetchLicenseForE2ETesting = async (): Promise<{ } }; -export const getIsEnterpriseEdition = async (): Promise => { +export const getEnterpriseLicense = async (): Promise<{ + active: boolean; + features: TEnterpriseLicenseFeatures | null; + lastChecked: Date; + isPendingDowngrade?: boolean; +}> => { if (!ENTERPRISE_LICENSE_KEY || ENTERPRISE_LICENSE_KEY.length === 0) { - return false; + return { + active: false, + features: null, + lastChecked: new Date(), + }; } if (E2E_TESTING) { const previousResult = await fetchLicenseForE2ETesting(); - return previousResult && previousResult.active !== null ? previousResult.active : false; + // return previousResult && previousResult.active !== null ? previousResult.active : false; + return { + active: previousResult ? previousResult.active : false, + features: previousResult ? previousResult.features : null, + lastChecked: previousResult ? previousResult.lastChecked : new Date(), + }; } // if the server responds with a boolean, we return it // if the server errors, we return null // null signifies an error const license = await fetchLicense(); - console.log({ license }); + const isValid = license ? license.status === "active" : null; const threeDaysInMillis = 3 * 24 * 60 * 60 * 1000; const currentTime = new Date(); - let message = ""; const previousResult = await getPreviousResult(); - console.log({ previousResult }); + if (previousResult.active === null) { if (isValid === null) { - await setPreviousResult({ + const newResult = { active: false, features: { isMultiOrgEnabled: false }, lastChecked: new Date(), - }); - return false; + }; + + await setPreviousResult(newResult); + return newResult; } } if (isValid !== null && license) { - await setPreviousResult({ active: isValid, features: license.features, lastChecked: new Date() }); - return isValid; + const newResult = { + active: isValid, + features: license.features, + lastChecked: new Date(), + }; + + await setPreviousResult(newResult); + return newResult; } else { // if result is undefined -> error // if the last check was less than 72 hours, return the previous value: const elapsedTime = currentTime.getTime() - previousResult.lastChecked.getTime(); - if (elapsedTime <= threeDaysInMillis) { - message = `We were unable to verify your license because the license server is unreachable. You will be downgraded to the Community Edition on ${new Date(previousResult.lastChecked.getTime() + threeDaysInMillis).toISOString()}.`; - } else { - message = - "We have been unable to verify your license because the license server has been unreachable for more than 3 days in a row. You have been downgraded to Community Edition."; - } - - const newActive = elapsedTime <= threeDaysInMillis ? previousResult.active : false; - if (previousResult.active !== newActive || previousResult.message !== message) { - const newResult = { - ...previousResult, - active: newActive, - message, + if (elapsedTime < threeDaysInMillis) { + return { + active: previousResult.active !== null ? previousResult.active : false, + features: previousResult.features, + lastChecked: previousResult.lastChecked, + isPendingDowngrade: true, }; - await setPreviousResult(newResult); } // Log error only after 72 hours - if (elapsedTime > threeDaysInMillis) { - console.error("Error while checking license: The license check failed"); - } + console.error("Error while checking license: The license check failed"); - return newActive; + return { + active: false, + features: null, + lastChecked: previousResult.lastChecked, + isPendingDowngrade: true, + }; } }; @@ -170,25 +183,6 @@ export const getLicenseFeatures = async (): Promise => { - const previousResult = await getPreviousResult(); - if (previousResult) { - return previousResult; - } else { - const license = await fetchLicense(); - - return { - active: license ? license.status === "active" : false, - features: license ? license.features : undefined, - message: "", - }; - } -}; - export const fetchLicense = async (): Promise => await cache( async () => { @@ -210,7 +204,7 @@ export const fetchLicense = async (): Promise const proxyUrl = env.HTTPS_PROXY || env.HTTP_PROXY; const agent = proxyUrl ? new HttpsProxyAgent(proxyUrl) : undefined; - const res = await fetch("https://ee.formbricxks.com/api/licenses/check", { + const res = await fetch("https://ee.formbricks.com/api/licenses/check", { body: JSON.stringify({ licenseKey: ENTERPRISE_LICENSE_KEY, usage: { responseCount: responseCount }, @@ -255,7 +249,7 @@ export const getRoleManagementPermission = async (organization: TOrganization): organization.billing.plan === PRODUCT_FEATURE_KEYS.SCALE || organization.billing.plan === PRODUCT_FEATURE_KEYS.ENTERPRISE ); - else if (!IS_FORMBRICKS_CLOUD) return await getIsEnterpriseEdition(); + else if (!IS_FORMBRICKS_CLOUD) return (await getEnterpriseLicense()).active; return false; }; @@ -265,13 +259,13 @@ export const getAdvancedTargetingPermission = async (organization: TOrganization organization.billing.plan === PRODUCT_FEATURE_KEYS.SCALE || organization.billing.plan === PRODUCT_FEATURE_KEYS.ENTERPRISE ); - else if (!IS_FORMBRICKS_CLOUD) return await getIsEnterpriseEdition(); + else if (!IS_FORMBRICKS_CLOUD) return (await getEnterpriseLicense()).active; else return false; }; export const getBiggerUploadFileSizePermission = async (organization: TOrganization): Promise => { if (IS_FORMBRICKS_CLOUD) return organization.billing.plan !== PRODUCT_FEATURE_KEYS.FREE; - else if (!IS_FORMBRICKS_CLOUD) return await getIsEnterpriseEdition(); + else if (!IS_FORMBRICKS_CLOUD) return (await getEnterpriseLicense()).active; return false; }; @@ -285,7 +279,7 @@ export const getMultiLanguagePermission = async (organization: TOrganization): P organization.billing.plan === PRODUCT_FEATURE_KEYS.SCALE || organization.billing.plan === PRODUCT_FEATURE_KEYS.ENTERPRISE ); - else if (!IS_FORMBRICKS_CLOUD) return await getIsEnterpriseEdition(); + else if (!IS_FORMBRICKS_CLOUD) return (await getEnterpriseLicense()).active; return false; }; diff --git a/packages/ui/PendingDowngradeBanner/index.tsx b/packages/ui/PendingDowngradeBanner/index.tsx new file mode 100644 index 0000000000..8df73f6413 --- /dev/null +++ b/packages/ui/PendingDowngradeBanner/index.tsx @@ -0,0 +1,45 @@ +interface PendingDowngradeBannerProps { + lastChecked: Date; + active: boolean; + isPendingDowngrade: boolean; +} + +export const PendingDowngradeBanner = ({ + lastChecked, + active, + isPendingDowngrade, +}: PendingDowngradeBannerProps) => { + const threeDaysInMillis = 3 * 24 * 60 * 60 * 1000; + + const isLastCheckedWithin72Hours = lastChecked + ? new Date().getTime() - lastChecked.getTime() < threeDaysInMillis + : false; + + const scheduledDowngradeDate = new Date(lastChecked.getTime() + threeDaysInMillis); + + if (active) { + if (isPendingDowngrade && isLastCheckedWithin72Hours) { + return ( + <> +
+ We were unable to verify your license because the license server is unreachable. You will be + downgraded to the Community Edition on {scheduledDowngradeDate.toLocaleDateString()} +
+ + ); + } + } + + if (isPendingDowngrade && !isLastCheckedWithin72Hours) { + return ( + <> +
+ We were unable to verify your license because the license server is unreachable. You are downgraded + to the Community Edition. +
+ + ); + } + + return null; +};