From d70fa88d26a1650248e7c8ced06df7961e4eefef Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 7 Nov 2025 17:57:22 +0000 Subject: [PATCH] Fix config restore for CLI exports (related to #646) The v4.26.4 fix inadvertently broke CLI export compatibility. The frontend attempted JSON.parse on all backup files and returned early with "Invalid JSON file format" when parsing failed. This prevented the format detection code from ever executing, breaking CLI-generated exports which are raw base64 strings without a JSON wrapper. Root cause: - CLI exports (`pulse config export`) output raw base64 via internal/config/export.go:128 - The fix at Settings.tsx:2030-2034 called showError() and returned immediately on parse failure - Format detection logic at lines 2040-2049 never executed for CLI exports This changes the parsing flow to: 1. Try JSON.parse first (handles UI exports with {status, data} format) 2. On parse success, extract data field as before 3. On parse failure, treat entire file contents as raw base64 (CLI format) This preserves the v4.26.4 improvements (12-char validation, better error messages) while restoring CLI export compatibility. Related to #646 where user confirmed v4.26.4 still failed to restore backups. --- .../src/components/Settings/Settings.tsx | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/frontend-modern/src/components/Settings/Settings.tsx b/frontend-modern/src/components/Settings/Settings.tsx index b5e243851..011cfe4d7 100644 --- a/frontend-modern/src/components/Settings/Settings.tsx +++ b/frontend-modern/src/components/Settings/Settings.tsx @@ -2024,28 +2024,30 @@ const Settings: Component = (props) => { try { const fileContent = await importFile()!.text(); - let exportData; - try { - exportData = JSON.parse(fileContent); - } catch (parseError) { - showError('Invalid JSON file format'); - logger.error('JSON parse error', parseError); - return; - } - // Support both formats: - // 1. New format: {status: "success", data: "base64string"} - // 2. Legacy/CLI format: raw base64 string or {data: "base64string"} + // Support three formats: + // 1. UI export: {status: "success", data: "base64string"} + // 2. Legacy format: {data: "base64string"} + // 3. CLI export: raw base64 string (no JSON wrapper) let encryptedData: string; - if (typeof exportData === 'string') { - // Raw base64 string from CLI export - encryptedData = exportData; - } else if (exportData.data) { - // Standard format with data field - encryptedData = exportData.data; - } else { - showError('Invalid backup file format. Expected encrypted data in "data" field.'); - return; + + // Try to parse as JSON first + try { + const exportData = JSON.parse(fileContent); + + if (typeof exportData === 'string') { + // Raw base64 string wrapped in JSON (edge case) + encryptedData = exportData; + } else if (exportData.data) { + // Standard format with data field + encryptedData = exportData.data; + } else { + showError('Invalid backup file format. Expected encrypted data in "data" field.'); + return; + } + } catch (parseError) { + // Not JSON - treat entire contents as raw base64 from CLI export + encryptedData = fileContent.trim(); } // Get CSRF token from cookie