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:
Mauricio Siu
2025-03-17 15:50:04 -06:00
parent 7c17cfb5c7
commit a61436b8f0
22 changed files with 5688 additions and 108 deletions

View 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 });

View File

@@ -0,0 +1,10 @@
import pino from "pino";
export const logger = pino({
transport: {
target: "pino-pretty",
options: {
colorize: true,
},
},
});

View 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>;

View 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;
};