From f12f81a1252921dabea9a417d6172eaabaebb39d Mon Sep 17 00:00:00 2001 From: tobspr Date: Sat, 15 Jan 2022 11:23:14 +0100 Subject: [PATCH] Refactor file system electron event handling --- electron/index.js | 63 +++++---------------- electron/preload.js | 6 +- electron_wegame/index.js | 86 +++++++++-------------------- src/js/platform/electron/storage.js | 67 ++++++---------------- src/js/states/mods.js | 2 +- 5 files changed, 61 insertions(+), 163 deletions(-) diff --git a/electron/index.js b/electron/index.js index c7b07c55..8ff7faa2 100644 --- a/electron/index.js +++ b/electron/index.js @@ -55,8 +55,8 @@ function createWindow() { autoHideMenuBar: true, webPreferences: { nodeIntegration: false, - webSecurity: false, - // sandbox: true, + webSecurity: true, + sandbox: true, contextIsolation: true, preload: path.join(__dirname, "preload.js"), }, @@ -229,70 +229,33 @@ async function writeFileSafe(filename, contents) { }); } -async function performFsJob(job) { - const fname = path.join(storePath, job.filename); - +ipcMain.handle("fs-job", async (event, job) => { + const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/i, ""); + const fname = path.join(storePath, filenameSafe); switch (job.type) { case "read": { if (!fs.existsSync(fname)) { - return { - // Special FILE_NOT_FOUND error code - error: "file_not_found", - }; - } - - try { - const data = await fs.promises.readFile(fname, "utf8"); - return { - success: true, - data, - }; - } catch (ex) { - return { - error: ex, - }; + // Special FILE_NOT_FOUND error code + return { error: "file_not_found" }; } + return await fs.promises.readFile(fname, "utf8"); } case "write": { - try { - await writeFileSafe(fname, job.contents); - return { - success: true, - data: job.contents, - }; - } catch (ex) { - return { - error: ex, - }; - } + await writeFileSafe(fname, job.contents); + return job.contents; } case "delete": { - try { - await fs.promises.unlink(fname); - } catch (ex) { - return { - error: ex, - }; - } - - return { - success: true, - data: null, - }; + await fs.promises.unlink(fname); + return; } default: throw new Error("Unknown fs job: " + job.type); } -} - -ipcMain.on("fs-job", async (event, arg) => { - const result = await performFsJob(arg); - event.reply("fs-response", { id: arg.id, result }); }); -ipcMain.on("open-mods-folder", async () => { +ipcMain.handle("open-mods-folder", async () => { shell.openPath(modsPath); }); diff --git a/electron/preload.js b/electron/preload.js index 5729634a..c6336230 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -1,3 +1,7 @@ const { contextBridge, ipcRenderer } = require("electron"); -contextBridge.exposeInMainWorld("ipcRenderer", ipcRenderer); +contextBridge.exposeInMainWorld("ipcRenderer", { + invoke: ipcRenderer.invoke.bind(ipcRenderer), + on: ipcRenderer.on.bind(ipcRenderer), + send: ipcRenderer.send.bind(ipcRenderer), +}); diff --git a/electron_wegame/index.js b/electron_wegame/index.js index 23c277c4..2c183f15 100644 --- a/electron_wegame/index.js +++ b/electron_wegame/index.js @@ -49,9 +49,12 @@ function createWindow() { // fullscreen: true, autoHideMenuBar: true, webPreferences: { - nodeIntegration: true, - webSecurity: false, - contextIsolation: false, + nodeIntegration: false, + webSecurity: true, + sandbox: true, + + contextIsolation: true, + preload: path.join(__dirname, "preload.js"), }, allowRunningInsecureContent: false, }); @@ -165,20 +168,20 @@ async function writeFileSafe(filename, contents) { console.warn(prefix, "Concurrent write process on", filename); } - await fileLock.acquire(filename, async () => { + fileLock.acquire(filename, async () => { console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId); if (!fs.existsSync(filename)) { // this one is easy console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename)); - fs.writeFileSync(filename, contents, { encoding: "utf8" }); + await fs.promises.writeFile(filename, contents, "utf8"); return; } // first, write a temporary file (.tmp-XXX) const tempName = filename + ".tmp-" + transactionId; console.log(prefix, "Writing temporary file", niceFileName(tempName)); - fs.writeFileSync(tempName, contents, { encoding: "utf8" }); + await fs.promises.writeFile(tempName, contents, "utf8"); // now, rename the original file to (.backup-XXX) const oldTemporaryName = filename + ".backup-" + transactionId; @@ -189,7 +192,7 @@ async function writeFileSafe(filename, contents) { "to", niceFileName(oldTemporaryName) ); - fs.renameSync(filename, oldTemporaryName); + await fs.promises.rename(filename, oldTemporaryName); // now, rename the temporary file (.tmp-XXX) to the target console.log( @@ -199,7 +202,7 @@ async function writeFileSafe(filename, contents) { "to the original", niceFileName(filename) ); - fs.renameSync(tempName, filename); + await fs.promises.rename(tempName, filename); // we are done now, try to create a backup, but don't fail if the backup fails try { @@ -208,82 +211,43 @@ async function writeFileSafe(filename, contents) { if (fs.existsSync(backupFileName)) { console.log(prefix, "Deleting old backup file", niceFileName(backupFileName)); // delete the old backup - fs.unlinkSync(backupFileName); + await fs.promises.unlink(backupFileName); } // rename the old file to the new backup file console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location"); - fs.renameSync(oldTemporaryName, backupFileName); + await fs.promises.rename(oldTemporaryName, backupFileName); } catch (ex) { console.error(prefix, "Failed to switch backup files:", ex); } }); } -async function performFsJob(job) { - const fname = path.join(storePath, job.filename); - +ipcMain.handle("fs-job", async (event, job) => { + const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/i, ""); + const fname = path.join(storePath, filenameSafe); switch (job.type) { case "read": { if (!fs.existsSync(fname)) { - return { - // Special FILE_NOT_FOUND error code - error: "file_not_found", - }; - } - - try { - const data = fs.readFileSync(fname, { encoding: "utf8" }); - return { - success: true, - data, - }; - } catch (ex) { - console.error(ex); - return { - error: ex, - }; + // Special FILE_NOT_FOUND error code + return { error: "file_not_found" }; } + return await fs.promises.readFile(fname, "utf8"); } case "write": { - try { - writeFileSafe(fname, job.contents); - return { - success: true, - data: job.contents, - }; - } catch (ex) { - console.error(ex); - return { - error: ex, - }; - } + await writeFileSafe(fname, job.contents); + return job.contents; } case "delete": { - try { - fs.unlinkSync(fname); - } catch (ex) { - console.error(ex); - return { - error: ex, - }; - } - - return { - success: true, - data: null, - }; + await fs.promises.unlink(fname); + return; } default: - throw new Error("Unkown fs job: " + job.type); + throw new Error("Unknown fs job: " + job.type); } -} - -ipcMain.on("fs-job", async (event, arg) => { - const result = await performFsJob(arg); - event.sender.send("fs-response", { id: arg.id, result }); }); + wegame.init(isDev); wegame.listen(); diff --git a/src/js/platform/electron/storage.js b/src/js/platform/electron/storage.js index 24aff500..65f0e507 100644 --- a/src/js/platform/electron/storage.js +++ b/src/js/platform/electron/storage.js @@ -1,29 +1,8 @@ -import { StorageInterface } from "../storage"; -import { createLogger } from "../../core/logging"; - -const logger = createLogger("electron-storage"); +import { FILE_NOT_FOUND, StorageInterface } from "../storage"; export class StorageImplElectron extends StorageInterface { constructor(app) { super(app); - - /** @type {Object.} */ - this.jobs = {}; - this.jobId = 0; - - ipcRenderer.on("fs-response", (event, arg) => { - const id = arg.id; - if (!this.jobs[id]) { - logger.warn("Got unhandled FS response, job not known:", id); - return; - } - const { resolve, reject } = this.jobs[id]; - if (arg.result.success) { - resolve(arg.result.data); - } else { - reject(arg.result.error); - } - }); } initialize() { @@ -31,44 +10,32 @@ export class StorageImplElectron extends StorageInterface { } writeFileAsync(filename, contents) { - return new Promise((resolve, reject) => { - // ipcMain - const jobId = ++this.jobId; - this.jobs[jobId] = { resolve, reject }; - - ipcRenderer.send("fs-job", { - type: "write", - filename, - contents, - id: jobId, - }); + return ipcRenderer.invoke("fs-job", { + type: "write", + filename, + contents, }); } readFileAsync(filename) { - return new Promise((resolve, reject) => { - // ipcMain - const jobId = ++this.jobId; - this.jobs[jobId] = { resolve, reject }; - - ipcRenderer.send("fs-job", { + return ipcRenderer + .invoke("fs-job", { type: "read", filename, - id: jobId, + }) + .then(res => { + if (res && res.error === FILE_NOT_FOUND) { + throw FILE_NOT_FOUND; + } + + return res; }); - }); } deleteFileAsync(filename) { - return new Promise((resolve, reject) => { - // ipcMain - const jobId = ++this.jobId; - this.jobs[jobId] = { resolve, reject }; - ipcRenderer.send("fs-job", { - type: "delete", - filename, - id: jobId, - }); + return ipcRenderer.invoke("fs-job", { + type: "delete", + filename, }); } } diff --git a/src/js/states/mods.js b/src/js/states/mods.js index a99d2e95..c3cbce52 100644 --- a/src/js/states/mods.js +++ b/src/js/states/mods.js @@ -116,7 +116,7 @@ export class ModsState extends TextualGameState { this.dialogs.showWarning(T.global.error, T.mods.folderOnlyStandalone); return; } - ipcRenderer.send("open-mods-folder"); + ipcRenderer.invoke("open-mods-folder"); } onSteamLinkClicked() {