diff --git a/apps/dokploy/__test__/env/shared.test.ts b/apps/dokploy/__test__/env/shared.test.ts index 4a8448aa9..5e231a5cc 100644 --- a/apps/dokploy/__test__/env/shared.test.ts +++ b/apps/dokploy/__test__/env/shared.test.ts @@ -177,3 +177,77 @@ COMPLEX_VAR="'Prefix \"DoubleQuoted\" and \${{project.APP_NAME}}'" ]); }); }); + +describe("prepareEnvironmentVariables (self references)", () => { + it("resolves self references correctly", () => { + const serviceEnv = ` +ENVIRONMENT=staging +DATABASE_URL=postgres://postgres:postgres@localhost:5432/project_db +SELF_REF=\${{ENVIRONMENT}} +`; + + const resolved = prepareEnvironmentVariables(serviceEnv, ""); + + expect(resolved).toEqual([ + "ENVIRONMENT=staging", + "DATABASE_URL=postgres://postgres:postgres@localhost:5432/project_db", + "SELF_REF=staging", + ]); + }); + + it("throws on undefined self references", () => { + const serviceEnv = ` +MISSING_VAR=\${{UNDEFINED_VAR}} +`; + + expect(() => prepareEnvironmentVariables(serviceEnv, "")).toThrow( + "Invalid service environment variable: UNDEFINED_VAR", + ); + }); + + it("allows overriding and still resolving from self", () => { + const serviceEnv = ` +ENVIRONMENT=production +OVERRIDE_ENV=\${{ENVIRONMENT}} +`; + + const resolved = prepareEnvironmentVariables(serviceEnv, ""); + + expect(resolved).toEqual([ + "ENVIRONMENT=production", + "OVERRIDE_ENV=production", + ]); + }); + + it("resolves multiple self references inside one value", () => { + const serviceEnv = ` +ENVIRONMENT=staging +APP_NAME=MyApp +COMPLEX=\${{APP_NAME}}-\${{ENVIRONMENT}}-\${{APP_NAME}} +`; + + const resolved = prepareEnvironmentVariables(serviceEnv, ""); + + expect(resolved).toEqual([ + "ENVIRONMENT=staging", + "APP_NAME=MyApp", + "COMPLEX=MyApp-staging-MyApp", + ]); + }); + + it("handles quotes with self references", () => { + const serviceEnv = ` +ENVIRONMENT=production +QUOTED="'\${{ENVIRONMENT}}'" +MIXED="\"Double \${{ENVIRONMENT}}\"" +`; + + const resolved = prepareEnvironmentVariables(serviceEnv, ""); + + expect(resolved).toEqual([ + "ENVIRONMENT=production", + "QUOTED='production'", + 'MIXED="Double production"', + ]); + }); +}); diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx index f664ec11e..c676e0233 100644 --- a/apps/dokploy/components/dashboard/organization/handle-organization.tsx +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -24,6 +24,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; const organizationSchema = z.object({ @@ -54,6 +55,8 @@ export function AddOrganization({ organizationId }: Props) { const { mutateAsync, isLoading } = organizationId ? api.organization.update.useMutation() : api.organization.create.useMutation(); + const { refetch: refetchActiveOrganization } = + authClient.useActiveOrganization(); const form = useForm({ resolver: zodResolver(organizationSchema), @@ -84,6 +87,10 @@ export function AddOrganization({ organizationId }: Props) { `Organization ${organizationId ? "updated" : "created"} successfully`, ); utils.organization.all.invalidate(); + if (organizationId) { + utils.organization.one.invalidate({ organizationId }); + refetchActiveOrganization(); + } setOpen(false); }) .catch((error) => { diff --git a/apps/dokploy/setup.ts b/apps/dokploy/setup.ts index 7abf9fa2d..13590e4e7 100644 --- a/apps/dokploy/setup.ts +++ b/apps/dokploy/setup.ts @@ -21,7 +21,7 @@ import { await initializeNetwork(); createDefaultTraefikConfig(); createDefaultServerTraefikConfig(); - await execAsync("docker pull traefik:v3.1.2"); + await execAsync("docker pull traefik:v3.5.0"); await initializeStandaloneTraefik(); await initializeRedis(); await initializePostgres(); diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index ccdfa30f8..5427291ef 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -13,7 +13,8 @@ export const TRAEFIK_PORT = Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80; export const TRAEFIK_HTTP3_PORT = Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443; -export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.1.2"; +export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.5.0"; + export interface TraefikOptions { env?: string[]; diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts index 0ce138d70..4bd38f874 100644 --- a/packages/server/src/utils/docker/domain.ts +++ b/packages/server/src/utils/docker/domain.ts @@ -254,6 +254,9 @@ export const addDomainToCompose = async ( if (!labels.includes("traefik.docker.network=dokploy-network")) { labels.unshift("traefik.docker.network=dokploy-network"); } + if (!labels.includes("traefik.swarm.network=dokploy-network")) { + labels.unshift("traefik.swarm.network=dokploy-network"); + } } } diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts index 12f46218f..ef7abf613 100644 --- a/packages/server/src/utils/docker/utils.ts +++ b/packages/server/src/utils/docker/utils.ts @@ -273,6 +273,14 @@ export const prepareEnvironmentVariables = ( throw new Error(`Invalid project environment variable: project.${ref}`); }); } + + resolvedValue = resolvedValue.replace(/\$\{\{(.*?)\}\}/g, (_, ref) => { + if (serviceVars[ref] !== undefined) { + return serviceVars[ref]; + } + throw new Error(`Invalid service environment variable: ${ref}`); + }); + return `${key}=${resolvedValue}`; });