From 9b312cd9d73cc9adc84f85871136f6a13397be7c Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 16 Sep 2024 23:49:24 -0600 Subject: [PATCH] refactor(multi-server): adapt paths on server and in dokploy ui --- apps/dokploy/__test__/drop/drop.test.test.ts | 4 +- .../deployments/show-deployment.tsx | 2 +- .../dashboard/project/add-compose.tsx | 12 ++-- .../settings/servers/setup-server.tsx | 1 + .../services/application/[applicationId].tsx | 5 +- apps/dokploy/server/api/routers/domain.ts | 10 ++- apps/dokploy/server/api/routers/settings.ts | 4 +- .../server/api/services/certificate.ts | 4 +- apps/dokploy/server/api/services/compose.ts | 4 +- .../dokploy/server/api/services/deployment.ts | 10 ++- apps/dokploy/server/api/services/domain.ts | 14 +++- apps/dokploy/server/api/services/mount.ts | 17 ++++- apps/dokploy/server/constants/index.ts | 70 +++++++++---------- apps/dokploy/server/monitoring/utilts.ts | 6 +- apps/dokploy/server/setup/config-paths.ts | 21 +++--- apps/dokploy/server/setup/registry-setup.ts | 4 +- apps/dokploy/server/setup/traefik-setup.ts | 6 +- .../server/utils/access-log/handler.ts | 3 +- apps/dokploy/server/utils/builders/compose.ts | 6 +- apps/dokploy/server/utils/builders/drop.ts | 3 +- apps/dokploy/server/utils/builders/index.ts | 2 +- .../dokploy/server/utils/databases/mariadb.ts | 11 ++- apps/dokploy/server/utils/databases/mongo.ts | 9 ++- apps/dokploy/server/utils/databases/mysql.ts | 12 ++-- .../server/utils/databases/postgres.ts | 12 ++-- apps/dokploy/server/utils/databases/redis.ts | 13 ++-- apps/dokploy/server/utils/docker/domain.ts | 3 +- apps/dokploy/server/utils/docker/utils.ts | 17 ++++- .../server/utils/filesystem/directory.ts | 11 +-- apps/dokploy/server/utils/filesystem/ssh.ts | 6 +- .../server/utils/providers/bitbucket.ts | 8 ++- apps/dokploy/server/utils/providers/git.ts | 8 ++- apps/dokploy/server/utils/providers/github.ts | 8 ++- apps/dokploy/server/utils/providers/gitlab.ts | 9 ++- apps/dokploy/server/utils/providers/raw.ts | 6 +- .../server/utils/servers/setup-server.ts | 32 ++++----- .../server/utils/traefik/application.ts | 14 +++- .../server/utils/traefik/middleware.ts | 5 +- apps/dokploy/server/utils/traefik/registry.ts | 5 +- .../server/utils/traefik/web-server.ts | 5 +- apps/dokploy/server/wss/listen-deployment.ts | 5 ++ apps/dokploy/server/wss/terminal.ts | 4 +- apps/dokploy/templates/utils/index.ts | 3 +- 43 files changed, 255 insertions(+), 159 deletions(-) diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.test.ts index 5561999c..c341eba7 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.test.ts @@ -1,6 +1,6 @@ import fs from "node:fs/promises"; import path from "node:path"; -import { APPLICATIONS_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { unzipDrop } from "@/server/utils/builders/drop"; import AdmZip from "adm-zip"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; @@ -10,7 +10,7 @@ if (typeof window === "undefined") { globalThis.File = undici.File as any; globalThis.FileList = undici.FileList as any; } - +const { APPLICATIONS_PATH } = paths(); vi.mock("@/server/constants", () => ({ APPLICATIONS_PATH: "./__test__/drop/zips/output", })); diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx index 36f77049..2e892523 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx @@ -22,7 +22,7 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; - const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}&serverId=${serverId}`; + const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}${serverId ? `&serverId=${serverId}` : ""}`; const ws = new WebSocket(wsUrl); ws.onmessage = (e) => { diff --git a/apps/dokploy/components/dashboard/project/add-compose.tsx b/apps/dokploy/components/dashboard/project/add-compose.tsx index eda270e9..89240a04 100644 --- a/apps/dokploy/components/dashboard/project/add-compose.tsx +++ b/apps/dokploy/components/dashboard/project/add-compose.tsx @@ -29,6 +29,12 @@ import { SelectValue, } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { slugify } from "@/lib/slug"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -37,12 +43,6 @@ import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; const AddComposeSchema = z.object({ composeType: z.enum(["docker-compose", "stack"]).optional(), diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx index cac2cdca..07c473a9 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx @@ -161,6 +161,7 @@ export const SetupServer = ({ serverId }: Props) => { ))} )} + setActiveLog(null)} diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index ad2bdba3..05f7ab65 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -154,10 +154,7 @@ const Service = (
- +
diff --git a/apps/dokploy/server/api/routers/domain.ts b/apps/dokploy/server/api/routers/domain.ts index 6cf533c1..7070e521 100644 --- a/apps/dokploy/server/api/routers/domain.ts +++ b/apps/dokploy/server/api/routers/domain.ts @@ -45,9 +45,13 @@ export const domainRouter = createTRPCRouter({ return await findDomainsByComposeId(input.composeId); }), generateDomain: protectedProcedure - .input(z.object({ serverId: z.string(), appName: z.string() })) - .mutation(async ({ input }) => { - return generateTraefikMeDomain(input.serverId, input.appName); + .input(z.object({ appName: z.string(), serverId: z.string().optional() })) + .mutation(async ({ input, ctx }) => { + return generateTraefikMeDomain( + input.appName, + ctx.user.adminId, + input.serverId, + ); }), update: protectedProcedure diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index f98b981b..30f4579f 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -1,4 +1,3 @@ -import { MAIN_TRAEFIK_PATH, MONITORING_PATH } from "@/server/constants"; import { apiAssignDomain, apiEnableDashboard, @@ -54,6 +53,7 @@ import { } from "../services/settings"; import { canAccessToTraefikFiles } from "../services/user"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; +import { paths } from "@/server/constants"; export const settingsRouter = createTRPCRouter({ reloadServer: adminProcedure.mutation(async () => { @@ -111,6 +111,7 @@ export const settingsRouter = createTRPCRouter({ return true; }), cleanMonitoring: adminProcedure.mutation(async () => { + const { MONITORING_PATH } = paths(); await recreateDirectory(MONITORING_PATH); return true; }), @@ -237,6 +238,7 @@ export const settingsRouter = createTRPCRouter({ throw new TRPCError({ code: "UNAUTHORIZED" }); } } + const { MAIN_TRAEFIK_PATH } = paths(); const result = readDirectory(MAIN_TRAEFIK_PATH); return result || []; }), diff --git a/apps/dokploy/server/api/services/certificate.ts b/apps/dokploy/server/api/services/certificate.ts index 6aca1b52..3ed1dcd1 100644 --- a/apps/dokploy/server/api/services/certificate.ts +++ b/apps/dokploy/server/api/services/certificate.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import path from "node:path"; -import { CERTIFICATES_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { db } from "@/server/db"; import { type apiCreateCertificate, certificates } from "@/server/db/schema"; import { removeDirectoryIfExistsContent } from "@/server/utils/filesystem/directory"; @@ -50,6 +50,7 @@ export const createCertificate = async ( }; export const removeCertificateById = async (certificateId: string) => { + const { CERTIFICATES_PATH } = paths(); const certificate = await findCertificateById(certificateId); const certDir = path.join(CERTIFICATES_PATH, certificate.certificatePath); @@ -74,6 +75,7 @@ export const findCertificates = async () => { }; const createCertificateFiles = (certificate: Certificate) => { + const { CERTIFICATES_PATH } = paths(); const dockerPath = "/etc/traefik"; const certDir = path.join(CERTIFICATES_PATH, certificate.certificatePath); const crtPath = path.join(certDir, "chain.crt"); diff --git a/apps/dokploy/server/api/services/compose.ts b/apps/dokploy/server/api/services/compose.ts index 5fc1cf86..d5f0c0f7 100644 --- a/apps/dokploy/server/api/services/compose.ts +++ b/apps/dokploy/server/api/services/compose.ts @@ -1,5 +1,5 @@ import { join } from "node:path"; -import { COMPOSE_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { db } from "@/server/db"; import { type apiCreateCompose, compose } from "@/server/db/schema"; import { generateAppName } from "@/server/db/schema/utils"; @@ -416,6 +416,7 @@ export const rebuildRemoteCompose = async ({ export const removeCompose = async (compose: Compose) => { try { + const { COMPOSE_PATH } = paths(!!compose.serverId); const projectPath = join(COMPOSE_PATH, compose.appName); if (compose.composeType === "stack") { @@ -448,6 +449,7 @@ export const removeCompose = async (compose: Compose) => { export const stopCompose = async (composeId: string) => { const compose = await findComposeById(composeId); try { + const { COMPOSE_PATH } = paths(!!compose.serverId); if (compose.composeType === "docker-compose") { console.log( `cd ${join(COMPOSE_PATH, compose.appName)} && docker compose -p ${compose.appName} stop`, diff --git a/apps/dokploy/server/api/services/deployment.ts b/apps/dokploy/server/api/services/deployment.ts index 7bedfb6f..c13047b5 100644 --- a/apps/dokploy/server/api/services/deployment.ts +++ b/apps/dokploy/server/api/services/deployment.ts @@ -1,6 +1,6 @@ import { existsSync, promises as fsPromises } from "node:fs"; import path from "node:path"; -import { LOGS_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { db } from "@/server/db"; import { type apiCreateDeployment, @@ -46,6 +46,7 @@ export const createDeployment = async ( const application = await findApplicationById(deployment.applicationId); // await removeLastTenDeployments(deployment.applicationId); + const { LOGS_PATH } = paths(!!application.serverId); const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss"); const fileName = `${application.appName}-${formattedDateTime}.log`; const logFilePath = path.join(LOGS_PATH, application.appName, fileName); @@ -102,6 +103,7 @@ export const createDeploymentCompose = async ( const compose = await findComposeById(deployment.composeId); // await removeLastTenComposeDeployments(deployment.composeId); + const { LOGS_PATH } = paths(!!compose.serverId); const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss"); const fileName = `${compose.appName}-${formattedDateTime}.log`; const logFilePath = path.join(LOGS_PATH, compose.appName, fileName); @@ -208,6 +210,7 @@ const removeLastTenComposeDeployments = async (composeId: string) => { export const removeDeployments = async (application: Application) => { const { appName, applicationId } = application; + const { LOGS_PATH } = paths(!!application.serverId); const logsPath = path.join(LOGS_PATH, appName); if (application.serverId) { await execAsyncRemote(application.serverId, `rm -rf ${logsPath}`); @@ -219,6 +222,7 @@ export const removeDeployments = async (application: Application) => { export const removeDeploymentsByComposeId = async (compose: Compose) => { const { appName } = compose; + const { LOGS_PATH } = paths(!!compose.serverId); const logsPath = path.join(LOGS_PATH, appName); if (compose.serverId) { await execAsyncRemote(compose.serverId, `rm -rf ${logsPath}`); @@ -287,8 +291,9 @@ export const createServerDeployment = async ( >, ) => { try { - const server = await findServerById(deployment.serverId); + const { LOGS_PATH } = paths(); + const server = await findServerById(deployment.serverId); await removeLastFiveDeployments(deployment.serverId); const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss"); const fileName = `${server.appName}-${formattedDateTime}.log`; @@ -341,6 +346,7 @@ export const removeLastFiveDeployments = async (serverId: string) => { }; export const removeDeploymentsByServerId = async (server: Server) => { + const { LOGS_PATH } = paths(); const { appName } = server; const logsPath = path.join(LOGS_PATH, appName); await removeDirectoryIfExistsContent(logsPath); diff --git a/apps/dokploy/server/api/services/domain.ts b/apps/dokploy/server/api/services/domain.ts index 5f944596..10f4fe50 100644 --- a/apps/dokploy/server/api/services/domain.ts +++ b/apps/dokploy/server/api/services/domain.ts @@ -4,7 +4,7 @@ import { manageDomain } from "@/server/utils/traefik/domain"; import { generateRandomDomain } from "@/templates/utils"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; -import { findAdmin } from "./admin"; +import { findAdmin, findAdminById } from "./admin"; import { findApplicationById } from "./application"; import { findServerById } from "./server"; @@ -35,8 +35,9 @@ export const createDomain = async (input: typeof apiCreateDomain._type) => { }; export const generateTraefikMeDomain = async ( - serverId: string, appName: string, + adminId: string, + serverId?: string, ) => { if (serverId) { const server = await findServerById(serverId); @@ -45,7 +46,14 @@ export const generateTraefikMeDomain = async ( projectName: appName, }); } - const admin = await findAdmin(); + + if (process.env.NODE_ENV === "development") { + return generateRandomDomain({ + serverIp: "", + projectName: appName, + }); + } + const admin = await findAdminById(adminId); return generateRandomDomain({ serverIp: admin?.serverIp || "", projectName: appName, diff --git a/apps/dokploy/server/api/services/mount.ts b/apps/dokploy/server/api/services/mount.ts index e1f96358..97528aa5 100644 --- a/apps/dokploy/server/api/services/mount.ts +++ b/apps/dokploy/server/api/services/mount.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { db } from "@/server/db"; import { type ServiceType, @@ -214,23 +214,36 @@ export const deleteFileMount = async (mountId: string) => { export const getBaseFilesPath = async (mountId: string) => { const mount = await findMountById(mountId); - let absoluteBasePath = path.resolve(APPLICATIONS_PATH); + let absoluteBasePath = ""; let appName = ""; let directoryPath = ""; if (mount.serviceType === "application" && mount.application) { + const { APPLICATIONS_PATH } = paths(!!mount.application.serverId); + absoluteBasePath = path.resolve(APPLICATIONS_PATH); appName = mount.application.appName; } else if (mount.serviceType === "postgres" && mount.postgres) { + const { APPLICATIONS_PATH } = paths(!!mount.postgres.serverId); + absoluteBasePath = path.resolve(APPLICATIONS_PATH); appName = mount.postgres.appName; } else if (mount.serviceType === "mariadb" && mount.mariadb) { + const { APPLICATIONS_PATH } = paths(!!mount.mariadb.serverId); + absoluteBasePath = path.resolve(APPLICATIONS_PATH); appName = mount.mariadb.appName; } else if (mount.serviceType === "mongo" && mount.mongo) { + const { APPLICATIONS_PATH } = paths(!!mount.mongo.serverId); + absoluteBasePath = path.resolve(APPLICATIONS_PATH); appName = mount.mongo.appName; } else if (mount.serviceType === "mysql" && mount.mysql) { + const { APPLICATIONS_PATH } = paths(!!mount.mysql.serverId); + absoluteBasePath = path.resolve(APPLICATIONS_PATH); appName = mount.mysql.appName; } else if (mount.serviceType === "redis" && mount.redis) { + const { APPLICATIONS_PATH } = paths(!!mount.redis.serverId); + absoluteBasePath = path.resolve(APPLICATIONS_PATH); appName = mount.redis.appName; } else if (mount.serviceType === "compose" && mount.compose) { + const { COMPOSE_PATH } = paths(!!mount.compose.serverId); appName = mount.compose.appName; absoluteBasePath = path.resolve(COMPOSE_PATH); } diff --git a/apps/dokploy/server/constants/index.ts b/apps/dokploy/server/constants/index.ts index b3126658..f2f1a4d8 100644 --- a/apps/dokploy/server/constants/index.ts +++ b/apps/dokploy/server/constants/index.ts @@ -1,43 +1,39 @@ import path from "node:path"; import Docker from "dockerode"; -// const IS_CLOUD = process.env.IS_CLOUD === "true"; - -export const BASE_PATH = - process.env.NODE_ENV === "production" - ? "/etc/dokploy" - : path.join(process.cwd(), ".docker"); -export const MAIN_TRAEFIK_PATH = `${BASE_PATH}/traefik`; -export const DYNAMIC_TRAEFIK_PATH = `/etc/dokploy/traefik/dynamic`; -export const LOGS_PATH = `/etc/dokploy/logs`; -export const APPLICATIONS_PATH = `/etc/dokploy/applications`; -export const COMPOSE_PATH = `/etc/dokploy/compose`; -export const SSH_PATH = `${BASE_PATH}/ssh`; -export const CERTIFICATES_PATH = `${DYNAMIC_TRAEFIK_PATH}/certificates`; -export const REGISTRY_PATH = `${DYNAMIC_TRAEFIK_PATH}/registry`; -export const MONITORING_PATH = `${BASE_PATH}/monitoring`; +export const IS_CLOUD = process.env.IS_CLOUD === "true"; export const docker = new Docker(); -export const getPaths = (basePath: string) => { - // return [ - // MAIN_TRAEFIK_PATH: `${basePath}/traefik`, - // DYNAMIC_TRAEFIK_PATH: `${basePath}/traefik/dynamic`, - // LOGS_PATH: `${basePath}/logs`, - // APPLICATIONS_PATH: `${basePath}/applications`, - // COMPOSE_PATH: `${basePath}/compose`, - // SSH_PATH: `${basePath}/ssh`, - // CERTIFICATES_PATH: `${basePath}/certificates`, - // MONITORING_PATH: `${basePath}/monitoring`, - // ]; - - return [ - `${basePath}/traefik`, - `${basePath}/traefik/dynamic`, - `${basePath}/logs`, - `${basePath}/applications`, - `${basePath}/compose`, - `${basePath}/ssh`, - `${basePath}/certificates`, - `${basePath}/monitoring`, - ]; +export const paths = (isServer = false) => { + if (isServer) { + const BASE_PATH = "/etc/dokploy"; + return { + BASE_PATH, + MAIN_TRAEFIK_PATH: `${BASE_PATH}/traefik`, + DYNAMIC_TRAEFIK_PATH: `${BASE_PATH}/traefik/dynamic`, + LOGS_PATH: `${BASE_PATH}/logs`, + APPLICATIONS_PATH: `${BASE_PATH}/applications`, + COMPOSE_PATH: `${BASE_PATH}/compose`, + SSH_PATH: `${BASE_PATH}/ssh`, + CERTIFICATES_PATH: `${BASE_PATH}/certificates`, + MONITORING_PATH: `${BASE_PATH}/monitoring`, + REGISTRY_PATH: `${BASE_PATH}/registry`, + }; + } + const BASE_PATH = + process.env.NODE_ENV === "production" + ? "/etc/dokploy" + : path.join(process.cwd(), ".docker"); + return { + BASE_PATH, + MAIN_TRAEFIK_PATH: `${BASE_PATH}/traefik`, + DYNAMIC_TRAEFIK_PATH: `${BASE_PATH}/traefik/dynamic`, + LOGS_PATH: `${BASE_PATH}/logs`, + APPLICATIONS_PATH: `${BASE_PATH}/applications`, + COMPOSE_PATH: `${BASE_PATH}/compose`, + SSH_PATH: `${BASE_PATH}/ssh`, + CERTIFICATES_PATH: `${BASE_PATH}/certificates`, + MONITORING_PATH: `${BASE_PATH}/monitoring`, + REGISTRY_PATH: `${BASE_PATH}/registry`, + }; }; diff --git a/apps/dokploy/server/monitoring/utilts.ts b/apps/dokploy/server/monitoring/utilts.ts index cf311096..f67d5705 100644 --- a/apps/dokploy/server/monitoring/utilts.ts +++ b/apps/dokploy/server/monitoring/utilts.ts @@ -1,12 +1,13 @@ import { promises } from "node:fs"; import type Dockerode from "dockerode"; import osUtils from "node-os-utils"; -import { MONITORING_PATH } from "../constants"; +import { paths } from "../constants"; export const recordAdvancedStats = async ( stats: Dockerode.ContainerStats, appName: string, ) => { + const { MONITORING_PATH } = paths(); const path = `${MONITORING_PATH}/${appName}`; await promises.mkdir(path, { recursive: true }); @@ -68,6 +69,7 @@ export const readStatsFile = async ( statType: "cpu" | "memory" | "disk" | "network" | "block", ) => { try { + const { MONITORING_PATH } = paths(); const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`; const data = await promises.readFile(filePath, "utf-8"); return JSON.parse(data); @@ -81,6 +83,7 @@ export const updateStatsFile = async ( statType: "cpu" | "memory" | "disk" | "network" | "block", value: number | string | unknown, ) => { + const { MONITORING_PATH } = paths(); const stats = await readStatsFile(appName, statType); stats.push({ value, time: new Date() }); @@ -100,6 +103,7 @@ export const readLastValueStatsFile = async ( statType: "cpu" | "memory" | "disk" | "network" | "block", ) => { try { + const { MONITORING_PATH } = paths(); const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`; const data = await promises.readFile(filePath, "utf-8"); const stats = JSON.parse(data); diff --git a/apps/dokploy/server/setup/config-paths.ts b/apps/dokploy/server/setup/config-paths.ts index d1676d21..190e438b 100644 --- a/apps/dokploy/server/setup/config-paths.ts +++ b/apps/dokploy/server/setup/config-paths.ts @@ -1,14 +1,5 @@ import { chmodSync, existsSync, mkdirSync } from "node:fs"; -import { - APPLICATIONS_PATH, - BASE_PATH, - CERTIFICATES_PATH, - DYNAMIC_TRAEFIK_PATH, - LOGS_PATH, - MAIN_TRAEFIK_PATH, - MONITORING_PATH, - SSH_PATH, -} from "../constants"; +import { paths } from "../constants"; const createDirectoryIfNotExist = (dirPath: string) => { if (!existsSync(dirPath)) { @@ -18,6 +9,16 @@ const createDirectoryIfNotExist = (dirPath: string) => { }; export const setupDirectories = () => { + const { + APPLICATIONS_PATH, + BASE_PATH, + CERTIFICATES_PATH, + DYNAMIC_TRAEFIK_PATH, + LOGS_PATH, + MAIN_TRAEFIK_PATH, + MONITORING_PATH, + SSH_PATH, + } = paths(); const directories = [ BASE_PATH, MAIN_TRAEFIK_PATH, diff --git a/apps/dokploy/server/setup/registry-setup.ts b/apps/dokploy/server/setup/registry-setup.ts index 688f2ead..e286270a 100644 --- a/apps/dokploy/server/setup/registry-setup.ts +++ b/apps/dokploy/server/setup/registry-setup.ts @@ -1,6 +1,6 @@ import type { CreateServiceOptions } from "dockerode"; import { generateRandomPassword } from "../auth/random-password"; -import { REGISTRY_PATH, docker } from "../constants"; +import { paths, docker } from "../constants"; import { pullImage } from "../utils/docker/utils"; import { execAsync } from "../utils/process/execAsync"; @@ -8,6 +8,7 @@ export const initializeRegistry = async ( username: string, password: string, ) => { + const { REGISTRY_PATH } = paths(); const imageName = "registry:2.8.3"; const containerName = "dokploy-registry"; await generateRegistryPassword(username, password); @@ -78,6 +79,7 @@ export const initializeRegistry = async ( const generateRegistryPassword = async (username: string, password: string) => { try { + const { REGISTRY_PATH } = paths(); const command = `htpasswd -nbB ${username} "${password}" > ${REGISTRY_PATH}/htpasswd`; const result = await execAsync(command); console.log("Password generated ✅"); diff --git a/apps/dokploy/server/setup/traefik-setup.ts b/apps/dokploy/server/setup/traefik-setup.ts index f78097f0..b5dc9f90 100644 --- a/apps/dokploy/server/setup/traefik-setup.ts +++ b/apps/dokploy/server/setup/traefik-setup.ts @@ -2,7 +2,7 @@ import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs"; import path from "node:path"; import type { ContainerTaskSpec, CreateServiceOptions } from "dockerode"; import { dump } from "js-yaml"; -import { DYNAMIC_TRAEFIK_PATH, MAIN_TRAEFIK_PATH, docker } from "../constants"; +import { docker, paths } from "../constants"; import { pullImage } from "../utils/docker/utils"; import type { FileConfig } from "../utils/traefik/file-types"; import type { MainTraefikConfig } from "../utils/traefik/types"; @@ -20,6 +20,7 @@ export const initializeTraefik = async ({ enableDashboard = false, env, }: TraefikOptions = {}) => { + const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(); const imageName = "traefik:v3.1.2"; const containerName = "dokploy-traefik"; const settings: CreateServiceOptions = { @@ -115,6 +116,7 @@ export const initializeTraefik = async ({ }; export const createDefaultServerTraefikConfig = () => { + const { DYNAMIC_TRAEFIK_PATH } = paths(); const configFilePath = path.join(DYNAMIC_TRAEFIK_PATH, "dokploy.yml"); if (existsSync(configFilePath)) { @@ -265,6 +267,7 @@ export const getDefaultServerTraefikConfig = () => { }; export const createDefaultTraefikConfig = () => { + const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(); const mainConfig = path.join(MAIN_TRAEFIK_PATH, "traefik.yml"); const acmeJsonPath = path.join(DYNAMIC_TRAEFIK_PATH, "acme.json"); @@ -297,6 +300,7 @@ export const getDefaultMiddlewares = () => { return yamlStr; }; export const createDefaultMiddlewares = () => { + const { DYNAMIC_TRAEFIK_PATH } = paths(); const middlewaresPath = path.join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml"); if (existsSync(middlewaresPath)) { console.log("Default middlewares already exists"); diff --git a/apps/dokploy/server/utils/access-log/handler.ts b/apps/dokploy/server/utils/access-log/handler.ts index 3fd7e5c9..24baa023 100644 --- a/apps/dokploy/server/utils/access-log/handler.ts +++ b/apps/dokploy/server/utils/access-log/handler.ts @@ -1,5 +1,5 @@ import { findAdmin, updateAdmin } from "@/server/api/services/admin"; -import { DYNAMIC_TRAEFIK_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { type RotatingFileStream, createStream } from "rotating-file-stream"; import { execAsync } from "../process/execAsync"; @@ -39,6 +39,7 @@ class LogRotationManager { } private async activateStream(): Promise { + const { DYNAMIC_TRAEFIK_PATH } = paths(); if (this.stream) { await this.deactivateStream(); } diff --git a/apps/dokploy/server/utils/builders/compose.ts b/apps/dokploy/server/utils/builders/compose.ts index cc7cd2c0..182bd6d3 100644 --- a/apps/dokploy/server/utils/builders/compose.ts +++ b/apps/dokploy/server/utils/builders/compose.ts @@ -5,7 +5,7 @@ import { writeFileSync, } from "node:fs"; import { dirname, join } from "node:path"; -import { COMPOSE_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import type { InferResultType } from "@/server/types/with"; import boxen from "boxen"; import { @@ -24,6 +24,7 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => { const writeStream = createWriteStream(logPath, { flags: "a" }); const { sourceType, appName, mounts, composeType, domains } = compose; try { + const { COMPOSE_PATH } = paths(); const command = createCommand(compose); await writeDomainsToCompose(compose, domains); createEnvFile(compose); @@ -73,6 +74,7 @@ export const getBuildComposeCommand = async ( compose: ComposeNested, logPath: string, ) => { + const { COMPOSE_PATH } = paths(true); const { sourceType, appName, mounts, composeType, domains, composePath } = compose; const command = createCommand(compose); @@ -158,6 +160,7 @@ export const createCommand = (compose: ComposeNested) => { }; const createEnvFile = (compose: ComposeNested) => { + const { COMPOSE_PATH } = paths(); const { env, composePath, appName } = compose; const composeFilePath = join(COMPOSE_PATH, appName, "code", composePath) || @@ -182,6 +185,7 @@ const createEnvFile = (compose: ComposeNested) => { }; export const getCreateEnvFileCommand = (compose: ComposeNested) => { + const { COMPOSE_PATH } = paths(true); const { env, composePath, appName } = compose; const composeFilePath = join(COMPOSE_PATH, appName, "code", composePath) || diff --git a/apps/dokploy/server/utils/builders/drop.ts b/apps/dokploy/server/utils/builders/drop.ts index 7c155096..b376251a 100644 --- a/apps/dokploy/server/utils/builders/drop.ts +++ b/apps/dokploy/server/utils/builders/drop.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path, { join } from "node:path"; import type { Application } from "@/server/api/services/application"; import { findServerById } from "@/server/api/services/server"; -import { APPLICATIONS_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import AdmZip from "adm-zip"; import { Client, type SFTPWrapper } from "ssh2"; import { @@ -17,6 +17,7 @@ export const unzipDrop = async (zipFile: File, application: Application) => { try { const { appName } = application; + const { APPLICATIONS_PATH } = paths(!!application.serverId); const outputPath = join(APPLICATIONS_PATH, appName, "code"); if (application.serverId) { await recreateDirectoryRemote(outputPath, application.serverId); diff --git a/apps/dokploy/server/utils/builders/index.ts b/apps/dokploy/server/utils/builders/index.ts index e58a63fb..4f7262a8 100644 --- a/apps/dokploy/server/utils/builders/index.ts +++ b/apps/dokploy/server/utils/builders/index.ts @@ -120,7 +120,7 @@ export const mechanizeDockerContainer = async ( } = generateConfigContainer(application); const bindsMount = generateBindMounts(mounts); - const filesMount = generateFileMounts(appName, mounts); + const filesMount = generateFileMounts(appName, application); const envVariables = prepareEnvironmentVariables(env); const image = getImageName(application); diff --git a/apps/dokploy/server/utils/databases/mariadb.ts b/apps/dokploy/server/utils/databases/mariadb.ts index 6df4d18b..d0ee6f84 100644 --- a/apps/dokploy/server/utils/databases/mariadb.ts +++ b/apps/dokploy/server/utils/databases/mariadb.ts @@ -1,5 +1,3 @@ -import type { Mariadb } from "@/server/api/services/mariadb"; -import type { Mount } from "@/server/api/services/mount"; import type { CreateServiceOptions } from "dockerode"; import { calculateResources, @@ -9,11 +7,10 @@ import { prepareEnvironmentVariables, } from "../docker/utils"; import { getRemoteDocker } from "../servers/remote-docker"; +import type { InferResultType } from "@/server/types/with"; -type MariadbWithMounts = Mariadb & { - mounts: Mount[]; -}; -export const buildMariadb = async (mariadb: MariadbWithMounts) => { +export type MariadbNested = InferResultType<"mariadb", { mounts: true }>; +export const buildMariadb = async (mariadb: MariadbNested) => { const { appName, env, @@ -43,7 +40,7 @@ export const buildMariadb = async (mariadb: MariadbWithMounts) => { const envVariables = prepareEnvironmentVariables(defaultMariadbEnv); const volumesMount = generateVolumeMounts(mounts); const bindsMount = generateBindMounts(mounts); - const filesMount = generateFileMounts(appName, mounts); + const filesMount = generateFileMounts(appName, mariadb); const docker = await getRemoteDocker(mariadb.serverId); diff --git a/apps/dokploy/server/utils/databases/mongo.ts b/apps/dokploy/server/utils/databases/mongo.ts index 5ec10cfa..c4d51753 100644 --- a/apps/dokploy/server/utils/databases/mongo.ts +++ b/apps/dokploy/server/utils/databases/mongo.ts @@ -9,12 +9,11 @@ import { prepareEnvironmentVariables, } from "../docker/utils"; import { getRemoteDocker } from "../servers/remote-docker"; +import type { InferResultType } from "@/server/types/with"; -type MongoWithMounts = Mongo & { - mounts: Mount[]; -}; +export type MongoNested = InferResultType<"mongo", { mounts: true }>; -export const buildMongo = async (mongo: MongoWithMounts) => { +export const buildMongo = async (mongo: MongoNested) => { const { appName, env, @@ -42,7 +41,7 @@ export const buildMongo = async (mongo: MongoWithMounts) => { const envVariables = prepareEnvironmentVariables(defaultMongoEnv); const volumesMount = generateVolumeMounts(mounts); const bindsMount = generateBindMounts(mounts); - const filesMount = generateFileMounts(appName, mounts); + const filesMount = generateFileMounts(appName, mongo); const docker = await getRemoteDocker(mongo.serverId); diff --git a/apps/dokploy/server/utils/databases/mysql.ts b/apps/dokploy/server/utils/databases/mysql.ts index f045d144..b0619af0 100644 --- a/apps/dokploy/server/utils/databases/mysql.ts +++ b/apps/dokploy/server/utils/databases/mysql.ts @@ -1,6 +1,3 @@ -import type { Mount } from "@/server/api/services/mount"; -import type { MySql } from "@/server/api/services/mysql"; -import { docker } from "@/server/constants"; import type { CreateServiceOptions } from "dockerode"; import { calculateResources, @@ -10,12 +7,11 @@ import { prepareEnvironmentVariables, } from "../docker/utils"; import { getRemoteDocker } from "../servers/remote-docker"; +import type { InferResultType } from "@/server/types/with"; -type MysqlWithMounts = MySql & { - mounts: Mount[]; -}; +export type MysqlNested = InferResultType<"mysql", { mounts: true }>; -export const buildMysql = async (mysql: MysqlWithMounts) => { +export const buildMysql = async (mysql: MysqlNested) => { const { appName, env, @@ -50,7 +46,7 @@ export const buildMysql = async (mysql: MysqlWithMounts) => { const envVariables = prepareEnvironmentVariables(defaultMysqlEnv); const volumesMount = generateVolumeMounts(mounts); const bindsMount = generateBindMounts(mounts); - const filesMount = generateFileMounts(appName, mounts); + const filesMount = generateFileMounts(appName, mysql); const docker = await getRemoteDocker(mysql.serverId); diff --git a/apps/dokploy/server/utils/databases/postgres.ts b/apps/dokploy/server/utils/databases/postgres.ts index 2c23980d..cbd6aae8 100644 --- a/apps/dokploy/server/utils/databases/postgres.ts +++ b/apps/dokploy/server/utils/databases/postgres.ts @@ -1,5 +1,3 @@ -import type { Mount } from "@/server/api/services/mount"; -import type { Postgres } from "@/server/api/services/postgres"; import type { CreateServiceOptions } from "dockerode"; import { calculateResources, @@ -9,12 +7,10 @@ import { prepareEnvironmentVariables, } from "../docker/utils"; import { getRemoteDocker } from "../servers/remote-docker"; +import type { InferResultType } from "@/server/types/with"; -type PostgresWithMounts = Postgres & { - mounts: Mount[]; -}; - -export const buildPostgres = async (postgres: PostgresWithMounts) => { +export type PostgresNested = InferResultType<"postgres", { mounts: true }>; +export const buildPostgres = async (postgres: PostgresNested) => { const { appName, env, @@ -43,7 +39,7 @@ export const buildPostgres = async (postgres: PostgresWithMounts) => { const envVariables = prepareEnvironmentVariables(defaultPostgresEnv); const volumesMount = generateVolumeMounts(mounts); const bindsMount = generateBindMounts(mounts); - const filesMount = generateFileMounts(appName, mounts); + const filesMount = generateFileMounts(appName, postgres); const docker = await getRemoteDocker(postgres.serverId); diff --git a/apps/dokploy/server/utils/databases/redis.ts b/apps/dokploy/server/utils/databases/redis.ts index 72d02e90..fd1d7d5d 100644 --- a/apps/dokploy/server/utils/databases/redis.ts +++ b/apps/dokploy/server/utils/databases/redis.ts @@ -1,6 +1,3 @@ -import type { Mount } from "@/server/api/services/mount"; -import type { Redis } from "@/server/api/services/redis"; -import { docker } from "@/server/constants"; import type { CreateServiceOptions } from "dockerode"; import { calculateResources, @@ -10,12 +7,10 @@ import { prepareEnvironmentVariables, } from "../docker/utils"; import { getRemoteDocker } from "../servers/remote-docker"; +import type { InferResultType } from "@/server/types/with"; -type RedisWithMounts = Redis & { - mounts: Mount[]; -}; - -export const buildRedis = async (redis: RedisWithMounts) => { +export type RedisNested = InferResultType<"redis", { mounts: true }>; +export const buildRedis = async (redis: RedisNested) => { const { appName, env, @@ -42,7 +37,7 @@ export const buildRedis = async (redis: RedisWithMounts) => { const envVariables = prepareEnvironmentVariables(defaultRedisEnv); const volumesMount = generateVolumeMounts(mounts); const bindsMount = generateBindMounts(mounts); - const filesMount = generateFileMounts(appName, mounts); + const filesMount = generateFileMounts(appName, redis); const docker = await getRemoteDocker(redis.serverId); diff --git a/apps/dokploy/server/utils/docker/domain.ts b/apps/dokploy/server/utils/docker/domain.ts index 174ce967..5b0ac076 100644 --- a/apps/dokploy/server/utils/docker/domain.ts +++ b/apps/dokploy/server/utils/docker/domain.ts @@ -3,7 +3,7 @@ import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import type { Compose } from "@/server/api/services/compose"; import type { Domain } from "@/server/api/services/domain"; -import { COMPOSE_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { dump, load } from "js-yaml"; import { execAsyncRemote } from "../process/execAsync"; import { @@ -63,6 +63,7 @@ export const cloneComposeRemote = async (compose: Compose) => { }; export const getComposePath = (compose: Compose) => { + const { COMPOSE_PATH } = paths(!!compose.serverId); const { appName, sourceType, composePath } = compose; let path = ""; diff --git a/apps/dokploy/server/utils/docker/utils.ts b/apps/dokploy/server/utils/docker/utils.ts index 5d9ec423..e4157242 100644 --- a/apps/dokploy/server/utils/docker/utils.ts +++ b/apps/dokploy/server/utils/docker/utils.ts @@ -1,12 +1,17 @@ import fs from "node:fs"; import path from "node:path"; import type { Readable } from "node:stream"; -import { APPLICATIONS_PATH, docker } from "@/server/constants"; +import { docker, paths } from "@/server/constants"; import type { ContainerInfo, ResourceRequirements } from "dockerode"; import { parse } from "dotenv"; import type { ApplicationNested } from "../builders"; import { execAsync, execAsyncRemote } from "../process/execAsync"; import { getRemoteDocker } from "../servers/remote-docker"; +import type { MongoNested } from "../databases/mongo"; +import type { MariadbNested } from "../databases/mariadb"; +import type { MysqlNested } from "../databases/mysql"; +import type { PostgresNested } from "../databases/postgres"; +import type { RedisNested } from "../databases/redis"; interface RegistryAuth { username: string; @@ -378,8 +383,16 @@ export const generateBindMounts = (mounts: ApplicationNested["mounts"]) => { export const generateFileMounts = ( appName: string, - mounts: ApplicationNested["mounts"], + service: + | ApplicationNested + | MongoNested + | MariadbNested + | MysqlNested + | PostgresNested + | RedisNested, ) => { + const { mounts } = service; + const { APPLICATIONS_PATH } = paths(!!service.serverId); if (!mounts || mounts.length === 0) { return []; } diff --git a/apps/dokploy/server/utils/filesystem/directory.ts b/apps/dokploy/server/utils/filesystem/directory.ts index 24daca17..bc3b03eb 100644 --- a/apps/dokploy/server/utils/filesystem/directory.ts +++ b/apps/dokploy/server/utils/filesystem/directory.ts @@ -1,11 +1,7 @@ import fs, { promises as fsPromises } from "node:fs"; import path from "node:path"; import type { Application } from "@/server/api/services/application"; -import { - APPLICATIONS_PATH, - COMPOSE_PATH, - MONITORING_PATH, -} from "@/server/constants"; +import { paths } from "@/server/constants"; import { execAsync, execAsyncRemote } from "../process/execAsync"; export const recreateDirectory = async (pathFolder: string): Promise => { @@ -52,6 +48,7 @@ export const removeDirectoryCode = async ( appName: string, serverId?: string | null, ) => { + const { APPLICATIONS_PATH } = paths(!!serverId); const directoryPath = path.join(APPLICATIONS_PATH, appName); const command = `rm -rf ${directoryPath}`; try { @@ -70,6 +67,7 @@ export const removeComposeDirectory = async ( appName: string, serverId?: string | null, ) => { + const { COMPOSE_PATH } = paths(!!serverId); const directoryPath = path.join(COMPOSE_PATH, appName); const command = `rm -rf ${directoryPath}`; try { @@ -88,6 +86,7 @@ export const removeMonitoringDirectory = async ( appName: string, serverId?: string | null, ) => { + const { MONITORING_PATH } = paths(!!serverId); const directoryPath = path.join(MONITORING_PATH, appName); const command = `rm -rf ${directoryPath}`; try { @@ -103,6 +102,7 @@ export const removeMonitoringDirectory = async ( }; export const getBuildAppDirectory = (application: Application) => { + const { APPLICATIONS_PATH } = paths(!!application.serverId); const { appName, buildType, sourceType, customGitBuildPath, dockerfile } = application; let buildPath = ""; @@ -132,6 +132,7 @@ export const getBuildAppDirectory = (application: Application) => { }; export const getDockerContextPath = (application: Application) => { + const { APPLICATIONS_PATH } = paths(!!application.serverId); const { appName, dockerContextPath } = application; if (!dockerContextPath) { diff --git a/apps/dokploy/server/utils/filesystem/ssh.ts b/apps/dokploy/server/utils/filesystem/ssh.ts index e587c506..62bf076a 100644 --- a/apps/dokploy/server/utils/filesystem/ssh.ts +++ b/apps/dokploy/server/utils/filesystem/ssh.ts @@ -1,9 +1,10 @@ import * as fs from "node:fs"; import * as path from "node:path"; -import { SSH_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { spawnAsync } from "../process/spawnAsync"; export const readSSHKey = async (id: string) => { + const { SSH_PATH } = paths(); try { if (!fs.existsSync(SSH_PATH)) { fs.mkdirSync(SSH_PATH, { recursive: true }); @@ -27,6 +28,7 @@ export const saveSSHKey = async ( publicKey: string, privateKey: string, ) => { + const { SSH_PATH } = paths(); const applicationDirectory = SSH_PATH; const privateKeyPath = path.join(applicationDirectory, `${id}_rsa`); @@ -42,6 +44,7 @@ export const saveSSHKey = async ( }; export const generateSSHKey = async (type: "rsa" | "ed25519" = "rsa") => { + const { SSH_PATH } = paths(); const applicationDirectory = SSH_PATH; if (!fs.existsSync(applicationDirectory)) { @@ -85,6 +88,7 @@ export const generateSSHKey = async (type: "rsa" | "ed25519" = "rsa") => { export const removeSSHKey = async (id: string) => { try { + const { SSH_PATH } = paths(); const publicKeyPath = path.join(SSH_PATH, `${id}_rsa.pub`); const privateKeyPath = path.join(SSH_PATH, `${id}_rsa`); await fs.promises.unlink(publicKeyPath); diff --git a/apps/dokploy/server/utils/providers/bitbucket.ts b/apps/dokploy/server/utils/providers/bitbucket.ts index e5f4bab6..c6eb0bbd 100644 --- a/apps/dokploy/server/utils/providers/bitbucket.ts +++ b/apps/dokploy/server/utils/providers/bitbucket.ts @@ -2,7 +2,7 @@ import { createWriteStream } from "node:fs"; import { join } from "node:path"; import { findBitbucketById } from "@/server/api/services/bitbucket"; import type { Compose } from "@/server/api/services/compose"; -import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import type { apiBitbucketTestConnection, apiFindBitbucketBranches, @@ -28,6 +28,7 @@ export const cloneBitbucketRepository = async ( logPath: string, isCompose = false, ) => { + const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(); const writeStream = createWriteStream(logPath, { flags: "a" }); const { appName, @@ -80,6 +81,7 @@ export const cloneBitbucketRepository = async ( }; export const cloneRawBitbucketRepository = async (entity: Compose) => { + const { COMPOSE_PATH } = paths(); const { appName, bitbucketRepository, @@ -119,6 +121,7 @@ export const cloneRawBitbucketRepository = async (entity: Compose) => { }; export const cloneRawBitbucketRepositoryRemote = async (compose: Compose) => { + const { COMPOSE_PATH } = paths(true); const { appName, bitbucketRepository, @@ -163,6 +166,7 @@ export const getBitbucketCloneCommand = async ( logPath: string, isCompose = false, ) => { + const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); const { appName, bitbucketRepository, @@ -193,7 +197,7 @@ export const getBitbucketCloneCommand = async ( } const bitbucketProvider = await findBitbucketById(bitbucketId); - const basePath = COMPOSE_PATH; + const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); await recreateDirectory(outputPath); const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`; diff --git a/apps/dokploy/server/utils/providers/git.ts b/apps/dokploy/server/utils/providers/git.ts index 17759c76..d348c0e5 100644 --- a/apps/dokploy/server/utils/providers/git.ts +++ b/apps/dokploy/server/utils/providers/git.ts @@ -2,7 +2,7 @@ import { createWriteStream } from "node:fs"; import path, { join } from "node:path"; import type { Compose } from "@/server/api/services/compose"; import { updateSSHKeyById } from "@/server/api/services/ssh-key"; -import { APPLICATIONS_PATH, COMPOSE_PATH, SSH_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { TRPCError } from "@trpc/server"; import { recreateDirectory } from "../filesystem/directory"; import { execAsync, execAsyncRemote } from "../process/execAsync"; @@ -18,6 +18,7 @@ export const cloneGitRepository = async ( logPath: string, isCompose = false, ) => { + const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths(); const { appName, customGitUrl, customGitBranch, customGitSSHKeyId } = entity; if (!customGitUrl || !customGitBranch) { @@ -99,6 +100,7 @@ export const getCustomGitCloneCommand = async ( logPath: string, isCompose = false, ) => { + const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); const { appName, customGitUrl, @@ -168,6 +170,7 @@ const isHttpOrHttps = (url: string): boolean => { }; const addHostToKnownHosts = async (repositoryURL: string) => { + const { SSH_PATH } = paths(); const { domain, port } = sanitizeRepoPathSSH(repositoryURL); const knownHostsPath = path.join(SSH_PATH, "known_hosts"); @@ -181,6 +184,7 @@ const addHostToKnownHosts = async (repositoryURL: string) => { }; const addHostToKnownHostsCommand = (repositoryURL: string) => { + const { SSH_PATH } = paths(); const { domain, port } = sanitizeRepoPathSSH(repositoryURL); const knownHostsPath = path.join(SSH_PATH, "known_hosts"); @@ -237,6 +241,7 @@ export const cloneGitRawRepository = async (entity: { }); } + const { SSH_PATH, COMPOSE_PATH } = paths(); const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); @@ -302,6 +307,7 @@ export const cloneRawGitRepositoryRemote = async (compose: Compose) => { }); } + const { SSH_PATH, COMPOSE_PATH } = paths(true); const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); diff --git a/apps/dokploy/server/utils/providers/github.ts b/apps/dokploy/server/utils/providers/github.ts index 64dac022..68a401b2 100644 --- a/apps/dokploy/server/utils/providers/github.ts +++ b/apps/dokploy/server/utils/providers/github.ts @@ -1,6 +1,6 @@ import { createWriteStream } from "node:fs"; import { join } from "node:path"; -import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import type { InferResultType } from "@/server/types/with"; import { createAppAuth } from "@octokit/auth-app"; import { TRPCError } from "@trpc/server"; @@ -79,6 +79,7 @@ export const cloneGithubRepository = async ( logPath: string, isCompose = false, ) => { + const { APPLICATIONS_PATH, COMPOSE_PATH } = paths(); const writeStream = createWriteStream(logPath, { flags: "a" }); const { appName, repository, owner, branch, githubId } = entity; @@ -191,7 +192,7 @@ export const getGithubCloneCommand = async ( await execAsyncRemote(serverId, bashCommand); return; } - + const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); const githubProvider = await findGithubById(githubId); const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); @@ -222,6 +223,7 @@ export const cloneRawGithubRepository = async (entity: Compose) => { message: "GitHub Provider not found", }); } + const { COMPOSE_PATH } = paths(); const githubProvider = await findGithubById(githubId); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); @@ -261,6 +263,8 @@ export const cloneRawGithubRepositoryRemote = async (compose: Compose) => { message: "GitHub Provider not found", }); } + + const { COMPOSE_PATH } = paths(true); const githubProvider = await findGithubById(githubId); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); diff --git a/apps/dokploy/server/utils/providers/gitlab.ts b/apps/dokploy/server/utils/providers/gitlab.ts index 97f70cfe..95649f5d 100644 --- a/apps/dokploy/server/utils/providers/gitlab.ts +++ b/apps/dokploy/server/utils/providers/gitlab.ts @@ -6,7 +6,7 @@ import { findGitlabById, updateGitlab, } from "@/server/api/services/gitlab"; -import { APPLICATIONS_PATH, COMPOSE_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import type { apiGitlabTestConnection } from "@/server/db/schema"; import type { InferResultType } from "@/server/types/with"; import { TRPCError } from "@trpc/server"; @@ -119,6 +119,8 @@ export const cloneGitlabRepository = async ( message: "Error: GitLab repository information is incomplete.", }); } + + const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(); const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); await recreateDirectory(outputPath); @@ -212,6 +214,7 @@ export const getGitlabCloneCommand = async ( return; } + const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true); await refreshGitlabToken(gitlabId); const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH; const outputPath = join(basePath, appName, "code"); @@ -343,7 +346,7 @@ export const cloneRawGitlabRepository = async (entity: Compose) => { } const gitlabProvider = await findGitlabById(gitlabId); - + const { COMPOSE_PATH } = paths(); await refreshGitlabToken(gitlabId); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); @@ -383,7 +386,7 @@ export const cloneRawGitlabRepositoryRemote = async (compose: Compose) => { }); } const gitlabProvider = await findGitlabById(gitlabId); - + const { COMPOSE_PATH } = paths(true); await refreshGitlabToken(gitlabId); const basePath = COMPOSE_PATH; const outputPath = join(basePath, appName, "code"); diff --git a/apps/dokploy/server/utils/providers/raw.ts b/apps/dokploy/server/utils/providers/raw.ts index 7988f95f..f0fdcb5c 100644 --- a/apps/dokploy/server/utils/providers/raw.ts +++ b/apps/dokploy/server/utils/providers/raw.ts @@ -2,12 +2,13 @@ import { createWriteStream } from "node:fs"; import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import type { Compose } from "@/server/api/services/compose"; -import { COMPOSE_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { encodeBase64 } from "../docker/utils"; import { recreateDirectory } from "../filesystem/directory"; import { execAsyncRemote } from "../process/execAsync"; export const createComposeFile = async (compose: Compose, logPath: string) => { + const { COMPOSE_PATH } = paths(); const { appName, composeFile } = compose; const writeStream = createWriteStream(logPath, { flags: "a" }); const outputPath = join(COMPOSE_PATH, appName, "code"); @@ -33,6 +34,7 @@ export const getCreateComposeFileCommand = ( compose: Compose, logPath: string, ) => { + const { COMPOSE_PATH } = paths(true); const { appName, composeFile } = compose; const outputPath = join(COMPOSE_PATH, appName, "code"); const filePath = join(outputPath, "docker-compose.yml"); @@ -47,6 +49,7 @@ export const getCreateComposeFileCommand = ( }; export const createComposeFileRaw = async (compose: Compose) => { + const { COMPOSE_PATH } = paths(); const { appName, composeFile } = compose; const outputPath = join(COMPOSE_PATH, appName, "code"); const filePath = join(outputPath, "docker-compose.yml"); @@ -59,6 +62,7 @@ export const createComposeFileRaw = async (compose: Compose) => { }; export const createComposeFileRawRemote = async (compose: Compose) => { + const { COMPOSE_PATH } = paths(true); const { appName, composeFile, serverId } = compose; const outputPath = join(COMPOSE_PATH, appName, "code"); const filePath = join(outputPath, "docker-compose.yml"); diff --git a/apps/dokploy/server/utils/servers/setup-server.ts b/apps/dokploy/server/utils/servers/setup-server.ts index 09818648..72bd32ad 100644 --- a/apps/dokploy/server/utils/servers/setup-server.ts +++ b/apps/dokploy/server/utils/servers/setup-server.ts @@ -6,7 +6,7 @@ import { updateDeploymentStatus, } from "@/server/api/services/deployment"; import { findServerById } from "@/server/api/services/server"; -import { LOGS_PATH, SSH_PATH, getPaths } from "@/server/constants"; +import { paths } from "@/server/constants"; import { getDefaultMiddlewares, getDefaultServerTraefikConfig, @@ -17,6 +17,7 @@ import { readSSHKey } from "../filesystem/ssh"; export const setupServer = async (serverId: string) => { const server = await findServerById(serverId); + const { LOGS_PATH } = paths(); const slugifyName = slugify(`server ${server.name}`); @@ -29,8 +30,8 @@ export const setupServer = async (serverId: string) => { title: "Setup Server", description: "Setup Server", }); - const writeStream = createWriteStream(deployment.logPath, { flags: "a" }); + const writeStream = createWriteStream(deployment.logPath, { flags: "a" }); try { writeStream.write("\nInstalling Server Dependencies: ✅\n"); await connectToServer(serverId, deployment.logPath); @@ -50,8 +51,18 @@ const connectToServer = async (serverId: string, logPath: string) => { const writeStream = createWriteStream(logPath, { flags: "a" }); const client = new Client(); const server = await findServerById(serverId); - if (!server.sshKeyId) return; + if (!server.sshKeyId) { + writeStream.write("❌ No SSH Key found"); + writeStream.close(); + throw new Error("No SSH Key found"); + } const keys = await readSSHKey(server.sshKeyId); + + if (!keys.privateKey) { + writeStream.write("❌ No SSH Key found"); + writeStream.close(); + throw new Error("No SSH Key found"); + } return new Promise((resolve, reject) => { client .once("ready", () => { @@ -106,23 +117,12 @@ const connectToServer = async (serverId: string, logPath: string) => { }; const setupDirectories = () => { - // const directories = [ - // BASE_PATH, - // MAIN_TRAEFIK_PATH, - // DYNAMIC_TRAEFIK_PATH, - // LOGS_PATH, - // APPLICATIONS_PATH, - // SSH_PATH, - // CERTIFICATES_PATH, - // MONITORING_PATH, - // ]; - - const directories = getPaths("/etc/dokploy"); + const { SSH_PATH } = paths(true); + const directories = Object.values(paths(true)); const createDirsCommand = directories .map((dir) => `mkdir -p "${dir}"`) .join(" && "); - const chmodCommand = `chmod 700 "${SSH_PATH}"`; const command = ` diff --git a/apps/dokploy/server/utils/traefik/application.ts b/apps/dokploy/server/utils/traefik/application.ts index 0f01bb42..86185dc6 100644 --- a/apps/dokploy/server/utils/traefik/application.ts +++ b/apps/dokploy/server/utils/traefik/application.ts @@ -1,7 +1,7 @@ import fs, { writeFileSync } from "node:fs"; import path from "node:path"; import type { Domain } from "@/server/api/services/domain"; -import { DYNAMIC_TRAEFIK_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { dump, load } from "js-yaml"; import { execAsyncRemote } from "../process/execAsync"; import type { FileConfig, HttpLoadBalancerService } from "./file-types"; @@ -39,6 +39,7 @@ export const createTraefikConfig = (appName: string) => { }, }; const yamlStr = dump(config); + const { DYNAMIC_TRAEFIK_PATH } = paths(); fs.mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); writeFileSync( path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`), @@ -52,6 +53,7 @@ export const removeTraefikConfig = async ( serverId?: string | null, ) => { try { + const { DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); if (serverId) { @@ -72,12 +74,14 @@ export const removeTraefikConfigRemote = async ( serverId: string, ) => { try { + const { DYNAMIC_TRAEFIK_PATH } = paths(true); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); await execAsyncRemote(serverId, `rm ${configPath}`); } catch (error) {} }; export const loadOrCreateConfig = (appName: string): FileConfig => { + const { DYNAMIC_TRAEFIK_PATH } = paths(); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); if (fs.existsSync(configPath)) { const yamlStr = fs.readFileSync(configPath, "utf8"); @@ -93,6 +97,7 @@ export const loadOrCreateConfigRemote = async ( serverId: string, appName: string, ) => { + const { DYNAMIC_TRAEFIK_PATH } = paths(true); const fileConfig: FileConfig = { http: { routers: {}, services: {} } }; const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); try { @@ -110,6 +115,7 @@ export const loadOrCreateConfigRemote = async ( }; export const readConfig = (appName: string) => { + const { DYNAMIC_TRAEFIK_PATH } = paths(); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); if (fs.existsSync(configPath)) { const yamlStr = fs.readFileSync(configPath, "utf8"); @@ -119,6 +125,7 @@ export const readConfig = (appName: string) => { }; export const readRemoteConfig = async (serverId: string, appName: string) => { + const { DYNAMIC_TRAEFIK_PATH } = paths(true); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); try { const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`); @@ -130,6 +137,7 @@ export const readRemoteConfig = async (serverId: string, appName: string) => { }; export const readMonitoringConfig = () => { + const { DYNAMIC_TRAEFIK_PATH } = paths(); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, "access.log"); if (fs.existsSync(configPath)) { const yamlStr = fs.readFileSync(configPath, "utf8"); @@ -149,6 +157,7 @@ export const readConfigInPath = (pathFile: string) => { export const writeConfig = (appName: string, traefikConfig: string) => { try { + const { DYNAMIC_TRAEFIK_PATH } = paths(); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); fs.writeFileSync(configPath, traefikConfig, "utf8"); } catch (e) { @@ -162,6 +171,7 @@ export const writeConfigRemote = async ( traefikConfig: string, ) => { try { + const { DYNAMIC_TRAEFIK_PATH } = paths(true); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); await execAsyncRemote(serverId, `echo '${traefikConfig}' > ${configPath}`); } catch (e) { @@ -186,6 +196,7 @@ export const writeTraefikConfig = ( appName: string, ) => { try { + const { DYNAMIC_TRAEFIK_PATH } = paths(); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); const yamlStr = dump(traefikConfig); fs.writeFileSync(configPath, yamlStr, "utf8"); @@ -200,6 +211,7 @@ export const writeTraefikConfigRemote = async ( serverId: string, ) => { try { + const { DYNAMIC_TRAEFIK_PATH } = paths(true); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); const yamlStr = dump(traefikConfig); await execAsyncRemote(serverId, `echo '${yamlStr}' > ${configPath}`); diff --git a/apps/dokploy/server/utils/traefik/middleware.ts b/apps/dokploy/server/utils/traefik/middleware.ts index 33480bf7..df836051 100644 --- a/apps/dokploy/server/utils/traefik/middleware.ts +++ b/apps/dokploy/server/utils/traefik/middleware.ts @@ -1,6 +1,6 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; -import { DYNAMIC_TRAEFIK_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { dump, load } from "js-yaml"; import type { ApplicationNested } from "../builders"; import { execAsyncRemote } from "../process/execAsync"; @@ -69,6 +69,7 @@ export const deleteAllMiddlewares = async (application: ApplicationNested) => { }; export const loadMiddlewares = () => { + const { DYNAMIC_TRAEFIK_PATH } = paths(); const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml"); if (!existsSync(configPath)) { throw new Error(`File not found: ${configPath}`); @@ -79,6 +80,7 @@ export const loadMiddlewares = () => { }; export const loadRemoteMiddlewares = async (serverId: string) => { + const { DYNAMIC_TRAEFIK_PATH } = paths(true); const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml"); try { @@ -98,6 +100,7 @@ export const loadRemoteMiddlewares = async (serverId: string) => { } }; export const writeMiddleware = (config: T) => { + const { DYNAMIC_TRAEFIK_PATH } = paths(); const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml"); const newYamlContent = dump(config); writeFileSync(configPath, newYamlContent, "utf8"); diff --git a/apps/dokploy/server/utils/traefik/registry.ts b/apps/dokploy/server/utils/traefik/registry.ts index 2335179f..eca4401d 100644 --- a/apps/dokploy/server/utils/traefik/registry.ts +++ b/apps/dokploy/server/utils/traefik/registry.ts @@ -1,12 +1,13 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import type { Registry } from "@/server/api/services/registry"; -import { REGISTRY_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { dump, load } from "js-yaml"; import { removeDirectoryIfExistsContent } from "../filesystem/directory"; import type { FileConfig, HttpRouter } from "./file-types"; export const manageRegistry = async (registry: Registry) => { + const { REGISTRY_PATH } = paths(); if (!existsSync(REGISTRY_PATH)) { mkdirSync(REGISTRY_PATH, { recursive: true }); } @@ -36,6 +37,7 @@ export const manageRegistry = async (registry: Registry) => { }; export const removeSelfHostedRegistry = async () => { + const { REGISTRY_PATH } = paths(); await removeDirectoryIfExistsContent(REGISTRY_PATH); }; @@ -60,6 +62,7 @@ const createRegistryRouterConfig = async (registry: Registry) => { }; const loadOrCreateConfig = (): FileConfig => { + const { REGISTRY_PATH } = paths(); const configPath = join(REGISTRY_PATH, "registry.yml"); if (existsSync(configPath)) { const yamlStr = readFileSync(configPath, "utf8"); diff --git a/apps/dokploy/server/utils/traefik/web-server.ts b/apps/dokploy/server/utils/traefik/web-server.ts index 5073383f..2b966e73 100644 --- a/apps/dokploy/server/utils/traefik/web-server.ts +++ b/apps/dokploy/server/utils/traefik/web-server.ts @@ -1,7 +1,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import type { Admin } from "@/server/api/services/admin"; -import { MAIN_TRAEFIK_PATH } from "@/server/constants"; +import { paths } from "@/server/constants"; import { dump, load } from "js-yaml"; import { loadOrCreateConfig, writeTraefikConfig } from "./application"; import type { FileConfig } from "./file-types"; @@ -42,6 +42,7 @@ export const updateServerTraefik = ( export const updateLetsEncryptEmail = (newEmail: string | null) => { try { if (!newEmail) return; + const { MAIN_TRAEFIK_PATH } = paths(); const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml"); const configContent = readFileSync(configPath, "utf8"); const config = load(configContent) as MainTraefikConfig; @@ -58,6 +59,7 @@ export const updateLetsEncryptEmail = (newEmail: string | null) => { }; export const readMainConfig = () => { + const { MAIN_TRAEFIK_PATH } = paths(); const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml"); if (existsSync(configPath)) { const yamlStr = readFileSync(configPath, "utf8"); @@ -68,6 +70,7 @@ export const readMainConfig = () => { export const writeMainConfig = (traefikConfig: string) => { try { + const { MAIN_TRAEFIK_PATH } = paths(); const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml"); writeFileSync(configPath, traefikConfig, "utf8"); } catch (e) { diff --git a/apps/dokploy/server/wss/listen-deployment.ts b/apps/dokploy/server/wss/listen-deployment.ts index ae8dbd78..00fca3ea 100644 --- a/apps/dokploy/server/wss/listen-deployment.ts +++ b/apps/dokploy/server/wss/listen-deployment.ts @@ -43,8 +43,11 @@ export const setupDeploymentLogsWebSocketServer = ( ws.close(); return; } + try { + console.log(serverId); if (serverId) { + console.log("Entre aca"); const server = await findServerById(serverId); if (!server.sshKeyId) return; @@ -87,6 +90,8 @@ export const setupDeploymentLogsWebSocketServer = ( }); }); } else { + console.log("Entre aca2"); + console.log(logPath); const tail = spawn("tail", ["-n", "+1", "-f", logPath]); tail.stdout.on("data", (data) => { diff --git a/apps/dokploy/server/wss/terminal.ts b/apps/dokploy/server/wss/terminal.ts index d86982ff..4d8f03f6 100644 --- a/apps/dokploy/server/wss/terminal.ts +++ b/apps/dokploy/server/wss/terminal.ts @@ -5,7 +5,7 @@ import { publicIpv4, publicIpv6 } from "public-ip"; import { WebSocketServer } from "ws"; import { findServerById } from "../api/services/server"; import { validateWebSocketRequest } from "../auth/auth"; -import { SSH_PATH } from "../constants"; +import { paths } from "../constants"; export const getPublicIpWithFallback = async () => { // @ts-ignore @@ -64,7 +64,7 @@ export const setupTerminalWebSocketServer = ( ws.close(); return; } - + const { SSH_PATH } = paths(); const privateKey = path.join(SSH_PATH, `${server.sshKeyId}_rsa`); const sshCommand = [ "ssh", diff --git a/apps/dokploy/templates/utils/index.ts b/apps/dokploy/templates/utils/index.ts index 325d2dab..9f2345b6 100644 --- a/apps/dokploy/templates/utils/index.ts +++ b/apps/dokploy/templates/utils/index.ts @@ -30,8 +30,7 @@ export const generateRandomDomain = ({ const hash = randomBytes(3).toString("hex"); const slugIp = serverIp.replaceAll(".", "-"); - return ""; - // return `${projectName}-${hash}${process.env.NODE_ENV === "production" || IS_CLOUD ? `-${slugIp}` : ""}.traefik.me`; + return `${projectName}-${hash}${slugIp === "" ? "" : `-${slugIp}`}.traefik.me`; }; export const generateHash = (projectName: string, quantity = 3): string => {