From 53df7d969e9192ebc05a66f948dd0af9a04ca095 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:13:22 -0600 Subject: [PATCH 1/2] refactor: make protected instead of admin --- apps/dokploy/server/api/routers/settings.ts | 1450 +++++++++---------- 1 file changed, 725 insertions(+), 725 deletions(-) diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 449a2233..0224e343 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -1,59 +1,59 @@ import { db } from "@/server/db"; import { - apiAssignDomain, - apiEnableDashboard, - apiModifyTraefikConfig, - apiReadStatsLogs, - apiReadTraefikConfig, - apiSaveSSHKey, - apiServerSchema, - apiTraefikConfig, - apiUpdateDockerCleanup, + apiAssignDomain, + apiEnableDashboard, + apiModifyTraefikConfig, + apiReadStatsLogs, + apiReadTraefikConfig, + apiSaveSSHKey, + apiServerSchema, + apiTraefikConfig, + apiUpdateDockerCleanup, } from "@/server/db/schema"; import { removeJob, schedule } from "@/server/utils/backup"; import { - DEFAULT_UPDATE_DATA, - IS_CLOUD, - canAccessToTraefikFiles, - cleanStoppedContainers, - cleanUpDockerBuilder, - cleanUpSystemPrune, - cleanUpUnusedImages, - cleanUpUnusedVolumes, - execAsync, - execAsyncRemote, - findAdmin, - findAdminById, - findServerById, - getDokployImage, - getDokployImageTag, - getUpdateData, - initializeTraefik, - logRotationManager, - parseRawConfig, - paths, - prepareEnvironmentVariables, - processLogs, - pullLatestRelease, - readConfig, - readConfigInPath, - readDirectory, - readMainConfig, - readMonitoringConfig, - recreateDirectory, - sendDockerCleanupNotifications, - spawnAsync, - startService, - startServiceRemote, - stopService, - stopServiceRemote, - updateAdmin, - updateLetsEncryptEmail, - updateServerById, - updateServerTraefik, - writeConfig, - writeMainConfig, - writeTraefikConfigInPath, + DEFAULT_UPDATE_DATA, + IS_CLOUD, + canAccessToTraefikFiles, + cleanStoppedContainers, + cleanUpDockerBuilder, + cleanUpSystemPrune, + cleanUpUnusedImages, + cleanUpUnusedVolumes, + execAsync, + execAsyncRemote, + findAdmin, + findAdminById, + findServerById, + getDokployImage, + getDokployImageTag, + getUpdateData, + initializeTraefik, + logRotationManager, + parseRawConfig, + paths, + prepareEnvironmentVariables, + processLogs, + pullLatestRelease, + readConfig, + readConfigInPath, + readDirectory, + readMainConfig, + readMonitoringConfig, + recreateDirectory, + sendDockerCleanupNotifications, + spawnAsync, + startService, + startServiceRemote, + stopService, + stopServiceRemote, + updateAdmin, + updateLetsEncryptEmail, + updateServerById, + updateServerTraefik, + writeConfig, + writeMainConfig, + writeTraefikConfigInPath, } from "@dokploy/server"; import { checkGPUStatus, setupGPUSupport } from "@dokploy/server"; import { generateOpenApiDocument } from "@dokploy/trpc-openapi"; @@ -65,736 +65,736 @@ import { z } from "zod"; import packageInfo from "../../../package.json"; import { appRouter } from "../root"; import { - adminProcedure, - createTRPCRouter, - protectedProcedure, - publicProcedure, + adminProcedure, + createTRPCRouter, + protectedProcedure, + publicProcedure, } from "../trpc"; export const settingsRouter = createTRPCRouter({ - reloadServer: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } - const { stdout } = await execAsync( - "docker service inspect dokploy --format '{{.ID}}'", - ); - await execAsync(`docker service update --force ${stdout.trim()}`); - return true; - }), - reloadTraefik: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - try { - if (input?.serverId) { - await stopServiceRemote(input.serverId, "dokploy-traefik"); - await startServiceRemote(input.serverId, "dokploy-traefik"); - } else if (!IS_CLOUD) { - await stopService("dokploy-traefik"); - await startService("dokploy-traefik"); - } - } catch (err) { - console.error(err); - } + reloadServer: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } + const { stdout } = await execAsync( + "docker service inspect dokploy --format '{{.ID}}'" + ); + await execAsync(`docker service update --force ${stdout.trim()}`); + return true; + }), + reloadTraefik: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + try { + if (input?.serverId) { + await stopServiceRemote(input.serverId, "dokploy-traefik"); + await startServiceRemote(input.serverId, "dokploy-traefik"); + } else if (!IS_CLOUD) { + await stopService("dokploy-traefik"); + await startService("dokploy-traefik"); + } + } catch (err) { + console.error(err); + } - return true; - }), - toggleDashboard: adminProcedure - .input(apiEnableDashboard) - .mutation(async ({ input }) => { - await initializeTraefik({ - enableDashboard: input.enableDashboard, - serverId: input.serverId, - }); - return true; - }), + return true; + }), + toggleDashboard: adminProcedure + .input(apiEnableDashboard) + .mutation(async ({ input }) => { + await initializeTraefik({ + enableDashboard: input.enableDashboard, + serverId: input.serverId, + }); + return true; + }), - cleanUnusedImages: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedImages(input?.serverId); - return true; - }), - cleanUnusedVolumes: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedVolumes(input?.serverId); - return true; - }), - cleanStoppedContainers: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanStoppedContainers(input?.serverId); - return true; - }), - cleanDockerBuilder: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpDockerBuilder(input?.serverId); - }), - cleanDockerPrune: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpSystemPrune(input?.serverId); - await cleanUpDockerBuilder(input?.serverId); + cleanUnusedImages: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedImages(input?.serverId); + return true; + }), + cleanUnusedVolumes: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedVolumes(input?.serverId); + return true; + }), + cleanStoppedContainers: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanStoppedContainers(input?.serverId); + return true; + }), + cleanDockerBuilder: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpDockerBuilder(input?.serverId); + }), + cleanDockerPrune: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpSystemPrune(input?.serverId); + await cleanUpDockerBuilder(input?.serverId); - return true; - }), - cleanAll: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedImages(input?.serverId); - await cleanStoppedContainers(input?.serverId); - await cleanUpDockerBuilder(input?.serverId); - await cleanUpSystemPrune(input?.serverId); + return true; + }), + cleanAll: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedImages(input?.serverId); + await cleanStoppedContainers(input?.serverId); + await cleanUpDockerBuilder(input?.serverId); + await cleanUpSystemPrune(input?.serverId); - return true; - }), - cleanMonitoring: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } - const { MONITORING_PATH } = paths(); - await recreateDirectory(MONITORING_PATH); - return true; - }), - saveSSHPrivateKey: adminProcedure - .input(apiSaveSSHKey) - .mutation(async ({ input, ctx }) => { - if (IS_CLOUD) { - return true; - } - await updateAdmin(ctx.user.authId, { - sshPrivateKey: input.sshPrivateKey, - }); + return true; + }), + cleanMonitoring: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } + const { MONITORING_PATH } = paths(); + await recreateDirectory(MONITORING_PATH); + return true; + }), + saveSSHPrivateKey: adminProcedure + .input(apiSaveSSHKey) + .mutation(async ({ input, ctx }) => { + if (IS_CLOUD) { + return true; + } + await updateAdmin(ctx.user.authId, { + sshPrivateKey: input.sshPrivateKey, + }); - return true; - }), - assignDomainServer: adminProcedure - .input(apiAssignDomain) - .mutation(async ({ ctx, input }) => { - if (IS_CLOUD) { - return true; - } - const admin = await updateAdmin(ctx.user.authId, { - host: input.host, - ...(input.letsEncryptEmail && { - letsEncryptEmail: input.letsEncryptEmail, - }), - certificateType: input.certificateType, - }); + return true; + }), + assignDomainServer: adminProcedure + .input(apiAssignDomain) + .mutation(async ({ ctx, input }) => { + if (IS_CLOUD) { + return true; + } + const admin = await updateAdmin(ctx.user.authId, { + host: input.host, + ...(input.letsEncryptEmail && { + letsEncryptEmail: input.letsEncryptEmail, + }), + certificateType: input.certificateType, + }); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } + if (!admin) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Admin not found", + }); + } - updateServerTraefik(admin, input.host); - if (input.letsEncryptEmail) { - updateLetsEncryptEmail(input.letsEncryptEmail); - } + updateServerTraefik(admin, input.host); + if (input.letsEncryptEmail) { + updateLetsEncryptEmail(input.letsEncryptEmail); + } - return admin; - }), - cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { - if (IS_CLOUD) { - return true; - } - await updateAdmin(ctx.user.authId, { - sshPrivateKey: null, - }); - return true; - }), - updateDockerCleanup: adminProcedure - .input(apiUpdateDockerCleanup) - .mutation(async ({ input, ctx }) => { - if (input.serverId) { - await updateServerById(input.serverId, { - enableDockerCleanup: input.enableDockerCleanup, - }); + return admin; + }), + cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { + if (IS_CLOUD) { + return true; + } + await updateAdmin(ctx.user.authId, { + sshPrivateKey: null, + }); + return true; + }), + updateDockerCleanup: adminProcedure + .input(apiUpdateDockerCleanup) + .mutation(async ({ input, ctx }) => { + if (input.serverId) { + await updateServerById(input.serverId, { + enableDockerCleanup: input.enableDockerCleanup, + }); - const server = await findServerById(input.serverId); + const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this server", - }); - } + if (server.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this server", + }); + } - if (server.enableDockerCleanup) { - const server = await findServerById(input.serverId); - if (server.serverStatus === "inactive") { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server is inactive", - }); - } - if (IS_CLOUD) { - await schedule({ - cronSchedule: "0 0 * * *", - serverId: input.serverId, - type: "server", - }); - } else { - scheduleJob(server.serverId, "0 0 * * *", async () => { - console.log( - `Docker Cleanup ${new Date().toLocaleString()}] Running...`, - ); - await cleanUpUnusedImages(server.serverId); - await cleanUpDockerBuilder(server.serverId); - await cleanUpSystemPrune(server.serverId); - await sendDockerCleanupNotifications(server.adminId); - }); - } - } else { - if (IS_CLOUD) { - await removeJob({ - cronSchedule: "0 0 * * *", - serverId: input.serverId, - type: "server", - }); - } else { - const currentJob = scheduledJobs[server.serverId]; - currentJob?.cancel(); - } - } - } else if (!IS_CLOUD) { - const admin = await findAdminById(ctx.user.adminId); + if (server.enableDockerCleanup) { + const server = await findServerById(input.serverId); + if (server.serverStatus === "inactive") { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Server is inactive", + }); + } + if (IS_CLOUD) { + await schedule({ + cronSchedule: "0 0 * * *", + serverId: input.serverId, + type: "server", + }); + } else { + scheduleJob(server.serverId, "0 0 * * *", async () => { + console.log( + `Docker Cleanup ${new Date().toLocaleString()}] Running...` + ); + await cleanUpUnusedImages(server.serverId); + await cleanUpDockerBuilder(server.serverId); + await cleanUpSystemPrune(server.serverId); + await sendDockerCleanupNotifications(server.adminId); + }); + } + } else { + if (IS_CLOUD) { + await removeJob({ + cronSchedule: "0 0 * * *", + serverId: input.serverId, + type: "server", + }); + } else { + const currentJob = scheduledJobs[server.serverId]; + currentJob?.cancel(); + } + } + } else if (!IS_CLOUD) { + const admin = await findAdminById(ctx.user.adminId); - if (admin.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this admin", - }); - } - const adminUpdated = await updateAdmin(ctx.user.authId, { - enableDockerCleanup: input.enableDockerCleanup, - }); + if (admin.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this admin", + }); + } + const adminUpdated = await updateAdmin(ctx.user.authId, { + enableDockerCleanup: input.enableDockerCleanup, + }); - if (adminUpdated?.enableDockerCleanup) { - scheduleJob("docker-cleanup", "0 0 * * *", async () => { - console.log( - `Docker Cleanup ${new Date().toLocaleString()}] Running...`, - ); - await cleanUpUnusedImages(); - await cleanUpDockerBuilder(); - await cleanUpSystemPrune(); - await sendDockerCleanupNotifications(admin.adminId); - }); - } else { - const currentJob = scheduledJobs["docker-cleanup"]; - currentJob?.cancel(); - } - } + if (adminUpdated?.enableDockerCleanup) { + scheduleJob("docker-cleanup", "0 0 * * *", async () => { + console.log( + `Docker Cleanup ${new Date().toLocaleString()}] Running...` + ); + await cleanUpUnusedImages(); + await cleanUpDockerBuilder(); + await cleanUpSystemPrune(); + await sendDockerCleanupNotifications(admin.adminId); + }); + } else { + const currentJob = scheduledJobs["docker-cleanup"]; + currentJob?.cancel(); + } + } - return true; - }), + return true; + }), - readTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readMainConfig(); - return traefikConfig; - }), + readTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readMainConfig(); + return traefikConfig; + }), - updateTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeMainConfig(input.traefikConfig); - return true; - }), + updateTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeMainConfig(input.traefikConfig); + return true; + }), - readWebServerTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readConfig("dokploy"); - return traefikConfig; - }), - updateWebServerTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeConfig("dokploy", input.traefikConfig); - return true; - }), + readWebServerTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readConfig("dokploy"); + return traefikConfig; + }), + updateWebServerTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeConfig("dokploy", input.traefikConfig); + return true; + }), - readMiddlewareTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readConfig("middlewares"); - return traefikConfig; - }), + readMiddlewareTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readConfig("middlewares"); + return traefikConfig; + }), - updateMiddlewareTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeConfig("middlewares", input.traefikConfig); - return true; - }), - getUpdateData: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return DEFAULT_UPDATE_DATA; - } + updateMiddlewareTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeConfig("middlewares", input.traefikConfig); + return true; + }), + getUpdateData: protectedProcedure.mutation(async () => { + if (IS_CLOUD) { + return DEFAULT_UPDATE_DATA; + } - return await getUpdateData(); - }), - updateServer: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } + return await getUpdateData(); + }), + updateServer: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } - await pullLatestRelease(); + await pullLatestRelease(); - // This causes restart of dokploy, thus it will not finish executing properly, so don't await it - // Status after restart is checked via frontend /api/health endpoint - void spawnAsync("docker", [ - "service", - "update", - "--force", - "--image", - getDokployImage(), - "dokploy", - ]); + // This causes restart of dokploy, thus it will not finish executing properly, so don't await it + // Status after restart is checked via frontend /api/health endpoint + void spawnAsync("docker", [ + "service", + "update", + "--force", + "--image", + getDokployImage(), + "dokploy", + ]); - return true; - }), + return true; + }), - getDokployVersion: adminProcedure.query(() => { - return packageInfo.version; - }), - getReleaseTag: adminProcedure.query(() => { - return getDokployImageTag(); - }), - readDirectories: protectedProcedure - .input(apiServerSchema) - .query(async ({ ctx, input }) => { - try { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + getDokployVersion: protectedProcedure.query(() => { + return packageInfo.version; + }), + getReleaseTag: protectedProcedure.query(() => { + return getDokployImageTag(); + }), + readDirectories: protectedProcedure + .input(apiServerSchema) + .query(async ({ ctx, input }) => { + try { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - const { MAIN_TRAEFIK_PATH } = paths(!!input?.serverId); - const result = await readDirectory(MAIN_TRAEFIK_PATH, input?.serverId); - return result || []; - } catch (error) { - throw error; - } - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + const { MAIN_TRAEFIK_PATH } = paths(!!input?.serverId); + const result = await readDirectory(MAIN_TRAEFIK_PATH, input?.serverId); + return result || []; + } catch (error) { + throw error; + } + }), - updateTraefikFile: protectedProcedure - .input(apiModifyTraefikConfig) - .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + updateTraefikFile: protectedProcedure + .input(apiModifyTraefikConfig) + .mutation(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - await writeTraefikConfigInPath( - input.path, - input.traefikConfig, - input?.serverId, - ); - return true; - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + await writeTraefikConfigInPath( + input.path, + input.traefikConfig, + input?.serverId + ); + return true; + }), - readTraefikFile: protectedProcedure - .input(apiReadTraefikConfig) - .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + readTraefikFile: protectedProcedure + .input(apiReadTraefikConfig) + .query(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - return readConfigInPath(input.path, input.serverId); - }), - getIp: protectedProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - const admin = await findAdmin(); - return admin.serverIp; - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + return readConfigInPath(input.path, input.serverId); + }), + getIp: protectedProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + const admin = await findAdmin(); + return admin.serverIp; + }), - getOpenApiDocument: protectedProcedure.query( - async ({ ctx }): Promise => { - const protocol = ctx.req.headers["x-forwarded-proto"]; - const url = `${protocol}://${ctx.req.headers.host}/api`; - const openApiDocument = generateOpenApiDocument(appRouter, { - title: "tRPC OpenAPI", - version: "1.0.0", - baseUrl: url, - docsUrl: `${url}/settings.getOpenApiDocument`, - tags: [ - "admin", - "docker", - "compose", - "registry", - "cluster", - "user", - "domain", - "destination", - "backup", - "deployment", - "mounts", - "certificates", - "settings", - "security", - "redirects", - "port", - "project", - "application", - "mysql", - "postgres", - "redis", - "mongo", - "mariadb", - "sshRouter", - "gitProvider", - "bitbucket", - "github", - "gitlab", - ], - }); + getOpenApiDocument: protectedProcedure.query( + async ({ ctx }): Promise => { + const protocol = ctx.req.headers["x-forwarded-proto"]; + const url = `${protocol}://${ctx.req.headers.host}/api`; + const openApiDocument = generateOpenApiDocument(appRouter, { + title: "tRPC OpenAPI", + version: "1.0.0", + baseUrl: url, + docsUrl: `${url}/settings.getOpenApiDocument`, + tags: [ + "admin", + "docker", + "compose", + "registry", + "cluster", + "user", + "domain", + "destination", + "backup", + "deployment", + "mounts", + "certificates", + "settings", + "security", + "redirects", + "port", + "project", + "application", + "mysql", + "postgres", + "redis", + "mongo", + "mariadb", + "sshRouter", + "gitProvider", + "bitbucket", + "github", + "gitlab", + ], + }); - openApiDocument.info = { - title: "Dokploy API", - description: "Endpoints for dokploy", - // TODO: get version from package.json - version: "1.0.0", - }; + openApiDocument.info = { + title: "Dokploy API", + description: "Endpoints for dokploy", + // TODO: get version from package.json + version: "1.0.0", + }; - return openApiDocument; - }, - ), - readTraefikEnv: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = - "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik"; + return openApiDocument; + } + ), + readTraefikEnv: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = + "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik"; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - return result.stdout.trim(); - } - if (!IS_CLOUD) { - const result = await execAsync(command); - return result.stdout.trim(); - } - }), + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + return result.stdout.trim(); + } + if (!IS_CLOUD) { + const result = await execAsync(command); + return result.stdout.trim(); + } + }), - writeTraefikEnv: adminProcedure - .input(z.object({ env: z.string(), serverId: z.string().optional() })) - .mutation(async ({ input }) => { - const envs = prepareEnvironmentVariables(input.env); - await initializeTraefik({ - env: envs, - serverId: input.serverId, - }); + writeTraefikEnv: adminProcedure + .input(z.object({ env: z.string(), serverId: z.string().optional() })) + .mutation(async ({ input }) => { + const envs = prepareEnvironmentVariables(input.env); + await initializeTraefik({ + env: envs, + serverId: input.serverId, + }); - return true; - }), - haveTraefikDashboardPortEnabled: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; + return true; + }), + haveTraefikDashboardPortEnabled: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; - let stdout = ""; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - stdout = result.stdout; - } else if (!IS_CLOUD) { - const result = await execAsync( - "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik", - ); - stdout = result.stdout; - } + let stdout = ""; + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + stdout = result.stdout; + } else if (!IS_CLOUD) { + const result = await execAsync( + "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik" + ); + stdout = result.stdout; + } - const parsed: any[] = JSON.parse(stdout.trim()); - for (const port of parsed) { - if (port.PublishedPort === 8080) { - return true; - } - } + const parsed: any[] = JSON.parse(stdout.trim()); + for (const port of parsed) { + if (port.PublishedPort === 8080) { + return true; + } + } - return false; - }), + return false; + }), - readStatsLogs: adminProcedure - .meta({ - openapi: { - path: "/read-stats-logs", - method: "POST", - override: true, - enabled: false, - }, - }) - .input(apiReadStatsLogs) - .query(({ input }) => { - if (IS_CLOUD) { - return { - data: [], - totalCount: 0, - }; - } - const rawConfig = readMonitoringConfig(); - const parsedConfig = parseRawConfig( - rawConfig as string, - input.page, - input.sort, - input.search, - input.status, - ); + readStatsLogs: adminProcedure + .meta({ + openapi: { + path: "/read-stats-logs", + method: "POST", + override: true, + enabled: false, + }, + }) + .input(apiReadStatsLogs) + .query(({ input }) => { + if (IS_CLOUD) { + return { + data: [], + totalCount: 0, + }; + } + const rawConfig = readMonitoringConfig(); + const parsedConfig = parseRawConfig( + rawConfig as string, + input.page, + input.sort, + input.search, + input.status + ); - return parsedConfig; - }), - readStats: adminProcedure.query(() => { - if (IS_CLOUD) { - return []; - } - const rawConfig = readMonitoringConfig(); - const processedLogs = processLogs(rawConfig as string); - return processedLogs || []; - }), - getLogRotateStatus: adminProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - return await logRotationManager.getStatus(); - }), - toggleLogRotate: adminProcedure - .input( - z.object({ - enable: z.boolean(), - }), - ) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - if (input.enable) { - await logRotationManager.activate(); - } else { - await logRotationManager.deactivate(); - } + return parsedConfig; + }), + readStats: adminProcedure.query(() => { + if (IS_CLOUD) { + return []; + } + const rawConfig = readMonitoringConfig(); + const processedLogs = processLogs(rawConfig as string); + return processedLogs || []; + }), + getLogRotateStatus: adminProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + return await logRotationManager.getStatus(); + }), + toggleLogRotate: adminProcedure + .input( + z.object({ + enable: z.boolean(), + }) + ) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + if (input.enable) { + await logRotationManager.activate(); + } else { + await logRotationManager.deactivate(); + } - return true; - }), - haveActivateRequests: adminProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - const config = readMainConfig(); + return true; + }), + haveActivateRequests: adminProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + const config = readMainConfig(); - if (!config) return false; - const parsedConfig = load(config) as { - accessLog?: { - filePath: string; - }; - }; + if (!config) return false; + const parsedConfig = load(config) as { + accessLog?: { + filePath: string; + }; + }; - return !!parsedConfig?.accessLog?.filePath; - }), - toggleRequests: adminProcedure - .input( - z.object({ - enable: z.boolean(), - }), - ) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - const mainConfig = readMainConfig(); - if (!mainConfig) return false; + return !!parsedConfig?.accessLog?.filePath; + }), + toggleRequests: adminProcedure + .input( + z.object({ + enable: z.boolean(), + }) + ) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + const mainConfig = readMainConfig(); + if (!mainConfig) return false; - const currentConfig = load(mainConfig) as { - accessLog?: { - filePath: string; - }; - }; + const currentConfig = load(mainConfig) as { + accessLog?: { + filePath: string; + }; + }; - if (input.enable) { - const config = { - accessLog: { - filePath: "/etc/dokploy/traefik/dynamic/access.log", - format: "json", - bufferingSize: 100, - filters: { - retryAttempts: true, - minDuration: "10ms", - }, - }, - }; - currentConfig.accessLog = config.accessLog; - } else { - currentConfig.accessLog = undefined; - } + if (input.enable) { + const config = { + accessLog: { + filePath: "/etc/dokploy/traefik/dynamic/access.log", + format: "json", + bufferingSize: 100, + filters: { + retryAttempts: true, + minDuration: "10ms", + }, + }, + }; + currentConfig.accessLog = config.accessLog; + } else { + currentConfig.accessLog = undefined; + } - writeMainConfig(dump(currentConfig)); + writeMainConfig(dump(currentConfig)); - return true; - }), - isCloud: protectedProcedure.query(async () => { - return IS_CLOUD; - }), - health: publicProcedure.query(async () => { - if (IS_CLOUD) { - try { - await db.execute(sql`SELECT 1`); - return { status: "ok" }; - } catch (error) { - console.error("Database connection error:", error); - throw error; - } - } - return { status: "not_cloud" }; - }), - setupGPU: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - }), - ) - .mutation(async ({ input }) => { - if (IS_CLOUD && !input.serverId) { - throw new Error("Select a server to enable the GPU Setup"); - } + return true; + }), + isCloud: protectedProcedure.query(async () => { + return IS_CLOUD; + }), + health: publicProcedure.query(async () => { + if (IS_CLOUD) { + try { + await db.execute(sql`SELECT 1`); + return { status: "ok" }; + } catch (error) { + console.error("Database connection error:", error); + throw error; + } + } + return { status: "not_cloud" }; + }), + setupGPU: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + }) + ) + .mutation(async ({ input }) => { + if (IS_CLOUD && !input.serverId) { + throw new Error("Select a server to enable the GPU Setup"); + } - try { - await setupGPUSupport(input.serverId); - return { success: true }; - } catch (error) { - console.error("GPU Setup Error:", error); - throw error; - } - }), - checkGPUStatus: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - }), - ) - .query(async ({ input }) => { - if (IS_CLOUD && !input.serverId) { - return { - driverInstalled: false, - driverVersion: undefined, - gpuModel: undefined, - runtimeInstalled: false, - runtimeConfigured: false, - cudaSupport: undefined, - cudaVersion: undefined, - memoryInfo: undefined, - availableGPUs: 0, - swarmEnabled: false, - gpuResources: 0, - }; - } + try { + await setupGPUSupport(input.serverId); + return { success: true }; + } catch (error) { + console.error("GPU Setup Error:", error); + throw error; + } + }), + checkGPUStatus: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + }) + ) + .query(async ({ input }) => { + if (IS_CLOUD && !input.serverId) { + return { + driverInstalled: false, + driverVersion: undefined, + gpuModel: undefined, + runtimeInstalled: false, + runtimeConfigured: false, + cudaSupport: undefined, + cudaVersion: undefined, + memoryInfo: undefined, + availableGPUs: 0, + swarmEnabled: false, + gpuResources: 0, + }; + } - try { - return await checkGPUStatus(input.serverId || ""); - } catch (error) { - throw new Error("Failed to check GPU status"); - } - }), - updateTraefikPorts: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - additionalPorts: z.array( - z.object({ - targetPort: z.number(), - publishedPort: z.number(), - publishMode: z.enum(["ingress", "host"]).default("host"), - }), - ), - }), - ) - .mutation(async ({ input }) => { - try { - if (IS_CLOUD && !input.serverId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Please set a serverId to update Traefik ports", - }); - } - await initializeTraefik({ - serverId: input.serverId, - additionalPorts: input.additionalPorts, - }); - return true; - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: - error instanceof Error - ? error.message - : "Error updating Traefik ports", - cause: error, - }); - } - }), - getTraefikPorts: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; + try { + return await checkGPUStatus(input.serverId || ""); + } catch (error) { + throw new Error("Failed to check GPU status"); + } + }), + updateTraefikPorts: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + additionalPorts: z.array( + z.object({ + targetPort: z.number(), + publishedPort: z.number(), + publishMode: z.enum(["ingress", "host"]).default("host"), + }) + ), + }) + ) + .mutation(async ({ input }) => { + try { + if (IS_CLOUD && !input.serverId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Please set a serverId to update Traefik ports", + }); + } + await initializeTraefik({ + serverId: input.serverId, + additionalPorts: input.additionalPorts, + }); + return true; + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: + error instanceof Error + ? error.message + : "Error updating Traefik ports", + cause: error, + }); + } + }), + getTraefikPorts: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; - try { - let stdout = ""; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - stdout = result.stdout; - } else if (!IS_CLOUD) { - const result = await execAsync(command); - stdout = result.stdout; - } + try { + let stdout = ""; + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + stdout = result.stdout; + } else if (!IS_CLOUD) { + const result = await execAsync(command); + stdout = result.stdout; + } - const ports: { - Protocol: string; - TargetPort: number; - PublishedPort: number; - PublishMode: string; - }[] = JSON.parse(stdout.trim()); + const ports: { + Protocol: string; + TargetPort: number; + PublishedPort: number; + PublishMode: string; + }[] = JSON.parse(stdout.trim()); - // Filter out the default ports (80, 443, and optionally 8080) - const additionalPorts = ports - .filter((port) => ![80, 443, 8080].includes(port.PublishedPort)) - .map((port) => ({ - targetPort: port.TargetPort, - publishedPort: port.PublishedPort, - publishMode: port.PublishMode.toLowerCase() as "host" | "ingress", - })); + // Filter out the default ports (80, 443, and optionally 8080) + const additionalPorts = ports + .filter((port) => ![80, 443, 8080].includes(port.PublishedPort)) + .map((port) => ({ + targetPort: port.TargetPort, + publishedPort: port.PublishedPort, + publishMode: port.PublishMode.toLowerCase() as "host" | "ingress", + })); - return additionalPorts; - } catch (error) { - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: "Failed to get Traefik ports", - cause: error, - }); - } - }), + return additionalPorts; + } catch (error) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to get Traefik ports", + cause: error, + }); + } + }), }); // { // "Parallelism": 1, From 02ff507094dcb5e5d7ae819278b74fbc681a75b5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:14:30 -0600 Subject: [PATCH 2/2] refactor: update lint --- apps/dokploy/server/api/routers/settings.ts | 1450 +++++++++---------- 1 file changed, 725 insertions(+), 725 deletions(-) diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 0224e343..cb0e32d9 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -1,59 +1,59 @@ import { db } from "@/server/db"; import { - apiAssignDomain, - apiEnableDashboard, - apiModifyTraefikConfig, - apiReadStatsLogs, - apiReadTraefikConfig, - apiSaveSSHKey, - apiServerSchema, - apiTraefikConfig, - apiUpdateDockerCleanup, + apiAssignDomain, + apiEnableDashboard, + apiModifyTraefikConfig, + apiReadStatsLogs, + apiReadTraefikConfig, + apiSaveSSHKey, + apiServerSchema, + apiTraefikConfig, + apiUpdateDockerCleanup, } from "@/server/db/schema"; import { removeJob, schedule } from "@/server/utils/backup"; import { - DEFAULT_UPDATE_DATA, - IS_CLOUD, - canAccessToTraefikFiles, - cleanStoppedContainers, - cleanUpDockerBuilder, - cleanUpSystemPrune, - cleanUpUnusedImages, - cleanUpUnusedVolumes, - execAsync, - execAsyncRemote, - findAdmin, - findAdminById, - findServerById, - getDokployImage, - getDokployImageTag, - getUpdateData, - initializeTraefik, - logRotationManager, - parseRawConfig, - paths, - prepareEnvironmentVariables, - processLogs, - pullLatestRelease, - readConfig, - readConfigInPath, - readDirectory, - readMainConfig, - readMonitoringConfig, - recreateDirectory, - sendDockerCleanupNotifications, - spawnAsync, - startService, - startServiceRemote, - stopService, - stopServiceRemote, - updateAdmin, - updateLetsEncryptEmail, - updateServerById, - updateServerTraefik, - writeConfig, - writeMainConfig, - writeTraefikConfigInPath, + DEFAULT_UPDATE_DATA, + IS_CLOUD, + canAccessToTraefikFiles, + cleanStoppedContainers, + cleanUpDockerBuilder, + cleanUpSystemPrune, + cleanUpUnusedImages, + cleanUpUnusedVolumes, + execAsync, + execAsyncRemote, + findAdmin, + findAdminById, + findServerById, + getDokployImage, + getDokployImageTag, + getUpdateData, + initializeTraefik, + logRotationManager, + parseRawConfig, + paths, + prepareEnvironmentVariables, + processLogs, + pullLatestRelease, + readConfig, + readConfigInPath, + readDirectory, + readMainConfig, + readMonitoringConfig, + recreateDirectory, + sendDockerCleanupNotifications, + spawnAsync, + startService, + startServiceRemote, + stopService, + stopServiceRemote, + updateAdmin, + updateLetsEncryptEmail, + updateServerById, + updateServerTraefik, + writeConfig, + writeMainConfig, + writeTraefikConfigInPath, } from "@dokploy/server"; import { checkGPUStatus, setupGPUSupport } from "@dokploy/server"; import { generateOpenApiDocument } from "@dokploy/trpc-openapi"; @@ -65,736 +65,736 @@ import { z } from "zod"; import packageInfo from "../../../package.json"; import { appRouter } from "../root"; import { - adminProcedure, - createTRPCRouter, - protectedProcedure, - publicProcedure, + adminProcedure, + createTRPCRouter, + protectedProcedure, + publicProcedure, } from "../trpc"; export const settingsRouter = createTRPCRouter({ - reloadServer: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } - const { stdout } = await execAsync( - "docker service inspect dokploy --format '{{.ID}}'" - ); - await execAsync(`docker service update --force ${stdout.trim()}`); - return true; - }), - reloadTraefik: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - try { - if (input?.serverId) { - await stopServiceRemote(input.serverId, "dokploy-traefik"); - await startServiceRemote(input.serverId, "dokploy-traefik"); - } else if (!IS_CLOUD) { - await stopService("dokploy-traefik"); - await startService("dokploy-traefik"); - } - } catch (err) { - console.error(err); - } + reloadServer: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } + const { stdout } = await execAsync( + "docker service inspect dokploy --format '{{.ID}}'", + ); + await execAsync(`docker service update --force ${stdout.trim()}`); + return true; + }), + reloadTraefik: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + try { + if (input?.serverId) { + await stopServiceRemote(input.serverId, "dokploy-traefik"); + await startServiceRemote(input.serverId, "dokploy-traefik"); + } else if (!IS_CLOUD) { + await stopService("dokploy-traefik"); + await startService("dokploy-traefik"); + } + } catch (err) { + console.error(err); + } - return true; - }), - toggleDashboard: adminProcedure - .input(apiEnableDashboard) - .mutation(async ({ input }) => { - await initializeTraefik({ - enableDashboard: input.enableDashboard, - serverId: input.serverId, - }); - return true; - }), + return true; + }), + toggleDashboard: adminProcedure + .input(apiEnableDashboard) + .mutation(async ({ input }) => { + await initializeTraefik({ + enableDashboard: input.enableDashboard, + serverId: input.serverId, + }); + return true; + }), - cleanUnusedImages: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedImages(input?.serverId); - return true; - }), - cleanUnusedVolumes: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedVolumes(input?.serverId); - return true; - }), - cleanStoppedContainers: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanStoppedContainers(input?.serverId); - return true; - }), - cleanDockerBuilder: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpDockerBuilder(input?.serverId); - }), - cleanDockerPrune: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpSystemPrune(input?.serverId); - await cleanUpDockerBuilder(input?.serverId); + cleanUnusedImages: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedImages(input?.serverId); + return true; + }), + cleanUnusedVolumes: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedVolumes(input?.serverId); + return true; + }), + cleanStoppedContainers: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanStoppedContainers(input?.serverId); + return true; + }), + cleanDockerBuilder: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpDockerBuilder(input?.serverId); + }), + cleanDockerPrune: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpSystemPrune(input?.serverId); + await cleanUpDockerBuilder(input?.serverId); - return true; - }), - cleanAll: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedImages(input?.serverId); - await cleanStoppedContainers(input?.serverId); - await cleanUpDockerBuilder(input?.serverId); - await cleanUpSystemPrune(input?.serverId); + return true; + }), + cleanAll: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedImages(input?.serverId); + await cleanStoppedContainers(input?.serverId); + await cleanUpDockerBuilder(input?.serverId); + await cleanUpSystemPrune(input?.serverId); - return true; - }), - cleanMonitoring: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } - const { MONITORING_PATH } = paths(); - await recreateDirectory(MONITORING_PATH); - return true; - }), - saveSSHPrivateKey: adminProcedure - .input(apiSaveSSHKey) - .mutation(async ({ input, ctx }) => { - if (IS_CLOUD) { - return true; - } - await updateAdmin(ctx.user.authId, { - sshPrivateKey: input.sshPrivateKey, - }); + return true; + }), + cleanMonitoring: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } + const { MONITORING_PATH } = paths(); + await recreateDirectory(MONITORING_PATH); + return true; + }), + saveSSHPrivateKey: adminProcedure + .input(apiSaveSSHKey) + .mutation(async ({ input, ctx }) => { + if (IS_CLOUD) { + return true; + } + await updateAdmin(ctx.user.authId, { + sshPrivateKey: input.sshPrivateKey, + }); - return true; - }), - assignDomainServer: adminProcedure - .input(apiAssignDomain) - .mutation(async ({ ctx, input }) => { - if (IS_CLOUD) { - return true; - } - const admin = await updateAdmin(ctx.user.authId, { - host: input.host, - ...(input.letsEncryptEmail && { - letsEncryptEmail: input.letsEncryptEmail, - }), - certificateType: input.certificateType, - }); + return true; + }), + assignDomainServer: adminProcedure + .input(apiAssignDomain) + .mutation(async ({ ctx, input }) => { + if (IS_CLOUD) { + return true; + } + const admin = await updateAdmin(ctx.user.authId, { + host: input.host, + ...(input.letsEncryptEmail && { + letsEncryptEmail: input.letsEncryptEmail, + }), + certificateType: input.certificateType, + }); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } + if (!admin) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Admin not found", + }); + } - updateServerTraefik(admin, input.host); - if (input.letsEncryptEmail) { - updateLetsEncryptEmail(input.letsEncryptEmail); - } + updateServerTraefik(admin, input.host); + if (input.letsEncryptEmail) { + updateLetsEncryptEmail(input.letsEncryptEmail); + } - return admin; - }), - cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { - if (IS_CLOUD) { - return true; - } - await updateAdmin(ctx.user.authId, { - sshPrivateKey: null, - }); - return true; - }), - updateDockerCleanup: adminProcedure - .input(apiUpdateDockerCleanup) - .mutation(async ({ input, ctx }) => { - if (input.serverId) { - await updateServerById(input.serverId, { - enableDockerCleanup: input.enableDockerCleanup, - }); + return admin; + }), + cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { + if (IS_CLOUD) { + return true; + } + await updateAdmin(ctx.user.authId, { + sshPrivateKey: null, + }); + return true; + }), + updateDockerCleanup: adminProcedure + .input(apiUpdateDockerCleanup) + .mutation(async ({ input, ctx }) => { + if (input.serverId) { + await updateServerById(input.serverId, { + enableDockerCleanup: input.enableDockerCleanup, + }); - const server = await findServerById(input.serverId); + const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this server", - }); - } + if (server.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this server", + }); + } - if (server.enableDockerCleanup) { - const server = await findServerById(input.serverId); - if (server.serverStatus === "inactive") { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server is inactive", - }); - } - if (IS_CLOUD) { - await schedule({ - cronSchedule: "0 0 * * *", - serverId: input.serverId, - type: "server", - }); - } else { - scheduleJob(server.serverId, "0 0 * * *", async () => { - console.log( - `Docker Cleanup ${new Date().toLocaleString()}] Running...` - ); - await cleanUpUnusedImages(server.serverId); - await cleanUpDockerBuilder(server.serverId); - await cleanUpSystemPrune(server.serverId); - await sendDockerCleanupNotifications(server.adminId); - }); - } - } else { - if (IS_CLOUD) { - await removeJob({ - cronSchedule: "0 0 * * *", - serverId: input.serverId, - type: "server", - }); - } else { - const currentJob = scheduledJobs[server.serverId]; - currentJob?.cancel(); - } - } - } else if (!IS_CLOUD) { - const admin = await findAdminById(ctx.user.adminId); + if (server.enableDockerCleanup) { + const server = await findServerById(input.serverId); + if (server.serverStatus === "inactive") { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Server is inactive", + }); + } + if (IS_CLOUD) { + await schedule({ + cronSchedule: "0 0 * * *", + serverId: input.serverId, + type: "server", + }); + } else { + scheduleJob(server.serverId, "0 0 * * *", async () => { + console.log( + `Docker Cleanup ${new Date().toLocaleString()}] Running...`, + ); + await cleanUpUnusedImages(server.serverId); + await cleanUpDockerBuilder(server.serverId); + await cleanUpSystemPrune(server.serverId); + await sendDockerCleanupNotifications(server.adminId); + }); + } + } else { + if (IS_CLOUD) { + await removeJob({ + cronSchedule: "0 0 * * *", + serverId: input.serverId, + type: "server", + }); + } else { + const currentJob = scheduledJobs[server.serverId]; + currentJob?.cancel(); + } + } + } else if (!IS_CLOUD) { + const admin = await findAdminById(ctx.user.adminId); - if (admin.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this admin", - }); - } - const adminUpdated = await updateAdmin(ctx.user.authId, { - enableDockerCleanup: input.enableDockerCleanup, - }); + if (admin.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this admin", + }); + } + const adminUpdated = await updateAdmin(ctx.user.authId, { + enableDockerCleanup: input.enableDockerCleanup, + }); - if (adminUpdated?.enableDockerCleanup) { - scheduleJob("docker-cleanup", "0 0 * * *", async () => { - console.log( - `Docker Cleanup ${new Date().toLocaleString()}] Running...` - ); - await cleanUpUnusedImages(); - await cleanUpDockerBuilder(); - await cleanUpSystemPrune(); - await sendDockerCleanupNotifications(admin.adminId); - }); - } else { - const currentJob = scheduledJobs["docker-cleanup"]; - currentJob?.cancel(); - } - } + if (adminUpdated?.enableDockerCleanup) { + scheduleJob("docker-cleanup", "0 0 * * *", async () => { + console.log( + `Docker Cleanup ${new Date().toLocaleString()}] Running...`, + ); + await cleanUpUnusedImages(); + await cleanUpDockerBuilder(); + await cleanUpSystemPrune(); + await sendDockerCleanupNotifications(admin.adminId); + }); + } else { + const currentJob = scheduledJobs["docker-cleanup"]; + currentJob?.cancel(); + } + } - return true; - }), + return true; + }), - readTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readMainConfig(); - return traefikConfig; - }), + readTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readMainConfig(); + return traefikConfig; + }), - updateTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeMainConfig(input.traefikConfig); - return true; - }), + updateTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeMainConfig(input.traefikConfig); + return true; + }), - readWebServerTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readConfig("dokploy"); - return traefikConfig; - }), - updateWebServerTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeConfig("dokploy", input.traefikConfig); - return true; - }), + readWebServerTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readConfig("dokploy"); + return traefikConfig; + }), + updateWebServerTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeConfig("dokploy", input.traefikConfig); + return true; + }), - readMiddlewareTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readConfig("middlewares"); - return traefikConfig; - }), + readMiddlewareTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readConfig("middlewares"); + return traefikConfig; + }), - updateMiddlewareTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeConfig("middlewares", input.traefikConfig); - return true; - }), - getUpdateData: protectedProcedure.mutation(async () => { - if (IS_CLOUD) { - return DEFAULT_UPDATE_DATA; - } + updateMiddlewareTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeConfig("middlewares", input.traefikConfig); + return true; + }), + getUpdateData: protectedProcedure.mutation(async () => { + if (IS_CLOUD) { + return DEFAULT_UPDATE_DATA; + } - return await getUpdateData(); - }), - updateServer: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } + return await getUpdateData(); + }), + updateServer: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } - await pullLatestRelease(); + await pullLatestRelease(); - // This causes restart of dokploy, thus it will not finish executing properly, so don't await it - // Status after restart is checked via frontend /api/health endpoint - void spawnAsync("docker", [ - "service", - "update", - "--force", - "--image", - getDokployImage(), - "dokploy", - ]); + // This causes restart of dokploy, thus it will not finish executing properly, so don't await it + // Status after restart is checked via frontend /api/health endpoint + void spawnAsync("docker", [ + "service", + "update", + "--force", + "--image", + getDokployImage(), + "dokploy", + ]); - return true; - }), + return true; + }), - getDokployVersion: protectedProcedure.query(() => { - return packageInfo.version; - }), - getReleaseTag: protectedProcedure.query(() => { - return getDokployImageTag(); - }), - readDirectories: protectedProcedure - .input(apiServerSchema) - .query(async ({ ctx, input }) => { - try { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + getDokployVersion: protectedProcedure.query(() => { + return packageInfo.version; + }), + getReleaseTag: protectedProcedure.query(() => { + return getDokployImageTag(); + }), + readDirectories: protectedProcedure + .input(apiServerSchema) + .query(async ({ ctx, input }) => { + try { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - const { MAIN_TRAEFIK_PATH } = paths(!!input?.serverId); - const result = await readDirectory(MAIN_TRAEFIK_PATH, input?.serverId); - return result || []; - } catch (error) { - throw error; - } - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + const { MAIN_TRAEFIK_PATH } = paths(!!input?.serverId); + const result = await readDirectory(MAIN_TRAEFIK_PATH, input?.serverId); + return result || []; + } catch (error) { + throw error; + } + }), - updateTraefikFile: protectedProcedure - .input(apiModifyTraefikConfig) - .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + updateTraefikFile: protectedProcedure + .input(apiModifyTraefikConfig) + .mutation(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - await writeTraefikConfigInPath( - input.path, - input.traefikConfig, - input?.serverId - ); - return true; - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + await writeTraefikConfigInPath( + input.path, + input.traefikConfig, + input?.serverId, + ); + return true; + }), - readTraefikFile: protectedProcedure - .input(apiReadTraefikConfig) - .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + readTraefikFile: protectedProcedure + .input(apiReadTraefikConfig) + .query(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - return readConfigInPath(input.path, input.serverId); - }), - getIp: protectedProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - const admin = await findAdmin(); - return admin.serverIp; - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + return readConfigInPath(input.path, input.serverId); + }), + getIp: protectedProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + const admin = await findAdmin(); + return admin.serverIp; + }), - getOpenApiDocument: protectedProcedure.query( - async ({ ctx }): Promise => { - const protocol = ctx.req.headers["x-forwarded-proto"]; - const url = `${protocol}://${ctx.req.headers.host}/api`; - const openApiDocument = generateOpenApiDocument(appRouter, { - title: "tRPC OpenAPI", - version: "1.0.0", - baseUrl: url, - docsUrl: `${url}/settings.getOpenApiDocument`, - tags: [ - "admin", - "docker", - "compose", - "registry", - "cluster", - "user", - "domain", - "destination", - "backup", - "deployment", - "mounts", - "certificates", - "settings", - "security", - "redirects", - "port", - "project", - "application", - "mysql", - "postgres", - "redis", - "mongo", - "mariadb", - "sshRouter", - "gitProvider", - "bitbucket", - "github", - "gitlab", - ], - }); + getOpenApiDocument: protectedProcedure.query( + async ({ ctx }): Promise => { + const protocol = ctx.req.headers["x-forwarded-proto"]; + const url = `${protocol}://${ctx.req.headers.host}/api`; + const openApiDocument = generateOpenApiDocument(appRouter, { + title: "tRPC OpenAPI", + version: "1.0.0", + baseUrl: url, + docsUrl: `${url}/settings.getOpenApiDocument`, + tags: [ + "admin", + "docker", + "compose", + "registry", + "cluster", + "user", + "domain", + "destination", + "backup", + "deployment", + "mounts", + "certificates", + "settings", + "security", + "redirects", + "port", + "project", + "application", + "mysql", + "postgres", + "redis", + "mongo", + "mariadb", + "sshRouter", + "gitProvider", + "bitbucket", + "github", + "gitlab", + ], + }); - openApiDocument.info = { - title: "Dokploy API", - description: "Endpoints for dokploy", - // TODO: get version from package.json - version: "1.0.0", - }; + openApiDocument.info = { + title: "Dokploy API", + description: "Endpoints for dokploy", + // TODO: get version from package.json + version: "1.0.0", + }; - return openApiDocument; - } - ), - readTraefikEnv: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = - "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik"; + return openApiDocument; + }, + ), + readTraefikEnv: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = + "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik"; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - return result.stdout.trim(); - } - if (!IS_CLOUD) { - const result = await execAsync(command); - return result.stdout.trim(); - } - }), + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + return result.stdout.trim(); + } + if (!IS_CLOUD) { + const result = await execAsync(command); + return result.stdout.trim(); + } + }), - writeTraefikEnv: adminProcedure - .input(z.object({ env: z.string(), serverId: z.string().optional() })) - .mutation(async ({ input }) => { - const envs = prepareEnvironmentVariables(input.env); - await initializeTraefik({ - env: envs, - serverId: input.serverId, - }); + writeTraefikEnv: adminProcedure + .input(z.object({ env: z.string(), serverId: z.string().optional() })) + .mutation(async ({ input }) => { + const envs = prepareEnvironmentVariables(input.env); + await initializeTraefik({ + env: envs, + serverId: input.serverId, + }); - return true; - }), - haveTraefikDashboardPortEnabled: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; + return true; + }), + haveTraefikDashboardPortEnabled: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; - let stdout = ""; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - stdout = result.stdout; - } else if (!IS_CLOUD) { - const result = await execAsync( - "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik" - ); - stdout = result.stdout; - } + let stdout = ""; + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + stdout = result.stdout; + } else if (!IS_CLOUD) { + const result = await execAsync( + "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik", + ); + stdout = result.stdout; + } - const parsed: any[] = JSON.parse(stdout.trim()); - for (const port of parsed) { - if (port.PublishedPort === 8080) { - return true; - } - } + const parsed: any[] = JSON.parse(stdout.trim()); + for (const port of parsed) { + if (port.PublishedPort === 8080) { + return true; + } + } - return false; - }), + return false; + }), - readStatsLogs: adminProcedure - .meta({ - openapi: { - path: "/read-stats-logs", - method: "POST", - override: true, - enabled: false, - }, - }) - .input(apiReadStatsLogs) - .query(({ input }) => { - if (IS_CLOUD) { - return { - data: [], - totalCount: 0, - }; - } - const rawConfig = readMonitoringConfig(); - const parsedConfig = parseRawConfig( - rawConfig as string, - input.page, - input.sort, - input.search, - input.status - ); + readStatsLogs: adminProcedure + .meta({ + openapi: { + path: "/read-stats-logs", + method: "POST", + override: true, + enabled: false, + }, + }) + .input(apiReadStatsLogs) + .query(({ input }) => { + if (IS_CLOUD) { + return { + data: [], + totalCount: 0, + }; + } + const rawConfig = readMonitoringConfig(); + const parsedConfig = parseRawConfig( + rawConfig as string, + input.page, + input.sort, + input.search, + input.status, + ); - return parsedConfig; - }), - readStats: adminProcedure.query(() => { - if (IS_CLOUD) { - return []; - } - const rawConfig = readMonitoringConfig(); - const processedLogs = processLogs(rawConfig as string); - return processedLogs || []; - }), - getLogRotateStatus: adminProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - return await logRotationManager.getStatus(); - }), - toggleLogRotate: adminProcedure - .input( - z.object({ - enable: z.boolean(), - }) - ) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - if (input.enable) { - await logRotationManager.activate(); - } else { - await logRotationManager.deactivate(); - } + return parsedConfig; + }), + readStats: adminProcedure.query(() => { + if (IS_CLOUD) { + return []; + } + const rawConfig = readMonitoringConfig(); + const processedLogs = processLogs(rawConfig as string); + return processedLogs || []; + }), + getLogRotateStatus: adminProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + return await logRotationManager.getStatus(); + }), + toggleLogRotate: adminProcedure + .input( + z.object({ + enable: z.boolean(), + }), + ) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + if (input.enable) { + await logRotationManager.activate(); + } else { + await logRotationManager.deactivate(); + } - return true; - }), - haveActivateRequests: adminProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - const config = readMainConfig(); + return true; + }), + haveActivateRequests: adminProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + const config = readMainConfig(); - if (!config) return false; - const parsedConfig = load(config) as { - accessLog?: { - filePath: string; - }; - }; + if (!config) return false; + const parsedConfig = load(config) as { + accessLog?: { + filePath: string; + }; + }; - return !!parsedConfig?.accessLog?.filePath; - }), - toggleRequests: adminProcedure - .input( - z.object({ - enable: z.boolean(), - }) - ) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - const mainConfig = readMainConfig(); - if (!mainConfig) return false; + return !!parsedConfig?.accessLog?.filePath; + }), + toggleRequests: adminProcedure + .input( + z.object({ + enable: z.boolean(), + }), + ) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + const mainConfig = readMainConfig(); + if (!mainConfig) return false; - const currentConfig = load(mainConfig) as { - accessLog?: { - filePath: string; - }; - }; + const currentConfig = load(mainConfig) as { + accessLog?: { + filePath: string; + }; + }; - if (input.enable) { - const config = { - accessLog: { - filePath: "/etc/dokploy/traefik/dynamic/access.log", - format: "json", - bufferingSize: 100, - filters: { - retryAttempts: true, - minDuration: "10ms", - }, - }, - }; - currentConfig.accessLog = config.accessLog; - } else { - currentConfig.accessLog = undefined; - } + if (input.enable) { + const config = { + accessLog: { + filePath: "/etc/dokploy/traefik/dynamic/access.log", + format: "json", + bufferingSize: 100, + filters: { + retryAttempts: true, + minDuration: "10ms", + }, + }, + }; + currentConfig.accessLog = config.accessLog; + } else { + currentConfig.accessLog = undefined; + } - writeMainConfig(dump(currentConfig)); + writeMainConfig(dump(currentConfig)); - return true; - }), - isCloud: protectedProcedure.query(async () => { - return IS_CLOUD; - }), - health: publicProcedure.query(async () => { - if (IS_CLOUD) { - try { - await db.execute(sql`SELECT 1`); - return { status: "ok" }; - } catch (error) { - console.error("Database connection error:", error); - throw error; - } - } - return { status: "not_cloud" }; - }), - setupGPU: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - }) - ) - .mutation(async ({ input }) => { - if (IS_CLOUD && !input.serverId) { - throw new Error("Select a server to enable the GPU Setup"); - } + return true; + }), + isCloud: protectedProcedure.query(async () => { + return IS_CLOUD; + }), + health: publicProcedure.query(async () => { + if (IS_CLOUD) { + try { + await db.execute(sql`SELECT 1`); + return { status: "ok" }; + } catch (error) { + console.error("Database connection error:", error); + throw error; + } + } + return { status: "not_cloud" }; + }), + setupGPU: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + }), + ) + .mutation(async ({ input }) => { + if (IS_CLOUD && !input.serverId) { + throw new Error("Select a server to enable the GPU Setup"); + } - try { - await setupGPUSupport(input.serverId); - return { success: true }; - } catch (error) { - console.error("GPU Setup Error:", error); - throw error; - } - }), - checkGPUStatus: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - }) - ) - .query(async ({ input }) => { - if (IS_CLOUD && !input.serverId) { - return { - driverInstalled: false, - driverVersion: undefined, - gpuModel: undefined, - runtimeInstalled: false, - runtimeConfigured: false, - cudaSupport: undefined, - cudaVersion: undefined, - memoryInfo: undefined, - availableGPUs: 0, - swarmEnabled: false, - gpuResources: 0, - }; - } + try { + await setupGPUSupport(input.serverId); + return { success: true }; + } catch (error) { + console.error("GPU Setup Error:", error); + throw error; + } + }), + checkGPUStatus: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + }), + ) + .query(async ({ input }) => { + if (IS_CLOUD && !input.serverId) { + return { + driverInstalled: false, + driverVersion: undefined, + gpuModel: undefined, + runtimeInstalled: false, + runtimeConfigured: false, + cudaSupport: undefined, + cudaVersion: undefined, + memoryInfo: undefined, + availableGPUs: 0, + swarmEnabled: false, + gpuResources: 0, + }; + } - try { - return await checkGPUStatus(input.serverId || ""); - } catch (error) { - throw new Error("Failed to check GPU status"); - } - }), - updateTraefikPorts: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - additionalPorts: z.array( - z.object({ - targetPort: z.number(), - publishedPort: z.number(), - publishMode: z.enum(["ingress", "host"]).default("host"), - }) - ), - }) - ) - .mutation(async ({ input }) => { - try { - if (IS_CLOUD && !input.serverId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Please set a serverId to update Traefik ports", - }); - } - await initializeTraefik({ - serverId: input.serverId, - additionalPorts: input.additionalPorts, - }); - return true; - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: - error instanceof Error - ? error.message - : "Error updating Traefik ports", - cause: error, - }); - } - }), - getTraefikPorts: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; + try { + return await checkGPUStatus(input.serverId || ""); + } catch (error) { + throw new Error("Failed to check GPU status"); + } + }), + updateTraefikPorts: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + additionalPorts: z.array( + z.object({ + targetPort: z.number(), + publishedPort: z.number(), + publishMode: z.enum(["ingress", "host"]).default("host"), + }), + ), + }), + ) + .mutation(async ({ input }) => { + try { + if (IS_CLOUD && !input.serverId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Please set a serverId to update Traefik ports", + }); + } + await initializeTraefik({ + serverId: input.serverId, + additionalPorts: input.additionalPorts, + }); + return true; + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: + error instanceof Error + ? error.message + : "Error updating Traefik ports", + cause: error, + }); + } + }), + getTraefikPorts: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; - try { - let stdout = ""; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - stdout = result.stdout; - } else if (!IS_CLOUD) { - const result = await execAsync(command); - stdout = result.stdout; - } + try { + let stdout = ""; + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + stdout = result.stdout; + } else if (!IS_CLOUD) { + const result = await execAsync(command); + stdout = result.stdout; + } - const ports: { - Protocol: string; - TargetPort: number; - PublishedPort: number; - PublishMode: string; - }[] = JSON.parse(stdout.trim()); + const ports: { + Protocol: string; + TargetPort: number; + PublishedPort: number; + PublishMode: string; + }[] = JSON.parse(stdout.trim()); - // Filter out the default ports (80, 443, and optionally 8080) - const additionalPorts = ports - .filter((port) => ![80, 443, 8080].includes(port.PublishedPort)) - .map((port) => ({ - targetPort: port.TargetPort, - publishedPort: port.PublishedPort, - publishMode: port.PublishMode.toLowerCase() as "host" | "ingress", - })); + // Filter out the default ports (80, 443, and optionally 8080) + const additionalPorts = ports + .filter((port) => ![80, 443, 8080].includes(port.PublishedPort)) + .map((port) => ({ + targetPort: port.TargetPort, + publishedPort: port.PublishedPort, + publishMode: port.PublishMode.toLowerCase() as "host" | "ingress", + })); - return additionalPorts; - } catch (error) { - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: "Failed to get Traefik ports", - cause: error, - }); - } - }), + return additionalPorts; + } catch (error) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to get Traefik ports", + cause: error, + }); + } + }), }); // { // "Parallelism": 1,