From 7218b3f79b434a5941a9ca883655bf3da39042d1 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 11 Mar 2025 00:38:10 -0600 Subject: [PATCH 1/2] feat(templates): support array-based environment variable configuration Add support for processing environment variables defined as an array in template configurations, allowing more flexible env var definitions with direct string values and variable interpolation --- .../templates/config.template.test.ts | 26 +++++++++++++++++++ packages/server/src/templates/processors.ts | 20 +++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/__test__/templates/config.template.test.ts b/apps/dokploy/__test__/templates/config.template.test.ts index 145e1372..4b0e8fac 100644 --- a/apps/dokploy/__test__/templates/config.template.test.ts +++ b/apps/dokploy/__test__/templates/config.template.test.ts @@ -169,6 +169,32 @@ describe("processTemplate", () => { expect(secretKey.split("=")[1]).toHaveLength(64); }); + it("should process env vars when provided as an array", () => { + const template: CompleteTemplate = { + metadata: {} as any, + variables: {}, + config: { + domains: [], + env: [ + 'CLOUDFLARE_TUNNEL_TOKEN=""', + 'ANOTHER_VAR="some value"', + "DOMAIN=${domain}", + ], + mounts: [], + }, + }; + + const result = processTemplate(template, mockSchema); + expect(result.envs).toHaveLength(3); + + // Should preserve exact format for static values + expect(result.envs[0]).toBe('CLOUDFLARE_TUNNEL_TOKEN=""'); + expect(result.envs[1]).toBe('ANOTHER_VAR="some value"'); + + // Should process variables in array items + expect(result.envs[2]).toContain(mockSchema.projectName); + }); + it("should allow using utility functions directly in env vars", () => { const template: CompleteTemplate = { metadata: {} as any, diff --git a/packages/server/src/templates/processors.ts b/packages/server/src/templates/processors.ts index c45a8b0f..e4b54809 100644 --- a/packages/server/src/templates/processors.ts +++ b/packages/server/src/templates/processors.ts @@ -45,7 +45,7 @@ export interface CompleteTemplate { variables: Record; config: { domains: DomainConfig[]; - env: Record; + env: Record | string[]; mounts?: MountConfig[]; }; } @@ -175,7 +175,8 @@ export function processDomains( variables: Record, schema: Schema, ): Template["domains"] { - return template.config.domains.map((domain: DomainConfig) => ({ + if (!template?.config?.domains) return []; + return template?.config?.domains?.map((domain: DomainConfig) => ({ ...domain, host: domain.host ? processValue(domain.host, variables, schema) @@ -191,6 +192,19 @@ export function processEnvVars( variables: Record, schema: Schema, ): Template["envs"] { + if (!template?.config?.env) return []; + + // Handle array of env vars + if (Array.isArray(template.config.env)) { + return template.config.env.map((env) => { + if (typeof env === "string") { + return processValue(env, variables, schema); + } + return env; + }); + } + + // Handle object of env vars return Object.entries(template.config.env).map( ([key, value]: [string, string]) => { const processedValue = processValue(value, variables, schema); @@ -207,7 +221,7 @@ export function processMounts( variables: Record, schema: Schema, ): Template["mounts"] { - if (!template.config.mounts) return []; + if (!template?.config?.mounts) return []; return template.config.mounts.map((mount: MountConfig) => ({ filePath: processValue(mount.filePath, variables, schema), From 56cbd1abb371c442fa9497aa462c773fc630b866 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 11 Mar 2025 00:41:53 -0600 Subject: [PATCH 2/2] test(templates): enhance secret key and base64 validation in template tests Improve test coverage for secret key generation by: - Adding more robust base64 validation checks - Verifying base64 string format and length - Ensuring generated keys meet specific cryptographic requirements --- .../templates/config.template.test.ts | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/apps/dokploy/__test__/templates/config.template.test.ts b/apps/dokploy/__test__/templates/config.template.test.ts index 4b0e8fac..902e3163 100644 --- a/apps/dokploy/__test__/templates/config.template.test.ts +++ b/apps/dokploy/__test__/templates/config.template.test.ts @@ -166,7 +166,12 @@ describe("processTemplate", () => { if (!baseUrl || !secretKey) return; expect(baseUrl).toContain(mockSchema.projectName); - expect(secretKey.split("=")[1]).toHaveLength(64); + const base64Value = secretKey.split("=")[1]; + expect(base64Value).toBeDefined(); + if (!base64Value) return; + expect(base64Value).toMatch(/^[A-Za-z0-9+/]+={0,2}$/); + expect(base64Value.length).toBeGreaterThanOrEqual(86); + expect(base64Value.length).toBeLessThanOrEqual(88); }); it("should process env vars when provided as an array", () => { @@ -221,7 +226,12 @@ describe("processTemplate", () => { if (!randomDomainEnv || !secretKeyEnv) return; expect(randomDomainEnv).toContain(mockSchema.projectName); - expect(secretKeyEnv.split("=")[1]).toHaveLength(32); + const base64Value = secretKeyEnv.split("=")[1]; + expect(base64Value).toBeDefined(); + if (!base64Value) return; + expect(base64Value).toMatch(/^[A-Za-z0-9+/]+={0,2}$/); + expect(base64Value.length).toBeGreaterThanOrEqual(42); + expect(base64Value.length).toBeLessThanOrEqual(44); }); }); @@ -351,8 +361,22 @@ describe("processTemplate", () => { if (!baseUrl || !secretKey || !totpKey) return; expect(baseUrl).toContain(mockSchema.projectName); - expect(secretKey.split("=")[1]).toHaveLength(64); - expect(totpKey.split("=")[1]).toHaveLength(32); + + // Check base64 lengths and format + const secretKeyValue = secretKey.split("=")[1]; + const totpKeyValue = totpKey.split("=")[1]; + + expect(secretKeyValue).toBeDefined(); + expect(totpKeyValue).toBeDefined(); + if (!secretKeyValue || !totpKeyValue) return; + + expect(secretKeyValue).toMatch(/^[A-Za-z0-9+/]+={0,2}$/); + expect(secretKeyValue.length).toBeGreaterThanOrEqual(86); + expect(secretKeyValue.length).toBeLessThanOrEqual(88); + + expect(totpKeyValue).toMatch(/^[A-Za-z0-9+/]+={0,2}$/); + expect(totpKeyValue.length).toBeGreaterThanOrEqual(42); + expect(totpKeyValue.length).toBeLessThanOrEqual(44); // Check mounts expect(result.mounts).toHaveLength(1); @@ -360,8 +384,8 @@ describe("processTemplate", () => { expect(mount).toBeDefined(); if (!mount) return; expect(mount.content).toContain(mockSchema.projectName); - expect(mount.content).toMatch(/secret=[A-Za-z0-9+/]{64}/); - expect(mount.content).toMatch(/totp=[A-Za-z0-9+/]{32}/); + expect(mount.content).toMatch(/secret=[A-Za-z0-9+/]{86,88}/); + expect(mount.content).toMatch(/totp=[A-Za-z0-9+/]{42,44}/); }); });