mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-11-13 18:21:00 +00:00
* feat(plugins): add isolated plugin host runtime * test(plugins): build runtime dependencies before tests * fix(plugins): harden runtime lifecycle and recovery * fix(plugins): harden runtime extension boundaries * fix(plugins): enforce runtime integrity boundaries * fix(plugins): await utility process exit * fix(plugins): harden utility process containment * fix(plugins): close runtime recovery gaps * fix(plugins): harden runtime lifecycle boundaries
74 lines
2.7 KiB
JavaScript
74 lines
2.7 KiB
JavaScript
"use strict";
|
|
|
|
const { isPluginDevelopmentEnabled } = require("./constants.cjs");
|
|
|
|
const CHANNELS = Object.freeze({
|
|
status: "netcatty:plugins:status",
|
|
list: "netcatty:plugins:list",
|
|
install: "netcatty:plugins:install",
|
|
setEnabled: "netcatty:plugins:set-enabled",
|
|
restart: "netcatty:plugins:restart",
|
|
uninstall: "netcatty:plugins:uninstall",
|
|
});
|
|
|
|
function createTrustedPluginBridgeSender(options = {}) {
|
|
const devServerOrigin = options.devServerUrl ? new URL(options.devServerUrl).origin : null;
|
|
return (event) => {
|
|
const senderUrl = event?.senderFrame?.url || event?.sender?.getURL?.() || "";
|
|
try {
|
|
const url = new URL(senderUrl);
|
|
if (url.protocol === "app:" && url.hostname === "netcatty") return true;
|
|
return Boolean(devServerOrigin && url.origin === devServerOrigin);
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
}
|
|
|
|
function registerPluginBridge(ipcMain, options) {
|
|
const manager = options.manager;
|
|
const env = options.env ?? process.env;
|
|
const isTrustedSender = options.isTrustedSender;
|
|
const configured = isPluginDevelopmentEnabled(env)
|
|
&& Boolean(manager)
|
|
&& typeof manager.initialize === "function";
|
|
const resolveManager = async () => {
|
|
if (!configured) throw new Error("Plugin development runtime is disabled or unavailable");
|
|
try {
|
|
await manager.initialize();
|
|
} catch (cause) {
|
|
throw new Error("Plugin development runtime is disabled or unavailable", { cause });
|
|
}
|
|
return manager;
|
|
};
|
|
const handle = (channel, callback) => {
|
|
ipcMain.handle(channel, async (event, payload) => {
|
|
if (!isTrustedSender(event)) throw new Error("Untrusted plugin management sender");
|
|
const activeManager = await resolveManager();
|
|
return callback(activeManager, payload);
|
|
});
|
|
};
|
|
ipcMain.handle(CHANNELS.status, async (event) => {
|
|
if (!isTrustedSender(event)) throw new Error("Untrusted plugin management sender");
|
|
let available = false;
|
|
try {
|
|
await resolveManager();
|
|
available = true;
|
|
} catch {}
|
|
return { available, experimental: true };
|
|
});
|
|
handle(CHANNELS.list, async (activeManager) => activeManager.list());
|
|
handle(CHANNELS.install, async (activeManager, payload) => activeManager.install(
|
|
payload?.archivePath,
|
|
{ enable: payload?.enable === true },
|
|
));
|
|
handle(CHANNELS.setEnabled, async (activeManager, payload) => activeManager.setEnabled(
|
|
payload?.pluginId,
|
|
payload?.enabled === true,
|
|
));
|
|
handle(CHANNELS.restart, async (activeManager, payload) => activeManager.restart(payload?.pluginId));
|
|
handle(CHANNELS.uninstall, async (activeManager, payload) => activeManager.uninstall(payload?.pluginId));
|
|
}
|
|
|
|
module.exports = { CHANNELS, createTrustedPluginBridgeSender, registerPluginBridge };
|