mirror of
https://github.com/MasterAcnolo/Freedom-Loader.git
synced 2026-07-29 18:25:47 +02:00
- Add JSDoc comments across frontend and backend modules - Improve code readability and maintainability with consistent documentation style - Document Electron IPC handlers, UI utilities, and theme system - Add missing function/class documentation in theme loader and app services - Minor structural improvements and cleanup across Electron main process modules - Minor fixes on variable names
233 lines
6.3 KiB
JavaScript
233 lines
6.3 KiB
JavaScript
/**
|
||
* Electron IPC main process registry.
|
||
*
|
||
* Centralizes all IPC channels exposed to renderer process:
|
||
* - Application info (version, features)
|
||
* - Window controls (topbar actions)
|
||
* - File system interactions (theme, config, download paths)
|
||
* - UI state synchronization (progress bar)
|
||
*
|
||
* Acts as the bridge between renderer and privileged Node/Electron APIs.
|
||
*/
|
||
|
||
const { ipcMain, dialog, shell } = require("electron");
|
||
const fs = require("fs");
|
||
const path = require("path");
|
||
const { logger, logDir } = require("../server/logger");
|
||
const { configFeatures, featuresPath } = require("../config");
|
||
const { getThemes, reloadThemes } = require("./themeManager");
|
||
const config = require("../config");
|
||
const { validateDownloadPath, getDefaultDownloadPath } = require("./pathValidator");
|
||
const { userThemesPath } = require("../server/helpers/path.helpers");
|
||
|
||
/**
|
||
* Security whitelist for feature flags that can be modified at runtime.
|
||
*
|
||
* Prevents unauthorized or unexpected configuration keys from being persisted.
|
||
* Acts as a basic validation layer for IPC "set-feature".
|
||
*/
|
||
const FEATURE_WHITELIST = new Set([
|
||
"autoUpdate",
|
||
"discordRPC",
|
||
"customTopBar",
|
||
"autoCheckInfo",
|
||
"addThumbnail",
|
||
"addMetadata",
|
||
"verboseLogs",
|
||
"autoDownloadPlaylist",
|
||
"customCodec",
|
||
"theme",
|
||
"createPlaylistFolders",
|
||
"notifySystem"
|
||
]);
|
||
|
||
/**
|
||
* Absolute path to the configuration file storing feature flags.
|
||
*/
|
||
const configFolderPath = featuresPath;
|
||
|
||
/**
|
||
* Directory containing user-installed themes.
|
||
*/
|
||
const themeFolderPath = userThemesPath;
|
||
|
||
/**
|
||
* Registers all IPC handlers and event listeners for Electron main process.
|
||
*
|
||
* This function wires renderer → main communication channels:
|
||
* - synchronous queries (handle)
|
||
* - fire-and-forget events (on)
|
||
*
|
||
* @param {Function} getMainWindow - Function returning current BrowserWindow instance
|
||
*/
|
||
function registerIpcHandlers(getMainWindow) {
|
||
|
||
/**
|
||
* Returns application version from config.
|
||
*/
|
||
ipcMain.handle("version", () => config.version);
|
||
|
||
/**
|
||
* Returns runtime feature configuration object.
|
||
*/
|
||
ipcMain.handle("features", () => configFeatures);
|
||
|
||
/**
|
||
* Opens native folder selection dialog and validates selected path.
|
||
*
|
||
* Ensures:
|
||
* - user cancellation is handled safely
|
||
* - selected path is validated against security rules
|
||
* - unsafe directories are rejected
|
||
*/
|
||
ipcMain.handle("select-download-folder", async () => {
|
||
const result = await dialog.showOpenDialog({ properties: ["openDirectory"] });
|
||
if (result.canceled) {
|
||
logger.info("Folder selection cancelled by user");
|
||
return null;
|
||
}
|
||
try {
|
||
const validated = validateDownloadPath(result.filePaths[0]);
|
||
logger.info(`Folder selected and validated: ${validated}`);
|
||
return validated;
|
||
} catch (err) {
|
||
logger.warn(`Unsafe or invalid folder rejected: ${err.message}`);
|
||
throw err;
|
||
}
|
||
});
|
||
|
||
ipcMain.handle("validate-download-path", (_, userPath) => validateDownloadPath(userPath));
|
||
ipcMain.handle("get-default-download-path", () => getDefaultDownloadPath());
|
||
|
||
/**
|
||
* Updates Windows/macOS taskbar progress indicator.
|
||
*
|
||
* @param {number} percent - Progress value (0–100)
|
||
*/
|
||
ipcMain.on("set-progress", (_, percent) => {
|
||
getMainWindow()?.setProgressBar(percent / 100);
|
||
});
|
||
|
||
/**
|
||
* Window minimize request from renderer.
|
||
*/
|
||
ipcMain.on("window-minimize", () => getMainWindow()?.minimize());
|
||
|
||
/**
|
||
* Toggles maximize/unmaximize state of main window.
|
||
*/
|
||
ipcMain.on("window-maximize", () => {
|
||
const win = getMainWindow();
|
||
if (!win) return;
|
||
win.isMaximized() ? win.unmaximize() : win.maximize();
|
||
});
|
||
|
||
/**
|
||
* Closes the main application window.
|
||
*/
|
||
ipcMain.on("window-close", () => getMainWindow()?.close());
|
||
|
||
|
||
/**
|
||
* Opens Chromium DevTools in detached mode.
|
||
*/
|
||
ipcMain.on("open-devtools", () =>
|
||
getMainWindow()?.webContents.openDevTools({ mode: "detach" })
|
||
);
|
||
|
||
/**
|
||
* Opens application logs directory in system file explorer.
|
||
*/
|
||
ipcMain.on("open-logs", () => logDir && shell.openPath(logDir));
|
||
|
||
/**
|
||
* Opens external website in default browser.
|
||
*/
|
||
ipcMain.on("open-website", () =>
|
||
shell.openExternal("https://masteracnolo.github.io/Freedom-Loader-Site/")
|
||
);
|
||
|
||
/**
|
||
* Opens official wiki page in external browser.
|
||
*/
|
||
ipcMain.on("open-wiki", () =>
|
||
shell.openExternal("https://masteracnolo.github.io/Freedom-Loader-Site/wiki")
|
||
);
|
||
|
||
/**
|
||
* Opens workshop page in external browser.
|
||
*/
|
||
ipcMain.on("open-workshop", () =>
|
||
shell.openExternal("https://masteracnolo.github.io/Freedom-Loader-Workshop")
|
||
);
|
||
|
||
/**
|
||
* Opens configuration folder in system file explorer.
|
||
*/
|
||
ipcMain.on("open-config", () => shell.openPath(configFolderPath));
|
||
|
||
|
||
|
||
/**
|
||
* Retrieves available themes from filesystem.
|
||
*/
|
||
ipcMain.handle("get-themes", () => getThemes());
|
||
|
||
/**
|
||
* Opens theme directory in file explorer.
|
||
*/
|
||
ipcMain.on("open-theme", () => shell.openPath(themeFolderPath));
|
||
|
||
/**
|
||
* Reloads themes from disk dynamically.
|
||
*/
|
||
ipcMain.handle("reload-themes", async () => {
|
||
return await reloadThemes();
|
||
});
|
||
|
||
/**
|
||
* Updates a runtime feature flag and persists it to disk.
|
||
*
|
||
* Flow:
|
||
* - Validates key against whitelist
|
||
* - Prevents unnecessary writes if value is unchanged
|
||
* - Updates in-memory config object
|
||
* - Persists to configuration file
|
||
*
|
||
* Security note:
|
||
* Only whitelisted keys can be modified via IPC to prevent config injection.
|
||
*
|
||
* @param {Electron.IpcMainEvent} event
|
||
* @param {{key: string, value: any}} payload
|
||
* @returns {boolean} success state
|
||
*/
|
||
ipcMain.handle("set-feature", (event, { key, value }) => {
|
||
try {
|
||
if (!FEATURE_WHITELIST.has(key)) {
|
||
logger.warn(`Rejected feature (not whitelisted): ${key}`);
|
||
return false;
|
||
}
|
||
|
||
if (configFeatures[key] === value) {
|
||
return true;
|
||
}
|
||
|
||
configFeatures[key] = value;
|
||
|
||
fs.writeFileSync(
|
||
configFolderPath,
|
||
JSON.stringify(configFeatures, null, 2),
|
||
"utf-8"
|
||
);
|
||
|
||
logger.info(`Feature updated: ${key} = ${value}`);
|
||
return true;
|
||
|
||
} catch (err) {
|
||
logger.error(`set-feature failed (${key}): ${err.message}`);
|
||
return false;
|
||
}
|
||
});
|
||
}
|
||
|
||
module.exports = { registerIpcHandlers }; |