fix(settings): clean up orphaned temp files and harden atomic writes
This commit is contained in:
@@ -560,10 +560,15 @@ const writeJsonFile = async (filePath, data) => {
|
||||
// Atomic: write to a temp file then rename. Readers never see a partial
|
||||
// JSON file that could parse-error and get coerced to {}.
|
||||
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
await fsp.writeFile(tmp, JSON.stringify(data, null, 2), { encoding: 'utf8', mode: 0o600 });
|
||||
if (process.platform !== 'win32') await fsp.chmod(tmp, 0o600);
|
||||
await fsp.rename(tmp, filePath);
|
||||
if (process.platform !== 'win32') await fsp.chmod(filePath, 0o600);
|
||||
try {
|
||||
await fsp.writeFile(tmp, JSON.stringify(data, null, 2), { encoding: 'utf8', mode: 0o600 });
|
||||
if (process.platform !== 'win32') await fsp.chmod(tmp, 0o600);
|
||||
await fsp.rename(tmp, filePath);
|
||||
if (process.platform !== 'win32') await fsp.chmod(filePath, 0o600);
|
||||
} catch (error) {
|
||||
await fsp.rm(tmp, { force: true }).catch(() => {});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const readSettingsRoot = () => {
|
||||
|
||||
@@ -77,10 +77,15 @@ const writeJsonRoot = async (settingsFilePath, root) => {
|
||||
await fsp.mkdir(path.dirname(settingsFilePath), { recursive: true });
|
||||
// Atomic write: concurrent readers (main.mjs, web server) would otherwise
|
||||
// see partial JSON and readJsonRoot()'s catch would silently coerce to {},
|
||||
// causing the next read-modify-write to wipe the entire settings file.
|
||||
// causing the next read-modify-write wipe the entire settings file.
|
||||
const tmp = `${settingsFilePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
await fsp.writeFile(tmp, JSON.stringify(root, null, 2));
|
||||
await fsp.rename(tmp, settingsFilePath);
|
||||
try {
|
||||
await fsp.writeFile(tmp, JSON.stringify(root, null, 2));
|
||||
await fsp.rename(tmp, settingsFilePath);
|
||||
} catch (error) {
|
||||
await fsp.rm(tmp, { force: true }).catch(() => {});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const defaultTrue = () => true;
|
||||
|
||||
Reference in New Issue
Block a user