first commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { useSession } from "next-auth/react";
|
||||
import type { AriaRole, ComponentType } from "react";
|
||||
import React, { Fragment, useEffect } from "react";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { EmptyScreen, Alert, Button } from "@calcom/ui";
|
||||
|
||||
type LicenseRequiredProps = {
|
||||
as?: keyof JSX.IntrinsicElements | "";
|
||||
className?: string;
|
||||
role?: AriaRole | undefined;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const LicenseRequired = ({ children, as = "", ...rest }: LicenseRequiredProps) => {
|
||||
const session = useSession();
|
||||
const { t } = useLocale();
|
||||
const Component = as || Fragment;
|
||||
const hasValidLicense = session.data ? session.data.hasValidLicense : null;
|
||||
|
||||
useEffect(() => {
|
||||
if (process.env.NODE_ENV === "development" && hasValidLicense === false) {
|
||||
// Very few people will see this, so we don't need to translate it
|
||||
console.info(
|
||||
`You're using a feature that requires a valid license. Please go to ${WEBAPP_URL}/auth/setup to enter a license key.`
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Component {...rest}>
|
||||
{hasValidLicense === null || hasValidLicense ? (
|
||||
children
|
||||
) : process.env.NODE_ENV === "development" ? (
|
||||
/** We only show a warning in development mode, but allow the feature to be displayed for development/testing purposes */
|
||||
<>
|
||||
<Alert
|
||||
className="mb-4"
|
||||
severity="warning"
|
||||
title={
|
||||
<>
|
||||
{t("enterprise_license_locally")} {t("enterprise_license_sales")}{" "}
|
||||
<a className="underline" href="https://bls.media/kontakt">
|
||||
{t("contact_sales")}
|
||||
</a>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{children}
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreen
|
||||
Icon="triangle-alert"
|
||||
headline={t("enterprise_license")}
|
||||
buttonRaw={
|
||||
<Button color="secondary" href="https://bls.media/kontakt">
|
||||
{t(`contact_sales`)}
|
||||
</Button>
|
||||
}
|
||||
description={t("enterprise_license_sales")}
|
||||
/>
|
||||
)}
|
||||
</Component>
|
||||
);
|
||||
};
|
||||
|
||||
export const withLicenseRequired =
|
||||
<T extends JSX.IntrinsicAttributes>(Component: ComponentType<T>) =>
|
||||
// eslint-disable-next-line react/display-name
|
||||
(hocProps: T) =>
|
||||
(
|
||||
<LicenseRequired>
|
||||
<Component {...hocProps} />
|
||||
</LicenseRequired>
|
||||
);
|
||||
|
||||
export default LicenseRequired;
|
||||
@@ -0,0 +1,25 @@
|
||||
import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
|
||||
interface ResponseUsernameApi {
|
||||
available: boolean;
|
||||
premium: boolean;
|
||||
message?: string;
|
||||
suggestion?: string;
|
||||
}
|
||||
|
||||
export async function checkPremiumUsername(_username: string): Promise<ResponseUsernameApi> {
|
||||
const username = slugify(_username);
|
||||
const response = await fetch(`${WEBSITE_URL}/api/username`, {
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ username }),
|
||||
method: "POST",
|
||||
mode: "cors",
|
||||
});
|
||||
|
||||
const json = await response.json();
|
||||
return json;
|
||||
}
|
||||
52
calcom/packages/features/ee/common/server/checkLicense.ts
Normal file
52
calcom/packages/features/ee/common/server/checkLicense.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import cache from "memory-cache";
|
||||
import { z } from "zod";
|
||||
|
||||
import { CONSOLE_URL } from "@calcom/lib/constants";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
|
||||
const CACHING_TIME = 86400000; // 24 hours in milliseconds
|
||||
|
||||
const schemaLicenseKey = z
|
||||
.string()
|
||||
// .uuid() exists but I'd to fix the situation where the CALCOM_LICENSE_KEY is wrapped in quotes
|
||||
.regex(/^\"?[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}\"?$/, {
|
||||
message: "License key must follow UUID format: 8-4-4-4-12",
|
||||
})
|
||||
.transform((v) => {
|
||||
// Remove the double quotes from the license key, as they 404 the fetch.
|
||||
return v != null && v.length >= 2 && v.charAt(0) == '"' && v.charAt(v.length - 1) == '"'
|
||||
? v.substring(1, v.length - 1)
|
||||
: v;
|
||||
});
|
||||
|
||||
async function checkLicense(
|
||||
/** The prisma client to use (necessary for public API to handle custom prisma instances) */
|
||||
prisma: PrismaClient
|
||||
): Promise<boolean> {
|
||||
/** We skip for E2E testing */
|
||||
if (!!process.env.NEXT_PUBLIC_IS_E2E) return true;
|
||||
/** We check first on env */
|
||||
let licenseKey = process.env.CALCOM_LICENSE_KEY;
|
||||
if (!licenseKey) {
|
||||
/** We try to check on DB only if env is undefined */
|
||||
const deployment = await prisma.deployment.findFirst({ where: { id: 1 } });
|
||||
licenseKey = deployment?.licenseKey ?? undefined;
|
||||
}
|
||||
if (!licenseKey) return false;
|
||||
const url = `${CONSOLE_URL}/api/license?key=${schemaLicenseKey.parse(licenseKey)}`;
|
||||
const cachedResponse = cache.get(url);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
} else {
|
||||
try {
|
||||
const response = await fetch(url, { mode: "cors" });
|
||||
const data = await response.json();
|
||||
cache.put(url, data.valid, CACHING_TIME);
|
||||
return data.valid;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default checkLicense;
|
||||
Reference in New Issue
Block a user