From ad6b4c716ca1a433868c1987ac7bc7d9ff840343 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Wed, 15 Oct 2025 21:27:44 +0530 Subject: [PATCH] fix: review comments --- .../laravel-forge/laravel-forge-sync-fns.ts | 80 +++++++++++++++---- .../laravel-forge-sync-schemas.ts | 9 +-- .../secret-syncs/laravel-forge.mdx | 3 - .../secret-syncs/SecretSyncModalHeader.tsx | 2 +- frontend/src/helpers/appConnections.ts | 4 +- frontend/src/helpers/secretSyncs.ts | 8 +- .../components/AppConnectionHeader.tsx | 2 +- 7 files changed, 72 insertions(+), 36 deletions(-) diff --git a/backend/src/services/secret-sync/laravel-forge/laravel-forge-sync-fns.ts b/backend/src/services/secret-sync/laravel-forge/laravel-forge-sync-fns.ts index ff778ab2c..bbb0f354e 100644 --- a/backend/src/services/secret-sync/laravel-forge/laravel-forge-sync-fns.ts +++ b/backend/src/services/secret-sync/laravel-forge/laravel-forge-sync-fns.ts @@ -29,27 +29,65 @@ const parseEnv = (str: string) => { const lines = str.split("\n"); const parsed: { key: string; value: string }[] = []; - lines.forEach((line) => { - const trimmed = line.trim(); + let i = 0; + while (i < lines.length) { + const trimmed = lines[i].trim(); - const isInvalidLine = trimmed === "" || trimmed.startsWith("#"); + // Skip empty lines and comments + if (trimmed === "" || trimmed.startsWith("#")) { + i += 1; + // eslint-disable-next-line no-continue + continue; + } - if (!isInvalidLine && trimmed.includes("=")) { + if (trimmed.includes("=")) { const equalIndex = trimmed.indexOf("="); const key = trimmed.substring(0, equalIndex).trim(); const valueRaw = trimmed.substring(equalIndex + 1).trim(); - let value = valueRaw; - if ((value.startsWith(`"`) && value.endsWith(`"`)) || (value.startsWith(`'`) && value.endsWith(`'`))) { - value = value.slice(1, -1); + // Check if value starts with a quote + const startsWithDoubleQuote = valueRaw.startsWith('"'); + const startsWithSingleQuote = valueRaw.startsWith("'"); + + if (startsWithDoubleQuote || startsWithSingleQuote) { + const quoteChar = startsWithDoubleQuote ? '"' : "'"; + + const closingQuoteIndex = valueRaw.indexOf(quoteChar, 1); + + if (closingQuoteIndex !== -1) { + // Single-line quoted value + const value = valueRaw.slice(1, closingQuoteIndex); + parsed.push({ key, value }); + i += 1; + } else { + // Multiline quoted value - collect lines until closing quote + let value = valueRaw.slice(1); + i += 1; + + while (i < lines.length) { + const nextLine = lines[i]; + const closingIndex = nextLine.indexOf(quoteChar); + + if (closingIndex !== -1) { + value += `\n${nextLine.substring(0, closingIndex)}`; + parsed.push({ key, value }); + i += 1; + break; + } else { + value += `\n${nextLine}`; + i += 1; + } + } + } + } else { + // Unquoted value + parsed.push({ key, value: valueRaw }); + i += 1; } - - parsed.push({ - key, - value - }); + } else { + i += 1; } - }); + } return parsed; }; @@ -70,12 +108,22 @@ const getLaravelForgeSecrets = async (secretSync: TLaravelForgeSyncWithCredentia }; const buildEnvString = (secrets: LaravelForgeSecret[]) => { + if (secrets.length === 0) { + return "# .env"; + } + return secrets .map((secret) => { - if (secret.value.includes(" ")) { - return `${secret.key}="${secret.value}"`; + const { value } = secret; + + if (value.includes(`"`)) { + return `${secret.key}='${value}'`; } - return `${secret.key}=${secret.value}`; + + if (value.includes(" ") || value.includes("\n") || value.includes(`'`)) { + return `${secret.key}="${value}"`; + } + return `${secret.key}=${value}`; }) .join("\n"); }; diff --git a/backend/src/services/secret-sync/laravel-forge/laravel-forge-sync-schemas.ts b/backend/src/services/secret-sync/laravel-forge/laravel-forge-sync-schemas.ts index 66632d2dd..069638da0 100644 --- a/backend/src/services/secret-sync/laravel-forge/laravel-forge-sync-schemas.ts +++ b/backend/src/services/secret-sync/laravel-forge/laravel-forge-sync-schemas.ts @@ -12,14 +12,11 @@ import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types" const LaravelForgeSyncDestinationConfigSchema = z.object({ orgSlug: z.string().min(1, "Org Slug is required").describe(SecretSyncs.DESTINATION_CONFIG.LARAVEL_FORGE.orgSlug), - orgName: z.string().min(1, "Org Name is required").describe(SecretSyncs.DESTINATION_CONFIG.LARAVEL_FORGE.orgName), + orgName: z.string().optional().describe(SecretSyncs.DESTINATION_CONFIG.LARAVEL_FORGE.orgName), serverId: z.string().min(1, "Server ID is required").describe(SecretSyncs.DESTINATION_CONFIG.LARAVEL_FORGE.serverId), - serverName: z - .string() - .min(1, "Server Name is required") - .describe(SecretSyncs.DESTINATION_CONFIG.LARAVEL_FORGE.serverName), + serverName: z.string().optional().describe(SecretSyncs.DESTINATION_CONFIG.LARAVEL_FORGE.serverName), siteId: z.string().min(1, "Site ID is required").describe(SecretSyncs.DESTINATION_CONFIG.LARAVEL_FORGE.siteId), - siteName: z.string().min(1, "Site Name is required").describe(SecretSyncs.DESTINATION_CONFIG.LARAVEL_FORGE.siteName) + siteName: z.string().optional().describe(SecretSyncs.DESTINATION_CONFIG.LARAVEL_FORGE.siteName) }); const LaravelForgeSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true }; diff --git a/docs/integrations/secret-syncs/laravel-forge.mdx b/docs/integrations/secret-syncs/laravel-forge.mdx index 2e3fd9c98..6ed77d67f 100644 --- a/docs/integrations/secret-syncs/laravel-forge.mdx +++ b/docs/integrations/secret-syncs/laravel-forge.mdx @@ -149,11 +149,8 @@ description: "Learn how to configure a Laravel Forge Sync for Infisical." "destination": "laravel-forge", "destinationConfig": { "orgSlug": "org-abc123", - "orgName": "org-name", "serverId": "server-abc123", - "serverName": "server-name", "siteId": "site-abc123", - "siteName": "site-name" } } } diff --git a/frontend/src/components/secret-syncs/SecretSyncModalHeader.tsx b/frontend/src/components/secret-syncs/SecretSyncModalHeader.tsx index a9882ecec..ebbe34d75 100644 --- a/frontend/src/components/secret-syncs/SecretSyncModalHeader.tsx +++ b/frontend/src/components/secret-syncs/SecretSyncModalHeader.tsx @@ -17,7 +17,7 @@ export const SecretSyncModalHeader = ({ destination, isConfigured }: Props) => { {`${destinationDetails.name}
diff --git a/frontend/src/helpers/appConnections.ts b/frontend/src/helpers/appConnections.ts index e6decfe47..da260ea63 100644 --- a/frontend/src/helpers/appConnections.ts +++ b/frontend/src/helpers/appConnections.ts @@ -63,7 +63,6 @@ export const APP_CONNECTION_MAP: Record< size?: number; icon?: IconDefinition; enterprise?: boolean; - imageClassName?: string; } > = { [AppConnection.AWS]: { name: "AWS", image: "Amazon Web Services.png" }, @@ -126,8 +125,7 @@ export const APP_CONNECTION_MAP: Record< [AppConnection.Redis]: { name: "Redis", image: "Redis.png" }, [AppConnection.LaravelForge]: { name: "Laravel Forge", - image: "Laravel Forge.png", - imageClassName: "object-contain" + image: "Laravel Forge.png" } }; diff --git a/frontend/src/helpers/secretSyncs.ts b/frontend/src/helpers/secretSyncs.ts index 35cd8a656..12cae6ea6 100644 --- a/frontend/src/helpers/secretSyncs.ts +++ b/frontend/src/helpers/secretSyncs.ts @@ -8,10 +8,7 @@ import { GcpSyncScope } from "@app/hooks/api/secretSyncs/types/gcp-sync"; import { HumanitecSyncScope } from "@app/hooks/api/secretSyncs/types/humanitec-sync"; import { RenderSyncScope } from "@app/hooks/api/secretSyncs/types/render-sync"; -export const SECRET_SYNC_MAP: Record< - SecretSync, - { name: string; image: string; imageClassName?: string } -> = { +export const SECRET_SYNC_MAP: Record = { [SecretSync.AWSParameterStore]: { name: "AWS Parameter Store", image: "Amazon Web Services.png" }, [SecretSync.AWSSecretsManager]: { name: "AWS Secrets Manager", image: "Amazon Web Services.png" }, [SecretSync.GitHub]: { name: "GitHub", image: "GitHub.png" }, @@ -119,8 +116,7 @@ export const SECRET_SYNC_MAP: Record< }, [SecretSync.LaravelForge]: { name: "Laravel Forge", - image: "Laravel Forge.png", - imageClassName: "object-contain" + image: "Laravel Forge.png" } }; diff --git a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionHeader.tsx b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionHeader.tsx index 44aa194b3..f77157945 100644 --- a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionHeader.tsx +++ b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionHeader.tsx @@ -19,7 +19,7 @@ export const AppConnectionHeader = ({ app, isConnected, onBack }: Props) => { {`${appDetails.name} {appDetails.icon && (