This commit is contained in:
pandeymangg
2024-06-20 16:49:29 +05:30
parent 190911eeaa
commit 222f572d63
4 changed files with 108 additions and 67 deletions

View File

@@ -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 && <div className="bg-red-500 p-4 text-white">{licenseErrorMessage}</div>}
<PendingDowngradeBanner
lastChecked={lastChecked}
isPendingDowngrade={isPendingDowngrade ?? false}
active={active}
/>
<div className="flex h-full">
<MainNavigation

View File

@@ -2,7 +2,7 @@ import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmen
import { CheckIcon } from "lucide-react";
import { getServerSession } from "next-auth";
import { notFound } from "next/navigation";
import { getIsEnterpriseEdition } from "@formbricks/ee/lib/service";
import { getEnterpriseLicense } from "@formbricks/ee/lib/service";
import { authOptions } from "@formbricks/lib/authOptions";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
@@ -37,7 +37,7 @@ const Page = async ({ params }) => {
notFound();
}
const isEnterpriseEdition = await getIsEnterpriseEdition();
const { active: isEnterpriseEdition } = await getEnterpriseLicense();
const paidFeatures = [
{
@@ -166,7 +166,7 @@ const Page = async ({ params }) => {
</li>
))}
</ul>
<p className="my-6 text-sm text-slate-700">
<p className="my-6 text-sm text-slate-700">
No call needed, no strings attached: Request a free 30-day trial license to test all features
by filling out this form:
</p>
@@ -176,7 +176,7 @@ const Page = async ({ params }) => {
target="_blank">
Request 30-day Trial License
</Button>
<p className="mt-2 text-xs text-slate-500">No credit card. No sales call. Just test it :)</p>
<p className="mt-2 text-xs text-slate-500">No credit card. No sales call. Just test it :)</p>
</div>
</div>
</div>

View File

@@ -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<boolean> => {
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<TEnterpriseLicenseFeatures |
}
};
export const getEnterpriseLicense = async (): Promise<{
active: boolean;
features?: TEnterpriseLicenseFeatures;
message?: string;
}> => {
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<TEnterpriseLicenseDetails | null> =>
await cache(
async () => {
@@ -210,7 +204,7 @@ export const fetchLicense = async (): Promise<TEnterpriseLicenseDetails | null>
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<boolean> => {
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;
};

View File

@@ -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 (
<>
<div className="z-40 flex h-5 items-center justify-center bg-orange-800 text-center text-xs text-white">
We were unable to verify your license because the license server is unreachable. You will be
downgraded to the Community Edition on {scheduledDowngradeDate.toLocaleDateString()}
</div>
</>
);
}
}
if (isPendingDowngrade && !isLastCheckedWithin72Hours) {
return (
<>
<div className="z-40 flex h-5 items-center justify-center bg-orange-800 text-center text-xs text-white">
We were unable to verify your license because the license server is unreachable. You are downgraded
to the Community Edition.
</div>
</>
);
}
return null;
};