From b971de0717a66ae6085670fe5d3a469e236a9446 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Tue, 26 Apr 2022 16:55:57 +0200 Subject: refactor: config file saving and debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the number of dependencies and the amount of code running in a security sensitive context. Instead of a deep comparison, we just compare the serialized versions of the config files. Signed-off-by: Kristóf Marussy --- packages/main/src/reactions/synchronizeConfig.ts | 40 +++++++++++++----------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'packages/main/src/reactions/synchronizeConfig.ts') diff --git a/packages/main/src/reactions/synchronizeConfig.ts b/packages/main/src/reactions/synchronizeConfig.ts index 7e366e2..247c2e2 100644 --- a/packages/main/src/reactions/synchronizeConfig.ts +++ b/packages/main/src/reactions/synchronizeConfig.ts @@ -18,7 +18,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import deepEqual from 'deep-equal'; import { debounce } from 'lodash-es'; import { reaction } from 'mobx'; @@ -32,36 +31,38 @@ const DEFAULT_CONFIG_DEBOUNCE_TIME_MS = 1000; const log = getLogger('synchronizeConfig'); +export function serializeConfig(config: Config): string { + return `${JSON.stringify(config, undefined, 2)}\n`; +} + export default async function synchronizeConfig( sharedStore: SharedStore, repository: ConfigRepository, debounceTime: number = DEFAULT_CONFIG_DEBOUNCE_TIME_MS, ): Promise { - let lastConfigOnDisk: Config | undefined; + let lastConfigOnDisk: string | undefined; - async function writeConfig(): Promise { - const { config } = sharedStore; - await repository.writeConfig(config); - lastConfigOnDisk = config; + async function writeConfig(serializedConfig: string): Promise { + await repository.writeConfig(serializedConfig); + lastConfigOnDisk = serializedConfig; } async function readConfig(): Promise { const result = await repository.readConfig(); if (result.found) { + const { contents } = result; try { // This cast is unsound if the config file is invalid, // but we'll throw an error in the end anyways. - sharedStore.loadConfig(result.data as Config); + const data = JSON.parse(contents) as Config; + sharedStore.loadConfig(data); } catch (error) { - log.error('Failed to apply config snapshot', result.data, error); + log.error('Failed to apply config snapshot', contents, error); return true; } - lastConfigOnDisk = sharedStore.config; - // We can't use `comparer.structural` from `mobx`, because - // it handles missing values and `undefined` values differently, - // but JSON is unable to distinguish them. - if (!deepEqual(result.data, lastConfigOnDisk, { strict: true })) { - await writeConfig(); + lastConfigOnDisk = serializeConfig(sharedStore.config); + if (contents !== lastConfigOnDisk) { + await writeConfig(lastConfigOnDisk); } } return result.found; @@ -69,16 +70,17 @@ export default async function synchronizeConfig( if (!(await readConfig())) { log.info('Config file was not found'); - await writeConfig(); + const serializedConfig = serializeConfig(sharedStore.config); + await writeConfig(serializedConfig); log.info('Created config file'); } const disposeReaction = reaction( () => sharedStore.config, - debounce((config) => { - // We can compare snapshots by reference, since it is only recreated on store changes. - if (!deepEqual(config, lastConfigOnDisk, { strict: true })) { - writeConfig().catch((error) => { + debounce(() => { + const serializedConfig = serializeConfig(sharedStore.config); + if (serializedConfig !== lastConfigOnDisk) { + writeConfig(serializedConfig).catch((error) => { log.error('Failed to write config on config change', error); }); } -- cgit v1.2.3-70-g09d2