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;
+};