mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
feat(licenses): implement license management and validation features
- Added a new licenses application to handle license key management. - Implemented API endpoints for validating license keys and managing paid features. - Introduced a new component for enabling paid features in the dashboard. - Updated database schema to include a licenseKey field for users. - Refactored server API to remove the admin router and integrate license validation into user operations. - Enhanced the monitoring setup process to require valid license keys. - Updated pnpm workspace configuration to include the new licenses app.
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import { Card } from "@/components/ui/card";
|
||||
import {
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { SparklesIcon } from "lucide-react";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { toast } from "sonner";
|
||||
import { api } from "@/utils/api";
|
||||
import { SetupMonitoring } from "./servers/setup-monitoring";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useState } from "react";
|
||||
|
||||
export const EnablePaidFeatures = () => {
|
||||
const { data, refetch } = api.user.get.useQuery();
|
||||
const { mutateAsync: validateLicense } =
|
||||
api.user.validateLicense.useMutation();
|
||||
const { mutateAsync: update } = api.user.update.useMutation();
|
||||
const [licenseKey, setLicenseKey] = useState("");
|
||||
|
||||
const handleValidateLicense = async () => {
|
||||
await validateLicense({
|
||||
licenseKey,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("License validated successfully");
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error validating license");
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="h-full bg-sidebar p-2.5 rounded-xl">
|
||||
<div className="rounded-xl bg-background shadow-md">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl flex flex-row items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-primary/10">
|
||||
<SparklesIcon className="size-5 text-primary" />
|
||||
</div>
|
||||
Paid Features
|
||||
</CardTitle>
|
||||
<CardDescription className="mt-2">
|
||||
Unlock advanced capabilities like monitoring and enhanced
|
||||
performance tracking
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex flex-row items-center justify-between p-4 border rounded-lg bg-card/50 hover:bg-card/80 transition-colors">
|
||||
<div className="space-y-1">
|
||||
<h3 className="font-medium">Enable Premium Features</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Access advanced monitoring tools and premium capabilities
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
className="ml-4"
|
||||
checked={data?.user?.enablePaidFeatures}
|
||||
onCheckedChange={() => {
|
||||
update({
|
||||
enablePaidFeatures: !data?.user?.enablePaidFeatures,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success(
|
||||
`Premium features ${
|
||||
data?.user?.enablePaidFeatures
|
||||
? "disabled"
|
||||
: "enabled"
|
||||
} successfully`,
|
||||
);
|
||||
refetch();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error updating premium features");
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{data?.user?.enablePaidFeatures && (
|
||||
<div className="flex flex-row items-center gap-4 p-4 border rounded-lg bg-card/50">
|
||||
<div className="flex-grow">
|
||||
<Input
|
||||
placeholder="Enter your license key"
|
||||
value={licenseKey}
|
||||
onChange={(e) => setLicenseKey(e.target.value)}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<Button onClick={handleValidateLicense} variant="secondary">
|
||||
Validate
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
{data?.user?.enablePaidFeatures && <SetupMonitoring />}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -202,7 +202,7 @@ export const SetupMonitoring = ({ serverId }: Props) => {
|
||||
|
||||
const { mutateAsync } = serverId
|
||||
? api.server.setupMonitoring.useMutation()
|
||||
: api.admin.setupMonitoring.useMutation();
|
||||
: api.user.setupMonitoring.useMutation();
|
||||
|
||||
const generateToken = () => {
|
||||
const array = new Uint8Array(64);
|
||||
|
||||
1
apps/dokploy/drizzle/0079_burly_lenny_balinger.sql
Normal file
1
apps/dokploy/drizzle/0079_burly_lenny_balinger.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "user_temp" ADD COLUMN "licenseKey" text;
|
||||
5169
apps/dokploy/drizzle/meta/0079_snapshot.json
Normal file
5169
apps/dokploy/drizzle/meta/0079_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -554,6 +554,13 @@
|
||||
"when": 1742112194375,
|
||||
"tag": "0078_uneven_omega_sentinel",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 79,
|
||||
"version": "7",
|
||||
"when": 1742188594159,
|
||||
"tag": "0079_burly_lenny_balinger",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { WebDomain } from "@/components/dashboard/settings/web-domain";
|
||||
import { WebServer } from "@/components/dashboard/settings/web-server";
|
||||
import { EnablePaidFeatures } from "@/components/dashboard/settings/enable-paid-features";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { getLocale, serverSideTranslations } from "@/utils/i18n";
|
||||
@@ -8,56 +9,16 @@ import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import type { ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
import { api } from "@/utils/api";
|
||||
|
||||
const Page = () => {
|
||||
const { data: isCloud } = api.settings.isCloud.useQuery();
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="h-full rounded-xl max-w-5xl mx-auto flex flex-col gap-4">
|
||||
<WebDomain />
|
||||
<WebServer />
|
||||
{/* <Card className="h-full bg-sidebar p-2.5 rounded-xl ">
|
||||
<div className="rounded-xl bg-background shadow-md ">
|
||||
<CardHeader className="">
|
||||
<CardTitle className="text-xl flex flex-row gap-2">
|
||||
<LayoutDashboardIcon className="size-6 text-muted-foreground self-center" />
|
||||
Paid Features
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Enable or disable paid features like monitoring
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<span className="text-sm font-medium text-muted-foreground">
|
||||
Enable Paid Features:
|
||||
</span>
|
||||
|
||||
<Switch
|
||||
checked={data?.enablePaidFeatures}
|
||||
onCheckedChange={() => {
|
||||
update({
|
||||
enablePaidFeatures: !data?.enablePaidFeatures,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success(
|
||||
`Paid features ${
|
||||
data?.enablePaidFeatures ? "disabled" : "enabled"
|
||||
} successfully`,
|
||||
);
|
||||
refetch();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error updating paid features");
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
{data?.enablePaidFeatures && <SetupMonitoring />}
|
||||
</div>
|
||||
</Card> */}
|
||||
|
||||
{/* */}
|
||||
{!isCloud && <EnablePaidFeatures />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { authRouter } from "@/server/api/routers/auth";
|
||||
import { createTRPCRouter } from "../api/trpc";
|
||||
import { adminRouter } from "./routers/admin";
|
||||
import { aiRouter } from "./routers/ai";
|
||||
import { applicationRouter } from "./routers/application";
|
||||
import { backupRouter } from "./routers/backup";
|
||||
@@ -42,7 +41,6 @@ import { userRouter } from "./routers/user";
|
||||
*/
|
||||
|
||||
export const appRouter = createTRPCRouter({
|
||||
admin: adminRouter,
|
||||
docker: dockerRouter,
|
||||
auth: authRouter,
|
||||
project: projectRouter,
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import { apiUpdateWebServerMonitoring } from "@/server/db/schema";
|
||||
import {
|
||||
IS_CLOUD,
|
||||
findUserById,
|
||||
setupWebMonitoring,
|
||||
updateUser,
|
||||
} from "@dokploy/server";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { adminProcedure, createTRPCRouter } from "../trpc";
|
||||
|
||||
export const adminRouter = createTRPCRouter({
|
||||
setupMonitoring: adminProcedure
|
||||
.input(apiUpdateWebServerMonitoring)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
if (IS_CLOUD) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Feature disabled on cloud",
|
||||
});
|
||||
}
|
||||
const user = await findUserById(ctx.user.ownerId);
|
||||
if (user.id !== ctx.user.ownerId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to setup the monitoring",
|
||||
});
|
||||
}
|
||||
|
||||
await updateUser(user.id, {
|
||||
metricsConfig: {
|
||||
server: {
|
||||
type: "Dokploy",
|
||||
refreshRate: input.metricsConfig.server.refreshRate,
|
||||
port: input.metricsConfig.server.port,
|
||||
token: input.metricsConfig.server.token,
|
||||
cronJob: input.metricsConfig.server.cronJob,
|
||||
urlCallback: input.metricsConfig.server.urlCallback,
|
||||
retentionDays: input.metricsConfig.server.retentionDays,
|
||||
thresholds: {
|
||||
cpu: input.metricsConfig.server.thresholds.cpu,
|
||||
memory: input.metricsConfig.server.thresholds.memory,
|
||||
},
|
||||
},
|
||||
containers: {
|
||||
refreshRate: input.metricsConfig.containers.refreshRate,
|
||||
services: {
|
||||
include: input.metricsConfig.containers.services.include || [],
|
||||
exclude: input.metricsConfig.containers.services.exclude || [],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const currentServer = await setupWebMonitoring(user.id);
|
||||
return currentServer;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
});
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
removeUserById,
|
||||
updateUser,
|
||||
createApiKey,
|
||||
setupWebMonitoring,
|
||||
} from "@dokploy/server";
|
||||
import { db } from "@dokploy/server/db";
|
||||
import {
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
invitation,
|
||||
member,
|
||||
apikey,
|
||||
apiUpdateWebServerMonitoring,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import * as bcrypt from "bcrypt";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
@@ -27,7 +29,7 @@ import {
|
||||
protectedProcedure,
|
||||
publicProcedure,
|
||||
} from "../trpc";
|
||||
|
||||
import { validateLicense } from "@/server/utils/validate-license";
|
||||
const apiCreateApiKey = z.object({
|
||||
name: z.string().min(1),
|
||||
prefix: z.string().optional(),
|
||||
@@ -138,6 +140,26 @@ export const userRouter = createTRPCRouter({
|
||||
}
|
||||
return await updateUser(ctx.user.id, input);
|
||||
}),
|
||||
validateLicense: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
licenseKey: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const isValid = await validateLicense(input.licenseKey);
|
||||
if (!isValid) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Invalid license key",
|
||||
});
|
||||
}
|
||||
|
||||
await updateUser(ctx.user.id, {
|
||||
licenseKey: input.licenseKey,
|
||||
});
|
||||
return isValid;
|
||||
}),
|
||||
getUserByToken: publicProcedure
|
||||
.input(apiFindOneToken)
|
||||
.query(async ({ input }) => {
|
||||
@@ -327,4 +349,62 @@ export const userRouter = createTRPCRouter({
|
||||
|
||||
return organizations.length;
|
||||
}),
|
||||
|
||||
setupMonitoring: adminProcedure
|
||||
.input(apiUpdateWebServerMonitoring)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
if (IS_CLOUD) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Feature disabled on cloud",
|
||||
});
|
||||
}
|
||||
|
||||
const user = await findUserById(ctx.user.id);
|
||||
|
||||
if (!validateLicense(user?.licenseKey || "")) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Invalid license key",
|
||||
});
|
||||
}
|
||||
if (user.id !== ctx.user.ownerId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to setup the monitoring",
|
||||
});
|
||||
}
|
||||
|
||||
await updateUser(user.id, {
|
||||
metricsConfig: {
|
||||
server: {
|
||||
type: "Dokploy",
|
||||
refreshRate: input.metricsConfig.server.refreshRate,
|
||||
port: input.metricsConfig.server.port,
|
||||
token: input.metricsConfig.server.token,
|
||||
cronJob: input.metricsConfig.server.cronJob,
|
||||
urlCallback: input.metricsConfig.server.urlCallback,
|
||||
retentionDays: input.metricsConfig.server.retentionDays,
|
||||
thresholds: {
|
||||
cpu: input.metricsConfig.server.thresholds.cpu,
|
||||
memory: input.metricsConfig.server.thresholds.memory,
|
||||
},
|
||||
},
|
||||
containers: {
|
||||
refreshRate: input.metricsConfig.containers.refreshRate,
|
||||
services: {
|
||||
include: input.metricsConfig.containers.services.include || [],
|
||||
exclude: input.metricsConfig.containers.services.exclude || [],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const currentServer = await setupWebMonitoring(user.id);
|
||||
return currentServer;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
8
apps/dokploy/server/utils/validate-license.ts
Normal file
8
apps/dokploy/server/utils/validate-license.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const validateLicense = async (licenseKey: string): Promise<boolean> => {
|
||||
const response = await fetch(`${process.env.SERVER_URL}/validate-license`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ licenseKey }),
|
||||
});
|
||||
|
||||
return response.ok;
|
||||
};
|
||||
2
apps/licenses/.env.example
Normal file
2
apps/licenses/.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
LEMON_SQUEEZY_API_KEY=""
|
||||
LEMON_SQUEEZY_STORE_ID=""
|
||||
28
apps/licenses/.gitignore
vendored
Normal file
28
apps/licenses/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# dev
|
||||
.yarn/
|
||||
!.yarn/releases
|
||||
.vscode/*
|
||||
!.vscode/launch.json
|
||||
!.vscode/*.code-snippets
|
||||
.idea/workspace.xml
|
||||
.idea/usage.statistics.xml
|
||||
.idea/shelf
|
||||
|
||||
# deps
|
||||
node_modules/
|
||||
|
||||
# env
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# logs
|
||||
logs/
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
8
apps/licenses/README.md
Normal file
8
apps/licenses/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
```
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
```
|
||||
open http://localhost:3000
|
||||
```
|
||||
32
apps/licenses/package.json
Normal file
32
apps/licenses/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@dokploy/licenses",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "PORT=4000 tsx watch src/index.ts",
|
||||
"build": "tsc --project tsconfig.json",
|
||||
"start": "node dist/index.js",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"pino": "9.4.0",
|
||||
"pino-pretty": "11.2.2",
|
||||
"@hono/zod-validator": "0.3.0",
|
||||
"zod": "^3.23.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"@dokploy/server": "workspace:*",
|
||||
"@hono/node-server": "^1.12.1",
|
||||
"hono": "^4.5.8",
|
||||
"dotenv": "^16.3.1",
|
||||
"stripe": "17.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.2",
|
||||
"@types/react": "^18.2.37",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@types/node": "^20.11.17",
|
||||
"tsx": "^4.7.1"
|
||||
},
|
||||
"packageManager": "pnpm@9.5.0"
|
||||
}
|
||||
39
apps/licenses/src/index.ts
Normal file
39
apps/licenses/src/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { serve } from "@hono/node-server";
|
||||
import { Hono } from "hono";
|
||||
import "dotenv/config";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { logger } from "./logger.js";
|
||||
import { deployJobSchema } from "./schema.js";
|
||||
import Stripe from "stripe";
|
||||
const app = new Hono();
|
||||
|
||||
app.post("/deploy", zValidator("json", deployJobSchema), (c) => {
|
||||
const data = c.req.valid("json");
|
||||
return c.json(
|
||||
{
|
||||
message: "Deployment Added",
|
||||
},
|
||||
200,
|
||||
);
|
||||
});
|
||||
|
||||
// Stripe webhook
|
||||
app.post("/stripe/webhook", async (c) => {
|
||||
const body = await c.req.json();
|
||||
|
||||
const event = stripe.webhooks.constructEvent(
|
||||
body,
|
||||
c.req.header("stripe-signature"),
|
||||
process.env.STRIPE_WEBHOOK_SECRET,
|
||||
);
|
||||
|
||||
return c.json({ status: "ok" });
|
||||
});
|
||||
|
||||
app.get("/health", async (c) => {
|
||||
return c.json({ status: "ok" });
|
||||
});
|
||||
|
||||
const port = Number.parseInt(process.env.PORT || "3000");
|
||||
logger.info("Starting Deployments Server ✅", port);
|
||||
serve({ fetch: app.fetch, port });
|
||||
10
apps/licenses/src/logger.ts
Normal file
10
apps/licenses/src/logger.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import pino from "pino";
|
||||
|
||||
export const logger = pino({
|
||||
transport: {
|
||||
target: "pino-pretty",
|
||||
options: {
|
||||
colorize: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
34
apps/licenses/src/schema.ts
Normal file
34
apps/licenses/src/schema.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const deployJobSchema = z.discriminatedUnion("applicationType", [
|
||||
z.object({
|
||||
applicationId: z.string(),
|
||||
titleLog: z.string(),
|
||||
descriptionLog: z.string(),
|
||||
server: z.boolean().optional(),
|
||||
type: z.enum(["deploy", "redeploy"]),
|
||||
applicationType: z.literal("application"),
|
||||
serverId: z.string().min(1),
|
||||
}),
|
||||
z.object({
|
||||
composeId: z.string(),
|
||||
titleLog: z.string(),
|
||||
descriptionLog: z.string(),
|
||||
server: z.boolean().optional(),
|
||||
type: z.enum(["deploy", "redeploy"]),
|
||||
applicationType: z.literal("compose"),
|
||||
serverId: z.string().min(1),
|
||||
}),
|
||||
z.object({
|
||||
applicationId: z.string(),
|
||||
previewDeploymentId: z.string(),
|
||||
titleLog: z.string(),
|
||||
descriptionLog: z.string(),
|
||||
server: z.boolean().optional(),
|
||||
type: z.enum(["deploy"]),
|
||||
applicationType: z.literal("application-preview"),
|
||||
serverId: z.string().min(1),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type DeployJob = z.infer<typeof deployJobSchema>;
|
||||
82
apps/licenses/src/utils.ts
Normal file
82
apps/licenses/src/utils.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
deployRemoteApplication,
|
||||
deployRemoteCompose,
|
||||
deployRemotePreviewApplication,
|
||||
rebuildRemoteApplication,
|
||||
rebuildRemoteCompose,
|
||||
updateApplicationStatus,
|
||||
updateCompose,
|
||||
updatePreviewDeployment,
|
||||
} from "@dokploy/server";
|
||||
import type { DeployJob } from "./schema";
|
||||
|
||||
export const deploy = async (job: DeployJob) => {
|
||||
try {
|
||||
if (job.applicationType === "application") {
|
||||
await updateApplicationStatus(job.applicationId, "running");
|
||||
if (job.server) {
|
||||
if (job.type === "redeploy") {
|
||||
await rebuildRemoteApplication({
|
||||
applicationId: job.applicationId,
|
||||
titleLog: job.titleLog,
|
||||
descriptionLog: job.descriptionLog,
|
||||
});
|
||||
} else if (job.type === "deploy") {
|
||||
await deployRemoteApplication({
|
||||
applicationId: job.applicationId,
|
||||
titleLog: job.titleLog,
|
||||
descriptionLog: job.descriptionLog,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (job.applicationType === "compose") {
|
||||
await updateCompose(job.composeId, {
|
||||
composeStatus: "running",
|
||||
});
|
||||
|
||||
if (job.server) {
|
||||
if (job.type === "redeploy") {
|
||||
await rebuildRemoteCompose({
|
||||
composeId: job.composeId,
|
||||
titleLog: job.titleLog,
|
||||
descriptionLog: job.descriptionLog,
|
||||
});
|
||||
} else if (job.type === "deploy") {
|
||||
await deployRemoteCompose({
|
||||
composeId: job.composeId,
|
||||
titleLog: job.titleLog,
|
||||
descriptionLog: job.descriptionLog,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (job.applicationType === "application-preview") {
|
||||
await updatePreviewDeployment(job.previewDeploymentId, {
|
||||
previewStatus: "running",
|
||||
});
|
||||
if (job.server) {
|
||||
if (job.type === "deploy") {
|
||||
await deployRemotePreviewApplication({
|
||||
applicationId: job.applicationId,
|
||||
titleLog: job.titleLog,
|
||||
descriptionLog: job.descriptionLog,
|
||||
previewDeploymentId: job.previewDeploymentId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
if (job.applicationType === "application") {
|
||||
await updateApplicationStatus(job.applicationId, "error");
|
||||
} else if (job.applicationType === "compose") {
|
||||
await updateCompose(job.composeId, {
|
||||
composeStatus: "error",
|
||||
});
|
||||
} else if (job.applicationType === "application-preview") {
|
||||
await updatePreviewDeployment(job.previewDeploymentId, {
|
||||
previewStatus: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
18
apps/licenses/tsconfig.json
Normal file
18
apps/licenses/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "dist",
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "hono/jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./*"],
|
||||
"@dokploy/server/*": ["../../packages/server/src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -56,6 +56,7 @@ export const users_temp = pgTable("user_temp", {
|
||||
logCleanupCron: text("logCleanupCron"),
|
||||
// Metrics
|
||||
enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false),
|
||||
licenseKey: text("licenseKey"),
|
||||
metricsConfig: jsonb("metricsConfig")
|
||||
.$type<{
|
||||
server: {
|
||||
|
||||
57
pnpm-lock.yaml
generated
57
pnpm-lock.yaml
generated
@@ -528,6 +528,58 @@ importers:
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0(@types/node@18.19.42)(terser@5.31.3)
|
||||
|
||||
apps/licenses:
|
||||
dependencies:
|
||||
'@dokploy/server':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/server
|
||||
'@hono/node-server':
|
||||
specifier: ^1.12.1
|
||||
version: 1.12.1
|
||||
'@hono/zod-validator':
|
||||
specifier: 0.3.0
|
||||
version: 0.3.0(hono@4.5.8)(zod@3.24.1)
|
||||
dotenv:
|
||||
specifier: ^16.3.1
|
||||
version: 16.4.5
|
||||
hono:
|
||||
specifier: ^4.5.8
|
||||
version: 4.5.8
|
||||
pino:
|
||||
specifier: 9.4.0
|
||||
version: 9.4.0
|
||||
pino-pretty:
|
||||
specifier: 11.2.2
|
||||
version: 11.2.2
|
||||
react:
|
||||
specifier: 18.2.0
|
||||
version: 18.2.0
|
||||
react-dom:
|
||||
specifier: 18.2.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
stripe:
|
||||
specifier: 17.2.0
|
||||
version: 17.2.0
|
||||
zod:
|
||||
specifier: ^3.23.4
|
||||
version: 3.24.1
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^20.11.17
|
||||
version: 20.14.10
|
||||
'@types/react':
|
||||
specifier: 18.3.5
|
||||
version: 18.3.5
|
||||
'@types/react-dom':
|
||||
specifier: 18.3.0
|
||||
version: 18.3.0
|
||||
tsx:
|
||||
specifier: ^4.7.1
|
||||
version: 4.16.2
|
||||
typescript:
|
||||
specifier: ^5.4.2
|
||||
version: 5.7.2
|
||||
|
||||
apps/schedules:
|
||||
dependencies:
|
||||
'@dokploy/server':
|
||||
@@ -8354,6 +8406,11 @@ snapshots:
|
||||
hono: 4.5.8
|
||||
zod: 3.23.8
|
||||
|
||||
'@hono/zod-validator@0.3.0(hono@4.5.8)(zod@3.24.1)':
|
||||
dependencies:
|
||||
hono: 4.5.8
|
||||
zod: 3.24.1
|
||||
|
||||
'@hookform/resolvers@3.9.0(react-hook-form@7.52.1(react@18.2.0))':
|
||||
dependencies:
|
||||
react-hook-form: 7.52.1(react@18.2.0)
|
||||
|
||||
@@ -5,3 +5,4 @@ packages:
|
||||
- "apps/schedules"
|
||||
- "apps/models"
|
||||
- "packages/server"
|
||||
- "apps/licenses"
|
||||
|
||||
Reference in New Issue
Block a user