[desktop] Improve export_status.json writes

This commit is contained in:
Manav Rathi 2025-02-27 09:18:14 +05:30
parent 8fb463028d
commit 4e6e3e7abf
No known key found for this signature in database
5 changed files with 30 additions and 1 deletions

View File

@ -41,6 +41,7 @@ import {
fsRm,
fsRmdir,
fsWriteFile,
fsWriteFileViaBackup,
} from "./services/fs";
import { convertToJPEG, generateImageThumbnail } from "./services/image";
import { logout } from "./services/logout";
@ -154,6 +155,12 @@ export const attachIPCHandlers = () => {
fsWriteFile(path, contents),
);
ipcMain.handle(
"fsWriteFileViaBackup",
(_, path: string, contents: string) =>
fsWriteFileViaBackup(path, contents),
);
ipcMain.handle("fsIsDir", (_, dirPath: string) => fsIsDir(dirPath));
ipcMain.handle("fsFindFiles", (_, folderPath: string) =>

View File

@ -24,6 +24,12 @@ export const fsReadTextFile = async (filePath: string) =>
export const fsWriteFile = (path: string, contents: string) =>
fs.writeFile(path, contents, { flush: true });
export const fsWriteFileViaBackup = async (path: string, contents: string) => {
const backupPath = path + ".backup";
await fs.writeFile(backupPath, contents, { flush: true });
return fs.rename(backupPath, path);
};
export const fsIsDir = async (dirPath: string) => {
if (!existsSync(dirPath)) return false;
const stat = await fs.stat(dirPath);

View File

@ -178,6 +178,9 @@ const fsReadTextFile = (path: string) =>
const fsWriteFile = (path: string, contents: string) =>
ipcRenderer.invoke("fsWriteFile", path, contents);
const fsWriteFileViaBackup = (path: string, contents: string) =>
ipcRenderer.invoke("fsWriteFileViaBackup", path, contents);
const fsIsDir = (dirPath: string) => ipcRenderer.invoke("fsIsDir", dirPath);
// - Conversion
@ -373,6 +376,7 @@ contextBridge.exposeInMainWorld("electron", {
rm: fsRm,
readTextFile: fsReadTextFile,
writeFile: fsWriteFile,
writeFileViaBackup: fsWriteFileViaBackup,
isDir: fsIsDir,
findFiles: fsFindFiles,
},

View File

@ -888,7 +888,7 @@ class ExportService {
try {
const exportRecord = await this.getExportRecord(folder);
const newRecord: ExportRecord = { ...exportRecord, ...newData };
await ensureElectron().fs.writeFile(
await ensureElectron().fs.writeFileViaBackup(
joinPath(folder, exportRecordFileName),
JSON.stringify(newRecord, null, 2),
);

View File

@ -259,6 +259,18 @@ export interface Electron {
*/
writeFile: (path: string, contents: string) => Promise<void>;
/**
* A variant of {@link writeFile} that first writes the file to a backup
* file, and then renames the backup file to the actual path. This both
* makes the write atomic (as far as the node's fs.rename guarantees
* atomicity), and also keeps the backup file around for recovery if
* something goes wrong during the rename.
*
* This behaviour of this function is tailored around for writes to the
* "export_status.json" during exports.
*/
writeFileViaBackup: (path: string, contents: string) => Promise<void>;
/**
* Return true if there is an item at {@link dirPath}, and it is as
* directory.