From 879fd8e8b6586bd73eeda08f8a2eb16d6e63bffe Mon Sep 17 00:00:00 2001 From: Hugo Richard Date: Thu, 14 Nov 2024 20:20:21 +0100 Subject: [PATCH] fix: error on copy paste if key value has '=' Fixes #308 Fix error on copy-paste if key value has '='. * **apps/shelve/app/components/project/CreateVariables.vue** - Update `parseEnvFile` function to split only on the first '='. - Update `paste` event handler to split only on the first '='. * **apps/lp/app/components/vault/Encrypt.vue** - Update `saveEnvFile` function to split only on the first '='. * **apps/lp/app/components/vault/Decrypt.vue** - Update `decryptEnvFile` function to split only on the first '='. * **packages/cli/src/utils/env.ts** - Update `copyEnv` function to split only on the first '='. - Update `downloadEnv` function to split only on the first '='. * **apps/shelve/app/utils/clipboard.ts** - Update `copyToClipboard` function to split only on the first '='. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/HugoRCD/shelve/issues/308?shareId=XXXX-XXXX-XXXX-XXXX). --- apps/lp/app/components/vault/Decrypt.vue | 8 ++++++- apps/lp/app/components/vault/Encrypt.vue | 8 ++++++- .../components/project/CreateVariables.vue | 6 +++-- apps/shelve/app/utils/clipboard.ts | 9 +++++++- packages/cli/src/utils/env.ts | 23 ++++++++++++++++++- 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/apps/lp/app/components/vault/Decrypt.vue b/apps/lp/app/components/vault/Decrypt.vue index a14cb562..7d2f1e25 100644 --- a/apps/lp/app/components/vault/Decrypt.vue +++ b/apps/lp/app/components/vault/Decrypt.vue @@ -15,7 +15,13 @@ async function decryptEnvFile() { const { decryptedValue, reads, ttl } = await $fetch(`/api/vault?id=${localId.value}`, { method: 'POST', }) - value.value = decryptedValue + const lines = decryptedValue.split('\n').filter((line) => line.trim() !== '') + const variables = lines.map((line) => { + const [key, ...valueParts] = line.split('=') + const value = valueParts.join('=') + return `${key}=${value}` + }) + value.value = variables.join('\n') readsLeft.value = reads timeLeft.value = ttl toast.success('Your secret(s) has been decrypted') diff --git a/apps/lp/app/components/vault/Encrypt.vue b/apps/lp/app/components/vault/Encrypt.vue index e98cc14b..33c0f7b4 100644 --- a/apps/lp/app/components/vault/Encrypt.vue +++ b/apps/lp/app/components/vault/Encrypt.vue @@ -13,10 +13,16 @@ const shareUrl = ref('') async function saveEnvFile() { loading.value = true try { + const lines = value.value.split('\n').filter((line) => line.trim() !== '') + const variables = lines.map((line) => { + const [key, ...valueParts] = line.split('=') + const value = valueParts.join('=') + return { key, value } + }) shareUrl.value = await $fetch('/api/vault', { method: 'POST', body: { - value: value.value, + value: variables.map((variable) => `${variable.key}=${variable.value}`).join('\n'), reads: reads.value, ttl: selectedTtl.value, }, diff --git a/apps/shelve/app/components/project/CreateVariables.vue b/apps/shelve/app/components/project/CreateVariables.vue index 397c4327..ada25cf9 100644 --- a/apps/shelve/app/components/project/CreateVariables.vue +++ b/apps/shelve/app/components/project/CreateVariables.vue @@ -131,7 +131,8 @@ function parseEnvFile(file: File) { const lines = content.split('\n').filter((line) => line.trim() !== '') const filteredLines = lines.filter((line) => !line.startsWith('#')) const variables = filteredLines.map((line, index) => { - const [key, value] = line.split('=') + const [key, ...valueParts] = line.split('=') + const value = valueParts.join('=') if (!key || !value) { toast.error('Invalid .env file') throw new Error('Invalid .env') @@ -167,7 +168,8 @@ onMounted(() => { const pastedDataArrayFiltered = pastedDataArray.filter((data) => data !== '') variablesToCreate.value = pastedDataArrayFiltered.length variablesInput.value.variables = pastedDataArrayFiltered.map((data, index) => { - const [key, value] = data.split('=') + const [key, ...valueParts] = data.split('=') + const value = valueParts.join('=') if (!key || !value) throw new Error('Invalid .env') return { index, diff --git a/apps/shelve/app/utils/clipboard.ts b/apps/shelve/app/utils/clipboard.ts index bcc510ba..6eaefb04 100644 --- a/apps/shelve/app/utils/clipboard.ts +++ b/apps/shelve/app/utils/clipboard.ts @@ -1,5 +1,12 @@ export function copyToClipboard(toCopy: string, message: string = 'Copied to clipboard') { - navigator.clipboard.writeText(toCopy).then(() => { + const lines = toCopy.split('\n').filter((line) => line.trim() !== '') + const variables = lines.map((line) => { + const [key, ...valueParts] = line.split('=') + const value = valueParts.join('=') + return `${key}=${value}` + }) + const formattedString = variables.join('\n') + navigator.clipboard.writeText(formattedString).then(() => { toast.success(message) }) } diff --git a/packages/cli/src/utils/env.ts b/packages/cli/src/utils/env.ts index 566786be..e94d6ec4 100644 --- a/packages/cli/src/utils/env.ts +++ b/packages/cli/src/utils/env.ts @@ -69,7 +69,8 @@ export async function getEnvFile(): Promise { const envFileContent = envFile.split('\n').filter((item) => item && !item.startsWith('#')).join('\n') if (!envFileContent) return [] return envFileContent.split('\n').map((item) => { - const [key, value] = item.split('=') + const [key, ...valueParts] = item.split('=') + const value = valueParts.join('=') if (!key || !value) { onCancel(`${ envFileName } file is invalid`) } @@ -139,3 +140,23 @@ export async function generateEnvExampleFile(): Promise { onCancel(`Failed to generate ${envExampleFile} file`) } } + +export function copyEnv(variables: Variable[], env: 'production' | 'preview' | 'development') { + if (variables.length === 0) return + const envVariables = variables.filter((variable) => variable.environment.includes(env)) + const envString = envVariables.map((variable) => `${variable.key}=${variable.value}`).join('\n') + copyToClipboard(envString, 'Copied to clipboard') +} + +export function downloadEnv(variables: Variable[], env: 'production' | 'preview' | 'development') { + if (variables.length === 0) return + const envVariables = variables.filter((variable) => variable.environment.includes(env)) + const envString = envVariables.map((variable) => `${variable.key}=${variable.value}`).join('\n') + const blob = new Blob([envString], { type: 'text/plain' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `.env.${env}` + a.click() + URL.revokeObjectURL(url) +}