From 9a8de9ae16329f55628e004171b204f76cba84c5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Thu, 29 Jan 2026 22:16:23 -0600 Subject: [PATCH] Add Enterprise Feature Gate Component: Introduce EnterpriseFeatureGate and EnterpriseFeatureLocked components to manage access to enterprise features based on license validation. Integrate the EnterpriseFeatureGate into the SSO settings page to conditionally render SSOSettings based on license status. --- .../proprietary/enterprise-feature-gate.tsx | 114 ++++++++++++++++++ apps/dokploy/pages/dashboard/settings/sso.tsx | 16 ++- 2 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 apps/dokploy/components/proprietary/enterprise-feature-gate.tsx diff --git a/apps/dokploy/components/proprietary/enterprise-feature-gate.tsx b/apps/dokploy/components/proprietary/enterprise-feature-gate.tsx new file mode 100644 index 000000000..875813fcb --- /dev/null +++ b/apps/dokploy/components/proprietary/enterprise-feature-gate.tsx @@ -0,0 +1,114 @@ +"use client"; + +import { Loader2, Lock } from "lucide-react"; +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { api } from "@/utils/api"; + +interface EnterpriseFeatureLockedProps { + /** Optional title override */ + title?: string; + /** Optional description override */ + description?: string; + /** Optional custom CTA label */ + ctaLabel?: string; + /** Optional CTA href (default: /dashboard/settings/license) */ + ctaHref?: string; + /** Compact variant (less padding, smaller icon) */ + compact?: boolean; +} + +/** + * Displays a locked state for enterprise features when the user has no valid license. + * Use standalone or via EnterpriseFeatureGate. + */ +export function EnterpriseFeatureLocked({ + title = "Enterprise feature", + description = "This feature is part of Dokploy Enterprise. Add a valid license to use it.", + ctaLabel = "Go to License", + ctaHref = "/dashboard/settings/license", + compact = false, +}: EnterpriseFeatureLockedProps) { + return ( + + +
+
+ +
+
+ {title} + + {description} + +
+
+
+ +
+ +
+
+
+ ); +} + +interface EnterpriseFeatureGateProps { + children: React.ReactNode; + /** Props for the locked state when license is invalid */ + lockedProps?: Omit; + /** Show loading spinner while checking license */ + fallback?: React.ReactNode; +} + +/** + * Renders children only when the instance has a valid enterprise license. + * Otherwise shows EnterpriseFeatureLocked. + */ +export function EnterpriseFeatureGate({ + children, + lockedProps, + fallback, +}: EnterpriseFeatureGateProps) { + const { data: haveValidLicense, isLoading } = + api.licenseKey.haveValidLicenseKey.useQuery(); + + if (isLoading) { + if (fallback) return <>{fallback}; + return ( +
+ + + Checking license... + +
+ ); + } + + if (!haveValidLicense) { + return ; + } + + return <>{children}; +} diff --git a/apps/dokploy/pages/dashboard/settings/sso.tsx b/apps/dokploy/pages/dashboard/settings/sso.tsx index 6085f7f0e..c0acedabb 100644 --- a/apps/dokploy/pages/dashboard/settings/sso.tsx +++ b/apps/dokploy/pages/dashboard/settings/sso.tsx @@ -4,6 +4,7 @@ import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; import superjson from "superjson"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; +import { EnterpriseFeatureGate } from "@/components/proprietary/enterprise-feature-gate"; import { SSOSettings } from "@/components/proprietary/sso/sso-settings"; import { Card } from "@/components/ui/card"; import { appRouter } from "@/server/api/root"; @@ -16,7 +17,16 @@ const Page = () => {
- + + +
@@ -31,9 +41,7 @@ Page.getLayout = (page: ReactElement) => { return {page}; }; -export async function getServerSideProps( - ctx: GetServerSidePropsContext>, -) { +export async function getServerSideProps(ctx: GetServerSidePropsContext) { const { req, res } = ctx; const locale = await getLocale(req.cookies); if (IS_CLOUD) {