diff --git a/apps/dokploy/__test__/compose/compose.test.ts b/apps/dokploy/__test__/compose/compose.test.ts index 4c47b111..626c98d4 100644 --- a/apps/dokploy/__test__/compose/compose.test.ts +++ b/apps/dokploy/__test__/compose/compose.test.ts @@ -1,5 +1,5 @@ -import { addSuffixToAllProperties } from "@/server/utils/docker/compose"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { addSuffixToAllProperties } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/config/config-root.test.ts b/apps/dokploy/__test__/compose/config/config-root.test.ts index 7f4311f6..cd45d786 100644 --- a/apps/dokploy/__test__/compose/config/config-root.test.ts +++ b/apps/dokploy/__test__/compose/config/config-root.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToConfigsRoot } from "@/server/utils/docker/compose/configs"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToConfigsRoot } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/config/config-service.test.ts b/apps/dokploy/__test__/compose/config/config-service.test.ts index 4d3051a5..d4d2bd3c 100644 --- a/apps/dokploy/__test__/compose/config/config-service.test.ts +++ b/apps/dokploy/__test__/compose/config/config-service.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToConfigsInServices } from "@/server/utils/docker/compose/configs"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToConfigsInServices } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/config/config.test.ts b/apps/dokploy/__test__/compose/config/config.test.ts index 4677fbe9..b3947f37 100644 --- a/apps/dokploy/__test__/compose/config/config.test.ts +++ b/apps/dokploy/__test__/compose/config/config.test.ts @@ -1,9 +1,9 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; +import { generateRandomHash } from "@dokploy/builders"; import { addSuffixToAllConfigs, addSuffixToConfigsRoot, -} from "@/server/utils/docker/compose/configs"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +} from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts index 9d5c0b52..bd339712 100644 --- a/apps/dokploy/__test__/compose/domain/labels.test.ts +++ b/apps/dokploy/__test__/compose/domain/labels.test.ts @@ -1,5 +1,5 @@ -import type { Domain } from "@/server/api/services/domain"; -import { createDomainLabels } from "@/server/utils/docker/domain"; +import type { Domain } from "@dokploy/builders"; +import { createDomainLabels } from "@dokploy/builders"; import { describe, expect, it } from "vitest"; describe("createDomainLabels", () => { diff --git a/apps/dokploy/__test__/compose/domain/network-root.test.ts b/apps/dokploy/__test__/compose/domain/network-root.test.ts index ca8b1655..dca7728f 100644 --- a/apps/dokploy/__test__/compose/domain/network-root.test.ts +++ b/apps/dokploy/__test__/compose/domain/network-root.test.ts @@ -1,4 +1,4 @@ -import { addDokployNetworkToRoot } from "@/server/utils/docker/domain"; +import { addDokployNetworkToRoot } from "@dokploy/builders"; import { describe, expect, it } from "vitest"; describe("addDokployNetworkToRoot", () => { diff --git a/apps/dokploy/__test__/compose/domain/network-service.test.ts b/apps/dokploy/__test__/compose/domain/network-service.test.ts index 18faf564..58513112 100644 --- a/apps/dokploy/__test__/compose/domain/network-service.test.ts +++ b/apps/dokploy/__test__/compose/domain/network-service.test.ts @@ -1,4 +1,4 @@ -import { addDokployNetworkToService } from "@/server/utils/docker/domain"; +import { addDokployNetworkToService } from "@dokploy/builders"; import { describe, expect, it } from "vitest"; describe("addDokployNetworkToService", () => { diff --git a/apps/dokploy/__test__/compose/network/network-root.test.ts b/apps/dokploy/__test__/compose/network/network-root.test.ts index b5156b17..5d0dcb71 100644 --- a/apps/dokploy/__test__/compose/network/network-root.test.ts +++ b/apps/dokploy/__test__/compose/network/network-root.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToNetworksRoot } from "@/server/utils/docker/compose/network"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToNetworksRoot } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/network/network-service.test.ts b/apps/dokploy/__test__/compose/network/network-service.test.ts index 92890e17..30b33b35 100644 --- a/apps/dokploy/__test__/compose/network/network-service.test.ts +++ b/apps/dokploy/__test__/compose/network/network-service.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNetworks } from "@/server/utils/docker/compose/network"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToServiceNetworks } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/network/network.test.ts b/apps/dokploy/__test__/compose/network/network.test.ts index ba1b1395..8a308e0b 100644 --- a/apps/dokploy/__test__/compose/network/network.test.ts +++ b/apps/dokploy/__test__/compose/network/network.test.ts @@ -1,10 +1,10 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; +import { generateRandomHash } from "@dokploy/builders"; import { addSuffixToAllNetworks, addSuffixToServiceNetworks, -} from "@/server/utils/docker/compose/network"; -import { addSuffixToNetworksRoot } from "@/server/utils/docker/compose/network"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +} from "@dokploy/builders"; +import { addSuffixToNetworksRoot } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts index 3185b966..61325d71 100644 --- a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToSecretsRoot } from "@/server/utils/docker/compose/secrets"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToSecretsRoot } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { dump, load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/secrets/secret-services.test.ts b/apps/dokploy/__test__/compose/secrets/secret-services.test.ts index 4a3df137..c22a5712 100644 --- a/apps/dokploy/__test__/compose/secrets/secret-services.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret-services.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToSecretsInServices } from "@/server/utils/docker/compose/secrets"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToSecretsInServices } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/secrets/secret.test.ts b/apps/dokploy/__test__/compose/secrets/secret.test.ts index ea3552f3..74a8002b 100644 --- a/apps/dokploy/__test__/compose/secrets/secret.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret.test.ts @@ -1,5 +1,5 @@ -import { addSuffixToAllSecrets } from "@/server/utils/docker/compose/secrets"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { addSuffixToAllSecrets } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-container-name.test.ts b/apps/dokploy/__test__/compose/service/service-container-name.test.ts index 623b5381..d6e8eb2e 100644 --- a/apps/dokploy/__test__/compose/service/service-container-name.test.ts +++ b/apps/dokploy/__test__/compose/service/service-container-name.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToServiceNames } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-depends-on.test.ts b/apps/dokploy/__test__/compose/service/service-depends-on.test.ts index 02d88591..7e2fe748 100644 --- a/apps/dokploy/__test__/compose/service/service-depends-on.test.ts +++ b/apps/dokploy/__test__/compose/service/service-depends-on.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToServiceNames } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-extends.test.ts b/apps/dokploy/__test__/compose/service/service-extends.test.ts index 5590005b..051d3483 100644 --- a/apps/dokploy/__test__/compose/service/service-extends.test.ts +++ b/apps/dokploy/__test__/compose/service/service-extends.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToServiceNames } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-links.test.ts b/apps/dokploy/__test__/compose/service/service-links.test.ts index 70697e29..c8e3ab2b 100644 --- a/apps/dokploy/__test__/compose/service/service-links.test.ts +++ b/apps/dokploy/__test__/compose/service/service-links.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToServiceNames } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-names.test.ts b/apps/dokploy/__test__/compose/service/service-names.test.ts index 95910ef0..d2b1b2b2 100644 --- a/apps/dokploy/__test__/compose/service/service-names.test.ts +++ b/apps/dokploy/__test__/compose/service/service-names.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToServiceNames } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service.test.ts b/apps/dokploy/__test__/compose/service/service.test.ts index 02ccaea2..29f8318b 100644 --- a/apps/dokploy/__test__/compose/service/service.test.ts +++ b/apps/dokploy/__test__/compose/service/service.test.ts @@ -1,8 +1,8 @@ import { addSuffixToAllServiceNames, addSuffixToServiceNames, -} from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +} from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts b/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts index 49c2b55e..544b8d69 100644 --- a/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts +++ b/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToServiceNames } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume-2.test.ts b/apps/dokploy/__test__/compose/volume/volume-2.test.ts index 9424bc0e..ee342b57 100644 --- a/apps/dokploy/__test__/compose/volume/volume-2.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume-2.test.ts @@ -1,9 +1,9 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; +import { generateRandomHash } from "@dokploy/builders"; import { addSuffixToAllVolumes, addSuffixToVolumesRoot, -} from "@/server/utils/docker/compose/volume"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +} from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume-root.test.ts b/apps/dokploy/__test__/compose/volume/volume-root.test.ts index 6c1f5d10..683cc3e1 100644 --- a/apps/dokploy/__test__/compose/volume/volume-root.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume-root.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToVolumesRoot } from "@/server/utils/docker/compose/volume"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToVolumesRoot } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume-services.test.ts b/apps/dokploy/__test__/compose/volume/volume-services.test.ts index 1940a4f2..c2e0e6ae 100644 --- a/apps/dokploy/__test__/compose/volume/volume-services.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume-services.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToVolumesInServices } from "@/server/utils/docker/compose/volume"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/builders"; +import { addSuffixToVolumesInServices } from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume.test.ts b/apps/dokploy/__test__/compose/volume/volume.test.ts index 8d799bcd..3160dae2 100644 --- a/apps/dokploy/__test__/compose/volume/volume.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume.test.ts @@ -1,9 +1,9 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; +import { generateRandomHash } from "@dokploy/builders"; import { addSuffixToAllVolumes, addSuffixToVolumesInServices, -} from "@/server/utils/docker/compose/volume"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +} from "@dokploy/builders"; +import type { ComposeSpecification } from "@dokploy/builders"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.test.ts index c411566a..c5960fce 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.test.ts @@ -2,8 +2,8 @@ import fs from "node:fs/promises"; import path from "node:path"; import { paths } from "@/server/constants"; const { APPLICATIONS_PATH } = paths(); -import type { ApplicationNested } from "@/server/utils/builders"; -import { unzipDrop } from "@/server/utils/builders/drop"; +import type { ApplicationNested } from "@dokploy/builders"; +import { unzipDrop } from "@dokploy/builders"; import AdmZip from "adm-zip"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; diff --git a/apps/dokploy/__test__/requests/request.test.ts b/apps/dokploy/__test__/requests/request.test.ts index 0c2e5f67..263f0864 100644 --- a/apps/dokploy/__test__/requests/request.test.ts +++ b/apps/dokploy/__test__/requests/request.test.ts @@ -1,4 +1,4 @@ -import { parseRawConfig, processLogs } from "@/server/utils/access-log/utils"; +import { parseRawConfig, processLogs } from "@dokploy/builders"; import { describe, expect, it } from "vitest"; const sampleLogEntry = `{"ClientAddr":"172.19.0.1:56732","ClientHost":"172.19.0.1","ClientPort":"56732","ClientUsername":"-","DownstreamContentSize":0,"DownstreamStatus":304,"Duration":14729375,"OriginContentSize":0,"OriginDuration":14051833,"OriginStatus":304,"Overhead":677542,"RequestAddr":"s222-umami-c381af.traefik.me","RequestContentSize":0,"RequestCount":122,"RequestHost":"s222-umami-c381af.traefik.me","RequestMethod":"GET","RequestPath":"/dashboard?_rsc=1rugv","RequestPort":"-","RequestProtocol":"HTTP/1.1","RequestScheme":"http","RetryAttempts":0,"RouterName":"s222-umami-60e104-47-web@docker","ServiceAddr":"10.0.1.15:3000","ServiceName":"s222-umami-60e104-47-web@docker","ServiceURL":{"Scheme":"http","Opaque":"","User":null,"Host":"10.0.1.15:3000","Path":"","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":""},"StartLocal":"2024-08-25T04:34:37.306691884Z","StartUTC":"2024-08-25T04:34:37.306691884Z","entryPointName":"web","level":"info","msg":"","time":"2024-08-25T04:34:37Z"}`; diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index 8dd5dbc0..0067903f 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -5,11 +5,12 @@ vi.mock("node:fs", () => ({ default: fs, })); -import type { Admin } from "@/server/api/services/admin"; -import { createDefaultServerTraefikConfig } from "@/server/setup/traefik-setup"; -import { loadOrCreateConfig } from "@/server/utils/traefik/application"; -import type { FileConfig } from "@/server/utils/traefik/file-types"; -import { updateServerTraefik } from "@/server/utils/traefik/web-server"; +import type { Admin, FileConfig } from "@dokploy/builders"; +import { + createDefaultServerTraefikConfig, + loadOrCreateConfig, + updateServerTraefik, +} from "@dokploy/builders"; import { beforeEach, expect, test, vi } from "vitest"; const baseAdmin: Admin = { diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 222f8fd7..eea3c5a9 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -1,7 +1,7 @@ -import type { Domain } from "@/server/api/services/domain"; -import type { Redirect } from "@/server/api/services/redirect"; -import type { ApplicationNested } from "@/server/utils/builders"; -import { createRouterConfig } from "@/server/utils/traefik/domain"; +import type { Domain } from "@dokploy/builders"; +import type { Redirect } from "@dokploy/builders"; +import type { ApplicationNested } from "@dokploy/builders"; +import { createRouterConfig } from "@dokploy/builders"; import { expect, test } from "vitest"; const baseApp: ApplicationNested = { diff --git a/apps/dokploy/components/layouts/navigation-tabs.tsx b/apps/dokploy/components/layouts/navigation-tabs.tsx index decab28a..1ec00dd3 100644 --- a/apps/dokploy/components/layouts/navigation-tabs.tsx +++ b/apps/dokploy/components/layouts/navigation-tabs.tsx @@ -1,6 +1,5 @@ import { AddProject } from "@/components/dashboard/projects/add"; -import type { Auth } from "@/server/api/services/auth"; -import type { User } from "@/server/api/services/user"; +import type { Auth, User } from "@dokploy/builders"; import { api } from "@/utils/api"; import { useRouter } from "next/router"; import { useEffect, useMemo, useState } from "react"; diff --git a/apps/dokploy/pages/api/[...trpc].ts b/apps/dokploy/pages/api/[...trpc].ts index 89c6616c..c5126fe9 100644 --- a/apps/dokploy/pages/api/[...trpc].ts +++ b/apps/dokploy/pages/api/[...trpc].ts @@ -1,7 +1,6 @@ import { appRouter } from "@/server/api/root"; import { createTRPCContext } from "@/server/api/trpc"; -import { validateRequest } from "@/server/auth/auth"; -import { validateBearerToken } from "@/server/auth/token"; +import { validateRequest, validateBearerToken } from "@dokploy/builders"; import { createOpenApiNextHandler } from "@dokploy/trpc-openapi"; import type { NextApiRequest, NextApiResponse } from "next"; @@ -19,7 +18,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { return; } - console.log(user); // @ts-ignore return createOpenApiNextHandler({ router: appRouter, diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 032d6b7e..6d1e912a 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -1,4 +1,4 @@ -import { findAdmin } from "@/server/api/services/admin"; +import { findAdmin } from "@dokploy/builders"; import { db } from "@/server/db"; import { applications, compose, github } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/deployments-queue"; diff --git a/apps/dokploy/pages/api/providers/github/setup.ts b/apps/dokploy/pages/api/providers/github/setup.ts index b9bd51ff..fca11f9b 100644 --- a/apps/dokploy/pages/api/providers/github/setup.ts +++ b/apps/dokploy/pages/api/providers/github/setup.ts @@ -1,4 +1,4 @@ -import { createGithub } from "@/server/api/services/github"; +import { createGithub } from "@dokploy/builders"; import { db } from "@/server/db"; import { github } from "@/server/db/schema"; import { eq } from "drizzle-orm"; diff --git a/apps/dokploy/pages/api/providers/gitlab/callback.ts b/apps/dokploy/pages/api/providers/gitlab/callback.ts index 54260b8b..97feca5d 100644 --- a/apps/dokploy/pages/api/providers/gitlab/callback.ts +++ b/apps/dokploy/pages/api/providers/gitlab/callback.ts @@ -1,4 +1,4 @@ -import { findGitlabById, updateGitlab } from "@/server/api/services/gitlab"; +import { findGitlabById, updateGitlab } from "@dokploy/builders"; import type { NextApiRequest, NextApiResponse } from "next"; export default async function handler( diff --git a/apps/dokploy/pages/api/teapot.ts b/apps/dokploy/pages/api/teapot.ts deleted file mode 100644 index 7485fa79..00000000 --- a/apps/dokploy/pages/api/teapot.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { NextRequest } from "next/server"; -import { renderToString } from "react-dom/server"; -import Page418 from "../hola"; // Importa la página 418 - -export const GET = async (req: NextRequest) => { - // Renderiza el componente de la página 418 como HTML - const htmlContent = renderToString(Page418()); - - // Devuelve la respuesta con el código de estado HTTP 418 - return new Response(htmlContent, { - headers: { - "Content-Type": "text/html", - }, - status: 418, - }); -}; - -export default GET; diff --git a/apps/dokploy/pages/dashboard/docker.tsx b/apps/dokploy/pages/dashboard/docker.tsx index baae4663..fb4e2c92 100644 --- a/apps/dokploy/pages/dashboard/docker.tsx +++ b/apps/dokploy/pages/dashboard/docker.tsx @@ -1,7 +1,7 @@ import { ShowContainers } from "@/components/dashboard/docker/show/show-containers"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/monitoring.tsx b/apps/dokploy/pages/dashboard/monitoring.tsx index f5602a2e..b08540a7 100644 --- a/apps/dokploy/pages/dashboard/monitoring.tsx +++ b/apps/dokploy/pages/dashboard/monitoring.tsx @@ -1,6 +1,6 @@ import { ShowMonitoring } from "@/components/dashboard/monitoring/web-server/show"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index acc4a191..113baef8 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -29,8 +29,8 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { appRouter } from "@/server/api/root"; -import type { findProjectById } from "@/server/api/services/project"; -import { validateRequest } from "@/server/auth/auth"; +import type { findProjectById } from "@dokploy/builders"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { CircuitBoard, FolderInput, GlobeIcon, PlusIcon } from "lucide-react"; 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 5c54b7c4..a2d6f876 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -25,7 +25,7 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { GlobeIcon } from "lucide-react"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index e393343f..40aa5ea8 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -19,7 +19,7 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { CircuitBoard } from "lucide-react"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index 83715394..a34241bc 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -20,7 +20,7 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index cc6753fb..916c4d07 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -20,7 +20,7 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index 5a119f81..a454451c 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -20,7 +20,7 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index 02c5ee09..cd6342ea 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -20,7 +20,7 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index 768b637d..d3059fba 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -19,7 +19,7 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { diff --git a/apps/dokploy/pages/dashboard/projects.tsx b/apps/dokploy/pages/dashboard/projects.tsx index 11bfd1fa..14e70684 100644 --- a/apps/dokploy/pages/dashboard/projects.tsx +++ b/apps/dokploy/pages/dashboard/projects.tsx @@ -1,6 +1,6 @@ import { ShowProjects } from "@/components/dashboard/projects/show"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/requests.tsx b/apps/dokploy/pages/dashboard/requests.tsx index 5427cd70..f5d33f1e 100644 --- a/apps/dokploy/pages/dashboard/requests.tsx +++ b/apps/dokploy/pages/dashboard/requests.tsx @@ -1,6 +1,6 @@ import { ShowRequests } from "@/components/dashboard/requests/show-requests"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; import * as React from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/appearance.tsx b/apps/dokploy/pages/dashboard/settings/appearance.tsx index a20f47cb..af31baaa 100644 --- a/apps/dokploy/pages/dashboard/settings/appearance.tsx +++ b/apps/dokploy/pages/dashboard/settings/appearance.tsx @@ -1,7 +1,7 @@ import { AppearanceForm } from "@/components/dashboard/settings/appearance-form"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/certificates.tsx b/apps/dokploy/pages/dashboard/settings/certificates.tsx index fae471cd..58d8ffa6 100644 --- a/apps/dokploy/pages/dashboard/settings/certificates.tsx +++ b/apps/dokploy/pages/dashboard/settings/certificates.tsx @@ -1,7 +1,7 @@ import { ShowCertificates } from "@/components/dashboard/settings/certificates/show-certificates"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/cluster.tsx b/apps/dokploy/pages/dashboard/settings/cluster.tsx index f7cac840..313fecb6 100644 --- a/apps/dokploy/pages/dashboard/settings/cluster.tsx +++ b/apps/dokploy/pages/dashboard/settings/cluster.tsx @@ -2,7 +2,7 @@ import { ShowNodes } from "@/components/dashboard/settings/cluster/nodes/show-no import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/show-registry"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/destinations.tsx b/apps/dokploy/pages/dashboard/settings/destinations.tsx index b9bde186..31c344d1 100644 --- a/apps/dokploy/pages/dashboard/settings/destinations.tsx +++ b/apps/dokploy/pages/dashboard/settings/destinations.tsx @@ -1,7 +1,7 @@ import { ShowDestinations } from "@/components/dashboard/settings/destination/show-destinations"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/git-providers.tsx b/apps/dokploy/pages/dashboard/settings/git-providers.tsx index 72ac06cc..91bd8f8e 100644 --- a/apps/dokploy/pages/dashboard/settings/git-providers.tsx +++ b/apps/dokploy/pages/dashboard/settings/git-providers.tsx @@ -2,7 +2,7 @@ import { ShowGitProviders } from "@/components/dashboard/settings/git/show-git-p import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/notifications.tsx b/apps/dokploy/pages/dashboard/settings/notifications.tsx index 635a3027..60008c9b 100644 --- a/apps/dokploy/pages/dashboard/settings/notifications.tsx +++ b/apps/dokploy/pages/dashboard/settings/notifications.tsx @@ -2,7 +2,7 @@ import { ShowDestinations } from "@/components/dashboard/settings/destination/sh import { ShowNotifications } from "@/components/dashboard/settings/notifications/show-notifications"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 7dadc3b5..d8580a68 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -2,7 +2,7 @@ import { GenerateToken } from "@/components/dashboard/settings/profile/generate- import { ProfileForm } from "@/components/dashboard/settings/profile/profile-form"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/server.tsx b/apps/dokploy/pages/dashboard/settings/server.tsx index 129aa422..e5458225 100644 --- a/apps/dokploy/pages/dashboard/settings/server.tsx +++ b/apps/dokploy/pages/dashboard/settings/server.tsx @@ -2,7 +2,7 @@ import { WebDomain } from "@/components/dashboard/settings/web-domain"; import { WebServer } from "@/components/dashboard/settings/web-server"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/servers.tsx b/apps/dokploy/pages/dashboard/settings/servers.tsx index 266f264b..1cb9ae7a 100644 --- a/apps/dokploy/pages/dashboard/settings/servers.tsx +++ b/apps/dokploy/pages/dashboard/settings/servers.tsx @@ -1,7 +1,7 @@ import { ShowServers } from "@/components/dashboard/settings/servers/show-servers"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx index 338ae3db..1c0ece80 100644 --- a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx +++ b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx @@ -2,7 +2,7 @@ import { ShowDestinations } from "@/components/dashboard/settings/ssh-keys/show- import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index d36de6d1..0b14214f 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -1,7 +1,7 @@ import { ShowUsers } from "@/components/dashboard/settings/users/show-users"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/dashboard/traefik.tsx b/apps/dokploy/pages/dashboard/traefik.tsx index 228e0551..d17421b0 100644 --- a/apps/dokploy/pages/dashboard/traefik.tsx +++ b/apps/dokploy/pages/dashboard/traefik.tsx @@ -1,7 +1,7 @@ import { ShowTraefikSystem } from "@/components/dashboard/file-system/show-traefik-system"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; diff --git a/apps/dokploy/pages/hola.tsx b/apps/dokploy/pages/hola.tsx deleted file mode 100644 index 65b3d1aa..00000000 --- a/apps/dokploy/pages/hola.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function hola() { - return
hola
; -} diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index f41ce2fa..cca4c628 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -17,8 +17,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { isAdminPresent } from "@/server/api/services/admin"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest, isAdminPresent } from "@dokploy/builders"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import type { GetServerSidePropsContext } from "next"; diff --git a/apps/dokploy/pages/invitation.tsx b/apps/dokploy/pages/invitation.tsx index 1038464d..051214cd 100644 --- a/apps/dokploy/pages/invitation.tsx +++ b/apps/dokploy/pages/invitation.tsx @@ -15,7 +15,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { getUserByToken } from "@/server/api/services/admin"; +import { getUserByToken } from "@dokploy/builders"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { AlertTriangle } from "lucide-react"; diff --git a/apps/dokploy/pages/register.tsx b/apps/dokploy/pages/register.tsx index 57502ce1..4428b6a4 100644 --- a/apps/dokploy/pages/register.tsx +++ b/apps/dokploy/pages/register.tsx @@ -15,7 +15,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { isAdminPresent } from "@/server/api/services/admin"; +import { isAdminPresent } from "@dokploy/builders"; // import { IS_CLOUD } from "@/server/constants"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; diff --git a/apps/dokploy/pages/swagger.tsx b/apps/dokploy/pages/swagger.tsx index 2efe9ad3..f83fae8c 100644 --- a/apps/dokploy/pages/swagger.tsx +++ b/apps/dokploy/pages/swagger.tsx @@ -1,5 +1,5 @@ import { appRouter } from "@/server/api/root"; -import { validateRequest } from "@/server/auth/auth"; +import { validateRequest } from "@dokploy/builders"; import { api } from "@/utils/api"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext, NextPage } from "next"; diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index 999c1384..0bded385 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -1,6 +1,3 @@ -import { lucia, validateRequest } from "@/server/auth/auth"; -import { luciaToken } from "@/server/auth/token"; -// import { IS_CLOUD } from "@/server/constants"; import { apiCreateAdmin, apiCreateUser, @@ -23,6 +20,9 @@ import { generate2FASecret, updateAuthById, verify2FA, + lucia, + validateRequest, + luciaToken, } from "@dokploy/builders"; import { adminProcedure, diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index 5a1328d8..fc310ac6 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -8,6 +8,11 @@ import { mysql, postgres, redis, + apiCreateProject, + apiFindOneProject, + apiRemoveProject, + apiUpdateProject, + projects, } from "@/server/db/schema"; import { TRPCError } from "@trpc/server"; @@ -15,11 +20,6 @@ import { desc, eq, sql } from "drizzle-orm"; import type { AnyPgColumn } from "drizzle-orm/pg-core"; import { - apiCreateProject, - apiFindOneProject, - apiRemoveProject, - apiUpdateProject, - projects, createProject, deleteProject, findProjectById, diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 8f644d09..a1d8eb64 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -51,10 +51,10 @@ import { updateServerById, canAccessToTraefikFiles, getDokployImage, - getDokployVersion, pullLatestRelease, readDirectory, } from "@dokploy/builders"; +import packageInfo from "../../../package.json"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; export const settingsRouter = createTRPCRouter({ @@ -269,7 +269,7 @@ export const settingsRouter = createTRPCRouter({ }), getDokployVersion: adminProcedure.query(() => { - return getDokployVersion(); + return packageInfo.version; }), readDirectories: protectedProcedure .input(apiServerSchema) diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index 1fd4aea1..d02082d7 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -20,8 +20,7 @@ import { import type { Session, User } from "lucia"; import superjson from "superjson"; import { ZodError } from "zod"; -import { validateRequest } from "../auth/auth"; -import { validateBearerToken } from "../auth/token"; +import { validateRequest, validateBearerToken } from "@dokploy/builders"; /** * 1. CONTEXT diff --git a/apps/dokploy/server/auth/auth.ts b/apps/dokploy/server/auth/auth.ts deleted file mode 100644 index 12c07e54..00000000 --- a/apps/dokploy/server/auth/auth.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { webcrypto } from "node:crypto"; -import type { IncomingMessage, ServerResponse } from "node:http"; -import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; -import { TimeSpan } from "lucia"; -import { Lucia } from "lucia/dist/core.js"; -import type { Session, User } from "lucia/dist/core.js"; -import { findAdminByAuthId, findUserByAuthId } from "@dokploy/builders"; -import { db } from "../db"; -import { type DatabaseUser, auth, sessionTable } from "../db/schema"; - -globalThis.crypto = webcrypto as Crypto; -export const adapter = new DrizzlePostgreSQLAdapter(db, sessionTable, auth); - -export const lucia = new Lucia(adapter, { - sessionCookie: { - attributes: { - secure: false, - }, - }, - sessionExpiresIn: new TimeSpan(1, "d"), - getUserAttributes: (attributes) => { - return { - email: attributes.email, - rol: attributes.rol, - secret: attributes.secret !== null, - adminId: attributes.adminId, - }; - }, -}); - -declare module "lucia" { - interface Register { - Lucia: typeof lucia; - DatabaseUserAttributes: Omit & { - authId: string; - adminId: string; - }; - } -} - -export type ReturnValidateToken = Promise<{ - user: (User & { authId: string; adminId: string }) | null; - session: Session | null; -}>; - -export async function validateRequest( - req: IncomingMessage, - res: ServerResponse, -): ReturnValidateToken { - const sessionId = lucia.readSessionCookie(req.headers.cookie ?? ""); - - if (!sessionId) { - return { - user: null, - session: null, - }; - } - const result = await lucia.validateSession(sessionId); - if (result?.session?.fresh) { - res.appendHeader( - "Set-Cookie", - lucia.createSessionCookie(result.session.id).serialize(), - ); - } - if (!result.session) { - res.appendHeader( - "Set-Cookie", - lucia.createBlankSessionCookie().serialize(), - ); - } - - if (result.user) { - if (result.user?.rol === "admin") { - const admin = await findAdminByAuthId(result.user.id); - result.user.adminId = admin.adminId; - } else if (result.user?.rol === "user") { - const userResult = await findUserByAuthId(result.user.id); - result.user.adminId = userResult.adminId; - } - } - - return { - session: result.session, - ...((result.user && { - user: { - authId: result.user.id, - email: result.user.email, - rol: result.user.rol, - id: result.user.id, - secret: result.user.secret, - adminId: result.user.adminId, - }, - }) || { - user: null, - }), - }; -} - -export async function validateWebSocketRequest( - req: IncomingMessage, -): Promise<{ user: User; session: Session } | { user: null; session: null }> { - const sessionId = lucia.readSessionCookie(req.headers.cookie ?? ""); - - if (!sessionId) { - return { - user: null, - session: null, - }; - } - const result = await lucia.validateSession(sessionId); - return result; -} diff --git a/apps/dokploy/server/auth/random-password.ts b/apps/dokploy/server/auth/random-password.ts deleted file mode 100644 index 150104b9..00000000 --- a/apps/dokploy/server/auth/random-password.ts +++ /dev/null @@ -1,20 +0,0 @@ -import bcrypt from "bcrypt"; - -export const generateRandomPassword = async () => { - const passwordLength = 16; - - const characters = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - let randomPassword = ""; - for (let i = 0; i < passwordLength; i++) { - randomPassword += characters.charAt( - Math.floor(Math.random() * characters.length), - ); - } - - const saltRounds = 10; - - const hashedPassword = await bcrypt.hash(randomPassword, saltRounds); - return { randomPassword, hashedPassword }; -}; diff --git a/apps/dokploy/server/auth/token.ts b/apps/dokploy/server/auth/token.ts deleted file mode 100644 index 54162fdc..00000000 --- a/apps/dokploy/server/auth/token.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { IncomingMessage } from "node:http"; -import { TimeSpan } from "lucia"; -import { Lucia } from "lucia/dist/core.js"; -import { type ReturnValidateToken, adapter } from "./auth"; - -export const luciaToken = new Lucia(adapter, { - sessionCookie: { - attributes: { - secure: false, - }, - }, - sessionExpiresIn: new TimeSpan(365, "d"), - getUserAttributes: (attributes) => { - return { - email: attributes.email, - rol: attributes.rol, - secret: attributes.secret !== null, - }; - }, -}); - -export const validateBearerToken = async ( - req: IncomingMessage, -): ReturnValidateToken => { - const authorizationHeader = req.headers.authorization; - const sessionId = luciaToken.readBearerToken(authorizationHeader ?? ""); - if (!sessionId) { - return { - user: null, - session: null, - }; - } - const result = await luciaToken.validateSession(sessionId); - return { - session: result.session, - ...((result.user && { - user: { - adminId: result.user.adminId, - authId: result.user.id, - email: result.user.email, - rol: result.user.rol, - id: result.user.id, - secret: result.user.secret, - }, - }) || { - user: null, - }), - }; -}; diff --git a/apps/dokploy/server/db/schema/index.ts b/apps/dokploy/server/db/schema/index.ts index b93be664..77b2e002 100644 --- a/apps/dokploy/server/db/schema/index.ts +++ b/apps/dokploy/server/db/schema/index.ts @@ -1 +1 @@ -export * from "@dokploy/builders"; +export * from "@dokploy/builders/dist/db/schema"; diff --git a/apps/dokploy/server/monitoring/utilts.ts b/apps/dokploy/server/monitoring/utilts.ts deleted file mode 100644 index f67d5705..00000000 --- a/apps/dokploy/server/monitoring/utilts.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { promises } from "node:fs"; -import type Dockerode from "dockerode"; -import osUtils from "node-os-utils"; -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 }); - - const cpuPercent = calculateCpuUsagePercent( - stats.cpu_stats, - stats.precpu_stats, - ); - const memoryStats = calculateMemoryStats(stats.memory_stats); - const blockIO = calculateBlockIO(stats.blkio_stats); - const networkUsage = calculateNetworkUsage(stats.networks); - - await updateStatsFile(appName, "cpu", cpuPercent); - await updateStatsFile(appName, "memory", { - used: memoryStats.used, - free: memoryStats.free, - usedPercentage: memoryStats.usedPercentage, - total: memoryStats.total, - }); - await updateStatsFile(appName, "block", { - readMb: blockIO.readMb, - writeMb: blockIO.writeMb, - }); - - await updateStatsFile(appName, "network", { - inputMb: networkUsage.inputMb, - outputMb: networkUsage.outputMb, - }); - - if (appName === "dokploy") { - const disk = await osUtils.drive.info("/"); - - const diskUsage = disk.usedGb; - const diskTotal = disk.totalGb; - const diskUsedPercentage = disk.usedPercentage; - const diskFree = disk.freeGb; - - await updateStatsFile(appName, "disk", { - diskTotal: +diskTotal, - diskUsedPercentage: +diskUsedPercentage, - diskUsage: +diskUsage, - diskFree: +diskFree, - }); - } -}; - -export const getAdvancedStats = async (appName: string) => { - return { - cpu: await readStatsFile(appName, "cpu"), - memory: await readStatsFile(appName, "memory"), - disk: await readStatsFile(appName, "disk"), - network: await readStatsFile(appName, "network"), - block: await readStatsFile(appName, "block"), - }; -}; - -export const readStatsFile = async ( - appName: string, - 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); - } catch (error) { - return []; - } -}; - -export const updateStatsFile = async ( - appName: string, - 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() }); - - if (stats.length > 288) { - stats.shift(); - } - - const content = JSON.stringify(stats); - await promises.writeFile( - `${MONITORING_PATH}/${appName}/${statType}.json`, - content, - ); -}; - -export const readLastValueStatsFile = async ( - appName: string, - 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); - return stats[stats.length - 1] || null; - } catch (error) { - return null; - } -}; - -export const getLastAdvancedStatsFile = async (appName: string) => { - return { - cpu: await readLastValueStatsFile(appName, "cpu"), - memory: await readLastValueStatsFile(appName, "memory"), - disk: await readLastValueStatsFile(appName, "disk"), - network: await readLastValueStatsFile(appName, "network"), - block: await readLastValueStatsFile(appName, "block"), - }; -}; - -const calculateCpuUsagePercent = ( - cpu_stats: Dockerode.ContainerStats["cpu_stats"], - precpu_stats: Dockerode.ContainerStats["precpu_stats"], -) => { - const cpuDelta = - cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage; - const systemDelta = - cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage; - - const numberCpus = - cpu_stats.online_cpus || - (cpu_stats.cpu_usage.percpu_usage - ? cpu_stats.cpu_usage.percpu_usage.length - : 1); - - if (systemDelta > 0 && cpuDelta > 0) { - return (cpuDelta / systemDelta) * numberCpus * 100.0; - } - return 0; -}; - -const calculateMemoryStats = ( - memory_stats: Dockerode.ContainerStats["memory_stats"], -) => { - const usedMemory = memory_stats.usage - (memory_stats.stats.cache || 0); - const availableMemory = memory_stats.limit; - const memoryUsedPercentage = (usedMemory / availableMemory) * 100.0; - - return { - used: usedMemory, - free: availableMemory - usedMemory, - usedPercentage: memoryUsedPercentage, - total: availableMemory, - }; -}; -const calculateBlockIO = ( - blkio_stats: Dockerode.ContainerStats["blkio_stats"], -) => { - let readIO = 0; - let writeIO = 0; - if (blkio_stats?.io_service_bytes_recursive) { - for (const io of blkio_stats.io_service_bytes_recursive) { - if (io.op === "read") { - readIO += io.value; - } else if (io.op === "write") { - writeIO += io.value; - } - } - } - return { - readMb: readIO / (1024 * 1024), - writeMb: writeIO / (1024 * 1024), - }; -}; - -const calculateNetworkUsage = ( - networks: Dockerode.ContainerStats["networks"], -) => { - let totalRx = 0; - let totalTx = 0; - - const stats = Object.keys(networks); - - for (const interfaceName of stats) { - const net = networks[interfaceName]; - totalRx += net?.rx_bytes || 0; - totalTx += net?.tx_bytes || 0; - } - return { - inputMb: totalRx / (1024 * 1024), - outputMb: totalTx / (1024 * 1024), - }; -}; diff --git a/apps/dokploy/server/queues/deployments-queue.ts b/apps/dokploy/server/queues/deployments-queue.ts index 07ad258a..c607620c 100644 --- a/apps/dokploy/server/queues/deployments-queue.ts +++ b/apps/dokploy/server/queues/deployments-queue.ts @@ -1,11 +1,4 @@ import { type Job, Worker } from "bullmq"; -// import { -// deployApplication, -// deployRemoteApplication, -// rebuildApplication, -// rebuildRemoteApplication, -// updateApplicationStatus, -// } from "../api/services/application"; import { deployApplication, deployRemoteApplication, diff --git a/apps/dokploy/server/server.ts b/apps/dokploy/server/server.ts index 1f4f67e3..6eb4b350 100644 --- a/apps/dokploy/server/server.ts +++ b/apps/dokploy/server/server.ts @@ -2,7 +2,6 @@ import http from "node:http"; import { migration } from "@/server/db/migration"; import { config } from "dotenv"; import next from "next"; -// import { IS_CLOUD } from "./constants"; import { deploymentWorker } from "./queues/deployments-queue"; import { setupDirectories, @@ -14,8 +13,8 @@ import { createDefaultTraefikConfig, initializeTraefik, initCronJobs, + sendDokployRestartNotifications, } from "@dokploy/builders"; -import { sendDokployRestartNotifications } from "./utils/notifications/dokploy-restart"; import { setupDockerContainerLogsWebSocketServer } from "./wss/docker-container-logs"; import { setupDockerContainerTerminalWebSocketServer } from "./wss/docker-container-terminal"; import { setupDockerStatsMonitoringSocketServer } from "./wss/docker-stats"; diff --git a/apps/dokploy/server/setup/config-paths.ts b/apps/dokploy/server/setup/config-paths.ts deleted file mode 100644 index 190e438b..00000000 --- a/apps/dokploy/server/setup/config-paths.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { chmodSync, existsSync, mkdirSync } from "node:fs"; -import { paths } from "../constants"; - -const createDirectoryIfNotExist = (dirPath: string) => { - if (!existsSync(dirPath)) { - mkdirSync(dirPath, { recursive: true }); - console.log(`Directory created: ${dirPath}`); - } -}; - -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, - DYNAMIC_TRAEFIK_PATH, - LOGS_PATH, - APPLICATIONS_PATH, - SSH_PATH, - CERTIFICATES_PATH, - MONITORING_PATH, - ]; - - for (const dir of directories) { - try { - createDirectoryIfNotExist(dir); - if (dir === SSH_PATH) { - chmodSync(SSH_PATH, "700"); - } - } catch (error) { - console.log(error, " On path: ", dir); - } - } -}; diff --git a/apps/dokploy/server/setup/postgres-setup.ts b/apps/dokploy/server/setup/postgres-setup.ts deleted file mode 100644 index ca606c2d..00000000 --- a/apps/dokploy/server/setup/postgres-setup.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { CreateServiceOptions } from "dockerode"; -import { docker } from "../constants"; -import { pullImage } from "../utils/docker/utils"; -export const initializePostgres = async () => { - const imageName = "postgres:16"; - const containerName = "dokploy-postgres"; - const settings: CreateServiceOptions = { - Name: containerName, - TaskTemplate: { - ContainerSpec: { - Image: imageName, - Env: [ - "POSTGRES_USER=dokploy", - "POSTGRES_DB=dokploy", - "POSTGRES_PASSWORD=amukds4wi9001583845717ad2", - ], - Mounts: [ - { - Type: "volume", - Source: "dokploy-postgres-database", - Target: "/var/lib/postgresql/data", - }, - ], - }, - Networks: [{ Target: "dokploy-network" }], - Placement: { - Constraints: ["node.role==manager"], - }, - }, - Mode: { - Replicated: { - Replicas: 1, - }, - }, - EndpointSpec: { - Ports: [ - { - TargetPort: 5432, - PublishedPort: process.env.NODE_ENV === "development" ? 5432 : 0, - Protocol: "tcp", - PublishMode: "host", - }, - ], - }, - }; - try { - await pullImage(imageName); - - const service = docker.getService(containerName); - const inspect = await service.inspect(); - await service.update({ - version: Number.parseInt(inspect.Version.Index), - ...settings, - }); - - console.log("Postgres Started ✅"); - } catch (error) { - await docker.createService(settings); - console.log("Postgres Not Found: Starting ✅"); - } -}; diff --git a/apps/dokploy/server/setup/redis-setup.ts b/apps/dokploy/server/setup/redis-setup.ts deleted file mode 100644 index abbacdd8..00000000 --- a/apps/dokploy/server/setup/redis-setup.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { CreateServiceOptions } from "dockerode"; -import { docker } from "../constants"; -import { pullImage } from "../utils/docker/utils"; - -export const initializeRedis = async () => { - const imageName = "redis:7"; - const containerName = "dokploy-redis"; - - const settings: CreateServiceOptions = { - Name: containerName, - TaskTemplate: { - ContainerSpec: { - Image: imageName, - Mounts: [ - { - Type: "volume", - Source: "redis-data-volume", - Target: "/data", - }, - ], - }, - Networks: [{ Target: "dokploy-network" }], - Placement: { - Constraints: ["node.role==manager"], - }, - }, - Mode: { - Replicated: { - Replicas: 1, - }, - }, - EndpointSpec: { - Ports: [ - { - TargetPort: 6379, - PublishedPort: process.env.NODE_ENV === "development" ? 6379 : 0, - Protocol: "tcp", - PublishMode: "host", - }, - ], - }, - }; - try { - await pullImage(imageName); - - const service = docker.getService(containerName); - const inspect = await service.inspect(); - await service.update({ - version: Number.parseInt(inspect.Version.Index), - ...settings, - }); - console.log("Redis Started ✅"); - } catch (error) { - await docker.createService(settings); - console.log("Redis Not Found: Starting ✅"); - } -}; diff --git a/apps/dokploy/server/setup/registry-setup.ts b/apps/dokploy/server/setup/registry-setup.ts deleted file mode 100644 index 8b18b108..00000000 --- a/apps/dokploy/server/setup/registry-setup.ts +++ /dev/null @@ -1,91 +0,0 @@ -import type { CreateServiceOptions } from "dockerode"; -import { generateRandomPassword } from "../auth/random-password"; -import { docker, paths } from "../constants"; -import { pullImage } from "../utils/docker/utils"; -import { execAsync } from "../utils/process/execAsync"; - -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); - const randomPass = await generateRandomPassword(); - const settings: CreateServiceOptions = { - Name: containerName, - TaskTemplate: { - ContainerSpec: { - Image: imageName, - Env: [ - "REGISTRY_STORAGE_DELETE_ENABLED=true", - "REGISTRY_AUTH=htpasswd", - "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", - "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd", - `REGISTRY_HTTP_SECRET=${randomPass.hashedPassword}`, - ], - Mounts: [ - { - Type: "bind", - Source: `${REGISTRY_PATH}/htpasswd`, - Target: "/auth/htpasswd", - ReadOnly: true, - }, - { - Type: "volume", - Source: "registry-data", - Target: "/var/lib/registry", - ReadOnly: false, - }, - ], - }, - Networks: [{ Target: "dokploy-network" }], - Placement: { - Constraints: ["node.role==manager"], - }, - }, - Mode: { - Replicated: { - Replicas: 1, - }, - }, - EndpointSpec: { - Ports: [ - { - TargetPort: 5000, - PublishedPort: 5000, - Protocol: "tcp", - PublishMode: "host", - }, - ], - }, - }; - try { - await pullImage(imageName); - - const service = docker.getService(containerName); - const inspect = await service.inspect(); - await service.update({ - version: Number.parseInt(inspect.Version.Index), - ...settings, - }); - console.log("Registry Started ✅"); - } catch (error) { - await docker.createService(settings); - console.log("Registry Not Found: Starting ✅"); - } -}; - -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 ✅"); - return result.stdout.trim(); - } catch (error) { - console.error("Error generating password:", error); - return null; - } -}; diff --git a/apps/dokploy/server/setup/server-setup.ts b/apps/dokploy/server/setup/server-setup.ts deleted file mode 100644 index 3b30b6c9..00000000 --- a/apps/dokploy/server/setup/server-setup.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { createWriteStream } from "node:fs"; -import path from "node:path"; -import { slugify } from "@/lib/slug"; -import { - createServerDeployment, - updateDeploymentStatus, -} from "@/server/api/services/deployment"; -import { findServerById } from "@/server/api/services/server"; -import { paths } from "@/server/constants"; -import { - getDefaultMiddlewares, - getDefaultServerTraefikConfig, -} from "@/server/setup/traefik-setup"; -import { Client } from "ssh2"; -import { recreateDirectory } from "../utils/filesystem/directory"; -import { readSSHKey } from "../utils/filesystem/ssh"; - -export const serverSetup = async (serverId: string) => { - const server = await findServerById(serverId); - const { LOGS_PATH } = paths(); - - const slugifyName = slugify(`server ${server.name}`); - - const fullPath = path.join(LOGS_PATH, slugifyName); - - await recreateDirectory(fullPath); - - const deployment = await createServerDeployment({ - serverId: server.serverId, - title: "Setup Server", - description: "Setup Server", - }); - - const writeStream = createWriteStream(deployment.logPath, { flags: "a" }); - try { - writeStream.write("\nInstalling Server Dependencies: ✅\n"); - await installRequirements(serverId, deployment.logPath); - writeStream.close(); - - await updateDeploymentStatus(deployment.deploymentId, "done"); - } catch (err) { - console.log(err); - await updateDeploymentStatus(deployment.deploymentId, "error"); - writeStream.write(err); - writeStream.close(); - } -}; - -const installRequirements = async (serverId: string, logPath: string) => { - const writeStream = createWriteStream(logPath, { flags: "a" }); - const client = new Client(); - const server = await findServerById(serverId); - 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", () => { - const bashCommand = ` - - ${validatePorts()} - - command_exists() { - command -v "$@" > /dev/null 2>&1 - } - ${installRClone()} - ${installDocker()} - ${setupSwarm()} - ${setupNetwork()} - ${setupMainDirectory()} - ${setupDirectories()} - ${createTraefikConfig()} - ${createDefaultMiddlewares()} - ${createTraefikInstance()} - ${installNixpacks()} - ${installBuildpacks()} - `; - - client.exec(bashCommand, (err, stream) => { - if (err) { - writeStream.write(err); - reject(err); - return; - } - stream - .on("close", () => { - writeStream.write("Connection closed ✅"); - client.end(); - resolve(); - }) - .on("data", (data: string) => { - writeStream.write(data.toString()); - }) - .stderr.on("data", (data) => { - writeStream.write(data.toString()); - }); - }); - }) - .on("error", (err) => { - client.end(); - if (err.level === "client-authentication") { - writeStream.write( - `Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`, - ); - reject( - new Error( - `Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`, - ), - ); - } else { - writeStream.write( - `SSH connection error: ${err.message} ${err.level}`, - ); - reject(new Error(`SSH connection error: ${err.message}`)); - } - }) - .connect({ - host: server.ipAddress, - port: server.port, - username: server.username, - privateKey: keys.privateKey, - timeout: 99999, - }); - }); -}; - -const setupDirectories = () => { - 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 = ` - ${createDirsCommand} - ${chmodCommand} - `; - - return command; -}; - -const setupMainDirectory = () => ` - # Check if the /etc/dokploy directory exists - if [ -d /etc/dokploy ]; then - echo "/etc/dokploy already exists ✅" - else - # Create the /etc/dokploy directory - mkdir -p /etc/dokploy - chmod 777 /etc/dokploy - - echo "Directory /etc/dokploy created ✅" - fi -`; - -export const setupSwarm = () => ` - # Check if the node is already part of a Docker Swarm - if docker info | grep -q 'Swarm: active'; then - echo "Already part of a Docker Swarm ✅" - else - # Get IP address - get_ip() { - # Try to get IPv4 - local ipv4=\$(curl -4s https://ifconfig.io 2>/dev/null) - - if [ -n "\$ipv4" ]; then - echo "\$ipv4" - else - # Try to get IPv6 - local ipv6=\$(curl -6s https://ifconfig.io 2>/dev/null) - if [ -n "\$ipv6" ]; then - echo "\$ipv6" - fi - fi - } - advertise_addr=\$(get_ip) - - # Initialize Docker Swarm - docker swarm init --advertise-addr \$advertise_addr - echo "Swarm initialized ✅" - fi - `; - -const setupNetwork = () => ` - # Check if the dokploy-network already exists - if docker network ls | grep -q 'dokploy-network'; then - echo "Network dokploy-network already exists ✅" - else - # Create the dokploy-network if it doesn't exist - docker network create --driver overlay --attachable dokploy-network - echo "Network created ✅" - fi -`; - -const installDocker = () => ` - if command_exists docker; then - echo "Docker already installed ✅" - else - echo "Installing Docker ✅" - curl -sSL https://get.docker.com | sh -s -- --version 27.2.0 - fi -`; - -const validatePorts = () => ` - # check if something is running on port 80 - if ss -tulnp | grep ':80 ' >/dev/null; then - echo "Something is already running on port 80" >&2 - fi - - # check if something is running on port 443 - if ss -tulnp | grep ':443 ' >/dev/null; then - echo "Something is already running on port 443" >&2 - fi -`; - -const createTraefikConfig = () => { - const config = getDefaultServerTraefikConfig(); - - const command = ` - if [ -f "/etc/dokploy/traefik/dynamic/acme.json" ]; then - chmod 600 "/etc/dokploy/traefik/dynamic/acme.json" - fi - if [ -f "/etc/dokploy/traefik/traefik.yml" ]; then - echo "Traefik config already exists ✅" - else - echo "${config}" > /etc/dokploy/traefik/traefik.yml - fi - `; - - return command; -}; - -export const createDefaultMiddlewares = () => { - const config = getDefaultMiddlewares(); - const command = ` - if [ -f "/etc/dokploy/traefik/dynamic/middlewares.yml" ]; then - echo "Middlewares config already exists ✅" - else - echo "${config}" > /etc/dokploy/traefik/dynamic/middlewares.yml - fi - `; - return command; -}; - -export const installRClone = () => ` -curl https://rclone.org/install.sh | sudo bash -`; - -export const createTraefikInstance = () => { - const command = ` - # Check if dokpyloy-traefik exists - if docker service ls | grep -q 'dokploy-traefik'; then - echo "Traefik already exists ✅" - else - # Create the dokploy-traefik service - docker service create \ - --name dokploy-traefik \ - --replicas 1 \ - --constraint 'node.role==manager' \ - --network dokploy-network \ - --mount type=bind,src=/etc/dokploy/traefik/traefik.yml,dst=/etc/traefik/traefik.yml \ - --mount type=bind,src=/etc/dokploy/traefik/dynamic,dst=/etc/dokploy/traefik/dynamic \ - --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \ - --label traefik.enable=true \ - --publish mode=host,target=443,published=443 \ - --publish mode=host,target=80,published=80 \ - traefik:v3.1.2 - fi - `; - - return command; -}; - -const installNixpacks = () => ` - if command_exists nixpacks; then - echo "Nixpacks already installed ✅" - else - VERSION=1.28.1 bash -c "$(curl -fsSL https://nixpacks.com/install.sh)" - echo "Nixpacks version 1.28.1 installed ✅" - fi -`; - -const installBuildpacks = () => ` - if command_exists pack; then - echo "Buildpacks already installed ✅" - else - curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.35.0/pack-v0.35.0-linux.tgz" | tar -C /usr/local/bin/ --no-same-owner -xzv pack - echo "Buildpacks version 0.35.0 installed ✅" - fi -`; diff --git a/apps/dokploy/server/setup/setup.ts b/apps/dokploy/server/setup/setup.ts deleted file mode 100644 index c5987702..00000000 --- a/apps/dokploy/server/setup/setup.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { docker } from "../constants"; - -export const initializeSwarm = async () => { - const swarmInitialized = await dockerSwarmInitialized(); - if (swarmInitialized) { - console.log("Swarm is already initilized"); - } else { - await docker.swarmInit({ - AdvertiseAddr: "127.0.0.1", - ListenAddr: "0.0.0.0", - }); - console.log("Swarm was initilized"); - } -}; - -export const dockerSwarmInitialized = async () => { - try { - await docker.swarmInspect(); - - return true; - } catch (e) { - return false; - } -}; - -export const initializeNetwork = async () => { - const networkInitialized = await dockerNetworkInitialized(); - if (networkInitialized) { - console.log("Network is already initilized"); - } else { - docker.createNetwork({ - Attachable: true, - Name: "dokploy-network", - Driver: "overlay", - }); - console.log("Network was initilized"); - } -}; - -export const dockerNetworkInitialized = async () => { - try { - await docker.getNetwork("dokploy-network").inspect(); - return true; - } catch (e) { - return false; - } -}; diff --git a/apps/dokploy/server/setup/traefik-setup.ts b/apps/dokploy/server/setup/traefik-setup.ts deleted file mode 100644 index 27bc99a4..00000000 --- a/apps/dokploy/server/setup/traefik-setup.ts +++ /dev/null @@ -1,320 +0,0 @@ -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 { paths } from "../constants"; -import { pullImage, pullRemoteImage } from "../utils/docker/utils"; -import { getRemoteDocker } from "../utils/servers/remote-docker"; -import type { FileConfig } from "../utils/traefik/file-types"; -import type { MainTraefikConfig } from "../utils/traefik/types"; - -const TRAEFIK_SSL_PORT = - Number.parseInt(process.env.TRAEFIK_SSL_PORT ?? "", 10) || 443; -const TRAEFIK_PORT = Number.parseInt(process.env.TRAEFIK_PORT ?? "", 10) || 80; - -interface TraefikOptions { - enableDashboard?: boolean; - env?: string[]; - serverId?: string; -} - -export const initializeTraefik = async ({ - enableDashboard = false, - env, - serverId, -}: TraefikOptions = {}) => { - const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); - const imageName = "traefik:v3.1.2"; - const containerName = "dokploy-traefik"; - const settings: CreateServiceOptions = { - Name: containerName, - TaskTemplate: { - ContainerSpec: { - Image: imageName, - Env: env, - Mounts: [ - { - Type: "bind", - Source: `${MAIN_TRAEFIK_PATH}/traefik.yml`, - Target: "/etc/traefik/traefik.yml", - }, - { - Type: "bind", - Source: DYNAMIC_TRAEFIK_PATH, - Target: "/etc/dokploy/traefik/dynamic", - }, - { - Type: "bind", - Source: "/var/run/docker.sock", - Target: "/var/run/docker.sock", - }, - ], - }, - Networks: [{ Target: "dokploy-network" }], - Placement: { - Constraints: ["node.role==manager"], - }, - }, - Mode: { - Replicated: { - Replicas: 1, - }, - }, - Labels: { - "traefik.enable": "true", - }, - EndpointSpec: { - Ports: [ - { - TargetPort: 443, - PublishedPort: TRAEFIK_SSL_PORT, - PublishMode: "host", - }, - { - TargetPort: 80, - PublishedPort: TRAEFIK_PORT, - PublishMode: "host", - }, - ...(enableDashboard - ? [ - { - TargetPort: 8080, - PublishedPort: 8080, - PublishMode: "host" as const, - }, - ] - : []), - ], - }, - }; - const docker = await getRemoteDocker(serverId); - try { - if (serverId) { - await pullRemoteImage(imageName, serverId); - } else { - await pullImage(imageName); - } - - const service = docker.getService(containerName); - const inspect = await service.inspect(); - - const existingEnv = inspect.Spec.TaskTemplate.ContainerSpec.Env || []; - const updatedEnv = !env ? existingEnv : env; - - const updatedSettings = { - ...settings, - TaskTemplate: { - ...settings.TaskTemplate, - ContainerSpec: { - ...(settings?.TaskTemplate as ContainerTaskSpec).ContainerSpec, - Env: updatedEnv, - }, - }, - }; - await service.update({ - version: Number.parseInt(inspect.Version.Index), - ...updatedSettings, - }); - - console.log("Traefik Started ✅"); - } catch (error) { - await docker.createService(settings); - console.log("Traefik Not Found: Starting ✅"); - } -}; - -export const createDefaultServerTraefikConfig = () => { - const { DYNAMIC_TRAEFIK_PATH } = paths(); - const configFilePath = path.join(DYNAMIC_TRAEFIK_PATH, "dokploy.yml"); - - if (existsSync(configFilePath)) { - console.log("Default traefik config already exists"); - return; - } - - const appName = "dokploy"; - const serviceURLDefault = `http://${appName}:${process.env.PORT || 3000}`; - const config: FileConfig = { - http: { - routers: { - [`${appName}-router-app`]: { - rule: `Host(\`${appName}.docker.localhost\`) && PathPrefix(\`/\`)`, - service: `${appName}-service-app`, - entryPoints: ["web"], - }, - }, - services: { - [`${appName}-service-app`]: { - loadBalancer: { - servers: [{ url: serviceURLDefault }], - passHostHeader: true, - }, - }, - }, - }, - }; - - const yamlStr = dump(config); - mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); - writeFileSync( - path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`), - yamlStr, - "utf8", - ); -}; - -export const getDefaultTraefikConfig = () => { - const configObject: MainTraefikConfig = { - providers: { - ...(process.env.NODE_ENV === "development" - ? { - docker: { - defaultRule: - "Host(`{{ trimPrefix `/` .Name }}.docker.localhost`)", - }, - } - : { - swarm: { - exposedByDefault: false, - watch: false, - }, - docker: { - exposedByDefault: false, - }, - }), - file: { - directory: "/etc/dokploy/traefik/dynamic", - watch: true, - }, - }, - entryPoints: { - web: { - address: `:${TRAEFIK_PORT}`, - }, - websecure: { - address: `:${TRAEFIK_SSL_PORT}`, - ...(process.env.NODE_ENV === "production" && { - http: { - tls: { - certResolver: "letsencrypt", - }, - }, - }), - }, - }, - api: { - insecure: true, - }, - ...(process.env.NODE_ENV === "production" && { - certificatesResolvers: { - letsencrypt: { - acme: { - email: "test@localhost.com", - storage: "/etc/dokploy/traefik/dynamic/acme.json", - httpChallenge: { - entryPoint: "web", - }, - }, - }, - }, - }), - }; - - const yamlStr = dump(configObject); - - return yamlStr; -}; - -export const getDefaultServerTraefikConfig = () => { - const configObject: MainTraefikConfig = { - providers: { - swarm: { - exposedByDefault: false, - watch: false, - }, - docker: { - exposedByDefault: false, - }, - file: { - directory: "/etc/dokploy/traefik/dynamic", - watch: true, - }, - }, - entryPoints: { - web: { - address: `:${TRAEFIK_PORT}`, - }, - websecure: { - address: `:${TRAEFIK_SSL_PORT}`, - http: { - tls: { - certResolver: "letsencrypt", - }, - }, - }, - }, - api: { - insecure: true, - }, - certificatesResolvers: { - letsencrypt: { - acme: { - email: "test@localhost.com", - storage: "/etc/dokploy/traefik/dynamic/acme.json", - httpChallenge: { - entryPoint: "web", - }, - }, - }, - }, - }; - - const yamlStr = dump(configObject); - - return yamlStr; -}; - -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"); - - if (existsSync(acmeJsonPath)) { - chmodSync(acmeJsonPath, "600"); - } - if (existsSync(mainConfig)) { - console.log("Main config already exists"); - return; - } - const yamlStr = getDefaultTraefikConfig(); - mkdirSync(MAIN_TRAEFIK_PATH, { recursive: true }); - writeFileSync(mainConfig, yamlStr, "utf8"); -}; - -export const getDefaultMiddlewares = () => { - const defaultMiddlewares = { - http: { - middlewares: { - "redirect-to-https": { - redirectScheme: { - scheme: "https", - permanent: true, - }, - }, - }, - }, - }; - const yamlStr = dump(defaultMiddlewares); - 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"); - return; - } - const yamlStr = getDefaultMiddlewares(); - mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); - writeFileSync(middlewaresPath, yamlStr, "utf8"); -}; diff --git a/apps/dokploy/server/utils/access-log/handler.ts b/apps/dokploy/server/utils/access-log/handler.ts deleted file mode 100644 index d5588259..00000000 --- a/apps/dokploy/server/utils/access-log/handler.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { findAdmin, updateAdmin } from "@/server/api/services/admin"; -import { paths } from "@/server/constants"; -import { type RotatingFileStream, createStream } from "rotating-file-stream"; -import { execAsync } from "../process/execAsync"; - -class LogRotationManager { - private static instance: LogRotationManager; - private stream: RotatingFileStream | null = null; - - private constructor() { - this.initialize().catch(console.error); - } - - public static getInstance(): LogRotationManager { - if (!LogRotationManager.instance) { - LogRotationManager.instance = new LogRotationManager(); - } - return LogRotationManager.instance; - } - - private async initialize(): Promise { - const isActive = await this.getStateFromDB(); - if (isActive) { - await this.activateStream(); - } - } - - private async getStateFromDB(): Promise { - const setting = await findAdmin(); - return setting?.enableLogRotation ?? false; - } - - private async setStateInDB(active: boolean): Promise { - const admin = await findAdmin(); - await updateAdmin(admin.authId, { - enableLogRotation: active, - }); - } - - private async activateStream(): Promise { - const { DYNAMIC_TRAEFIK_PATH } = paths(); - if (this.stream) { - await this.deactivateStream(); - } - - this.stream = createStream("access.log", { - size: "100M", - interval: "1d", - path: DYNAMIC_TRAEFIK_PATH, - rotate: 6, - compress: "gzip", - }); - - this.stream.on("rotation", this.handleRotation.bind(this)); - } - - private async deactivateStream(): Promise { - return new Promise((resolve) => { - if (this.stream) { - this.stream.end(() => { - this.stream = null; - resolve(); - }); - } else { - resolve(); - } - }); - } - - public async activate(): Promise { - const currentState = await this.getStateFromDB(); - if (currentState) { - return true; - } - - await this.setStateInDB(true); - await this.activateStream(); - return true; - } - - public async deactivate(): Promise { - console.log("Deactivating log rotation..."); - const currentState = await this.getStateFromDB(); - if (!currentState) { - console.log("Log rotation is already inactive in DB"); - return true; - } - - await this.setStateInDB(false); - await this.deactivateStream(); - console.log("Log rotation deactivated successfully"); - return true; - } - - private async handleRotation() { - try { - const status = await this.getStatus(); - if (!status) { - await this.deactivateStream(); - } - await execAsync( - "docker kill -s USR1 $(docker ps -q --filter name=dokploy-traefik)", - ); - console.log("USR1 Signal send to Traefik"); - } catch (error) { - console.error("Error to send USR1 Signal to Traefik:", error); - } - } - public async getStatus(): Promise { - const dbState = await this.getStateFromDB(); - return dbState; - } -} -export const logRotationManager = LogRotationManager.getInstance(); diff --git a/apps/dokploy/server/utils/access-log/types.ts b/apps/dokploy/server/utils/access-log/types.ts deleted file mode 100644 index 6c35f9bb..00000000 --- a/apps/dokploy/server/utils/access-log/types.ts +++ /dev/null @@ -1,48 +0,0 @@ -export interface LogEntry { - ClientAddr: string; - ClientHost: string; - ClientPort: string; - ClientUsername: string; - DownstreamContentSize: number; - DownstreamStatus: number; - Duration: number; - OriginContentSize: number; - OriginDuration: number; - OriginStatus: number; - Overhead: number; - RequestAddr: string; - RequestContentSize: number; - RequestCount: number; - RequestHost: string; - RequestMethod: string; - RequestPath: string; - RequestPort: string; - RequestProtocol: string; - RequestScheme: string; - RetryAttempts: number; - RouterName: string; - ServiceAddr: string; - ServiceName: string; - ServiceURL: { - Scheme: string; - Opaque: string; - User: null; - Host: string; - Path: string; - RawPath: string; - ForceQuery: boolean; - RawQuery: string; - Fragment: string; - RawFragment: string; - }; - StartLocal: string; - StartUTC: string; - downstream_Content_Type: string; - entryPointName: string; - level: string; - msg: string; - origin_Content_Type: string; - request_Content_Type: string; - request_User_Agent: string; - time: string; -} diff --git a/apps/dokploy/server/utils/access-log/utils.ts b/apps/dokploy/server/utils/access-log/utils.ts deleted file mode 100644 index 0a322f95..00000000 --- a/apps/dokploy/server/utils/access-log/utils.ts +++ /dev/null @@ -1,119 +0,0 @@ -import _ from "lodash"; -import type { LogEntry } from "./types"; - -interface HourlyData { - hour: string; - count: number; -} - -export function processLogs(logString: string): HourlyData[] { - if (_.isEmpty(logString)) { - return []; - } - - const hourlyData = _(logString) - .split("\n") - .compact() - .map((entry) => { - try { - const log: LogEntry = JSON.parse(entry); - if (log.ServiceName === "dokploy-service-app@file") { - return null; - } - const date = new Date(log.StartUTC); - return `${date.toISOString().slice(0, 13)}:00:00Z`; - } catch (error) { - console.error("Error parsing log entry:", error); - return null; - } - }) - .compact() - .countBy() - .map((count, hour) => ({ hour, count })) - .value(); - - return _.sortBy(hourlyData, (entry) => new Date(entry.hour).getTime()); -} - -interface PageInfo { - pageIndex: number; - pageSize: number; -} - -interface SortInfo { - id: string; - desc: boolean; -} - -export function parseRawConfig( - rawConfig: string, - page?: PageInfo, - sort?: SortInfo, - search?: string, - status?: string[], -): { data: LogEntry[]; totalCount: number } { - try { - if (_.isEmpty(rawConfig)) { - return { data: [], totalCount: 0 }; - } - - let parsedLogs = _(rawConfig) - .split("\n") - .compact() - .map((line) => JSON.parse(line) as LogEntry) - .value(); - - parsedLogs = parsedLogs.filter( - (log) => log.ServiceName !== "dokploy-service-app@file", - ); - - if (search) { - parsedLogs = parsedLogs.filter((log) => - log.RequestPath.toLowerCase().includes(search.toLowerCase()), - ); - } - - if (status && status.length > 0) { - parsedLogs = parsedLogs.filter((log) => - status.some((range) => isStatusInRange(log.DownstreamStatus, range)), - ); - } - const totalCount = parsedLogs.length; - - if (sort) { - parsedLogs = _.orderBy( - parsedLogs, - [sort.id], - [sort.desc ? "desc" : "asc"], - ); - } else { - parsedLogs = _.orderBy(parsedLogs, ["time"], ["desc"]); - } - - if (page) { - const startIndex = page.pageIndex * page.pageSize; - parsedLogs = parsedLogs.slice(startIndex, startIndex + page.pageSize); - } - - return { data: parsedLogs, totalCount }; - } catch (error) { - console.error("Error parsing rawConfig:", error); - throw new Error("Failed to parse rawConfig"); - } -} -const isStatusInRange = (status: number, range: string) => { - switch (range) { - case "info": - return status >= 100 && status <= 199; - case "success": - return status >= 200 && status <= 299; - case "redirect": - return status >= 300 && status <= 399; - case "client": - return status >= 400 && status <= 499; - case "server": - return status >= 500 && status <= 599; - default: - return false; - } -}; diff --git a/apps/dokploy/server/utils/docker/compose.ts b/apps/dokploy/server/utils/docker/compose.ts deleted file mode 100644 index 8b9f8b7c..00000000 --- a/apps/dokploy/server/utils/docker/compose.ts +++ /dev/null @@ -1,56 +0,0 @@ -import crypto from "node:crypto"; -import { findComposeById } from "@/server/api/services/compose"; -import { dump, load } from "js-yaml"; -import { addSuffixToAllConfigs } from "./compose/configs"; -import { addSuffixToAllNetworks } from "./compose/network"; -import { addSuffixToAllSecrets } from "./compose/secrets"; -import { addSuffixToAllServiceNames } from "./compose/service"; -import { addSuffixToAllVolumes } from "./compose/volume"; -import type { ComposeSpecification } from "./types"; - -export const generateRandomHash = (): string => { - return crypto.randomBytes(4).toString("hex"); -}; - -export const randomizeComposeFile = async ( - composeId: string, - suffix?: string, -) => { - const compose = await findComposeById(composeId); - const composeFile = compose.composeFile; - const composeData = load(composeFile) as ComposeSpecification; - - const randomSuffix = suffix || generateRandomHash(); - - const newComposeFile = addSuffixToAllProperties(composeData, randomSuffix); - - return dump(newComposeFile); -}; - -export const randomizeSpecificationFile = ( - composeSpec: ComposeSpecification, - suffix?: string, -) => { - if (!suffix) { - return composeSpec; - } - const newComposeFile = addSuffixToAllProperties(composeSpec, suffix); - return newComposeFile; -}; - -export const addSuffixToAllProperties = ( - composeData: ComposeSpecification, - suffix: string, -): ComposeSpecification => { - let updatedComposeData = { ...composeData }; - - updatedComposeData = addSuffixToAllServiceNames(updatedComposeData, suffix); - - updatedComposeData = addSuffixToAllVolumes(updatedComposeData, suffix); - - updatedComposeData = addSuffixToAllNetworks(updatedComposeData, suffix); - updatedComposeData = addSuffixToAllConfigs(updatedComposeData, suffix); - - updatedComposeData = addSuffixToAllSecrets(updatedComposeData, suffix); - return updatedComposeData; -}; diff --git a/apps/dokploy/server/utils/docker/compose/configs.ts b/apps/dokploy/server/utils/docker/compose/configs.ts deleted file mode 100644 index b985db1a..00000000 --- a/apps/dokploy/server/utils/docker/compose/configs.ts +++ /dev/null @@ -1,73 +0,0 @@ -import _ from "lodash"; -import type { - ComposeSpecification, - DefinitionsConfig, - DefinitionsService, -} from "../types"; - -export const addSuffixToConfigsRoot = ( - configs: { [key: string]: DefinitionsConfig }, - suffix: string, -): { [key: string]: DefinitionsConfig } => { - const newConfigs: { [key: string]: DefinitionsConfig } = {}; - - _.forEach(configs, (config, configName) => { - const newConfigName = `${configName}-${suffix}`; - newConfigs[newConfigName] = _.cloneDeep(config); - }); - - return newConfigs; -}; - -export const addSuffixToConfigsInServices = ( - services: { [key: string]: DefinitionsService }, - suffix: string, -): { [key: string]: DefinitionsService } => { - const newServices: { [key: string]: DefinitionsService } = {}; - - _.forEach(services, (serviceConfig, serviceName) => { - const newServiceConfig = _.cloneDeep(serviceConfig); - - // Reemplazar nombres de configs en configs - if (_.has(newServiceConfig, "configs")) { - newServiceConfig.configs = _.map(newServiceConfig.configs, (config) => { - if (_.isString(config)) { - return `${config}-${suffix}`; - } - if (_.isObject(config) && config.source) { - return { - ...config, - source: `${config.source}-${suffix}`, - }; - } - return config; - }); - } - - newServices[serviceName] = newServiceConfig; - }); - - return newServices; -}; - -export const addSuffixToAllConfigs = ( - composeData: ComposeSpecification, - suffix: string, -): ComposeSpecification => { - const updatedComposeData = { ...composeData }; - if (composeData?.configs) { - updatedComposeData.configs = addSuffixToConfigsRoot( - composeData.configs, - suffix, - ); - } - - if (composeData?.services) { - updatedComposeData.services = addSuffixToConfigsInServices( - composeData.services, - suffix, - ); - } - - return updatedComposeData; -}; diff --git a/apps/dokploy/server/utils/docker/compose/network.ts b/apps/dokploy/server/utils/docker/compose/network.ts deleted file mode 100644 index 1245fb60..00000000 --- a/apps/dokploy/server/utils/docker/compose/network.ts +++ /dev/null @@ -1,83 +0,0 @@ -import _ from "lodash"; -import type { - ComposeSpecification, - DefinitionsNetwork, - DefinitionsService, -} from "../types"; - -export const addSuffixToNetworksRoot = ( - networks: { [key: string]: DefinitionsNetwork }, - suffix: string, -): { [key: string]: DefinitionsNetwork } => { - return _.mapKeys(networks, (_value, key) => { - if (key === "dokploy-network") { - return "dokploy-network"; - } - return `${key}-${suffix}`; - }); -}; - -export const addSuffixToServiceNetworks = ( - services: { [key: string]: DefinitionsService }, - suffix: string, -): { [key: string]: DefinitionsService } => { - return _.mapValues(services, (service) => { - if (service.networks) { - // 1 Case the most common - if (Array.isArray(service.networks)) { - service.networks = service.networks.map((network: string) => { - if (network === "dokploy-network") { - return "dokploy-network"; - } - return `${network}-${suffix}`; - }); - } else { - // 2 Case - service.networks = _.mapKeys(service.networks, (_value, key) => { - if (key === "dokploy-network") { - return "dokploy-network"; - } - return `${key}-${suffix}`; - }); - - // 3 Case - service.networks = _.mapValues(service.networks, (value) => { - if (value && typeof value === "object") { - return _.mapKeys(value, (_val, innerKey) => { - if (innerKey === "aliases") { - return "aliases"; - } - return `${innerKey}-${suffix}`; - }); - } - - return value; - }); - } - } - return service; - }); -}; - -export const addSuffixToAllNetworks = ( - composeData: ComposeSpecification, - suffix: string, -): ComposeSpecification => { - const updatedComposeData = { ...composeData }; - - if (updatedComposeData.networks) { - updatedComposeData.networks = addSuffixToNetworksRoot( - updatedComposeData.networks, - suffix, - ); - } - - if (updatedComposeData.services) { - updatedComposeData.services = addSuffixToServiceNetworks( - updatedComposeData.services, - suffix, - ); - } - - return updatedComposeData; -}; diff --git a/apps/dokploy/server/utils/docker/compose/secrets.ts b/apps/dokploy/server/utils/docker/compose/secrets.ts deleted file mode 100644 index 16c23308..00000000 --- a/apps/dokploy/server/utils/docker/compose/secrets.ts +++ /dev/null @@ -1,68 +0,0 @@ -import _ from "lodash"; -import type { ComposeSpecification, DefinitionsService } from "../types"; - -export const addSuffixToSecretsRoot = ( - secrets: ComposeSpecification["secrets"], - suffix: string, -): ComposeSpecification["secrets"] => { - const newSecrets: ComposeSpecification["secrets"] = {}; - _.forEach(secrets, (secretConfig, secretName) => { - const newSecretName = `${secretName}-${suffix}`; - newSecrets[newSecretName] = _.cloneDeep(secretConfig); - }); - return newSecrets; -}; - -export const addSuffixToSecretsInServices = ( - services: { [key: string]: DefinitionsService }, - suffix: string, -): { [key: string]: DefinitionsService } => { - const newServices: { [key: string]: DefinitionsService } = {}; - - _.forEach(services, (serviceConfig, serviceName) => { - const newServiceConfig = _.cloneDeep(serviceConfig); - - // Replace secret names in secrets - if (_.has(newServiceConfig, "secrets")) { - newServiceConfig.secrets = _.map(newServiceConfig.secrets, (secret) => { - if (_.isString(secret)) { - return `${secret}-${suffix}`; - } - if (_.isObject(secret) && secret.source) { - return { - ...secret, - source: `${secret.source}-${suffix}`, - }; - } - return secret; - }); - } - - newServices[serviceName] = newServiceConfig; - }); - - return newServices; -}; - -export const addSuffixToAllSecrets = ( - composeData: ComposeSpecification, - suffix: string, -): ComposeSpecification => { - const updatedComposeData = { ...composeData }; - - if (composeData?.secrets) { - updatedComposeData.secrets = addSuffixToSecretsRoot( - composeData.secrets, - suffix, - ); - } - - if (composeData?.services) { - updatedComposeData.services = addSuffixToSecretsInServices( - composeData.services, - suffix, - ); - } - - return updatedComposeData; -}; diff --git a/apps/dokploy/server/utils/docker/compose/service.ts b/apps/dokploy/server/utils/docker/compose/service.ts deleted file mode 100644 index e22c7eb1..00000000 --- a/apps/dokploy/server/utils/docker/compose/service.ts +++ /dev/null @@ -1,90 +0,0 @@ -// En la sección depends_on de otros servicios: Para definir dependencias entre servicios. -// En la sección networks de otros servicios: Aunque esto no es común, es posible referenciar servicios en redes personalizadas. -// En la sección volumes_from de otros servicios: Para reutilizar volúmenes definidos por otro servicio. -// En la sección links de otros servicios: Para crear enlaces entre servicios. -// En la sección extends de otros servicios: Para extender la configuración de otro servicio. - -import _ from "lodash"; -import type { ComposeSpecification, DefinitionsService } from "../types"; -type DependsOnObject = NonNullable< - Exclude extends infer T - ? { [K in keyof T]: T[K] } - : never ->; - -export const addSuffixToServiceNames = ( - services: { [key: string]: DefinitionsService }, - suffix: string, -): { [key: string]: DefinitionsService } => { - const newServices: { [key: string]: DefinitionsService } = {}; - - for (const [serviceName, serviceConfig] of Object.entries(services)) { - const newServiceName = `${serviceName}-${suffix}`; - const newServiceConfig = _.cloneDeep(serviceConfig); - - // Reemplazar nombres de servicios en depends_on - if (newServiceConfig.depends_on) { - if (Array.isArray(newServiceConfig.depends_on)) { - newServiceConfig.depends_on = newServiceConfig.depends_on.map( - (dep) => `${dep}-${suffix}`, - ); - } else { - const newDependsOn: DependsOnObject = {}; - for (const [depName, depConfig] of Object.entries( - newServiceConfig.depends_on, - )) { - newDependsOn[`${depName}-${suffix}`] = depConfig; - } - newServiceConfig.depends_on = newDependsOn; - } - } - - // Reemplazar nombre en container_name - if (newServiceConfig.container_name) { - newServiceConfig.container_name = `${newServiceConfig.container_name}-${suffix}`; - } - - // Reemplazar nombres de servicios en links - if (newServiceConfig.links) { - newServiceConfig.links = newServiceConfig.links.map( - (link) => `${link}-${suffix}`, - ); - } - - // Reemplazar nombres de servicios en extends - if (newServiceConfig.extends) { - if (typeof newServiceConfig.extends === "string") { - newServiceConfig.extends = `${newServiceConfig.extends}-${suffix}`; - } else { - newServiceConfig.extends.service = `${newServiceConfig.extends.service}-${suffix}`; - } - } - - // Reemplazar nombres de servicios en volumes_from - if (newServiceConfig.volumes_from) { - newServiceConfig.volumes_from = newServiceConfig.volumes_from.map( - (vol) => `${vol}-${suffix}`, - ); - } - - newServices[newServiceName] = newServiceConfig; - } - - return newServices; -}; - -export const addSuffixToAllServiceNames = ( - composeData: ComposeSpecification, - suffix: string, -): ComposeSpecification => { - const updatedComposeData = { ...composeData }; - - if (updatedComposeData.services) { - updatedComposeData.services = addSuffixToServiceNames( - updatedComposeData.services, - suffix, - ); - } - - return updatedComposeData; -}; diff --git a/apps/dokploy/server/utils/docker/compose/volume.ts b/apps/dokploy/server/utils/docker/compose/volume.ts deleted file mode 100644 index 91bde78c..00000000 --- a/apps/dokploy/server/utils/docker/compose/volume.ts +++ /dev/null @@ -1,78 +0,0 @@ -import _ from "lodash"; -import type { - ComposeSpecification, - DefinitionsService, - DefinitionsVolume, -} from "../types"; - -// Función para agregar prefijo a volúmenes -export const addSuffixToVolumesRoot = ( - volumes: { [key: string]: DefinitionsVolume }, - suffix: string, -): { [key: string]: DefinitionsVolume } => { - return _.mapKeys(volumes, (_value, key) => `${key}-${suffix}`); -}; - -export const addSuffixToVolumesInServices = ( - services: { [key: string]: DefinitionsService }, - suffix: string, -): { [key: string]: DefinitionsService } => { - const newServices: { [key: string]: DefinitionsService } = {}; - - _.forEach(services, (serviceConfig, serviceName) => { - const newServiceConfig = _.cloneDeep(serviceConfig); - - // Reemplazar nombres de volúmenes en volumes - if (_.has(newServiceConfig, "volumes")) { - newServiceConfig.volumes = _.map(newServiceConfig.volumes, (volume) => { - if (_.isString(volume)) { - const [volumeName, path] = volume.split(":"); - - // skip bind mounts and variables (e.g. $PWD) - if ( - volumeName?.startsWith(".") || - volumeName?.startsWith("/") || - volumeName?.startsWith("$") - ) { - return volume; - } - return `${volumeName}-${suffix}:${path}`; - } - if (_.isObject(volume) && volume.type === "volume" && volume.source) { - return { - ...volume, - source: `${volume.source}-${suffix}`, - }; - } - return volume; - }); - } - - newServices[serviceName] = newServiceConfig; - }); - - return newServices; -}; - -export const addSuffixToAllVolumes = ( - composeData: ComposeSpecification, - suffix: string, -): ComposeSpecification => { - const updatedComposeData = { ...composeData }; - - if (updatedComposeData.volumes) { - updatedComposeData.volumes = addSuffixToVolumesRoot( - updatedComposeData.volumes, - suffix, - ); - } - - if (updatedComposeData.services) { - updatedComposeData.services = addSuffixToVolumesInServices( - updatedComposeData.services, - suffix, - ); - } - - return updatedComposeData; -}; diff --git a/apps/dokploy/server/utils/docker/domain.ts b/apps/dokploy/server/utils/docker/domain.ts deleted file mode 100644 index 5b0ac076..00000000 --- a/apps/dokploy/server/utils/docker/domain.ts +++ /dev/null @@ -1,327 +0,0 @@ -import fs, { existsSync, readFileSync } from "node:fs"; -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 { paths } from "@/server/constants"; -import { dump, load } from "js-yaml"; -import { execAsyncRemote } from "../process/execAsync"; -import { - cloneRawBitbucketRepository, - cloneRawBitbucketRepositoryRemote, -} from "../providers/bitbucket"; -import { - cloneGitRawRepository, - cloneRawGitRepositoryRemote, -} from "../providers/git"; -import { - cloneRawGithubRepository, - cloneRawGithubRepositoryRemote, -} from "../providers/github"; -import { - cloneRawGitlabRepository, - cloneRawGitlabRepositoryRemote, -} from "../providers/gitlab"; -import { - createComposeFileRaw, - createComposeFileRawRemote, -} from "../providers/raw"; -import { randomizeSpecificationFile } from "./compose"; -import type { - ComposeSpecification, - DefinitionsService, - PropertiesNetworks, -} from "./types"; -import { encodeBase64 } from "./utils"; - -export const cloneCompose = async (compose: Compose) => { - if (compose.sourceType === "github") { - await cloneRawGithubRepository(compose); - } else if (compose.sourceType === "gitlab") { - await cloneRawGitlabRepository(compose); - } else if (compose.sourceType === "bitbucket") { - await cloneRawBitbucketRepository(compose); - } else if (compose.sourceType === "git") { - await cloneGitRawRepository(compose); - } else if (compose.sourceType === "raw") { - await createComposeFileRaw(compose); - } -}; - -export const cloneComposeRemote = async (compose: Compose) => { - if (compose.sourceType === "github") { - await cloneRawGithubRepositoryRemote(compose); - } else if (compose.sourceType === "gitlab") { - await cloneRawGitlabRepositoryRemote(compose); - } else if (compose.sourceType === "bitbucket") { - await cloneRawBitbucketRepositoryRemote(compose); - } else if (compose.sourceType === "git") { - await cloneRawGitRepositoryRemote(compose); - } else if (compose.sourceType === "raw") { - await createComposeFileRawRemote(compose); - } -}; - -export const getComposePath = (compose: Compose) => { - const { COMPOSE_PATH } = paths(!!compose.serverId); - const { appName, sourceType, composePath } = compose; - let path = ""; - - if (sourceType === "raw") { - path = "docker-compose.yml"; - } else { - path = composePath; - } - - return join(COMPOSE_PATH, appName, "code", path); -}; - -export const loadDockerCompose = async ( - compose: Compose, -): Promise => { - const path = getComposePath(compose); - - if (existsSync(path)) { - const yamlStr = readFileSync(path, "utf8"); - const parsedConfig = load(yamlStr) as ComposeSpecification; - return parsedConfig; - } - return null; -}; - -export const loadDockerComposeRemote = async ( - compose: Compose, -): Promise => { - const path = getComposePath(compose); - try { - if (!compose.serverId) { - return null; - } - const { stdout, stderr } = await execAsyncRemote( - compose.serverId, - `cat ${path}`, - ); - - if (stderr) { - return null; - } - if (!stdout) return null; - const parsedConfig = load(stdout) as ComposeSpecification; - return parsedConfig; - } catch (err) { - return null; - } -}; - -export const readComposeFile = async (compose: Compose) => { - const path = getComposePath(compose); - if (existsSync(path)) { - const yamlStr = readFileSync(path, "utf8"); - return yamlStr; - } - return null; -}; - -export const writeDomainsToCompose = async ( - compose: Compose, - domains: Domain[], -) => { - if (!domains.length) { - return; - } - const composeConverted = await addDomainToCompose(compose, domains); - - const path = getComposePath(compose); - const composeString = dump(composeConverted, { lineWidth: 1000 }); - try { - await writeFile(path, composeString, "utf8"); - } catch (error) { - throw error; - } -}; - -export const writeDomainsToComposeRemote = async ( - compose: Compose, - domains: Domain[], - logPath: string, -) => { - if (!domains.length) { - return ""; - } - - try { - const composeConverted = await addDomainToCompose(compose, domains); - const path = getComposePath(compose); - - if (!composeConverted) { - return ` -echo "❌ Error: Compose file not found" >> ${logPath}; -exit 1; - `; - } - if (compose.serverId) { - const composeString = dump(composeConverted, { lineWidth: 1000 }); - const encodedContent = encodeBase64(composeString); - return `echo "${encodedContent}" | base64 -d > "${path}";`; - } - } catch (error) { - // @ts-ignore - return `echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath}; -exit 1; - `; - } -}; -// (node:59875) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGTERM listeners added to [process]. Use emitter.setMaxListeners() to increase limit -export const addDomainToCompose = async ( - compose: Compose, - domains: Domain[], -) => { - const { appName } = compose; - - let result: ComposeSpecification | null; - - if (compose.serverId) { - result = await loadDockerComposeRemote(compose); // aca hay que ir al servidor e ir a traer el compose file al servidor - } else { - result = await loadDockerCompose(compose); - } - - if (!result || domains.length === 0) { - return null; - } - - if (compose.randomize) { - const randomized = randomizeSpecificationFile(result, compose.suffix); - result = randomized; - } - - for (const domain of domains) { - const { serviceName, https } = domain; - if (!serviceName) { - throw new Error("Service name not found"); - } - if (!result?.services?.[serviceName]) { - throw new Error(`The service ${serviceName} not found in the compose`); - } - if (!result.services[serviceName].labels) { - result.services[serviceName].labels = []; - } - - const httpLabels = await createDomainLabels(appName, domain, "web"); - if (https) { - const httpsLabels = await createDomainLabels( - appName, - domain, - "websecure", - ); - httpLabels.push(...httpsLabels); - } - - const labels = result.services[serviceName].labels; - - if (Array.isArray(labels)) { - if (!labels.includes("traefik.enable=true")) { - labels.push("traefik.enable=true"); - } - labels.push(...httpLabels); - } - - // Add the dokploy-network to the service - result.services[serviceName].networks = addDokployNetworkToService( - result.services[serviceName].networks, - ); - } - - // Add dokploy-network to the root of the compose file - result.networks = addDokployNetworkToRoot(result.networks); - - return result; -}; - -export const writeComposeFile = async ( - compose: Compose, - composeSpec: ComposeSpecification, -) => { - const path = getComposePath(compose); - - try { - const composeFile = dump(composeSpec, { - lineWidth: 1000, - }); - fs.writeFileSync(path, composeFile, "utf8"); - } catch (e) { - console.error("Error saving the YAML config file:", e); - } -}; - -export const createDomainLabels = async ( - appName: string, - domain: Domain, - entrypoint: "web" | "websecure", -) => { - const { host, port, https, uniqueConfigKey, certificateType } = domain; - const routerName = `${appName}-${uniqueConfigKey}-${entrypoint}`; - const labels = [ - `traefik.http.routers.${routerName}.rule=Host(\`${host}\`)`, - `traefik.http.routers.${routerName}.entrypoints=${entrypoint}`, - `traefik.http.services.${routerName}.loadbalancer.server.port=${port}`, - `traefik.http.routers.${routerName}.service=${routerName}`, - ]; - - if (entrypoint === "web" && https) { - labels.push( - `traefik.http.routers.${routerName}.middlewares=redirect-to-https@file`, - ); - } - - if (entrypoint === "websecure") { - if (certificateType === "letsencrypt") { - labels.push( - `traefik.http.routers.${routerName}.tls.certresolver=letsencrypt`, - ); - } - } - - return labels; -}; - -export const addDokployNetworkToService = ( - networkService: DefinitionsService["networks"], -) => { - let networks = networkService; - const network = "dokploy-network"; - if (!networks) { - networks = []; - } - - if (Array.isArray(networks)) { - if (!networks.includes(network)) { - networks.push(network); - } - } else if (networks && typeof networks === "object") { - if (!(network in networks)) { - networks[network] = {}; - } - } - - return networks; -}; - -export const addDokployNetworkToRoot = ( - networkRoot: PropertiesNetworks | undefined, -) => { - let networks = networkRoot; - const network = "dokploy-network"; - - if (!networks) { - networks = {}; - } - - if (networks[network] || !networks[network]) { - networks[network] = { - external: true, - }; - } - - return networks; -}; diff --git a/apps/dokploy/server/utils/docker/types.ts b/apps/dokploy/server/utils/docker/types.ts deleted file mode 100644 index ce0f95fc..00000000 --- a/apps/dokploy/server/utils/docker/types.ts +++ /dev/null @@ -1,879 +0,0 @@ -export type DefinitionsInclude = - | string - | { - path?: StringOrList; - env_file?: StringOrList; - project_directory?: string; - }; -export type StringOrList = string | ListOfStrings; -export type ListOfStrings = string[]; -export type DefinitionsDevelopment = { - watch?: { - ignore?: string[]; - path: string; - action: "rebuild" | "sync" | "sync+restart"; - target?: string; - [k: string]: unknown; - }[]; - [k: string]: unknown; -} & Development; -export type Development = { - watch?: { - ignore?: string[]; - path: string; - action: "rebuild" | "sync" | "sync+restart"; - target?: string; - [k: string]: unknown; - }[]; - [k: string]: unknown; -} | null; -export type DefinitionsDeployment = { - mode?: string; - endpoint_mode?: string; - replicas?: number; - labels?: ListOrDict; - rollback_config?: { - parallelism?: number; - delay?: string; - failure_action?: string; - monitor?: string; - max_failure_ratio?: number; - order?: "start-first" | "stop-first"; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - update_config?: { - parallelism?: number; - delay?: string; - failure_action?: string; - monitor?: string; - max_failure_ratio?: number; - order?: "start-first" | "stop-first"; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - resources?: { - limits?: { - cpus?: number | string; - memory?: string; - pids?: number; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - reservations?: { - cpus?: number | string; - memory?: string; - generic_resources?: DefinitionsGenericResources; - devices?: DefinitionsDevices; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - restart_policy?: { - condition?: string; - delay?: string; - max_attempts?: number; - window?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - placement?: { - constraints?: string[]; - preferences?: { - spread?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }[]; - max_replicas_per_node?: number; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - * - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} & Deployment; -export type ListOrDict = - | { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` ".+". - */ - [k: string]: string | number | boolean | null; - } - | string[]; -export type DefinitionsGenericResources = { - discrete_resource_spec?: { - kind?: string; - value?: number; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -}[]; -export type DefinitionsDevices = { - capabilities?: ListOfStrings; - count?: string | number; - device_ids?: ListOfStrings; - driver?: string; - options?: ListOrDict; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -}[]; -export type Deployment = { - mode?: string; - endpoint_mode?: string; - replicas?: number; - labels?: ListOrDict; - rollback_config?: { - parallelism?: number; - delay?: string; - failure_action?: string; - monitor?: string; - max_failure_ratio?: number; - order?: "start-first" | "stop-first"; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - update_config?: { - parallelism?: number; - delay?: string; - failure_action?: string; - monitor?: string; - max_failure_ratio?: number; - order?: "start-first" | "stop-first"; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - resources?: { - limits?: { - cpus?: number | string; - memory?: string; - pids?: number; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - reservations?: { - cpus?: number | string; - memory?: string; - generic_resources?: DefinitionsGenericResources; - devices?: DefinitionsDevices; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - restart_policy?: { - condition?: string; - delay?: string; - max_attempts?: number; - window?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - placement?: { - constraints?: string[]; - preferences?: { - spread?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }[]; - max_replicas_per_node?: number; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - * - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} | null; -export type ServiceConfigOrSecret = ( - | string - | { - source?: string; - target?: string; - uid?: string; - gid?: string; - mode?: number; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - } -)[]; -export type Command = null | string | string[]; -export type EnvFile = - | string - | ( - | string - | { - path: string; - required?: boolean; - } - )[]; -/** - * This interface was referenced by `PropertiesNetworks`'s JSON-Schema definition - * via the `patternProperty` "^[a-zA-Z0-9._-]+$". - */ -export type DefinitionsNetwork = { - name?: string; - driver?: string; - driver_opts?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string | number; - }; - ipam?: { - driver?: string; - config?: { - subnet?: string; - ip_range?: string; - gateway?: string; - aux_addresses?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }[]; - options?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - external?: - | boolean - | { - name?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - internal?: boolean; - enable_ipv6?: boolean; - attachable?: boolean; - labels?: ListOrDict; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - * - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} & Network; -export type Network = { - name?: string; - driver?: string; - driver_opts?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string | number; - }; - ipam?: { - driver?: string; - config?: { - subnet?: string; - ip_range?: string; - gateway?: string; - aux_addresses?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }[]; - options?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - external?: - | boolean - | { - name?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - internal?: boolean; - enable_ipv6?: boolean; - attachable?: boolean; - labels?: ListOrDict; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - * - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} | null; -/** - * This interface was referenced by `PropertiesVolumes`'s JSON-Schema definition - * via the `patternProperty` "^[a-zA-Z0-9._-]+$". - */ -export type DefinitionsVolume = { - name?: string; - driver?: string; - driver_opts?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string | number; - }; - external?: - | boolean - | { - name?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - labels?: ListOrDict; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - * - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} & Volume; -export type Volume = { - name?: string; - driver?: string; - driver_opts?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string | number; - }; - external?: - | boolean - | { - name?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - labels?: ListOrDict; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - * - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} | null; - -/** - * The Compose file is a YAML file defining a multi-containers based application. - */ -export interface ComposeSpecification { - /** - * declared for backward compatibility, ignored. - */ - version?: string; - /** - * define the Compose project name, until user defines one explicitly. - */ - name?: string; - /** - * compose sub-projects to be included. - */ - include?: DefinitionsInclude[]; - services?: PropertiesServices; - networks?: PropertiesNetworks; - volumes?: PropertiesVolumes; - secrets?: PropertiesSecrets; - configs?: PropertiesConfigs; - /** - * This interface was referenced by `ComposeSpecification`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} -export interface PropertiesServices { - [k: string]: DefinitionsService; -} -/** - * This interface was referenced by `PropertiesServices`'s JSON-Schema definition - * via the `patternProperty` "^[a-zA-Z0-9._-]+$". - */ -export interface DefinitionsService { - develop?: DefinitionsDevelopment; - deploy?: DefinitionsDeployment; - annotations?: ListOrDict; - attach?: boolean; - build?: - | string - | { - context?: string; - dockerfile?: string; - dockerfile_inline?: string; - entitlements?: string[]; - args?: ListOrDict; - ssh?: ListOrDict; - labels?: ListOrDict; - cache_from?: string[]; - cache_to?: string[]; - no_cache?: boolean; - additional_contexts?: ListOrDict; - network?: string; - pull?: boolean; - target?: string; - shm_size?: number | string; - extra_hosts?: ListOrDict; - isolation?: string; - privileged?: boolean; - secrets?: ServiceConfigOrSecret; - tags?: string[]; - ulimits?: Ulimits; - platforms?: string[]; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - blkio_config?: { - device_read_bps?: BlkioLimit[]; - device_read_iops?: BlkioLimit[]; - device_write_bps?: BlkioLimit[]; - device_write_iops?: BlkioLimit[]; - weight?: number; - weight_device?: BlkioWeight[]; - }; - cap_add?: string[]; - cap_drop?: string[]; - cgroup?: "host" | "private"; - cgroup_parent?: string; - command?: Command; - configs?: ServiceConfigOrSecret; - container_name?: string; - cpu_count?: number; - cpu_percent?: number; - cpu_shares?: number | string; - cpu_quota?: number | string; - cpu_period?: number | string; - cpu_rt_period?: number | string; - cpu_rt_runtime?: number | string; - cpus?: number | string; - cpuset?: string; - credential_spec?: { - config?: string; - file?: string; - registry?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - depends_on?: - | ListOfStrings - | { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^[a-zA-Z0-9._-]+$". - */ - [k: string]: { - restart?: boolean; - required?: boolean; - condition: - | "service_started" - | "service_healthy" - | "service_completed_successfully"; - }; - }; - device_cgroup_rules?: ListOfStrings; - devices?: string[]; - dns?: StringOrList; - dns_opt?: string[]; - dns_search?: StringOrList; - domainname?: string; - entrypoint?: Command; - env_file?: EnvFile; - environment?: ListOrDict; - expose?: (string | number)[]; - extends?: - | string - | { - service: string; - file?: string; - }; - external_links?: string[]; - extra_hosts?: ListOrDict; - group_add?: (string | number)[]; - healthcheck?: DefinitionsHealthcheck; - hostname?: string; - image?: string; - init?: boolean; - ipc?: string; - isolation?: string; - labels?: ListOrDict; - links?: string[]; - logging?: { - driver?: string; - options?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string | number | null; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - mac_address?: string; - mem_limit?: number | string; - mem_reservation?: string | number; - mem_swappiness?: number; - memswap_limit?: number | string; - network_mode?: string; - networks?: - | ListOfStrings - | { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^[a-zA-Z0-9._-]+$". - */ - [k: string]: { - aliases?: ListOfStrings; - ipv4_address?: string; - ipv6_address?: string; - link_local_ips?: ListOfStrings; - mac_address?: string; - driver_opts?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string | number; - }; - priority?: number; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - } | null; - }; - oom_kill_disable?: boolean; - oom_score_adj?: number; - pid?: string | null; - pids_limit?: number | string; - platform?: string; - ports?: ( - | number - | string - | { - name?: string; - mode?: string; - host_ip?: string; - target?: number; - published?: string | number; - protocol?: string; - app_protocol?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - } - )[]; - privileged?: boolean; - profiles?: ListOfStrings; - pull_policy?: "always" | "never" | "if_not_present" | "build" | "missing"; - read_only?: boolean; - restart?: string; - runtime?: string; - scale?: number; - security_opt?: string[]; - shm_size?: number | string; - secrets?: ServiceConfigOrSecret; - sysctls?: ListOrDict; - stdin_open?: boolean; - stop_grace_period?: string; - stop_signal?: string; - storage_opt?: { - [k: string]: unknown; - }; - tmpfs?: StringOrList; - tty?: boolean; - ulimits?: Ulimits; - user?: string; - uts?: string; - userns_mode?: string; - volumes?: ( - | string - | { - type: string; - source?: string; - target?: string; - read_only?: boolean; - consistency?: string; - bind?: { - propagation?: string; - create_host_path?: boolean; - selinux?: "z" | "Z"; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - volume?: { - nocopy?: boolean; - subpath?: string; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - tmpfs?: { - size?: number | string; - mode?: number; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - } - )[]; - volumes_from?: string[]; - working_dir?: string; - /** - * This interface was referenced by `DefinitionsService`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} -export interface Ulimits { - /** - * This interface was referenced by `Ulimits`'s JSON-Schema definition - * via the `patternProperty` "^[a-z]+$". - */ - [k: string]: - | number - | { - hard: number; - soft: number; - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; - }; -} -export interface BlkioLimit { - path?: string; - rate?: number | string; -} -export interface BlkioWeight { - path?: string; - weight?: number; -} -export interface DefinitionsHealthcheck { - disable?: boolean; - interval?: string; - retries?: number; - test?: string | string[]; - timeout?: string; - start_period?: string; - start_interval?: string; - /** - * This interface was referenced by `DefinitionsHealthcheck`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} -export interface PropertiesNetworks { - [k: string]: DefinitionsNetwork; -} -export interface PropertiesVolumes { - [k: string]: DefinitionsVolume; -} -export interface PropertiesSecrets { - [k: string]: DefinitionsSecret; -} -/** - * This interface was referenced by `PropertiesSecrets`'s JSON-Schema definition - * via the `patternProperty` "^[a-zA-Z0-9._-]+$". - */ -export interface DefinitionsSecret { - name?: string; - environment?: string; - file?: string; - external?: - | boolean - | { - name?: string; - [k: string]: unknown; - }; - labels?: ListOrDict; - driver?: string; - driver_opts?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "^.+$". - */ - [k: string]: string | number; - }; - template_driver?: string; - /** - * This interface was referenced by `DefinitionsSecret`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} -export interface PropertiesConfigs { - [k: string]: DefinitionsConfig; -} -/** - * This interface was referenced by `PropertiesConfigs`'s JSON-Schema definition - * via the `patternProperty` "^[a-zA-Z0-9._-]+$". - */ -export interface DefinitionsConfig { - name?: string; - content?: string; - environment?: string; - file?: string; - external?: - | boolean - | { - name?: string; - [k: string]: unknown; - }; - labels?: ListOrDict; - template_driver?: string; - /** - * This interface was referenced by `DefinitionsConfig`'s JSON-Schema definition - * via the `patternProperty` "^x-". - */ - [k: string]: unknown; -} diff --git a/apps/dokploy/server/utils/docker/utils.ts b/apps/dokploy/server/utils/docker/utils.ts deleted file mode 100644 index 3dcc27ac..00000000 --- a/apps/dokploy/server/utils/docker/utils.ts +++ /dev/null @@ -1,525 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import type { Readable } from "node:stream"; -import { docker, paths } from "@/server/constants"; -import type { ContainerInfo, ResourceRequirements } from "dockerode"; -import { parse } from "dotenv"; -import type { ApplicationNested } from "../builders"; -import type { MariadbNested } from "../databases/mariadb"; -import type { MongoNested } from "../databases/mongo"; -import type { MysqlNested } from "../databases/mysql"; -import type { PostgresNested } from "../databases/postgres"; -import type { RedisNested } from "../databases/redis"; -import { execAsync, execAsyncRemote } from "../process/execAsync"; -import { getRemoteDocker } from "../servers/remote-docker"; - -interface RegistryAuth { - username: string; - password: string; - serveraddress: string; -} - -export const pullImage = async ( - dockerImage: string, - onData?: (data: any) => void, - authConfig?: Partial, -): Promise => { - try { - if (!dockerImage) { - throw new Error("Docker image not found"); - } - - await new Promise((resolve, reject) => { - docker.pull(dockerImage, { authconfig: authConfig }, (err, stream) => { - if (err) { - reject(err); - return; - } - - docker.modem.followProgress( - stream as Readable, - (err: Error | null, res) => { - if (!err) { - resolve(res); - } - if (err) { - reject(err); - } - }, - (event) => { - onData?.(event); - }, - ); - }); - }); - } catch (error) { - throw error; - } -}; - -export const pullRemoteImage = async ( - dockerImage: string, - serverId: string, - onData?: (data: any) => void, - authConfig?: Partial, -): Promise => { - try { - if (!dockerImage) { - throw new Error("Docker image not found"); - } - - const remoteDocker = await getRemoteDocker(serverId); - - await new Promise((resolve, reject) => { - remoteDocker.pull( - dockerImage, - { authconfig: authConfig }, - (err, stream) => { - if (err) { - reject(err); - return; - } - - remoteDocker.modem.followProgress( - stream as Readable, - (err: Error | null, res) => { - if (!err) { - resolve(res); - } - if (err) { - reject(err); - } - }, - (event) => { - onData?.(event); - }, - ); - }, - ); - }); - } catch (error) { - throw error; - } -}; - -export const containerExists = async (containerName: string) => { - const container = docker.getContainer(containerName); - try { - await container.inspect(); - return true; - } catch (error) { - return false; - } -}; - -export const stopService = async (appName: string) => { - try { - await execAsync(`docker service scale ${appName}=0 `); - } catch (error) { - console.error(error); - return error; - } -}; - -export const stopServiceRemote = async (serverId: string, appName: string) => { - try { - await execAsyncRemote(serverId, `docker service scale ${appName}=0 `); - } catch (error) { - console.error(error); - return error; - } -}; - -export const getContainerByName = (name: string): Promise => { - const opts = { - limit: 1, - filters: { - name: [name], - }, - }; - return new Promise((resolve, reject) => { - docker.listContainers(opts, (err, containers) => { - if (err) { - reject(err); - } else if (containers?.length === 0) { - reject(new Error(`No container found with name: ${name}`)); - } else if (containers && containers?.length > 0 && containers[0]) { - resolve(containers[0]); - } - }); - }); -}; -export const cleanUpUnusedImages = async (serverId?: string) => { - try { - if (serverId) { - await execAsyncRemote(serverId, "docker image prune --all --force"); - } else { - await execAsync("docker image prune --all --force"); - } - } catch (error) { - console.error(error); - throw error; - } -}; - -export const cleanStoppedContainers = async (serverId?: string) => { - try { - if (serverId) { - await execAsyncRemote(serverId, "docker container prune --force"); - } else { - await execAsync("docker container prune --force"); - } - } catch (error) { - console.error(error); - throw error; - } -}; - -export const cleanUpUnusedVolumes = async (serverId?: string) => { - try { - if (serverId) { - await execAsyncRemote(serverId, "docker volume prune --all --force"); - } else { - await execAsync("docker volume prune --all --force"); - } - } catch (error) { - console.error(error); - throw error; - } -}; - -export const cleanUpInactiveContainers = async () => { - try { - const containers = await docker.listContainers({ all: true }); - const inactiveContainers = containers.filter( - (container) => container.State !== "running", - ); - - for (const container of inactiveContainers) { - await docker.getContainer(container.Id).remove({ force: true }); - console.log(`Cleaning up inactive container: ${container.Id}`); - } - } catch (error) { - console.error("Error cleaning up inactive containers:", error); - throw error; - } -}; - -export const cleanUpDockerBuilder = async (serverId?: string) => { - if (serverId) { - await execAsyncRemote(serverId, "docker builder prune --all --force"); - } else { - await execAsync("docker builder prune --all --force"); - } -}; - -export const cleanUpSystemPrune = async (serverId?: string) => { - if (serverId) { - await execAsyncRemote( - serverId, - "docker system prune --all --force --volumes", - ); - } else { - await execAsync("docker system prune --all --force --volumes"); - } -}; - -export const startService = async (appName: string) => { - try { - await execAsync(`docker service scale ${appName}=1 `); - } catch (error) { - console.error(error); - throw error; - } -}; - -export const startServiceRemote = async (serverId: string, appName: string) => { - try { - await execAsyncRemote(serverId, `docker service scale ${appName}=1 `); - } catch (error) { - console.error(error); - throw error; - } -}; - -export const removeService = async ( - appName: string, - serverId?: string | null, -) => { - try { - const command = `docker service rm ${appName}`; - if (serverId) { - await execAsyncRemote(serverId, command); - } else { - await execAsync(command); - } - } catch (error) { - return error; - } -}; - -export const prepareEnvironmentVariables = (env: string | null) => - Object.entries(parse(env ?? "")).map(([key, value]) => `${key}=${value}`); - -export const prepareBuildArgs = (input: string | null) => { - const pairs = (input ?? "").split("\n"); - - const jsonObject: Record = {}; - - for (const pair of pairs) { - const [key, value] = pair.split("="); - if (key && value) { - jsonObject[key] = value; - } - } - - return jsonObject; -}; - -export const generateVolumeMounts = (mounts: ApplicationNested["mounts"]) => { - if (!mounts || mounts.length === 0) { - return []; - } - - return mounts - .filter((mount) => mount.type === "volume") - .map((mount) => ({ - Type: "volume" as const, - Source: mount.volumeName || "", - Target: mount.mountPath, - })); -}; - -type Resources = { - memoryLimit: number | null; - memoryReservation: number | null; - cpuLimit: number | null; - cpuReservation: number | null; -}; -export const calculateResources = ({ - memoryLimit, - memoryReservation, - cpuLimit, - cpuReservation, -}: Resources): ResourceRequirements => { - return { - Limits: { - MemoryBytes: memoryLimit ? memoryLimit * 1024 * 1024 : undefined, - NanoCPUs: memoryLimit ? (cpuLimit || 1) * 1000 * 1000 * 1000 : undefined, - }, - Reservations: { - MemoryBytes: memoryLimit - ? (memoryReservation || 1) * 1024 * 1024 - : undefined, - NanoCPUs: memoryLimit - ? (cpuReservation || 1) * 1000 * 1000 * 1000 - : undefined, - }, - }; -}; - -export const generateConfigContainer = (application: ApplicationNested) => { - const { - healthCheckSwarm, - restartPolicySwarm, - placementSwarm, - updateConfigSwarm, - rollbackConfigSwarm, - modeSwarm, - labelsSwarm, - replicas, - mounts, - networkSwarm, - } = application; - - const haveMounts = mounts.length > 0; - - return { - ...(healthCheckSwarm && { - HealthCheck: healthCheckSwarm, - }), - ...(restartPolicySwarm - ? { - RestartPolicy: restartPolicySwarm, - } - : {}), - ...(placementSwarm - ? { - Placement: placementSwarm, - } - : { - // if app have mounts keep manager as constraint - Placement: { - Constraints: haveMounts ? ["node.role==manager"] : [], - }, - }), - ...(labelsSwarm && { - Labels: labelsSwarm, - }), - ...(modeSwarm - ? { - Mode: modeSwarm, - } - : { - // use replicas value if no modeSwarm provided - Mode: { - Replicated: { - Replicas: replicas, - }, - }, - }), - ...(rollbackConfigSwarm && { - RollbackConfig: rollbackConfigSwarm, - }), - ...(updateConfigSwarm - ? { UpdateConfig: updateConfigSwarm } - : { - // default config if no updateConfigSwarm provided - UpdateConfig: { - Parallelism: 1, - Order: "start-first", - }, - }), - ...(networkSwarm - ? { - Networks: networkSwarm, - } - : { - Networks: [{ Target: "dokploy-network" }], - }), - }; -}; - -export const generateBindMounts = (mounts: ApplicationNested["mounts"]) => { - if (!mounts || mounts.length === 0) { - return []; - } - - return mounts - .filter((mount) => mount.type === "bind") - .map((mount) => ({ - Type: "bind" as const, - Source: mount.hostPath || "", - Target: mount.mountPath, - })); -}; - -export const generateFileMounts = ( - appName: string, - service: - | ApplicationNested - | MongoNested - | MariadbNested - | MysqlNested - | PostgresNested - | RedisNested, -) => { - const { mounts } = service; - const { APPLICATIONS_PATH } = paths(!!service.serverId); - if (!mounts || mounts.length === 0) { - return []; - } - - return mounts - .filter((mount) => mount.type === "file") - .map((mount) => { - const fileName = mount.filePath; - const absoluteBasePath = path.resolve(APPLICATIONS_PATH); - const directory = path.join(absoluteBasePath, appName, "files"); - const sourcePath = path.join(directory, fileName || ""); - return { - Type: "bind" as const, - Source: sourcePath, - Target: mount.mountPath, - }; - }); -}; - -export const createFile = async ( - outputPath: string, - filePath: string, - content: string, -) => { - try { - const fullPath = path.join(outputPath, filePath); - if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) { - fs.mkdirSync(fullPath, { recursive: true }); - return; - } - - const directory = path.dirname(fullPath); - fs.mkdirSync(directory, { recursive: true }); - fs.writeFileSync(fullPath, content || ""); - } catch (error) { - throw error; - } -}; -export const encodeBase64 = (content: string) => - Buffer.from(content, "utf-8").toString("base64"); - -export const getCreateFileCommand = ( - outputPath: string, - filePath: string, - content: string, -) => { - const fullPath = path.join(outputPath, filePath); - if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) { - return `mkdir -p ${fullPath};`; - } - - const directory = path.dirname(fullPath); - const encodedContent = encodeBase64(content); - return ` - mkdir -p ${directory}; - echo "${encodedContent}" | base64 -d > "${fullPath}"; - `; -}; - -export const getServiceContainer = async (appName: string) => { - try { - const filter = { - status: ["running"], - label: [`com.docker.swarm.service.name=${appName}`], - }; - - const containers = await docker.listContainers({ - filters: JSON.stringify(filter), - }); - - if (containers.length === 0 || !containers[0]) { - throw new Error(`No container found with name: ${appName}`); - } - - const container = containers[0]; - - return container; - } catch (error) { - throw error; - } -}; - -export const getRemoteServiceContainer = async ( - serverId: string, - appName: string, -) => { - try { - const filter = { - status: ["running"], - label: [`com.docker.swarm.service.name=${appName}`], - }; - const remoteDocker = await getRemoteDocker(serverId); - const containers = await remoteDocker.listContainers({ - filters: JSON.stringify(filter), - }); - - if (containers.length === 0 || !containers[0]) { - throw new Error(`No container found with name: ${appName}`); - } - - const container = containers[0]; - - return container; - } catch (error) { - throw error; - } -}; diff --git a/apps/dokploy/server/utils/filesystem/directory.ts b/apps/dokploy/server/utils/filesystem/directory.ts deleted file mode 100644 index bc3b03eb..00000000 --- a/apps/dokploy/server/utils/filesystem/directory.ts +++ /dev/null @@ -1,142 +0,0 @@ -import fs, { promises as fsPromises } from "node:fs"; -import path from "node:path"; -import type { Application } from "@/server/api/services/application"; -import { paths } from "@/server/constants"; -import { execAsync, execAsyncRemote } from "../process/execAsync"; - -export const recreateDirectory = async (pathFolder: string): Promise => { - try { - await removeDirectoryIfExistsContent(pathFolder); - await fsPromises.mkdir(pathFolder, { recursive: true }); - } catch (error) { - console.error(`Error recreating directory '${pathFolder}':`, error); - } -}; - -export const recreateDirectoryRemote = async ( - pathFolder: string, - serverId: string | null, -): Promise => { - try { - await execAsyncRemote( - serverId, - `rm -rf ${pathFolder}; mkdir -p ${pathFolder}`, - ); - } catch (error) { - console.error(`Error recreating directory '${pathFolder}':`, error); - } -}; - -export const removeDirectoryIfExistsContent = async ( - path: string, -): Promise => { - if (fs.existsSync(path) && fs.readdirSync(path).length !== 0) { - await execAsync(`rm -rf ${path}`); - } -}; - -export const removeFileOrDirectory = async (path: string) => { - try { - await execAsync(`rm -rf ${path}`); - } catch (error) { - console.error(`Error to remove ${path}: ${error}`); - throw error; - } -}; - -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 { - if (serverId) { - await execAsyncRemote(serverId, command); - } else { - await execAsync(command); - } - } catch (error) { - console.error(`Error to remove ${directoryPath}: ${error}`); - throw error; - } -}; - -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 { - if (serverId) { - await execAsyncRemote(serverId, command); - } else { - await execAsync(command); - } - } catch (error) { - console.error(`Error to remove ${directoryPath}: ${error}`); - throw error; - } -}; - -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 { - if (serverId) { - await execAsyncRemote(serverId, command); - } else { - await execAsync(command); - } - } catch (error) { - console.error(`Error to remove ${directoryPath}: ${error}`); - throw error; - } -}; - -export const getBuildAppDirectory = (application: Application) => { - const { APPLICATIONS_PATH } = paths(!!application.serverId); - const { appName, buildType, sourceType, customGitBuildPath, dockerfile } = - application; - let buildPath = ""; - - if (sourceType === "github") { - buildPath = application?.buildPath || ""; - } else if (sourceType === "gitlab") { - buildPath = application?.gitlabBuildPath || ""; - } else if (sourceType === "bitbucket") { - buildPath = application?.bitbucketBuildPath || ""; - } else if (sourceType === "drop") { - buildPath = application?.dropBuildPath || ""; - } else if (sourceType === "git") { - buildPath = customGitBuildPath || ""; - } - if (buildType === "dockerfile") { - return path.join( - APPLICATIONS_PATH, - appName, - "code", - buildPath ?? "", - dockerfile || "", - ); - } - - return path.join(APPLICATIONS_PATH, appName, "code", buildPath ?? ""); -}; - -export const getDockerContextPath = (application: Application) => { - const { APPLICATIONS_PATH } = paths(!!application.serverId); - const { appName, dockerContextPath } = application; - - if (!dockerContextPath) { - return null; - } - return path.join(APPLICATIONS_PATH, appName, "code", dockerContextPath); -}; diff --git a/apps/dokploy/server/utils/filesystem/ssh.ts b/apps/dokploy/server/utils/filesystem/ssh.ts deleted file mode 100644 index 62bf076a..00000000 --- a/apps/dokploy/server/utils/filesystem/ssh.ts +++ /dev/null @@ -1,99 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -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 }); - } - - return { - privateKey: fs.readFileSync(path.join(SSH_PATH, `${id}_rsa`), { - encoding: "utf-8", - }), - publicKey: fs.readFileSync(path.join(SSH_PATH, `${id}_rsa.pub`), { - encoding: "utf-8", - }), - }; - } catch (error) { - throw error; - } -}; - -export const saveSSHKey = async ( - id: string, - publicKey: string, - privateKey: string, -) => { - const { SSH_PATH } = paths(); - const applicationDirectory = SSH_PATH; - - const privateKeyPath = path.join(applicationDirectory, `${id}_rsa`); - const publicKeyPath = path.join(applicationDirectory, `${id}_rsa.pub`); - - const privateKeyStream = fs.createWriteStream(privateKeyPath, { - mode: 0o600, - }); - privateKeyStream.write(privateKey); - privateKeyStream.end(); - - fs.writeFileSync(publicKeyPath, publicKey); -}; - -export const generateSSHKey = async (type: "rsa" | "ed25519" = "rsa") => { - const { SSH_PATH } = paths(); - const applicationDirectory = SSH_PATH; - - if (!fs.existsSync(applicationDirectory)) { - fs.mkdirSync(applicationDirectory, { recursive: true }); - } - - const keyPath = path.join(applicationDirectory, "temp_rsa"); - - if (fs.existsSync(`${keyPath}`)) { - fs.unlinkSync(`${keyPath}`); - } - - if (fs.existsSync(`${keyPath}.pub`)) { - fs.unlinkSync(`${keyPath}.pub`); - } - - const args = [ - "-t", - type, - "-b", - "4096", - "-C", - "dokploy", - "-m", - "PEM", - "-f", - keyPath, - "-N", - "", - ]; - - try { - await spawnAsync("ssh-keygen", args); - const data = await readSSHKey("temp"); - await removeSSHKey("temp"); - return data; - } catch (error) { - throw error; - } -}; - -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); - await fs.promises.unlink(privateKeyPath); - } catch (error) { - throw error; - } -}; diff --git a/apps/dokploy/server/utils/process/execAsync.ts b/apps/dokploy/server/utils/process/execAsync.ts deleted file mode 100644 index 2fa846d7..00000000 --- a/apps/dokploy/server/utils/process/execAsync.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { exec } from "node:child_process"; -import util from "node:util"; -import { findServerById } from "@dokploy/builders"; -import { Client } from "ssh2"; -import { readSSHKey } from "../filesystem/ssh"; -export const execAsync = util.promisify(exec); - -export const execAsyncRemote = async ( - serverId: string | null, - command: string, -): Promise<{ stdout: string; stderr: string }> => { - if (!serverId) return { stdout: "", stderr: "" }; - const server = await findServerById(serverId); - if (!server.sshKeyId) throw new Error("No SSH key available for this server"); - - const keys = await readSSHKey(server.sshKeyId); - - let stdout = ""; - let stderr = ""; - return new Promise((resolve, reject) => { - const conn = new Client(); - - sleep(1000); - conn - .once("ready", () => { - conn.exec(command, (err, stream) => { - if (err) throw err; - stream - .on("close", (code: number, signal: string) => { - conn.end(); - if (code === 0) { - resolve({ stdout, stderr }); - } else { - reject( - new Error( - `Command exited with code ${code}. Stderr: ${stderr}, command: ${command}`, - ), - ); - } - }) - .on("data", (data: string) => { - stdout += data.toString(); - }) - .stderr.on("data", (data) => { - stderr += data.toString(); - }); - }); - }) - .on("error", (err) => { - conn.end(); - if (err.level === "client-authentication") { - reject( - new Error( - `Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`, - ), - ); - } else { - reject(new Error(`SSH connection error: ${err.message}`)); - } - }) - .connect({ - host: server.ipAddress, - port: server.port, - username: server.username, - privateKey: keys.privateKey, - timeout: 99999, - }); - }); -}; - -export const sleep = (ms: number) => { - return new Promise((resolve) => setTimeout(resolve, ms)); -}; diff --git a/apps/dokploy/server/utils/process/spawnAsync.ts b/apps/dokploy/server/utils/process/spawnAsync.ts deleted file mode 100644 index 84855ec0..00000000 --- a/apps/dokploy/server/utils/process/spawnAsync.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - type ChildProcess, - type SpawnOptions, - spawn, -} from "node:child_process"; -import BufferList from "bl"; - -export const spawnAsync = ( - command: string, - args?: string[] | undefined, - onData?: (data: string) => void, // Callback opcional para manejar datos en tiempo real - options?: SpawnOptions, -): Promise & { child: ChildProcess } => { - const child = spawn(command, args ?? [], options ?? {}); - const stdout = child.stdout ? new BufferList() : new BufferList(); - const stderr = child.stderr ? new BufferList() : new BufferList(); - - if (child.stdout) { - child.stdout.on("data", (data) => { - stdout.append(data); - if (onData) { - onData(data.toString()); - } - }); - } - if (child.stderr) { - child.stderr.on("data", (data) => { - stderr.append(data); - if (onData) { - onData(data.toString()); - } - }); - } - - const promise = new Promise((resolve, reject) => { - child.on("error", reject); - - child.on("close", (code) => { - if (code === 0) { - resolve(stdout); - } else { - const err = new Error(`${stderr.toString()}`) as Error & { - code: number; - stderr: BufferList; - stdout: BufferList; - }; - err.code = code || -1; - err.stderr = stderr; - err.stdout = stdout; - reject(err); - } - }); - }) as Promise & { child: ChildProcess }; - - promise.child = child; - - return promise; -}; diff --git a/apps/dokploy/server/utils/servers/remote-docker.ts b/apps/dokploy/server/utils/servers/remote-docker.ts deleted file mode 100644 index b8bc4920..00000000 --- a/apps/dokploy/server/utils/servers/remote-docker.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { findServerById } from "@dokploy/builders"; -import { docker } from "@/server/constants"; -import Dockerode from "dockerode"; -import { readSSHKey } from "../filesystem/ssh"; - -export const getRemoteDocker = async (serverId?: string | null) => { - if (!serverId) return docker; - const server = await findServerById(serverId); - if (!server.sshKeyId) return docker; - const keys = await readSSHKey(server.sshKeyId); - const dockerode = new Dockerode({ - host: server.ipAddress, - port: server.port, - username: server.username, - protocol: "ssh", - // @ts-ignore - sshOptions: { - privateKey: keys.privateKey, - }, - }); - - return dockerode; -}; diff --git a/apps/dokploy/server/utils/traefik/application.ts b/apps/dokploy/server/utils/traefik/application.ts deleted file mode 100644 index c149e2ad..00000000 --- a/apps/dokploy/server/utils/traefik/application.ts +++ /dev/null @@ -1,250 +0,0 @@ -import fs, { writeFileSync } from "node:fs"; -import path from "node:path"; -import type { Domain } from "@/server/api/services/domain"; -import { paths } from "@/server/constants"; -import { dump, load } from "js-yaml"; -import { encodeBase64 } from "../docker/utils"; -import { execAsyncRemote } from "../process/execAsync"; -import type { FileConfig, HttpLoadBalancerService } from "./file-types"; - -export const createTraefikConfig = (appName: string) => { - const defaultPort = 3000; - const serviceURLDefault = `http://${appName}:${defaultPort}`; - const domainDefault = `Host(\`${appName}.docker.localhost\`)`; - const config: FileConfig = { - http: { - routers: { - ...(process.env.NODE_ENV === "production" - ? {} - : { - [`${appName}-router-1`]: { - rule: domainDefault, - service: `${appName}-service-1`, - entryPoints: ["web"], - }, - }), - }, - - services: { - ...(process.env.NODE_ENV === "production" - ? {} - : { - [`${appName}-service-1`]: { - loadBalancer: { - servers: [{ url: serviceURLDefault }], - passHostHeader: true, - }, - }, - }), - }, - }, - }; - const yamlStr = dump(config); - const { DYNAMIC_TRAEFIK_PATH } = paths(); - fs.mkdirSync(DYNAMIC_TRAEFIK_PATH, { recursive: true }); - writeFileSync( - path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`), - yamlStr, - "utf8", - ); -}; - -export const removeTraefikConfig = async ( - appName: string, - serverId?: string | null, -) => { - try { - const { DYNAMIC_TRAEFIK_PATH } = paths(!!serverId); - const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); - - if (serverId) { - await execAsyncRemote(serverId, `rm ${configPath}`); - } else { - if (fs.existsSync(configPath)) { - await fs.promises.unlink(configPath); - } - } - if (fs.existsSync(configPath)) { - await fs.promises.unlink(configPath); - } - } catch (error) {} -}; - -export const removeTraefikConfigRemote = async ( - appName: string, - 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"); - const parsedConfig = (load(yamlStr) as FileConfig) || { - http: { routers: {}, services: {} }, - }; - return parsedConfig; - } - return { http: { routers: {}, services: {} } }; -}; - -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 { - const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`); - - if (!stdout) return fileConfig; - - const parsedConfig = (load(stdout) as FileConfig) || { - http: { routers: {}, services: {} }, - }; - return parsedConfig; - } catch (err) { - return fileConfig; - } -}; - -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"); - return yamlStr; - } - return null; -}; - -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}`); - if (!stdout) return null; - return stdout; - } catch (err) { - return null; - } -}; - -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"); - return yamlStr; - } - return null; -}; - -export const readConfigInPath = async (pathFile: string, serverId?: string) => { - const configPath = path.join(pathFile); - - if (serverId) { - const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`); - if (!stdout) return null; - return stdout; - } - if (fs.existsSync(configPath)) { - const yamlStr = fs.readFileSync(configPath, "utf8"); - return yamlStr; - } - return null; -}; - -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) { - console.error("Error saving the YAML config file:", e); - } -}; - -export const writeConfigRemote = async ( - serverId: string, - appName: string, - 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) { - console.error("Error saving the YAML config file:", e); - } -}; - -export const writeTraefikConfigInPath = async ( - pathFile: string, - traefikConfig: string, - serverId?: string, -) => { - try { - const configPath = path.join(pathFile); - if (serverId) { - const encoded = encodeBase64(traefikConfig); - await execAsyncRemote( - serverId, - `echo "${encoded}" | base64 -d > "${configPath}"`, - ); - } else { - fs.writeFileSync(configPath, traefikConfig, "utf8"); - } - fs.writeFileSync(configPath, traefikConfig, "utf8"); - } catch (e) { - console.error("Error saving the YAML config file:", e); - } -}; - -export const writeTraefikConfig = ( - traefikConfig: FileConfig, - 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"); - } catch (e) { - console.error("Error saving the YAML config file:", e); - } -}; - -export const writeTraefikConfigRemote = async ( - traefikConfig: FileConfig, - appName: string, - 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}`); - } catch (e) { - console.error("Error saving the YAML config file:", e); - } -}; - -export const createServiceConfig = ( - appName: string, - domain: Domain, -): { - loadBalancer: HttpLoadBalancerService; -} => ({ - loadBalancer: { - servers: [{ url: `http://${appName}:${domain.port || 80}` }], - passHostHeader: true, - }, -}); diff --git a/apps/dokploy/server/utils/traefik/domain.ts b/apps/dokploy/server/utils/traefik/domain.ts deleted file mode 100644 index 684586fc..00000000 --- a/apps/dokploy/server/utils/traefik/domain.ts +++ /dev/null @@ -1,145 +0,0 @@ -import type { Domain } from "@/server/api/services/domain"; -import type { ApplicationNested } from "../builders"; -import { - createServiceConfig, - loadOrCreateConfig, - loadOrCreateConfigRemote, - removeTraefikConfig, - removeTraefikConfigRemote, - writeTraefikConfig, - writeTraefikConfigRemote, -} from "./application"; -import type { FileConfig, HttpRouter } from "./file-types"; - -export const manageDomain = async (app: ApplicationNested, domain: Domain) => { - const { appName } = app; - let config: FileConfig; - - if (app.serverId) { - config = await loadOrCreateConfigRemote(app.serverId, appName); - } else { - config = loadOrCreateConfig(appName); - } - const serviceName = `${appName}-service-${domain.uniqueConfigKey}`; - const routerName = `${appName}-router-${domain.uniqueConfigKey}`; - const routerNameSecure = `${appName}-router-websecure-${domain.uniqueConfigKey}`; - - config.http = config.http || { routers: {}, services: {} }; - config.http.routers = config.http.routers || {}; - config.http.services = config.http.services || {}; - - config.http.routers[routerName] = await createRouterConfig( - app, - domain, - "web", - ); - - if (domain.https) { - config.http.routers[routerNameSecure] = await createRouterConfig( - app, - domain, - "websecure", - ); - } else { - delete config.http.routers[routerNameSecure]; - } - - config.http.services[serviceName] = createServiceConfig(appName, domain); - - if (app.serverId) { - await writeTraefikConfigRemote(config, appName, app.serverId); - } else { - writeTraefikConfig(config, appName); - } -}; - -export const removeDomain = async ( - application: ApplicationNested, - uniqueKey: number, -) => { - const { appName, serverId } = application; - let config: FileConfig; - - if (serverId) { - config = await loadOrCreateConfigRemote(serverId, appName); - } else { - config = loadOrCreateConfig(appName); - } - - const routerKey = `${appName}-router-${uniqueKey}`; - const routerSecureKey = `${appName}-router-websecure-${uniqueKey}`; - - const serviceKey = `${appName}-service-${uniqueKey}`; - if (config.http?.routers?.[routerKey]) { - delete config.http.routers[routerKey]; - } - if (config.http?.routers?.[routerSecureKey]) { - delete config.http.routers[routerSecureKey]; - } - if (config.http?.services?.[serviceKey]) { - delete config.http.services[serviceKey]; - } - - // verify if is the last router if so we delete the router - if ( - config?.http?.routers && - Object.keys(config?.http?.routers).length === 0 - ) { - if (serverId) { - await removeTraefikConfigRemote(appName, serverId); - } else { - await removeTraefikConfig(appName); - } - } else { - if (serverId) { - await writeTraefikConfigRemote(config, appName, serverId); - } else { - writeTraefikConfig(config, appName); - } - } -}; - -export const createRouterConfig = async ( - app: ApplicationNested, - domain: Domain, - entryPoint: "web" | "websecure", -) => { - const { appName, redirects, security } = app; - const { certificateType } = domain; - - const { host, path, https, uniqueConfigKey } = domain; - const routerConfig: HttpRouter = { - rule: `Host(\`${host}\`)${path !== null && path !== "/" ? ` && PathPrefix(\`${path}\`)` : ""}`, - service: `${appName}-service-${uniqueConfigKey}`, - middlewares: [], - entryPoints: [entryPoint], - }; - - if (entryPoint === "web" && https) { - routerConfig.middlewares = ["redirect-to-https"]; - } - - if ((entryPoint === "websecure" && https) || !https) { - // redirects - for (const redirect of redirects) { - const middlewareName = `redirect-${appName}-${redirect.uniqueConfigKey}`; - routerConfig.middlewares?.push(middlewareName); - } - - // security - if (security.length > 0) { - const middlewareName = `auth-${appName}`; - routerConfig.middlewares?.push(middlewareName); - } - } - - if (entryPoint === "websecure") { - if (certificateType === "letsencrypt") { - routerConfig.tls = { certResolver: "letsencrypt" }; - } else if (certificateType === "none") { - routerConfig.tls = undefined; - } - } - - return routerConfig; -}; diff --git a/apps/dokploy/server/utils/traefik/file-types.ts b/apps/dokploy/server/utils/traefik/file-types.ts deleted file mode 100644 index e761cb51..00000000 --- a/apps/dokploy/server/utils/traefik/file-types.ts +++ /dev/null @@ -1,1274 +0,0 @@ -/* eslint-disable */ - -/** - * The Services are responsible for configuring how to reach the actual services that will eventually handle the incoming requests. - */ - -/** - * Traefik v2 Dynamic Configuration File Provider - */ -export interface FileConfig { - http?: { - routers?: { - [k: string]: HttpRouter; - }; - services?: { - [k: string]: HttpService; - }; - /** - * Attached to the routers, pieces of middleware are a means of tweaking the requests before they are sent to your service (or before the answer from the services are sent to the clients). - * - * There are several available middleware in Traefik, some can modify the request, the headers, some are in charge of redirections, some add authentication, and so on. - * - * Pieces of middleware can be combined in chains to fit every scenario. - */ - middlewares?: { - [k: string]: HttpMiddleware; - }; - [k: string]: unknown; - }; - tcp?: { - routers?: { - [k: string]: TcpRouter; - }; - /** - * Each of the fields of the service section represents a kind of service. Which means, that for each specified service, one of the fields, and only one, has to be enabled to define what kind of service is created. Currently, the two available kinds are LoadBalancer, and Weighted. - */ - services?: { - [k: string]: TcpService; - }; - [k: string]: unknown; - }; - udp?: { - /** - * Similarly to TCP, as UDP is the transport layer, there is no concept of a request, so there is no notion of an URL path prefix to match an incoming UDP packet with. Furthermore, as there is no good TLS support at the moment for multiple hosts, there is no Host SNI notion to match against either. Therefore, there is no criterion that could be used as a rule to match incoming packets in order to route them. So UDP "routers" at this time are pretty much only load-balancers in one form or another. - */ - routers?: { - [k: string]: UdpRouter; - }; - /** - * Each of the fields of the service section represents a kind of service. Which means, that for each specified service, one of the fields, and only one, has to be enabled to define what kind of service is created. Currently, the two available kinds are LoadBalancer, and Weighted. - */ - services?: { - [k: string]: UdpService; - }; - }; - /** - * Configures the TLS connection, TLS options, and certificate stores. - */ - tls?: { - certificates?: { - certFile?: string; - keyFile?: string; - /** - * A list of stores can be specified here to indicate where the certificates should be stored. Although the stores list will actually be ignored and automatically set to ["default"]. - */ - stores?: string[]; - [k: string]: unknown; - }[]; - /** - * The TLS options allow one to configure some parameters of the TLS connection. - */ - options?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: { - /** - * Minimum TLS Version - */ - minVersion?: string; - /** - * Maximum TLS Version. It is discouraged to use of this setting to disable TLS1.3. The recommended approach is to update the clients to support TLS1.3. - */ - maxVersion?: string; - /** - * Cipher suites defined for TLS 1.2 and below cannot be used in TLS 1.3, and vice versa. With TLS 1.3, the cipher suites are not configurable (all supported cipher suites are safe in this case). - */ - cipherSuites?: string[]; - /** - * This option allows to set the preferred elliptic curves in a specific order. - * - * The names of the curves defined by crypto (e.g. CurveP521) and the RFC defined names (e.g. secp521r1) can be used. - */ - curvePreferences?: string[]; - /** - * With strict SNI checking enabled, Traefik won't allow connections from clients that do not specify a server_name extension or don't match any certificate configured on the tlsOption. - */ - sniStrict?: boolean; - /** - * This option allows the server to choose its most preferred cipher suite instead of the client's. Please note that this is enabled automatically when minVersion or maxVersion are set. - */ - preferServerCipherSuites?: boolean; - /** - * Traefik supports mutual authentication, through the clientAuth section. - */ - clientAuth?: { - /** - * For authentication policies that require verification of the client certificate, the certificate authority for the certificate should be set here. - */ - caFiles?: string[]; - clientAuthType?: string; - [k: string]: unknown; - }; - [k: string]: unknown; - }; - }; - /** - * Any store definition other than the default one (named default) will be ignored, and there is therefore only one globally available TLS store. - */ - stores?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: { - /** - * Traefik can use a default certificate for connections without a SNI, or without a matching domain. If no default certificate is provided, Traefik generates and uses a self-signed certificate. - */ - defaultCertificate?: { - certFile?: string; - keyFile?: string; - }; - /** - * GeneratedCert defines the default generated certificate configuration. - */ - defaultGeneratedCert?: { - /** - * Resolver is the name of the resolver that will be used to issue the DefaultCertificate. - */ - resolver?: string; - /** - * Domain is the domain definition for the DefaultCertificate. - */ - domain?: { - /** - * Main defines the main domain name. - */ - main?: string; - /** - * SANs defines the subject alternative domain names. - */ - sans?: string[]; - [k: string]: unknown; - }; - }; - }; - }; - }; -} - -export type HttpService = - | { - loadBalancer?: HttpLoadBalancerService; - } - | { - weighted?: HttpWeightedService; - } - | { - mirroring?: HttpMirroringService; - } - | { - failover?: HttpFailoverService; - }; -export type HttpMiddleware = - | { - addPrefix?: AddPrefixMiddleware; - } - | { - basicAuth?: BasicAuthMiddleware; - } - | { - buffering?: BufferingMiddleware; - } - | { - chain?: ChainMiddleware; - } - | { - circuitBreaker?: CircuitBreakerMiddleware; - } - | { - compress?: CompressMiddleware; - } - | { - contentType?: ContentTypeMiddleware; - } - | { - digestAuth?: DigestAuthMiddleware; - } - | { - errors?: ErrorsMiddleware; - } - | { - forwardAuth?: ForwardAuthMiddleware; - } - | { - headers?: HeadersMiddleware; - } - | { - ipWhiteList?: IpWhiteListMiddleware; - } - | { - inFlightReq?: InFlightReqMiddleware; - } - | { - passTLSClientCert?: PassTLSClientCertMiddleware; - } - | { - plugin?: PluginMiddleware; - } - | { - rateLimit?: RateLimitMiddleware; - } - | { - redirectRegex?: RedirectRegexMiddleware; - } - | { - redirectScheme?: RedirectSchemeMiddleware; - } - | { - replacePath?: ReplacePathMiddleware; - } - | { - replacePathRegex?: ReplacePathRegexMiddleware; - } - | { - retry?: RetryMiddleware; - } - | { - stripPrefix?: StripPrefixMiddleware; - } - | { - stripPrefixRegex?: StripPrefixRegexMiddleware; - }; -export type TcpService = - | { - loadBalancer?: TcpLoadBalancerService; - } - | { - weighted?: TcpWeightedService; - }; -export type UdpService = - | { - loadBalancer?: UdpLoadBalancerService; - } - | { - weighted?: UdpWeightedService; - }; - -/** - * A router is in charge of connecting incoming requests to the services that can handle them. In the process, routers may use pieces of middleware to update the request, or act before forwarding the request to the service. - */ -export interface HttpRouter { - /** - * If not specified, HTTP routers will accept requests from all defined entry points. If you want to limit the router scope to a set of entry points, set the entryPoints option. - */ - entryPoints?: string[]; - /** - * Rules are a set of matchers configured with values, that determine if a particular request matches specific criteria. If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service. - */ - rule: string; - /** - * To avoid path overlap, routes are sorted, by default, in descending order using rules length. The priority is directly equal to the length of the rule, and so the longest length has the highest priority. A value of 0 for the priority is ignored: priority = 0 means that the default rules length sorting is used. - */ - priority?: number; - /** - * You can attach a list of middlewares to each HTTP router. The middlewares will take effect only if the rule matches, and before forwarding the request to the service. Middlewares are applied in the same order as their declaration in router. - */ - middlewares?: string[]; - /** - * Each request must eventually be handled by a service, which is why each router definition should include a service target, which is basically where the request will be passed along to. HTTP routers can only target HTTP services (not TCP services). - */ - service: string; - /** - * When a TLS section is specified, it instructs Traefik that the current router is dedicated to HTTPS requests only (and that the router should ignore HTTP (non TLS) requests). Traefik will terminate the SSL connections (meaning that it will send decrypted data to the services). If you need to define the same route for both HTTP and HTTPS requests, you will need to define two different routers: one with the tls section, one without. - */ - tls?: { - /** - * The options field enables fine-grained control of the TLS parameters. It refers to a TLS Options and will be applied only if a Host rule is defined. - */ - options?: string; - /** - * If certResolver is defined, Traefik will try to generate certificates based on routers Host & HostSNI rules. - */ - certResolver?: string; - /** - * You can set SANs (alternative domains) for each main domain. Every domain must have A/AAAA records pointing to Traefik. Each domain & SAN will lead to a certificate request. - */ - domains?: { - /** - * Main defines the main domain name. - */ - main?: string; - /** - * SANs defines the subject alternative domain names. - */ - sans?: string[]; - [k: string]: unknown; - }[]; - [k: string]: unknown; - }; -} -/** - * The load balancers are able to load balance the requests between multiple instances of your programs. - * - * Each service has a load-balancer, even if there is only one server to forward traffic to. - */ -export interface HttpLoadBalancerService { - /** - * Servers declare a single instance of your program. - * - * @minItems 1 - */ - servers: [ - { - /** - * The url option point to a specific instance. Paths in the servers' url have no effect. If you want the requests to be sent to a specific path on your servers, configure your routers to use a corresponding middleware (e.g. the AddPrefix or ReplacePath) middlewares. - */ - url: string; - [k: string]: unknown; - }, - ...{ - /** - * The url option point to a specific instance. Paths in the servers' url have no effect. If you want the requests to be sent to a specific path on your servers, configure your routers to use a corresponding middleware (e.g. the AddPrefix or ReplacePath) middlewares. - */ - url: string; - [k: string]: unknown; - }[], - ]; - /** - * When sticky sessions are enabled, a cookie is set on the initial request and response to let the client know which server handles the first response. On subsequent requests, to keep the session alive with the same server, the client should resend the same cookie. - */ - sticky?: { - cookie?: { - /** - * The default cookie name is an abbreviation of a sha1 (ex: _1d52e). - */ - name?: string; - secure?: boolean; - httpOnly?: boolean; - /** - * Can be none, lax, strict or empty. - */ - sameSite?: string; - [k: string]: unknown; - }; - [k: string]: unknown; - }; - /** - * Configure health check to remove unhealthy servers from the load balancing rotation. Traefik will consider your servers healthy as long as they return status codes between 2XX and 3XX to the health check requests (carried out every interval). Traefik keeps monitoring the health of unhealthy servers. If a server has recovered (returning 2xx -> 3xx responses again), it will be added back to the load balancer rotation pool. - */ - healthCheck?: { - /** - * If defined, will apply this Method for the health check request. - */ - method?: string; - /** - * path is appended to the server URL to set the health check endpoint. - */ - path?: string; - /** - * If defined, will replace the server URL scheme for the health check endpoint - */ - scheme?: string; - /** - * If defined, will apply Host header hostname to the health check request. - */ - hostname?: string; - /** - * If defined, will replace the server URL port for the health check endpoint. - */ - port?: number; - /** - * Defines the frequency of the health check calls. Interval is to be given in a format understood by `time.ParseDuration`. The interval must be greater than the timeout. If configuration doesn't reflect this, the interval will be set to timeout + 1 second. - */ - interval?: string; - /** - * Defines the maximum duration Traefik will wait for a health check request before considering the server failed (unhealthy). Timeout is to be given in a format understood by `time.ParseDuration`. - */ - timeout?: string; - /** - * Defines custom headers to be sent to the health check endpoint. - */ - headers?: { - [k: string]: string; - }; - /** - * Defines whether redirects should be followed during the health check calls (default: true). - */ - followRedirects?: boolean; - [k: string]: unknown; - }; - /** - * The passHostHeader allows to forward client Host header to server. By default, passHostHeader is true. - */ - passHostHeader?: boolean; - /** - * Defines how Traefik forwards the response from the backend server to the client. - */ - responseForwarding?: { - /** - * Specifies the interval in between flushes to the client while copying the response body. It is a duration in milliseconds, defaulting to 100. A negative value means to flush immediately after each write to the client. The flushInterval is ignored when ReverseProxy recognizes a response as a streaming response; for such responses, writes are flushed to the client immediately. - */ - flushInterval?: string; - [k: string]: unknown; - }; - serversTransport?: string; -} -/** - * The WRR is able to load balance the requests between multiple services based on weights. - * - * This strategy is only available to load balance between services and not between servers. - */ -export interface HttpWeightedService { - services?: { - name?: string; - weight?: number; - [k: string]: unknown; - }[]; - /** - * When sticky sessions are enabled, a cookie is set on the initial request and response to let the client know which server handles the first response. On subsequent requests, to keep the session alive with the same server, the client should resend the same cookie. - */ - sticky?: { - cookie?: { - /** - * The default cookie name is an abbreviation of a sha1 (ex: _1d52e). - */ - name?: string; - secure?: boolean; - httpOnly?: boolean; - /** - * Can be none, lax, strict or empty. - */ - sameSite?: string; - [k: string]: unknown; - }; - [k: string]: unknown; - }; - healthCheck?: { - [k: string]: unknown; - }; -} -/** - * The mirroring is able to mirror requests sent to a service to other services. Please note that by default the whole request is buffered in memory while it is being mirrored. See the maxBodySize option for how to modify this behaviour. - */ -export interface HttpMirroringService { - service?: string; - /** - * maxBodySize is the maximum size allowed for the body of the request. If the body is larger, the request is not mirrored. Default value is -1, which means unlimited size. - */ - maxBodySize?: number; - mirrors?: { - name?: string; - percent?: number; - [k: string]: unknown; - }[]; - healthCheck?: { - [k: string]: unknown; - }; -} -export interface HttpFailoverService { - service?: string; - fallback?: string; - healthCheck?: { - [k: string]: unknown; - }; -} -/** - * The AddPrefix middleware updates the URL Path of the request before forwarding it. - */ -export interface AddPrefixMiddleware { - /** - * prefix is the string to add before the current path in the requested URL. It should include the leading slash (/). - */ - prefix?: string; -} -/** - * The BasicAuth middleware is a quick way to restrict access to your services to known users. If both users and usersFile are provided, the two are merged. The contents of usersFile have precedence over the values in users. - */ -export interface BasicAuthMiddleware { - /** - * The users option is an array of authorized users. Each user will be declared using the `name:hashed-password` format. - */ - users?: string[]; - /** - * The usersFile option is the path to an external file that contains the authorized users for the middleware. - * - * The file content is a list of `name:hashed-password`. - */ - usersFile?: string; - /** - * You can customize the realm for the authentication with the realm option. The default value is traefik. - */ - realm?: string; - /** - * You can define a header field to store the authenticated user using the headerField option. - */ - headerField?: string; - /** - * Set the removeHeader option to true to remove the authorization header before forwarding the request to your service. (Default value is false.) - */ - removeHeader?: boolean; - [k: string]: unknown; -} -/** - * The Buffering middleware gives you control on how you want to read the requests before sending them to services. - * - * With Buffering, Traefik reads the entire request into memory (possibly buffering large requests into disk), and rejects requests that are over a specified limit. - * - * This can help services deal with large data (multipart/form-data for example), and can minimize time spent sending data to a service. - */ -export interface BufferingMiddleware { - /** - * With the maxRequestBodyBytes option, you can configure the maximum allowed body size for the request (in Bytes). - * - * If the request exceeds the allowed size, it is not forwarded to the service and the client gets a 413 (Request Entity Too Large) response. - */ - maxRequestBodyBytes?: number; - /** - * You can configure a threshold (in Bytes) from which the request will be buffered on disk instead of in memory with the memRequestBodyBytes option. - */ - memRequestBodyBytes?: number; - /** - * With the maxResponseBodyBytes option, you can configure the maximum allowed response size from the service (in Bytes). - * - * If the response exceeds the allowed size, it is not forwarded to the client. The client gets a 413 (Request Entity Too Large) response instead. - */ - maxResponseBodyBytes?: number; - /** - * You can configure a threshold (in Bytes) from which the response will be buffered on disk instead of in memory with the memResponseBodyBytes option. - */ - memResponseBodyBytes?: number; - /** - * You can have the Buffering middleware replay the request with the help of the retryExpression option. - */ - retryExpression?: string; -} -/** - * The Chain middleware enables you to define reusable combinations of other pieces of middleware. It makes reusing the same groups easier. - */ -export interface ChainMiddleware { - /** - * @minItems 1 - */ - middlewares?: [string, ...string[]]; -} -/** - * The circuit breaker protects your system from stacking requests to unhealthy services (resulting in cascading failures). - * - * When your system is healthy, the circuit is closed (normal operations). When your system becomes unhealthy, the circuit becomes open and the requests are no longer forwarded (but handled by a fallback mechanism). - * - * To assess if your system is healthy, the circuit breaker constantly monitors the services. - */ -export interface CircuitBreakerMiddleware { - /** - * You can specify an expression that, once matched, will trigger the circuit breaker (and apply the fallback mechanism instead of calling your services). - */ - expression?: string; - /** - * The interval between successive checks of the circuit breaker condition (when in standby state) - */ - checkPeriod?: string; - /** - * The duration for which the circuit breaker will wait before trying to recover (from a tripped state). - */ - fallbackDuration?: string; - /** - * The duration for which the circuit breaker will try to recover (as soon as it is in recovering state). - */ - recoveryDuration?: string; -} -/** - * The Compress middleware enables the gzip compression. - */ -export interface CompressMiddleware { - /** - * excludedContentTypes specifies a list of content types to compare the Content-Type header of the incoming requests to before compressing. - * - * The requests with content types defined in excludedContentTypes are not compressed. - * - * Content types are compared in a case-insensitive, whitespace-ignored manner. - */ - excludedContentTypes?: string[]; - /** - * specifies the minimum amount of bytes a response body must have to be compressed. - */ - minResponseBodyBytes?: number; -} -/** - * The Content-Type middleware - or rather its unique autoDetect option - specifies whether to let the Content-Type header, if it has not been set by the backend, be automatically set to a value derived from the contents of the response. - * - * As a proxy, the default behavior should be to leave the header alone, regardless of what the backend did with it. However, the historic default was to always auto-detect and set the header if it was nil, and it is going to be kept that way in order to support users currently relying on it. This middleware exists to enable the correct behavior until at least the default one can be changed in a future version. - */ -export interface ContentTypeMiddleware { - /** - * autoDetect specifies whether to let the Content-Type header, if it has not been set by the backend, be automatically set to a value derived from the contents of the response. - */ - autoDetect?: boolean; -} -/** - * The DigestAuth middleware is a quick way to restrict access to your services to known users. If both users and usersFile are provided, the two are merged. The contents of usersFile have precedence over the values in users. - */ -export interface DigestAuthMiddleware { - /** - * The users option is an array of authorized users. Each user will be declared using the `name:realm:encoded-password` format. - */ - users?: string[]; - /** - * The usersFile option is the path to an external file that contains the authorized users for the middleware. - * - * The file content is a list of `name:realm:encoded-password`. - */ - usersFile?: string; - /** - * You can customize the realm for the authentication with the realm option. The default value is traefik. - */ - realm?: string; - /** - * You can customize the header field for the authenticated user using the headerField option. - */ - headerField?: string; - /** - * Set the removeHeader option to true to remove the authorization header before forwarding the request to your service. (Default value is false.) - */ - removeHeader?: boolean; -} -/** - * The ErrorPage middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. The error page itself is not hosted by Traefik. - */ -export interface ErrorsMiddleware { - /** - * The status that will trigger the error page. - * - * The status code ranges are inclusive (500-599 will trigger with every code between 500 and 599, 500 and 599 included). You can define either a status code like 500 or ranges with a syntax like 500-599. - */ - status?: string[]; - /** - * The service that will serve the new requested error page. - */ - service?: string; - /** - * The URL for the error page (hosted by service). You can use {status} in the query, that will be replaced by the received status code. - */ - query?: string; -} -/** - * The ForwardAuth middleware delegate the authentication to an external service. If the service response code is 2XX, access is granted and the original request is performed. Otherwise, the response from the authentication server is returned. - */ -export interface ForwardAuthMiddleware { - /** - * The address option defines the authentication server address. - */ - address?: string; - /** - * The tls option is the TLS configuration from Traefik to the authentication server. - */ - tls?: { - /** - * Certificate Authority used for the secured connection to the authentication server. - */ - ca?: string; - /** - * Policy used for the secured connection with TLS Client Authentication to the authentication server. Requires tls.ca to be defined. - */ - caOptional?: boolean; - /** - * Public certificate used for the secured connection to the authentication server. - */ - cert?: string; - /** - * Private certificate used for the secure connection to the authentication server. - */ - key?: string; - /** - * If insecureSkipVerify is true, TLS for the connection to authentication server accepts any certificate presented by the server and any host name in that certificate. - */ - insecureSkipVerify?: boolean; - [k: string]: unknown; - }; - /** - * Set the trustForwardHeader option to true to trust all the existing X-Forwarded-* headers. - */ - trustForwardHeader?: boolean; - /** - * The authResponseHeaders option is the list of the headers to copy from the authentication server to the request. - */ - authResponseHeaders?: string[]; - /** - * The authResponseHeadersRegex option is the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - */ - authResponseHeadersRegex?: string; - /** - * The authRequestHeaders option is the list of the headers to copy from the request to the authentication server. - */ - authRequestHeaders?: string[]; -} -/** - * The Headers middleware can manage the requests/responses headers. - */ -export interface HeadersMiddleware { - /** - * The customRequestHeaders option lists the Header names and values to apply to the request. - */ - customRequestHeaders?: { - [k: string]: string; - }; - /** - * The customResponseHeaders option lists the Header names and values to apply to the response. - */ - customResponseHeaders?: { - [k: string]: string; - }; - /** - * The accessControlAllowCredentials indicates whether the request can include user credentials. - */ - accessControlAllowCredentials?: boolean; - /** - * The accessControlAllowHeaders indicates which header field names can be used as part of the request. - */ - accessControlAllowHeaders?: string[]; - /** - * The accessControlAllowMethods indicates which methods can be used during requests. - */ - accessControlAllowMethods?: string[]; - /** - * The accessControlAllowOriginList indicates whether a resource can be shared by returning different values. - * - * A wildcard origin * can also be configured, and will match all requests. If this value is set by a backend server, it will be overwritten by Traefik - * - * This value can contain a list of allowed origins. - */ - accessControlAllowOriginList?: string[]; - /** - * The accessControlAllowOriginListRegex option is the counterpart of the accessControlAllowOriginList option with regular expressions instead of origin values. - */ - accessControlAllowOriginListRegex?: string[]; - /** - * The accessControlExposeHeaders indicates which headers are safe to expose to the api of a CORS API specification. - */ - accessControlExposeHeaders?: string[]; - /** - * The accessControlMaxAge indicates how long (in seconds) a preflight request can be cached. - */ - accessControlMaxAge?: number; - /** - * The addVaryHeader is used in conjunction with accessControlAllowOriginList to determine whether the vary header should be added or modified to demonstrate that server responses can differ based on the value of the origin header. - */ - addVaryHeader?: boolean; - /** - * The allowedHosts option lists fully qualified domain names that are allowed. - */ - allowedHosts?: string[]; - /** - * The hostsProxyHeaders option is a set of header keys that may hold a proxied hostname value for the request. - */ - hostsProxyHeaders?: string[]; - /** - * The sslRedirect is set to true, then only allow https requests. - */ - sslRedirect?: boolean; - /** - * Set the sslTemporaryRedirect to true to force an SSL redirection using a 302 (instead of a 301). - */ - sslTemporaryRedirect?: boolean; - /** - * The sslHost option is the host name that is used to redirect http requests to https. - */ - sslHost?: string; - /** - * The sslProxyHeaders option is set of header keys with associated values that would indicate a valid https request. Useful when using other proxies with header like: "X-Forwarded-Proto": "https". - */ - sslProxyHeaders?: { - [k: string]: string; - }; - /** - * Set sslForceHost to true and set SSLHost to forced requests to use SSLHost even the ones that are already using SSL. - */ - sslForceHost?: boolean; - /** - * The stsSeconds is the max-age of the Strict-Transport-Security header. If set to 0, would NOT include the header. - */ - stsSeconds?: number; - /** - * The stsIncludeSubdomains is set to true, the includeSubDomains directive will be appended to the Strict-Transport-Security header. - */ - stsIncludeSubdomains?: boolean; - /** - * Set stsPreload to true to have the preload flag appended to the Strict-Transport-Security header. - */ - stsPreload?: boolean; - /** - * Set forceSTSHeader to true, to add the STS header even when the connection is HTTP. - */ - forceSTSHeader?: boolean; - /** - * Set frameDeny to true to add the X-Frame-Options header with the value of DENY. - */ - frameDeny?: boolean; - /** - * The customFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option. - */ - customFrameOptionsValue?: string; - /** - * Set contentTypeNosniff to true to add the X-Content-Type-Options header with the value nosniff. - */ - contentTypeNosniff?: boolean; - /** - * Set browserXssFilter to true to add the X-XSS-Protection header with the value 1; mode=block. - */ - browserXssFilter?: boolean; - /** - * The customBrowserXssValue option allows the X-XSS-Protection header value to be set with a custom value. This overrides the BrowserXssFilter option. - */ - customBrowserXSSValue?: string; - /** - * The contentSecurityPolicy option allows the Content-Security-Policy header value to be set with a custom value. - */ - contentSecurityPolicy?: string; - /** - * The publicKey implements HPKP to prevent MITM attacks with forged certificates. - */ - publicKey?: string; - /** - * The referrerPolicy allows sites to control when browsers will pass the Referer header to other sites. - */ - referrerPolicy?: string; - /** - * The featurePolicy allows sites to control browser features. - */ - featurePolicy?: string; - /** - * The permissionsPolicy allows sites to control browser features. - */ - permissionsPolicy?: string; - /** - * Set isDevelopment to true when developing. The AllowedHosts, SSL, and STS options can cause some unwanted effects. Usually testing happens on http, not https, and on localhost, not your production domain. - * If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as false. - */ - isDevelopment?: boolean; -} -/** - * IPWhitelist accepts / refuses requests based on the client IP. - */ -export interface IpWhiteListMiddleware { - /** - * The sourceRange option sets the allowed IPs (or ranges of allowed IPs by using CIDR notation). - */ - sourceRange?: string[]; - ipStrategy?: IpStrategy; -} -/** - * The ipStrategy option defines parameters that set how Traefik will determine the client IP. - */ -export interface IpStrategy { - /** - * The depth option tells Traefik to use the X-Forwarded-For header and take the IP located at the depth position (starting from the right). If depth is greater than the total number of IPs in X-Forwarded-For, then the client IP will be empty. depth is ignored if its value is lesser than or equal to 0. - */ - depth?: number; - /** - * excludedIPs tells Traefik to scan the X-Forwarded-For header and pick the first IP not in the list. If depth is specified, excludedIPs is ignored. - */ - excludedIPs?: string[]; -} -/** - * To proactively prevent services from being overwhelmed with high load, a limit on the number of simultaneous in-flight requests can be applied. - */ -export interface InFlightReqMiddleware { - /** - * The amount option defines the maximum amount of allowed simultaneous in-flight request. The middleware will return an HTTP 429 Too Many Requests if there are already amount requests in progress (based on the same sourceCriterion strategy). - */ - amount?: number; - sourceCriterion?: SourceCriterion; -} -/** - * SourceCriterion defines what criterion is used to group requests as originating from a common source. The precedence order is ipStrategy, then requestHeaderName, then requestHost. If none are set, the default is to use the requestHost. - */ -export interface SourceCriterion { - ipStrategy?: IpStrategy; - /** - * Requests having the same value for the given header are grouped as coming from the same source. - */ - requestHeaderName?: string; - /** - * Whether to consider the request host as the source. - */ - requestHost?: boolean; -} -/** - * PassTLSClientCert adds in header the selected data from the passed client tls certificate. - */ -export interface PassTLSClientCertMiddleware { - /** - * The pem option sets the X-Forwarded-Tls-Client-Cert header with the escape certificate. - */ - pem?: boolean; - /** - * The info option select the specific client certificate details you want to add to the X-Forwarded-Tls-Client-Cert-Info header. The value of the header will be an escaped concatenation of all the selected certificate details. - */ - info?: { - /** - * Set the notAfter option to true to add the Not After information from the Validity part. - */ - notAfter?: boolean; - /** - * Set the notBefore option to true to add the Not Before information from the Validity part. - */ - notBefore?: boolean; - /** - * Set the sans option to true to add the Subject Alternative Name information from the Subject Alternative Name part. - */ - sans?: boolean; - /** - * The subject select the specific client certificate subject details you want to add to the X-Forwarded-Tls-Client-Cert-Info header. - */ - subject?: { - /** - * Set the country option to true to add the country information into the subject. - */ - country?: boolean; - /** - * Set the province option to true to add the province information into the subject. - */ - province?: boolean; - /** - * Set the locality option to true to add the locality information into the subject. - */ - locality?: boolean; - /** - * Set the organization option to true to add the organization information into the subject. - */ - organization?: boolean; - /** - * Set the commonName option to true to add the commonName information into the subject. - */ - commonName?: boolean; - /** - * Set the serialNumber option to true to add the serialNumber information into the subject. - */ - serialNumber?: boolean; - /** - * Set the domainComponent option to true to add the domainComponent information into the subject. - */ - domainComponent?: boolean; - [k: string]: unknown; - }; - /** - * The issuer select the specific client certificate issuer details you want to add to the X-Forwarded-Tls-Client-Cert-Info header. - */ - issuer?: { - /** - * Set the country option to true to add the country information into the issuer. - */ - country?: boolean; - /** - * Set the province option to true to add the province information into the issuer. - */ - province?: boolean; - /** - * Set the locality option to true to add the locality information into the issuer. - */ - locality?: boolean; - /** - * Set the organization option to true to add the organization information into the issuer. - */ - organization?: boolean; - /** - * Set the commonName option to true to add the commonName information into the issuer. - */ - commonName?: boolean; - /** - * Set the serialNumber option to true to add the serialNumber information into the issuer. - */ - serialNumber?: boolean; - /** - * Set the domainComponent option to true to add the domainComponent information into the issuer. - */ - domainComponent?: boolean; - [k: string]: unknown; - }; - [k: string]: unknown; - }; -} -/** - * Some plugins will need to be configured by adding a dynamic configuration. - */ -export interface PluginMiddleware { - [k: string]: { - [k: string]: unknown; - }; -} -/** - * The RateLimit middleware ensures that services will receive a fair number of requests, and allows one to define what fair is. - */ -export interface RateLimitMiddleware { - /** - * average is the maximum rate, by default in requests by second, allowed for the given source. - * - * It defaults to 0, which means no rate limiting. - * - * The rate is actually defined by dividing average by period. So for a rate below 1 req/s, one needs to define a period larger than a second. - */ - average?: string | number; - /** - * period, in combination with average, defines the actual maximum rate. - * - * It defaults to 1 second. - */ - period?: string | number; - /** - * burst is the maximum number of requests allowed to go through in the same arbitrarily small period of time. - * - * It defaults to 1. - */ - burst?: number; - sourceCriterion?: SourceCriterion; -} -/** - * RegexRedirect redirect a request from an url to another with regex matching and replacement. - */ -export interface RedirectRegexMiddleware { - /** - * Set the permanent option to true to apply a permanent redirection. - */ - permanent?: boolean; - /** - * The regex option is the regular expression to match and capture elements from the request URL. - */ - regex?: string; - /** - * The replacement option defines how to modify the URL to have the new target URL. Care should be taken when defining replacement expand variables: $1x is equivalent to ${1x}, not ${1}x (see Regexp.Expand), so use ${1} syntax. - */ - replacement?: string; -} -/** - * RedirectScheme redirect request from a scheme to another. - */ -export interface RedirectSchemeMiddleware { - /** - * Set the permanent option to true to apply a permanent redirection. - */ - permanent?: boolean; - /** - * The scheme option defines the scheme of the new url. - */ - scheme?: string; - /** - * The port option defines the port of the new url. Port in this configuration is a string, not a numeric value. - */ - port?: string; -} -/** - * Replace the path of the request url. It will replace the actual path by the specified one and will store the original path in a X-Replaced-Path header. - */ -export interface ReplacePathMiddleware { - /** - * The path option defines the path to use as replacement in the request url. - */ - path?: string; -} -/** - * The ReplaceRegex replace a path from an url to another with regex matching and replacement. It will replace the actual path by the specified one and store the original path in a X-Replaced-Path header. - */ -export interface ReplacePathRegexMiddleware { - /** - * The regex option is the regular expression to match and capture the path from the request URL. - */ - regex?: string; - /** - * The replacement option defines how to modify the path to have the new target path. Care should be taken when defining replacement expand variables: $1x is equivalent to ${1x}, not ${1}x (see Regexp.Expand), so use ${1} syntax. - */ - replacement?: string; -} -/** - * The Retry middleware is in charge of reissuing a request a given number of times to a backend server if that server does not reply. To be clear, as soon as the server answers, the middleware stops retrying, regardless of the response status. - */ -export interface RetryMiddleware { - /** - * The attempts option defines how many times the request should be retried. - */ - attempts: number; - /** - * The initialInterval option defines the first wait time in the exponential backoff series. - */ - initialInterval?: string; -} -/** - * Remove the specified prefixes from the URL path. It will strip the matching path prefix and will store the matching path prefix in a X-Forwarded-Prefix header. - */ -export interface StripPrefixMiddleware { - /** - * The prefixes option defines the prefixes to strip from the request URL - */ - prefixes?: string[]; - /** - * The forceSlash option makes sure that the resulting stripped path is not the empty string, by replacing it with / when necessary. - * - * This option was added to keep the initial (non-intuitive) behavior of this middleware, in order to avoid introducing a breaking change. - * - * It's recommended to explicitly set forceSlash to false. - */ - forceSlash?: boolean; -} -/** - * Remove the matching prefixes from the URL path. It will strip the matching path prefix and will store the matching path prefix in a X-Forwarded-Prefix header. - */ -export interface StripPrefixRegexMiddleware { - /** - * The regex option is the regular expression to match the path prefix from the request URL. - */ - regex?: string[]; -} -/** - * If both HTTP routers and TCP routers listen to the same entry points, the TCP routers will apply before the HTTP routers. If no matching route is found for the TCP routers, then the HTTP routers will take over. - */ -export interface TcpRouter { - /** - * If not specified, TCP routers will accept requests from all defined entry points. If you want to limit the router scope to a set of entry points, set the entry points option. - */ - entryPoints?: string[]; - middlewares?: string[]; - /** - * It is important to note that the Server Name Indication is an extension of the TLS protocol. Hence, only TLS routers will be able to specify a domain name with that rule. However, non-TLS routers will have to explicitly use that rule with * (every domain) to state that every non-TLS request will be handled by the router. - */ - rule: string; - /** - * You must attach a TCP service per TCP router. Services are the target for the router. TCP routers can only target TCP services (not HTTP services). - */ - service: string; - /** - * To avoid path overlap, routes are sorted, by default, in descending order using rules length. The priority is directly equal to the length of the rule, and so the longest length has the highest priority. A value of 0 for the priority is ignored: priority = 0 means that the default rules length sorting is used. - */ - priority?: number; - /** - * When a TLS section is specified, it instructs Traefik that the current router is dedicated to TLS requests only (and that the router should ignore non-TLS requests). - * - * By default, a router with a TLS section will terminate the TLS connections, meaning that it will send decrypted data to the services. - */ - tls?: { - /** - * A TLS router will terminate the TLS connection by default. However, the passthrough option can be specified to set whether the requests should be forwarded "as is", keeping all data encrypted. - */ - passthrough?: boolean; - /** - * The options field enables fine-grained control of the TLS parameters. It refers to a TLS Options and will be applied only if a Host rule is defined. - */ - options?: string; - /** - * If certResolver is defined, Traefik will try to generate certificates based on routers Host & HostSNI rules. - */ - certResolver?: string; - /** - * You can set SANs (alternative domains) for each main domain. Every domain must have A/AAAA records pointing to Traefik. Each domain & SAN will lead to a certificate request. - */ - domains?: { - /** - * Main defines the main domain name. - */ - main?: string; - /** - * SANs defines the subject alternative domain names. - */ - sans?: string[]; - [k: string]: unknown; - }[]; - [k: string]: unknown; - }; -} -export interface TcpLoadBalancerService { - /** - * Servers declare a single instance of your program. - * - * @minItems 1 - */ - servers: [ - { - /** - * The address option (IP:Port) point to a specific instance. - */ - address: string; - [k: string]: unknown; - }, - ...{ - /** - * The address option (IP:Port) point to a specific instance. - */ - address: string; - [k: string]: unknown; - }[], - ]; - /** - * As a proxy between a client and a server, it can happen that either side (e.g. client side) decides to terminate its writing capability on the connection (i.e. issuance of a FIN packet). The proxy needs to propagate that intent to the other side, and so when that happens, it also does the same on its connection with the other side (e.g. backend side). - * - * However, if for some reason (bad implementation, or malicious intent) the other side does not eventually do the same as well, the connection would stay half-open, which would lock resources for however long. - * - * To that end, as soon as the proxy enters this termination sequence, it sets a deadline on fully terminating the connections on both sides. - * - * The termination delay controls that deadline. It is a duration in milliseconds, defaulting to 100. A negative value means an infinite deadline (i.e. the connection is never fully terminated by the proxy itself). - */ - terminationDelay?: number; - proxyProtocol?: { - version?: number; - [k: string]: unknown; - }; -} -export interface TcpWeightedService { - /** - * @minItems 1 - */ - services: [ - { - name: string; - weight: number; - }, - ...{ - name: string; - weight: number; - }[], - ]; -} -export interface UdpRouter { - /** - * If not specified, UDP routers will accept packets from all defined (UDP) entry points. If one wants to limit the router scope to a set of entry points, one should set the entry points option. - */ - entryPoints?: string[]; - /** - * There must be one (and only one) UDP service referenced per UDP router. Services are the target for the router. - */ - service: string; -} -/** - * The servers load balancer is in charge of balancing the requests between the servers of the same service. - */ -export interface UdpLoadBalancerService { - /** - * The servers field defines all the servers that are part of this load-balancing group, i.e. each address (IP:Port) on which an instance of the service's program is deployed. - * - * @minItems 1 - */ - servers: [ - { - address: string; - [k: string]: unknown; - }, - ...{ - address: string; - [k: string]: unknown; - }[], - ]; -} -export interface UdpWeightedService { - /** - * @minItems 1 - */ - services: [ - { - name: string; - weight: number; - }, - ...{ - name: string; - weight: number; - }[], - ]; -} diff --git a/apps/dokploy/server/utils/traefik/middleware.ts b/apps/dokploy/server/utils/traefik/middleware.ts deleted file mode 100644 index df836051..00000000 --- a/apps/dokploy/server/utils/traefik/middleware.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { existsSync, readFileSync, writeFileSync } from "node:fs"; -import { join } from "node:path"; -import { paths } from "@/server/constants"; -import { dump, load } from "js-yaml"; -import type { ApplicationNested } from "../builders"; -import { execAsyncRemote } from "../process/execAsync"; -import { writeTraefikConfigRemote } from "./application"; -import type { FileConfig } from "./file-types"; - -export const addMiddleware = (config: FileConfig, middlewareName: string) => { - if (config.http?.routers) { - const values = Object.keys(config.http.routers); - - for (const routerName of values) { - const router = config.http.routers[routerName]; - - if (router) { - if (!router.middlewares) { - router.middlewares = []; - } - if (!router.middlewares.includes(middlewareName)) { - router.middlewares.push(middlewareName); - } - } - } - } -}; - -export const deleteMiddleware = ( - config: FileConfig, - middlewareName: string, -) => { - if (config.http?.routers) { - const values = Object.keys(config?.http?.routers); - - for (const routerName of values) { - const router = config.http.routers[routerName]; - if (router?.middlewares) { - router.middlewares = router.middlewares.filter( - (m) => m !== middlewareName, - ); - } - } - } -}; - -export const deleteAllMiddlewares = async (application: ApplicationNested) => { - const config = loadMiddlewares(); - const { security, appName, redirects } = application; - - if (config.http?.middlewares) { - if (security.length > 0) { - const middlewareName = `auth-${appName}`; - - delete config.http.middlewares[middlewareName]; - } - - for (const redirect of redirects) { - const middlewareName = `redirect-${appName}-${redirect.uniqueConfigKey}`; - delete config.http.middlewares[middlewareName]; - } - } - - if (application.serverId) { - await writeTraefikConfigRemote(config, "middlewares", application.serverId); - } else { - writeMiddleware(config); - } -}; - -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}`); - } - const yamlStr = readFileSync(configPath, "utf8"); - const config = load(yamlStr) as T; - return config; -}; - -export const loadRemoteMiddlewares = async (serverId: string) => { - const { DYNAMIC_TRAEFIK_PATH } = paths(true); - const configPath = join(DYNAMIC_TRAEFIK_PATH, "middlewares.yml"); - - try { - const { stdout, stderr } = await execAsyncRemote( - serverId, - `cat ${configPath}`, - ); - - if (stderr) { - console.error(`Error: ${stderr}`); - throw new Error(`File not found: ${configPath}`); - } - const config = load(stdout) as FileConfig; - return config; - } catch (error) { - throw new Error(`File not found: ${configPath}`); - } -}; -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/redirect.ts b/apps/dokploy/server/utils/traefik/redirect.ts deleted file mode 100644 index 6b3ea4ae..00000000 --- a/apps/dokploy/server/utils/traefik/redirect.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { Redirect } from "@/server/api/services/redirect"; -import type { ApplicationNested } from "../builders"; -import { - loadOrCreateConfig, - loadOrCreateConfigRemote, - writeTraefikConfig, - writeTraefikConfigRemote, -} from "./application"; -import type { FileConfig } from "./file-types"; -import { - addMiddleware, - deleteMiddleware, - loadMiddlewares, - loadRemoteMiddlewares, - writeMiddleware, -} from "./middleware"; - -export const updateRedirectMiddleware = async ( - application: ApplicationNested, - data: Redirect, -) => { - const { appName, serverId } = application; - let config: FileConfig; - - if (serverId) { - config = await loadRemoteMiddlewares(serverId); - } else { - config = loadMiddlewares(); - } - const middlewareName = `redirect-${appName}-${data.uniqueConfigKey}`; - - if (config?.http?.middlewares?.[middlewareName]) { - config.http.middlewares[middlewareName] = { - redirectRegex: { - regex: data.regex, - replacement: data.replacement, - permanent: data.permanent, - }, - }; - } - - if (serverId) { - await writeTraefikConfigRemote(config, "middlewares", serverId); - } else { - writeMiddleware(config); - } -}; -export const createRedirectMiddleware = async ( - application: ApplicationNested, - data: Redirect, -) => { - const { appName, serverId } = application; - - let config: FileConfig; - - if (serverId) { - config = await loadRemoteMiddlewares(serverId); - } else { - config = loadMiddlewares(); - } - - const middlewareName = `redirect-${appName}-${data.uniqueConfigKey}`; - const newMiddleware = { - [middlewareName]: { - redirectRegex: { - regex: data.regex, - replacement: data.replacement, - permanent: data.permanent, - }, - }, - }; - - if (config?.http) { - config.http.middlewares = { - ...config.http.middlewares, - ...newMiddleware, - }; - } - - let appConfig: FileConfig; - - if (serverId) { - appConfig = await loadOrCreateConfigRemote(serverId, appName); - } else { - appConfig = loadOrCreateConfig(appName); - } - - addMiddleware(appConfig, middlewareName); - - if (serverId) { - await writeTraefikConfigRemote(config, "middlewares", serverId); - await writeTraefikConfigRemote(appConfig, appName, serverId); - } else { - writeMiddleware(config); - writeTraefikConfig(appConfig, appName); - } -}; - -export const removeRedirectMiddleware = async ( - application: ApplicationNested, - data: Redirect, -) => { - const { appName, serverId } = application; - let config: FileConfig; - - if (serverId) { - config = await loadRemoteMiddlewares(serverId); - } else { - config = loadMiddlewares(); - } - const middlewareName = `redirect-${appName}-${data.uniqueConfigKey}`; - - if (config?.http?.middlewares?.[middlewareName]) { - delete config.http.middlewares[middlewareName]; - } - let appConfig: FileConfig; - if (serverId) { - appConfig = await loadOrCreateConfigRemote(serverId, appName); - } else { - appConfig = loadOrCreateConfig(appName); - } - - deleteMiddleware(appConfig, middlewareName); - - if (serverId) { - await writeTraefikConfigRemote(config, "middlewares", serverId); - await writeTraefikConfigRemote(appConfig, appName, serverId); - } else { - writeTraefikConfig(appConfig, appName); - writeMiddleware(config); - } -}; diff --git a/apps/dokploy/server/utils/traefik/registry.ts b/apps/dokploy/server/utils/traefik/registry.ts deleted file mode 100644 index eca4401d..00000000 --- a/apps/dokploy/server/utils/traefik/registry.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; -import { join } from "node:path"; -import type { Registry } from "@/server/api/services/registry"; -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 }); - } - - const appName = "dokploy-registry"; - const config: FileConfig = loadOrCreateConfig(); - - const serviceName = `${appName}-service`; - const routerName = `${appName}-router`; - - config.http = config.http || { routers: {}, services: {} }; - config.http.routers = config.http.routers || {}; - config.http.services = config.http.services || {}; - - config.http.routers[routerName] = await createRegistryRouterConfig(registry); - - config.http.services[serviceName] = { - loadBalancer: { - servers: [{ url: `http://${appName}:5000` }], - passHostHeader: true, - }, - }; - - const yamlConfig = dump(config); - const configFile = join(REGISTRY_PATH, "registry.yml"); - writeFileSync(configFile, yamlConfig); -}; - -export const removeSelfHostedRegistry = async () => { - const { REGISTRY_PATH } = paths(); - await removeDirectoryIfExistsContent(REGISTRY_PATH); -}; - -const createRegistryRouterConfig = async (registry: Registry) => { - const { registryUrl } = registry; - const routerConfig: HttpRouter = { - rule: `Host(\`${registryUrl}\`)`, - service: "dokploy-registry-service", - middlewares: ["redirect-to-https"], - entryPoints: [ - "web", - ...(process.env.NODE_ENV === "production" ? ["websecure"] : []), - ], - ...(process.env.NODE_ENV === "production" - ? { - tls: { certResolver: "letsencrypt" }, - } - : {}), - }; - - return routerConfig; -}; - -const loadOrCreateConfig = (): FileConfig => { - const { REGISTRY_PATH } = paths(); - const configPath = join(REGISTRY_PATH, "registry.yml"); - if (existsSync(configPath)) { - const yamlStr = readFileSync(configPath, "utf8"); - const parsedConfig = (load(yamlStr) as FileConfig) || { - http: { routers: {}, services: {} }, - }; - return parsedConfig; - } - return { http: { routers: {}, services: {} } }; -}; diff --git a/apps/dokploy/server/utils/traefik/security.ts b/apps/dokploy/server/utils/traefik/security.ts deleted file mode 100644 index 3b3d81bb..00000000 --- a/apps/dokploy/server/utils/traefik/security.ts +++ /dev/null @@ -1,129 +0,0 @@ -import type { Security } from "@/server/api/services/security"; -import * as bcrypt from "bcrypt"; -import type { ApplicationNested } from "../builders"; -import { - loadOrCreateConfig, - loadOrCreateConfigRemote, - writeTraefikConfig, - writeTraefikConfigRemote, -} from "./application"; -import type { - BasicAuthMiddleware, - FileConfig, - HttpMiddleware, -} from "./file-types"; -import { - addMiddleware, - deleteMiddleware, - loadMiddlewares, - loadRemoteMiddlewares, - writeMiddleware, -} from "./middleware"; - -export const createSecurityMiddleware = async ( - application: ApplicationNested, - data: Security, -) => { - const { appName, serverId } = application; - let config: FileConfig; - - if (serverId) { - config = await loadRemoteMiddlewares(serverId); - } else { - config = loadMiddlewares(); - } - const middlewareName = `auth-${appName}`; - - const user = `${data.username}:${await bcrypt.hash(data.password, 10)}`; - - if (config.http?.middlewares) { - const currentMiddleware = config.http.middlewares[middlewareName]; - if (isBasicAuthMiddleware(currentMiddleware)) { - currentMiddleware.basicAuth.users = [ - ...(currentMiddleware.basicAuth.users || []), - user, - ]; - } else { - config.http.middlewares[middlewareName] = { - basicAuth: { - removeHeader: true, - users: [user], - }, - }; - } - } - let appConfig: FileConfig; - - if (serverId) { - appConfig = await loadOrCreateConfigRemote(serverId, appName); - } else { - appConfig = loadOrCreateConfig(appName); - } - addMiddleware(appConfig, middlewareName); - if (serverId) { - await writeTraefikConfigRemote(config, "middlewares", serverId); - await writeTraefikConfigRemote(appConfig, appName, serverId); - } else { - writeTraefikConfig(appConfig, appName); - writeMiddleware(config); - } -}; - -export const removeSecurityMiddleware = async ( - application: ApplicationNested, - data: Security, -) => { - const { appName, serverId } = application; - let config: FileConfig; - - if (serverId) { - config = await loadRemoteMiddlewares(serverId); - } else { - config = loadMiddlewares(); - } - let appConfig: FileConfig; - - if (serverId) { - appConfig = await loadOrCreateConfigRemote(serverId, appName); - } else { - appConfig = loadOrCreateConfig(appName); - } - const middlewareName = `auth-${appName}`; - - if (config.http?.middlewares) { - const currentMiddleware = config.http.middlewares[middlewareName]; - if (isBasicAuthMiddleware(currentMiddleware)) { - const users = currentMiddleware.basicAuth.users; - const filteredUsers = - users?.filter((user) => { - const [username] = user.split(":"); - return username !== data.username; - }) || []; - currentMiddleware.basicAuth.users = filteredUsers; - - if (filteredUsers.length === 0) { - if (config?.http?.middlewares?.[middlewareName]) { - delete config.http.middlewares[middlewareName]; - } - deleteMiddleware(appConfig, middlewareName); - if (serverId) { - await writeTraefikConfigRemote(appConfig, appName, serverId); - } else { - writeTraefikConfig(appConfig, appName); - } - } - } - } - - if (serverId) { - await writeTraefikConfigRemote(config, "middlewares", serverId); - } else { - writeMiddleware(config); - } -}; - -const isBasicAuthMiddleware = ( - middleware: HttpMiddleware | undefined, -): middleware is { basicAuth: BasicAuthMiddleware } => { - return !!middleware && "basicAuth" in middleware; -}; diff --git a/apps/dokploy/server/utils/traefik/types.ts b/apps/dokploy/server/utils/traefik/types.ts deleted file mode 100644 index 632ab0ac..00000000 --- a/apps/dokploy/server/utils/traefik/types.ts +++ /dev/null @@ -1,574 +0,0 @@ -/* eslint-disable */ - -export interface MainTraefikConfig { - accessLog?: { - filePath?: string; - format?: string; - filters?: { - statusCodes?: string[]; - retryAttempts?: boolean; - minDuration?: string; - [k: string]: unknown; - }; - fields?: { - defaultMode?: string; - names?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: string; - }; - headers?: { - defaultMode?: string; - names?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: string; - }; - [k: string]: unknown; - }; - [k: string]: unknown; - }; - bufferingSize?: number; - }; - api?: { - insecure?: boolean; - dashboard?: boolean; - debug?: boolean; - }; - certificatesResolvers?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: { - acme?: { - email?: string; - caServer?: string; - certificatesDuration?: number; - preferredChain?: string; - storage?: string; - keyType?: string; - eab?: { - kid?: string; - hmacEncoded?: string; - [k: string]: unknown; - }; - dnsChallenge?: { - provider?: string; - delayBeforeCheck?: string; - resolvers?: string[]; - disablePropagationCheck?: boolean; - [k: string]: unknown; - }; - httpChallenge?: { - entryPoint?: string; - [k: string]: unknown; - }; - tlsChallenge?: { - [k: string]: unknown; - }; - [k: string]: unknown; - }; - }; - }; - entryPoints?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: { - address?: string; - transport?: { - lifeCycle?: { - requestAcceptGraceTimeout?: string; - graceTimeOut?: string; - [k: string]: unknown; - }; - respondingTimeouts?: { - readTimeout?: string; - writeTimeout?: string; - idleTimeout?: string; - [k: string]: unknown; - }; - [k: string]: unknown; - }; - proxyProtocol?: { - insecure?: boolean; - trustedIPs?: string[]; - [k: string]: unknown; - }; - forwardedHeaders?: { - insecure?: boolean; - trustedIPs?: string[]; - [k: string]: unknown; - }; - http?: { - redirections?: { - entryPoint?: { - to?: string; - scheme?: string; - permanent?: boolean; - priority?: number; - [k: string]: unknown; - }; - [k: string]: unknown; - }; - middlewares?: string[]; - tls?: { - options?: string; - certResolver?: string; - domains?: { - main?: string; - sans?: string[]; - [k: string]: unknown; - }[]; - [k: string]: unknown; - }; - [k: string]: unknown; - }; - http2?: { - maxConcurrentStreams?: number; - [k: string]: unknown; - }; - http3?: { - advertisedPort?: number; - [k: string]: unknown; - }; - udp?: { - timeout?: string; - [k: string]: unknown; - }; - }; - }; - experimental?: { - kubernetesGateway?: boolean; - http3?: boolean; - hub?: boolean; - plugins?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: { - moduleName?: string; - version?: string; - }; - }; - localPlugins?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: { - moduleName?: string; - }; - }; - }; - global?: { - checkNewVersion?: boolean; - sendAnonymousUsage?: boolean; - }; - hostResolver?: { - cnameFlattening?: boolean; - resolvConfig?: string; - resolvDepth?: number; - }; - hub?: { - tls?: { - insecure?: boolean; - ca?: string; - cert?: string; - key?: string; - [k: string]: unknown; - }; - }; - log?: { - level?: string; - filePath?: string; - format?: string; - }; - metrics?: { - prometheus?: { - buckets?: number[]; - addEntryPointsLabels?: boolean; - addRoutersLabels?: boolean; - addServicesLabels?: boolean; - entryPoint?: string; - manualRouting?: boolean; - }; - datadog?: { - address?: string; - pushInterval?: string; - addEntryPointsLabels?: boolean; - addRoutersLabels?: boolean; - addServicesLabels?: boolean; - prefix?: string; - }; - statsD?: { - address?: string; - pushInterval?: string; - addEntryPointsLabels?: boolean; - addRoutersLabels?: boolean; - addServicesLabels?: boolean; - prefix?: string; - }; - influxDB?: { - address?: string; - protocol?: string; - pushInterval?: string; - database?: string; - retentionPolicy?: string; - username?: string; - password?: string; - addEntryPointsLabels?: boolean; - addRoutersLabels?: boolean; - addServicesLabels?: boolean; - additionalLabels?: { - [k: string]: unknown; - }; - }; - influxDB2?: { - address?: string; - token?: string; - pushInterval?: string; - org?: string; - bucket?: string; - addEntryPointsLabels?: boolean; - addRoutersLabels?: boolean; - addServicesLabels?: boolean; - additionalLabels?: { - [k: string]: unknown; - }; - }; - }; - pilot?: { - token?: string; - dashboard?: boolean; - }; - ping?: { - entryPoint?: string; - manualRouting?: boolean; - terminatingStatusCode?: number; - }; - providers?: { - providersThrottleDuration?: string; - docker?: { - allowEmptyServices?: boolean; - constraints?: string; - defaultRule?: string; - endpoint?: string; - exposedByDefault?: boolean; - httpClientTimeout?: number; - network?: string; - swarmMode?: boolean; - swarmModeRefreshSeconds?: string; - tls?: { - ca?: string; - caOptional?: boolean; - cert?: string; - key?: string; - insecureSkipVerify?: boolean; - }; - useBindPortIP?: boolean; - watch?: boolean; - }; - file?: { - directory?: string; - watch?: boolean; - filename?: string; - debugLogGeneratedTemplate?: boolean; - }; - marathon?: { - constraints?: string; - trace?: boolean; - watch?: boolean; - endpoint?: string; - defaultRule?: string; - exposedByDefault?: boolean; - dcosToken?: string; - tls?: { - ca?: string; - caOptional?: boolean; - cert?: string; - key?: string; - insecureSkipVerify?: boolean; - }; - dialerTimeout?: string; - responseHeaderTimeout?: string; - tlsHandshakeTimeout?: string; - keepAlive?: string; - forceTaskHostname?: boolean; - basic?: { - httpBasicAuthUser?: string; - httpBasicPassword?: string; - }; - respectReadinessChecks?: boolean; - }; - kubernetesIngress?: { - endpoint?: string; - token?: string; - certAuthFilePath?: string; - namespaces?: string[]; - labelSelector?: string; - ingressClass?: string; - throttleDuration?: string; - allowEmptyServices?: boolean; - allowExternalNameServices?: boolean; - ingressEndpoint?: { - ip?: string; - hostname?: string; - publishedService?: string; - }; - }; - kubernetesCRD?: { - endpoint?: string; - token?: string; - certAuthFilePath?: string; - namespaces?: string[]; - allowCrossNamespace?: boolean; - allowExternalNameServices?: boolean; - labelSelector?: string; - ingressClass?: string; - throttleDuration?: string; - allowEmptyServices?: boolean; - }; - kubernetesGateway?: { - endpoint?: string; - token?: string; - certAuthFilePath?: string; - namespaces?: string[]; - labelSelector?: string; - throttleDuration?: string; - }; - rest?: { - insecure?: boolean; - }; - rancher?: { - constraints?: string; - watch?: boolean; - defaultRule?: string; - exposedByDefault?: boolean; - enableServiceHealthFilter?: boolean; - refreshSeconds?: number; - intervalPoll?: boolean; - prefix?: string; - }; - consulCatalog?: { - constraints?: string; - prefix?: string; - refreshInterval?: string; - requireConsistent?: boolean; - stale?: boolean; - cache?: boolean; - exposedByDefault?: boolean; - defaultRule?: string; - connectAware?: boolean; - connectByDefault?: boolean; - serviceName?: string; - namespace?: string; - namespaces?: string[]; - watch?: boolean; - endpoint?: { - address?: string; - scheme?: string; - datacenter?: string; - token?: string; - endpointWaitTime?: string; - tls?: { - ca?: string; - caOptional?: boolean; - cert?: string; - key?: string; - insecureSkipVerify?: boolean; - }; - httpAuth?: { - username?: string; - password?: string; - }; - }; - [k: string]: unknown; - }; - nomad?: { - constraints?: string; - prefix?: string; - refreshInterval?: string; - stale?: boolean; - exposedByDefault?: boolean; - defaultRule?: string; - namespace?: string; - endpoint?: { - address?: string; - region?: string; - token?: string; - endpointWaitTime?: string; - tls?: { - ca?: string; - caOptional?: boolean; - cert?: string; - key?: string; - insecureSkipVerify?: boolean; - }; - }; - }; - ecs?: { - constraints?: string; - exposedByDefault?: boolean; - ecsAnywhere?: boolean; - refreshSeconds?: number; - defaultRule?: string; - clusters?: string[]; - autoDiscoverClusters?: boolean; - region?: string; - accessKeyID?: string; - secretAccessKey?: string; - }; - consul?: { - rootKey?: string; - endpoints?: string[]; - token?: string; - namespace?: string; - namespaces?: string[]; - tls?: { - ca?: string; - caOptional?: boolean; - cert?: string; - key?: string; - insecureSkipVerify?: boolean; - }; - }; - etcd?: { - rootKey?: string; - endpoints?: string[]; - username?: string; - password?: string; - tls?: { - ca?: string; - caOptional?: boolean; - cert?: string; - key?: string; - insecureSkipVerify?: boolean; - }; - }; - zooKeeper?: { - rootKey?: string; - endpoints?: string[]; - username?: string; - password?: string; - }; - redis?: { - rootKey?: string; - endpoints?: string[]; - username?: string; - password?: string; - db?: number; - tls?: { - ca?: string; - caOptional?: boolean; - cert?: string; - key?: string; - insecureSkipVerify?: boolean; - }; - }; - http?: { - endpoint?: string; - pollInterval?: string; - pollTimeout?: string; - tls?: { - ca?: string; - caOptional?: boolean; - cert?: string; - key?: string; - insecureSkipVerify?: boolean; - }; - }; - plugin?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: { - [k: string]: unknown; - }; - }; - [k: string]: unknown; - }; - serversTransport?: { - insecureSkipVerify?: boolean; - rootCAs?: string[]; - maxIdleConnsPerHost?: number; - forwardingTimeouts?: { - dialTimeout?: string; - responseHeaderTimeout?: string; - idleConnTimeout?: string; - }; - }; - tracing?: { - serviceName?: string; - spanNameLimit?: number; - jaeger?: { - samplingServerURL?: string; - samplingType?: string; - samplingParam?: number; - localAgentHostPort?: string; - gen128Bit?: boolean; - propagation?: string; - traceContextHeaderName?: string; - disableAttemptReconnecting?: boolean; - collector?: { - endpoint?: string; - user?: string; - password?: string; - }; - }; - zipkin?: { - httpEndpoint?: string; - sameSpan?: boolean; - id128Bit?: boolean; - sampleRate?: number; - }; - datadog?: { - localAgentHostPort?: string; - globalTag?: string; - /** - * Sets a list of key:value tags on all spans. - */ - globalTags?: { - /** - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "[a-zA-Z0-9-_]+". - */ - [k: string]: string; - }; - debug?: boolean; - prioritySampling?: boolean; - traceIDHeaderName?: string; - parentIDHeaderName?: string; - samplingPriorityHeaderName?: string; - bagagePrefixHeaderName?: string; - }; - instana?: { - localAgentHost?: string; - localAgentPort?: number; - logLevel?: string; - enableAutoProfile?: boolean; - }; - haystack?: { - localAgentHost?: string; - localAgentPort?: number; - globalTag?: string; - traceIDHeaderName?: string; - parentIDHeaderName?: string; - spanIDHeaderName?: string; - baggagePrefixHeaderName?: string; - }; - elastic?: { - serverURL?: string; - secretToken?: string; - serviceEnvironment?: string; - }; - }; -} diff --git a/apps/dokploy/server/utils/traefik/web-server.ts b/apps/dokploy/server/utils/traefik/web-server.ts deleted file mode 100644 index 2b966e73..00000000 --- a/apps/dokploy/server/utils/traefik/web-server.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { existsSync, readFileSync, writeFileSync } from "node:fs"; -import { join } from "node:path"; -import type { Admin } from "@/server/api/services/admin"; -import { paths } from "@/server/constants"; -import { dump, load } from "js-yaml"; -import { loadOrCreateConfig, writeTraefikConfig } from "./application"; -import type { FileConfig } from "./file-types"; -import type { MainTraefikConfig } from "./types"; - -export const updateServerTraefik = ( - admin: Admin | null, - newHost: string | null, -) => { - const appName = "dokploy"; - const config: FileConfig = loadOrCreateConfig(appName); - - config.http = config.http || { routers: {}, services: {} }; - config.http.routers = config.http.routers || {}; - - const currentRouterConfig = config.http.routers[`${appName}-router-app`]; - - if (currentRouterConfig && newHost) { - currentRouterConfig.rule = `Host(\`${newHost}\`)`; - - if (admin?.certificateType === "letsencrypt") { - config.http.routers[`${appName}-router-app-secure`] = { - ...currentRouterConfig, - entryPoints: ["websecure"], - tls: { certResolver: "letsencrypt" }, - }; - - currentRouterConfig.middlewares = ["redirect-to-https"]; - } else { - delete config.http.routers[`${appName}-router-app-secure`]; - currentRouterConfig.middlewares = []; - } - } - - writeTraefikConfig(config, appName); -}; - -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; - if (config?.certificatesResolvers?.letsencrypt?.acme) { - config.certificatesResolvers.letsencrypt.acme.email = newEmail; - } else { - throw new Error("Invalid Let's Encrypt configuration structure."); - } - const newYamlContent = dump(config); - writeFileSync(configPath, newYamlContent, "utf8"); - } catch (error) { - throw error; - } -}; - -export const readMainConfig = () => { - const { MAIN_TRAEFIK_PATH } = paths(); - const configPath = join(MAIN_TRAEFIK_PATH, "traefik.yml"); - if (existsSync(configPath)) { - const yamlStr = readFileSync(configPath, "utf8"); - return yamlStr; - } - return null; -}; - -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) { - console.error("Error saving the YAML config file:", e); - } -}; diff --git a/apps/dokploy/server/wss/docker-container-logs.ts b/apps/dokploy/server/wss/docker-container-logs.ts index f1e767d9..304d0f48 100644 --- a/apps/dokploy/server/wss/docker-container-logs.ts +++ b/apps/dokploy/server/wss/docker-container-logs.ts @@ -2,9 +2,11 @@ import type http from "node:http"; import { spawn } from "node-pty"; import { Client } from "ssh2"; import { WebSocketServer } from "ws"; -import { findServerById } from "@dokploy/builders"; -import { validateWebSocketRequest } from "../auth/auth"; -import { readSSHKey } from "../utils/filesystem/ssh"; +import { + findServerById, + readSSHKey, + validateWebSocketRequest, +} from "@dokploy/builders"; import { getShell } from "./utils"; export const setupDockerContainerLogsWebSocketServer = ( diff --git a/apps/dokploy/server/wss/docker-container-terminal.ts b/apps/dokploy/server/wss/docker-container-terminal.ts index 4ecd7f48..b2e8aa6a 100644 --- a/apps/dokploy/server/wss/docker-container-terminal.ts +++ b/apps/dokploy/server/wss/docker-container-terminal.ts @@ -2,9 +2,11 @@ import type http from "node:http"; import { spawn } from "node-pty"; import { Client } from "ssh2"; import { WebSocketServer } from "ws"; -import { findServerById } from "@dokploy/builders"; -import { validateWebSocketRequest } from "../auth/auth"; -import { readSSHKey } from "../utils/filesystem/ssh"; +import { + findServerById, + readSSHKey, + validateWebSocketRequest, +} from "@dokploy/builders"; import { getShell } from "./utils"; export const setupDockerContainerTerminalWebSocketServer = ( diff --git a/apps/dokploy/server/wss/docker-stats.ts b/apps/dokploy/server/wss/docker-stats.ts index ed1dc46f..7672f722 100644 --- a/apps/dokploy/server/wss/docker-stats.ts +++ b/apps/dokploy/server/wss/docker-stats.ts @@ -1,11 +1,11 @@ import type http from "node:http"; import { WebSocketServer } from "ws"; -import { validateWebSocketRequest } from "../auth/auth"; -import { docker } from "../constants"; import { + docker, getLastAdvancedStatsFile, recordAdvancedStats, -} from "../monitoring/utilts"; + validateWebSocketRequest, +} from "@dokploy/builders"; export const setupDockerStatsMonitoringSocketServer = ( server: http.Server, diff --git a/apps/dokploy/server/wss/listen-deployment.ts b/apps/dokploy/server/wss/listen-deployment.ts index 9bcda985..16da8288 100644 --- a/apps/dokploy/server/wss/listen-deployment.ts +++ b/apps/dokploy/server/wss/listen-deployment.ts @@ -2,9 +2,11 @@ import { spawn } from "node:child_process"; import type http from "node:http"; import { Client } from "ssh2"; import { WebSocketServer } from "ws"; -import { findServerById } from "@dokploy/builders"; -import { validateWebSocketRequest } from "../auth/auth"; -import { readSSHKey } from "../utils/filesystem/ssh"; +import { + findServerById, + readSSHKey, + validateWebSocketRequest, +} from "@dokploy/builders"; export const setupDeploymentLogsWebSocketServer = ( server: http.Server, diff --git a/apps/dokploy/server/wss/terminal.ts b/apps/dokploy/server/wss/terminal.ts index e43415d4..8d06d15e 100644 --- a/apps/dokploy/server/wss/terminal.ts +++ b/apps/dokploy/server/wss/terminal.ts @@ -3,9 +3,11 @@ import path from "node:path"; import { spawn } from "node-pty"; import { publicIpv4, publicIpv6 } from "public-ip"; import { WebSocketServer } from "ws"; -import { findServerById } from "@dokploy/builders"; -import { validateWebSocketRequest } from "../auth/auth"; -import { paths } from "../constants"; +import { + findServerById, + validateWebSocketRequest, + paths, +} from "@dokploy/builders"; export const getPublicIpWithFallback = async () => { // @ts-ignore diff --git a/apps/dokploy/templates/utils/index.ts b/apps/dokploy/templates/utils/index.ts index 9f2345b6..8ee96aa1 100644 --- a/apps/dokploy/templates/utils/index.ts +++ b/apps/dokploy/templates/utils/index.ts @@ -1,7 +1,7 @@ import { randomBytes } from "node:crypto"; import { readFile } from "node:fs/promises"; import { join } from "node:path"; -import type { Domain } from "@/server/api/services/domain"; +import type { Domain } from "@dokploy/builders"; // import { IS_CLOUD } from "@/server/constants"; import { TRPCError } from "@trpc/server"; import { templates } from "../templates"; diff --git a/packages/builders/src/index.ts b/packages/builders/src/index.ts index 883c2db2..6b221851 100644 --- a/packages/builders/src/index.ts +++ b/packages/builders/src/index.ts @@ -1,4 +1,6 @@ export * from "./auth/auth"; +export * from "./auth/token"; +export * from "./auth/random-password"; // export * from "./db"; export * from "./services/admin"; export * from "./services/user"; @@ -31,37 +33,37 @@ export * from "./services/auth"; export * from "./services/gitlab"; export * from "./services/server"; export * from "./services/application"; -export * from "./db/schema/application"; -export * from "./db/schema/postgres"; -export * from "./db/schema/user"; -export * from "./db/schema/admin"; -export * from "./db/schema/auth"; -export * from "./db/schema/project"; -export * from "./db/schema/domain"; -export * from "./db/schema/mariadb"; -export * from "./db/schema/mongo"; -export * from "./db/schema/mysql"; -export * from "./db/schema/backups"; -export * from "./db/schema/destination"; -export * from "./db/schema/deployment"; -export * from "./db/schema/mount"; -export * from "./db/schema/certificate"; -export * from "./db/schema/session"; -export * from "./db/schema/redirects"; -export * from "./db/schema/security"; -export * from "./db/schema/port"; -export * from "./db/schema/redis"; -export * from "./db/schema/shared"; -export * from "./db/schema/compose"; -export * from "./db/schema/registry"; -export * from "./db/schema/notification"; -export * from "./db/schema/ssh-key"; -export * from "./db/schema/git-provider"; -export * from "./db/schema/bitbucket"; -export * from "./db/schema/github"; -export * from "./db/schema/gitlab"; -export * from "./db/schema/server"; -export * from "./db/schema/utils"; +// export * from "./db/schema/application"; +// export * from "./db/schema/postgres"; +// export * from "./db/schema/user"; +// export * from "./db/schema/admin"; +// export * from "./db/schema/auth"; +// export * from "./db/schema/project"; +// export * from "./db/schema/domain"; +// export * from "./db/schema/mariadb"; +// export * from "./db/schema/mongo"; +// export * from "./db/schema/mysql"; +// export * from "./db/schema/backups"; +// export * from "./db/schema/destination"; +// export * from "./db/schema/deployment"; +// export * from "./db/schema/mount"; +// export * from "./db/schema/certificate"; +// export * from "./db/schema/session"; +// export * from "./db/schema/redirects"; +// export * from "./db/schema/security"; +// export * from "./db/schema/port"; +// export * from "./db/schema/redis"; +// export * from "./db/schema/shared"; +// export * from "./db/schema/compose"; +// export * from "./db/schema/registry"; +// export * from "./db/schema/notification"; +// export * from "./db/schema/ssh-key"; +// export * from "./db/schema/git-provider"; +// export * from "./db/schema/bitbucket"; +// export * from "./db/schema/github"; +// export * from "./db/schema/gitlab"; +// export * from "./db/schema/server"; +// export * from "./db/schema/utils"; export * from "./setup/config-paths"; export * from "./setup/postgres-setup"; @@ -100,6 +102,7 @@ export * from "./utils/cluster/upload"; export * from "./utils/docker/compose"; export * from "./utils/docker/domain"; export * from "./utils/docker/utils"; +export * from "./utils/docker/types"; export * from "./utils/docker/compose/configs"; export * from "./utils/docker/compose/network"; export * from "./utils/docker/compose/secrets"; @@ -141,3 +144,5 @@ export * from "./utils/access-log/handler"; export * from "./utils/access-log/types"; export * from "./utils/access-log/utils"; export * from "./constants/index"; + +export * from "./monitoring/utilts"; diff --git a/packages/builders/src/utils/docker/types.ts b/packages/builders/src/utils/docker/types.ts index ce0f95fc..8f93f9cf 100644 --- a/packages/builders/src/utils/docker/types.ts +++ b/packages/builders/src/utils/docker/types.ts @@ -160,7 +160,7 @@ export type DefinitionsDevices = { */ [k: string]: unknown; }[]; -export type Deployment = { +type Deployment = { mode?: string; endpoint_mode?: string; replicas?: number;