mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge pull request #547 from Dokploy/536-implement-custom-certificates-in-external-server
feat(certificates): create certificates in a remote server
This commit is contained in:
@@ -4,6 +4,7 @@ import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { auth } from "./auth";
|
||||
import { certificates } from "./certificate";
|
||||
import { registry } from "./registry";
|
||||
import { certificateType } from "./shared";
|
||||
import { sshKeys } from "./ssh-key";
|
||||
@@ -37,6 +38,7 @@ export const adminsRelations = relations(admins, ({ one, many }) => ({
|
||||
users: many(users),
|
||||
registry: many(registry),
|
||||
sshKeys: many(sshKeys),
|
||||
certificates: many(certificates),
|
||||
}));
|
||||
|
||||
const createSchema = createInsertSchema(admins, {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { boolean, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { admins } from "./admin";
|
||||
import { server } from "./server";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
export const certificates = pgTable("certificate", {
|
||||
@@ -17,13 +20,34 @@ export const certificates = pgTable("certificate", {
|
||||
.$defaultFn(() => generateAppName("certificate"))
|
||||
.unique(),
|
||||
autoRenew: boolean("autoRenew"),
|
||||
adminId: text("adminId").references(() => admins.adminId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
serverId: text("serverId").references(() => server.serverId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const certificatesRelations = relations(
|
||||
certificates,
|
||||
({ one, many }) => ({
|
||||
server: one(server, {
|
||||
fields: [certificates.serverId],
|
||||
references: [server.serverId],
|
||||
}),
|
||||
admin: one(admins, {
|
||||
fields: [certificates.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
export const apiCreateCertificate = createInsertSchema(certificates, {
|
||||
name: z.string().min(1),
|
||||
certificateData: z.string().min(1),
|
||||
privateKey: z.string().min(1),
|
||||
autoRenew: z.boolean().optional(),
|
||||
serverId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiFindCertificate = z.object({
|
||||
|
||||
@@ -6,6 +6,7 @@ import { z } from "zod";
|
||||
|
||||
import { admins } from "./admin";
|
||||
import { applications } from "./application";
|
||||
import { certificates } from "./certificate";
|
||||
import { compose } from "./compose";
|
||||
import { deployments } from "./deployment";
|
||||
import { mariadb } from "./mariadb";
|
||||
@@ -58,6 +59,7 @@ export const serverRelations = relations(server, ({ one, many }) => ({
|
||||
mongo: many(mongo),
|
||||
mysql: many(mysql),
|
||||
postgres: many(postgres),
|
||||
certificates: many(certificates),
|
||||
}));
|
||||
|
||||
const createSchema = createInsertSchema(server, {
|
||||
|
||||
@@ -8,6 +8,8 @@ import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { dump } from "js-yaml";
|
||||
import type { z } from "zod";
|
||||
import { encodeBase64 } from "../utils/docker/utils";
|
||||
import { execAsyncRemote } from "../utils/process/execAsync";
|
||||
|
||||
export type Certificate = typeof certificates.$inferSelect;
|
||||
|
||||
@@ -28,11 +30,13 @@ export const findCertificateById = async (certificateId: string) => {
|
||||
|
||||
export const createCertificate = async (
|
||||
certificateData: z.infer<typeof apiCreateCertificate>,
|
||||
adminId: string,
|
||||
) => {
|
||||
const certificate = await db
|
||||
.insert(certificates)
|
||||
.values({
|
||||
...certificateData,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning();
|
||||
|
||||
@@ -46,15 +50,21 @@ export const createCertificate = async (
|
||||
const cer = certificate[0];
|
||||
|
||||
createCertificateFiles(cer);
|
||||
|
||||
return cer;
|
||||
};
|
||||
|
||||
export const removeCertificateById = async (certificateId: string) => {
|
||||
const { CERTIFICATES_PATH } = paths();
|
||||
const certificate = await findCertificateById(certificateId);
|
||||
const { CERTIFICATES_PATH } = paths(!!certificate.serverId);
|
||||
const certDir = path.join(CERTIFICATES_PATH, certificate.certificatePath);
|
||||
|
||||
await removeDirectoryIfExistsContent(certDir);
|
||||
if (certificate.serverId) {
|
||||
await execAsyncRemote(certificate.serverId, `rm -rf ${certDir}`);
|
||||
} else {
|
||||
await removeDirectoryIfExistsContent(certDir);
|
||||
}
|
||||
|
||||
const result = await db
|
||||
.delete(certificates)
|
||||
.where(eq(certificates.certificateId, certificateId))
|
||||
@@ -70,27 +80,14 @@ export const removeCertificateById = async (certificateId: string) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
export const findCertificates = async () => {
|
||||
return await db.query.certificates.findMany();
|
||||
};
|
||||
|
||||
const createCertificateFiles = (certificate: Certificate) => {
|
||||
const { CERTIFICATES_PATH } = paths();
|
||||
const dockerPath = "/etc/traefik";
|
||||
const createCertificateFiles = async (certificate: Certificate) => {
|
||||
const { CERTIFICATES_PATH } = paths(!!certificate.serverId);
|
||||
const certDir = path.join(CERTIFICATES_PATH, certificate.certificatePath);
|
||||
const crtPath = path.join(certDir, "chain.crt");
|
||||
const keyPath = path.join(certDir, "privkey.key");
|
||||
|
||||
const chainPath = path.join(dockerPath, certDir, "chain.crt");
|
||||
const keyPathDocker = path.join(dockerPath, certDir, "privkey.key");
|
||||
|
||||
if (!fs.existsSync(certDir)) {
|
||||
fs.mkdirSync(certDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(crtPath, certificate.certificateData);
|
||||
fs.writeFileSync(keyPath, certificate.privateKey);
|
||||
|
||||
const chainPath = path.join(certDir, "chain.crt");
|
||||
const keyPathDocker = path.join(certDir, "privkey.key");
|
||||
const traefikConfig = {
|
||||
tls: {
|
||||
certificates: [
|
||||
@@ -101,8 +98,28 @@ const createCertificateFiles = (certificate: Certificate) => {
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const yamlConfig = dump(traefikConfig);
|
||||
const configFile = path.join(certDir, "certificate.yml");
|
||||
fs.writeFileSync(configFile, yamlConfig);
|
||||
|
||||
if (certificate.serverId) {
|
||||
const certificateData = encodeBase64(certificate.certificateData);
|
||||
const privateKey = encodeBase64(certificate.privateKey);
|
||||
const command = `
|
||||
mkdir -p ${certDir};
|
||||
echo "${certificateData}" | base64 -d > "${crtPath}";
|
||||
echo "${privateKey}" | base64 -d > "${keyPath}";
|
||||
echo "${yamlConfig}" > "${configFile}";
|
||||
`;
|
||||
|
||||
await execAsyncRemote(certificate.serverId, command);
|
||||
} else {
|
||||
if (!fs.existsSync(certDir)) {
|
||||
fs.mkdirSync(certDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(crtPath, certificate.certificateData);
|
||||
fs.writeFileSync(keyPath, certificate.privateKey);
|
||||
|
||||
fs.writeFileSync(configFile, yamlConfig);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user