diff --git a/server/lib/isLicencedOrSubscribed.ts b/server/lib/isLicencedOrSubscribed.ts index 8f1b3d00..3de3a915 100644 --- a/server/lib/isLicencedOrSubscribed.ts +++ b/server/lib/isLicencedOrSubscribed.ts @@ -1,22 +1,16 @@ import { build } from "@server/build"; -import license from "@server/license/license"; +import license from "#dynamic/license/license"; import { getOrgTierData } from "#dynamic/lib/billing"; -import { TierId } from "./billing/tiers"; +import { TierId } from "@server/lib/billing/tiers"; export async function isLicensedOrSubscribed(orgId: string): Promise { if (build === "enterprise") { - const isUnlocked = await license.isUnlocked(); - if (!isUnlocked) { - return false; - } + return await license.isUnlocked(); } if (build === "saas") { const { tier } = await getOrgTierData(orgId); - const subscribed = tier === TierId.STANDARD; - if (!subscribed) { - return false; - } + return tier === TierId.STANDARD; } return true; diff --git a/server/private/lib/traefik/getTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts index 64bb2bdf..4293f746 100644 --- a/server/private/lib/traefik/getTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -430,6 +430,7 @@ export async function getTraefikConfig( continue; } + const domainParts = fullDomain.split("."); let wildCard; if (domainParts.length <= 2) { diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index 2051b075..0c4fc556 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -343,6 +343,7 @@ async function updateHttpResource( const isLicensed = await isLicensedOrSubscribed(resource.orgId); if (build == "enterprise" && !isLicensed) { + logger.warn("Server is not licensed! Clearing set maintenance screen values") // null the maintenance mode fields if not licensed updateData.maintenanceModeEnabled = undefined; updateData.maintenanceModeType = undefined; diff --git a/src/app/[orgId]/settings/resources/[niceId]/general/page.tsx b/src/app/[orgId]/settings/resources/[niceId]/general/page.tsx deleted file mode 100644 index 1a4d080b..00000000 --- a/src/app/[orgId]/settings/resources/[niceId]/general/page.tsx +++ /dev/null @@ -1,858 +0,0 @@ -"use client"; - -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; -import { formatAxiosError } from "@app/lib/api"; -import { Button } from "@/components/ui/button"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; -import { useResourceContext } from "@app/hooks/useResourceContext"; -import { ListSitesResponse } from "@server/routers/site"; -import { useEffect, useState } from "react"; -import { AxiosResponse } from "axios"; -import { useParams, useRouter } from "next/navigation"; -import { useForm } from "react-hook-form"; -import { toast } from "@app/hooks/useToast"; -import { - SettingsContainer, - SettingsSection, - SettingsSectionHeader, - SettingsSectionTitle, - SettingsSectionDescription, - SettingsSectionBody, - SettingsSectionForm, - SettingsSectionFooter -} from "@app/components/Settings"; -import { useOrgContext } from "@app/hooks/useOrgContext"; -import { createApiClient } from "@app/lib/api"; -import { useEnvContext } from "@app/hooks/useEnvContext"; -import { Label } from "@app/components/ui/label"; -import { ListDomainsResponse } from "@server/routers/domain"; -import { UpdateResourceResponse } from "@server/routers/resource"; -import { SwitchInput } from "@app/components/SwitchInput"; -import { useTranslations } from "next-intl"; -import { - Credenza, - CredenzaBody, - CredenzaClose, - CredenzaContent, - CredenzaDescription, - CredenzaFooter, - CredenzaHeader, - CredenzaTitle -} from "@app/components/Credenza"; -import DomainPicker from "@app/components/DomainPicker"; -import { AlertCircle, Globe, Info } from "lucide-react"; -import { build } from "@server/build"; -import { finalizeSubdomainSanitize } from "@app/lib/subdomain-utils"; -import { DomainRow } from "../../../../../../components/DomainsTable"; -import { toASCII, toUnicode } from "punycode"; -import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; -import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; -import { useUserContext } from "@app/hooks/useUserContext"; -import { Alert, AlertDescription } from "@app/components/ui/alert"; -import { RadioGroup, RadioGroupItem } from "@app/components/ui/radio-group"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger -} from "@app/components/ui/tooltip"; -import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert"; - -export default function GeneralForm() { - const [formKey, setFormKey] = useState(0); - const params = useParams(); - const { resource, updateResource } = useResourceContext(); - const { org } = useOrgContext(); - const router = useRouter(); - const t = useTranslations(); - const [editDomainOpen, setEditDomainOpen] = useState(false); - const subscriptionStatus = useSubscriptionStatusContext(); - const { licenseStatus, isUnlocked } = useLicenseStatusContext(); - const subscription = useSubscriptionStatusContext(); - const { user } = useUserContext(); - - const { env } = useEnvContext(); - - const orgId = params.orgId; - - const api = createApiClient({ env }); - - const [sites, setSites] = useState([]); - const [saveLoading, setSaveLoading] = useState(false); - const [transferLoading, setTransferLoading] = useState(false); - const [open, setOpen] = useState(false); - const [baseDomains, setBaseDomains] = useState< - ListDomainsResponse["domains"] - >([]); - - const [loadingPage, setLoadingPage] = useState(true); - const [resourceFullDomain, setResourceFullDomain] = useState( - `${resource.ssl ? "https" : "http"}://${toUnicode(resource.fullDomain || "")}` - ); - const [selectedDomain, setSelectedDomain] = useState<{ - domainId: string; - subdomain?: string; - fullDomain: string; - baseDomain: string; - } | null>(null); - - // Check if security features are disabled due to licensing/subscription - const isSecurityFeatureDisabled = () => { - const isEnterpriseNotLicensed = build === "enterprise" && !isUnlocked(); - const isSaasNotSubscribed = - build === "saas" && !subscription?.isSubscribed(); - return isEnterpriseNotLicensed || isSaasNotSubscribed; - }; - - const GeneralFormSchema = z - .object({ - enabled: z.boolean(), - subdomain: z.string().optional(), - name: z.string().min(1).max(255), - niceId: z.string().min(1).max(255).optional(), - domainId: z.string().optional(), - proxyPort: z.number().int().min(1).max(65535).optional(), - // enableProxy: z.boolean().optional() - maintenanceModeEnabled: z.boolean().optional(), - maintenanceModeType: z.enum(["forced", "automatic"]).optional(), - maintenanceTitle: z.string().max(255).optional(), - maintenanceMessage: z.string().max(2000).optional(), - maintenanceEstimatedTime: z.string().max(100).optional() - }) - .refine( - (data) => { - // For non-HTTP resources, proxyPort should be defined - if (!resource.http) { - return data.proxyPort !== undefined; - } - // For HTTP resources, proxyPort should be undefined - return data.proxyPort === undefined; - }, - { - message: !resource.http - ? "Port number is required for non-HTTP resources" - : "Port number should not be set for HTTP resources", - path: ["proxyPort"] - } - ); - - type GeneralFormValues = z.infer; - - const form = useForm({ - resolver: zodResolver(GeneralFormSchema), - defaultValues: { - enabled: resource.enabled, - name: resource.name, - niceId: resource.niceId, - subdomain: resource.subdomain ? resource.subdomain : undefined, - domainId: resource.domainId || undefined, - proxyPort: resource.proxyPort || undefined, - // enableProxy: resource.enableProxy || false - maintenanceModeEnabled: resource.maintenanceModeEnabled || false, - maintenanceModeType: resource.maintenanceModeType || "automatic", - maintenanceTitle: - resource.maintenanceTitle || "We'll be back soon!", - maintenanceMessage: - resource.maintenanceMessage || - "We are currently performing scheduled maintenance. Please check back soon.", - maintenanceEstimatedTime: resource.maintenanceEstimatedTime || "" - }, - mode: "onChange" - }); - - const isMaintenanceEnabled = form.watch("maintenanceModeEnabled"); - const maintenanceModeType = form.watch("maintenanceModeType"); - - useEffect(() => { - const fetchSites = async () => { - const res = await api.get>( - `/org/${orgId}/sites/` - ); - setSites(res.data.data.sites); - }; - - const fetchDomains = async () => { - const res = await api - .get< - AxiosResponse - >(`/org/${orgId}/domains/`) - .catch((e) => { - toast({ - variant: "destructive", - title: t("domainErrorFetch"), - description: formatAxiosError( - e, - t("domainErrorFetchDescription") - ) - }); - }); - - if (res?.status === 200) { - const rawDomains = res.data.data.domains as DomainRow[]; - const domains = rawDomains.map((domain) => ({ - ...domain, - baseDomain: toUnicode(domain.baseDomain) - })); - setBaseDomains(domains); - setFormKey((key) => key + 1); - } - }; - - const load = async () => { - await fetchDomains(); - await fetchSites(); - - setLoadingPage(false); - }; - - load(); - }, []); - - async function onSubmit(data: GeneralFormValues) { - setSaveLoading(true); - - const res = await api - .post>( - `resource/${resource?.resourceId}`, - { - enabled: data.enabled, - name: data.name, - niceId: data.niceId, - subdomain: data.subdomain - ? toASCII(data.subdomain) - : undefined, - domainId: data.domainId, - proxyPort: data.proxyPort, - // ...(!resource.http && { - // enableProxy: data.enableProxy - // }) - maintenanceModeEnabled: data.maintenanceModeEnabled, - maintenanceModeType: data.maintenanceModeType, - maintenanceTitle: data.maintenanceTitle || null, - maintenanceMessage: data.maintenanceMessage || null, - maintenanceEstimatedTime: - data.maintenanceEstimatedTime || null - } - ) - .catch((e) => { - toast({ - variant: "destructive", - title: t("resourceErrorUpdate"), - description: formatAxiosError( - e, - t("resourceErrorUpdateDescription") - ) - }); - }); - - if (res && res.status === 200) { - const updated = res.data.data; - - updateResource({ - enabled: data.enabled, - name: data.name, - niceId: data.niceId, - subdomain: data.subdomain, - fullDomain: resource.fullDomain, - proxyPort: data.proxyPort, - // ...(!resource.http && { - // enableProxy: data.enableProxy - // }) - maintenanceModeEnabled: data.maintenanceModeEnabled, - maintenanceModeType: data.maintenanceModeType, - maintenanceTitle: data.maintenanceTitle || null, - maintenanceMessage: data.maintenanceMessage || null, - maintenanceEstimatedTime: data.maintenanceEstimatedTime || null - }); - - toast({ - title: t("resourceUpdated"), - description: t("resourceUpdatedDescription") - }); - - if (data.niceId && data.niceId !== resource?.niceId) { - router.replace( - `/${updated.orgId}/settings/resources/${data.niceId}/general` - ); - } else { - router.refresh(); - } - - setSaveLoading(false); - } - - setSaveLoading(false); - } - - return ( - !loadingPage && ( - <> - - - - - {t("resourceGeneral")} - - - {t("resourceGeneralDescription")} - - - - - -
- - ( - -
- - - form.setValue( - "enabled", - val - ) - } - /> - -
- -
- )} - /> - - ( - - - {t("name")} - - - - - - - )} - /> - - ( - - - {t("identifier")} - - - - - - - )} - /> - - {!resource.http && ( - <> - ( - - - {t( - "resourcePortNumber" - )} - - - - field.onChange( - e - .target - .value - ? parseInt( - e - .target - .value - ) - : undefined - ) - } - /> - - - - {t( - "resourcePortNumberDescription" - )} - - - )} - /> - - {/* {build == "oss" && ( - ( - - - - -
- - {t( - "resourceEnableProxy" - )} - - - {t( - "resourceEnableProxyDescription" - )} - -
-
- )} - /> - )} */} - - )} - - {resource.http && ( - <> -
- -
- - - {resourceFullDomain} - - -
-
- - )} - - -
-
-
-
- - {build !== "oss" && resource.http && ( - - - - - {t("maintenanceMode")} - - - {t("maintenanceModeDescription")} - - - - - - - -
- - { - const isDisabled = - isSecurityFeatureDisabled(); - - return ( - -
- - - - -
- { - if ( - !isDisabled - ) { - form.setValue( - "maintenanceModeEnabled", - val - ); - } - }} - /> -
-
-
-
-
-
- - {t( - "showMaintenancePage" - )} - - -
- ); - }} - /> - - {isMaintenanceEnabled && ( -
- ( - - - {t( - "maintenanceModeType" - )} - - - - - - - -
- - - {t( - "automatic" - )} - {" "} - ( - {t( - "recommended" - )} - - ) - - - {t( - "automaticModeDescription" - )} - -
-
- - - - -
- - - {t( - "forced" - )} - - - - {t( - "forcedModeDescription" - )} - -
-
-
-
- -
- )} - /> - - {maintenanceModeType === - "forced" && ( - - - - - {t( - "warning:" - )} - {" "} - {t( - "forcedeModeWarning" - )} - - - )} - - ( - - - {t( - "pageTitle" - )} - - - - - - {t( - "pageTitleDescription" - )} - - - - )} - /> - - ( - - - {t( - "maintenancePageMessage" - )} - - -