mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-07 20:45:17 +00:00
Add enterprise features management: implement license key settings and update user schema
This commit is contained in:
@@ -0,0 +1,108 @@
|
|||||||
|
import { Key } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { CardTitle } from "@/components/ui/card";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
|
export function LicenseKeySettings() {
|
||||||
|
const utils = api.useUtils();
|
||||||
|
const { data, isLoading } = api.licenseKey.getEnterpriseSettings.useQuery();
|
||||||
|
const { mutateAsync: updateEnterpriseSettings, isLoading: isSaving } =
|
||||||
|
api.licenseKey.updateEnterpriseSettings.useMutation();
|
||||||
|
|
||||||
|
const [licenseKey, setLicenseKey] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data?.licenseKey !== undefined) {
|
||||||
|
setLicenseKey(data.licenseKey ?? "");
|
||||||
|
}
|
||||||
|
}, [data?.licenseKey]);
|
||||||
|
|
||||||
|
const enabled = !!data?.enableEnterpriseFeatures;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-4 rounded-lg border p-4">
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Key className="size-6 text-muted-foreground" />
|
||||||
|
<CardTitle className="text-xl">License Key</CardTitle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{enabled ? "Enabled" : "Disabled"}
|
||||||
|
</span>
|
||||||
|
<Switch
|
||||||
|
checked={enabled}
|
||||||
|
disabled={isLoading || isSaving}
|
||||||
|
onCheckedChange={async (next) => {
|
||||||
|
try {
|
||||||
|
await updateEnterpriseSettings({
|
||||||
|
enableEnterpriseFeatures: next,
|
||||||
|
});
|
||||||
|
await utils.licenseKey.getEnterpriseSettings.invalidate();
|
||||||
|
toast.success("Enterprise features updated");
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
toast.error("Failed to update enterprise features");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
To unlock extra features you need an enterprise license key. Contact us{" "}
|
||||||
|
<Link
|
||||||
|
href="http://localhost:3001/contact"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="underline underline-offset-4"
|
||||||
|
>
|
||||||
|
here
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{enabled && (
|
||||||
|
<div className="grid gap-3 md:grid-cols-[1fr_auto] md:items-end">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium" htmlFor="licenseKey">
|
||||||
|
License Key
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
id="licenseKey"
|
||||||
|
placeholder="Enter your enterprise license key"
|
||||||
|
value={licenseKey}
|
||||||
|
onChange={(e) => setLicenseKey(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="md:justify-self-end">
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
disabled={isSaving}
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
await updateEnterpriseSettings({ licenseKey });
|
||||||
|
await utils.licenseKey.getEnterpriseSettings.invalidate();
|
||||||
|
toast.success("License key saved");
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
toast.error("Failed to save license key");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,8 +6,8 @@ import superjson from "superjson";
|
|||||||
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";
|
import { ShowBackups } from "@/components/dashboard/database/backups/show-backups";
|
||||||
import { WebDomain } from "@/components/dashboard/settings/web-domain";
|
import { WebDomain } from "@/components/dashboard/settings/web-domain";
|
||||||
import { WebServer } from "@/components/dashboard/settings/web-server";
|
import { WebServer } from "@/components/dashboard/settings/web-server";
|
||||||
import { LicenseKeySettings } from "@/components/dashboard/settings/web-server/license-key";
|
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
|
import { LicenseKeySettings } from "@/components/proprietary/license-keys/license-key";
|
||||||
import { Card } from "@/components/ui/card";
|
import { Card } from "@/components/ui/card";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { mountRouter } from "./routers/mount";
|
|||||||
import { mysqlRouter } from "./routers/mysql";
|
import { mysqlRouter } from "./routers/mysql";
|
||||||
import { notificationRouter } from "./routers/notification";
|
import { notificationRouter } from "./routers/notification";
|
||||||
import { organizationRouter } from "./routers/organization";
|
import { organizationRouter } from "./routers/organization";
|
||||||
|
import { licenseKeyRouter } from "./routers/proprietary/license-key";
|
||||||
import { portRouter } from "./routers/port";
|
import { portRouter } from "./routers/port";
|
||||||
import { postgresRouter } from "./routers/postgres";
|
import { postgresRouter } from "./routers/postgres";
|
||||||
import { previewDeploymentRouter } from "./routers/preview-deployment";
|
import { previewDeploymentRouter } from "./routers/preview-deployment";
|
||||||
@@ -82,6 +83,7 @@ export const appRouter = createTRPCRouter({
|
|||||||
swarm: swarmRouter,
|
swarm: swarmRouter,
|
||||||
ai: aiRouter,
|
ai: aiRouter,
|
||||||
organization: organizationRouter,
|
organization: organizationRouter,
|
||||||
|
licenseKey: licenseKeyRouter,
|
||||||
schedule: scheduleRouter,
|
schedule: scheduleRouter,
|
||||||
rollback: rollbackRouter,
|
rollback: rollbackRouter,
|
||||||
volumeBackups: volumeBackupsRouter,
|
volumeBackups: volumeBackupsRouter,
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { user } from "@dokploy/server/db/schema";
|
||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { adminProcedure, createTRPCRouter } from "@/server/api/trpc";
|
||||||
|
import { db } from "@/server/db";
|
||||||
|
|
||||||
|
export const licenseKeyRouter = createTRPCRouter({
|
||||||
|
getEnterpriseSettings: adminProcedure.query(async ({ ctx }) => {
|
||||||
|
const currentUserId = ctx.user.id;
|
||||||
|
const currentUser = await db.query.user.findFirst({
|
||||||
|
where: eq(user.id, currentUserId),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!currentUser) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "User not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
enableEnterpriseFeatures: !!currentUser.enableEnterpriseFeatures,
|
||||||
|
licenseKey: currentUser.licenseKey ?? "",
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
updateEnterpriseSettings: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
enableEnterpriseFeatures: z.boolean().optional(),
|
||||||
|
licenseKey: z.string().optional(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const currentUserId = ctx.user.id;
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(user)
|
||||||
|
.set({
|
||||||
|
...(input.enableEnterpriseFeatures === undefined
|
||||||
|
? {}
|
||||||
|
: { enableEnterpriseFeatures: input.enableEnterpriseFeatures }),
|
||||||
|
...(input.licenseKey === undefined
|
||||||
|
? {}
|
||||||
|
: { licenseKey: input.licenseKey }),
|
||||||
|
})
|
||||||
|
.where(eq(user.id, currentUserId));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user