mirror of
https://github.com/Nystik-gh/ignis.git
synced 2026-06-17 04:35:53 +00:00
refactor bridge plugin loading
This commit is contained in:
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 80
|
||||
}
|
||||
70
server/bridge-plugin.js
Normal file
70
server/bridge-plugin.js
Normal file
@@ -0,0 +1,70 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const {
|
||||
installObsidianPlugin,
|
||||
isObsidianPluginInstalled,
|
||||
} = require("./plugin-system/obsidian-plugin");
|
||||
|
||||
const BRIDGE_PLUGIN_ID = "ignis-bridge";
|
||||
const BRIDGE_PLUGIN_DIR = path.join(__dirname, "..", "plugin");
|
||||
|
||||
// .ignis metadata helpers
|
||||
|
||||
async function getIgnisMeta(vaultPath) {
|
||||
const metaFile = path.join(vaultPath, ".ignis", "meta.json");
|
||||
|
||||
try {
|
||||
const content = await fs.promises.readFile(metaFile, "utf-8");
|
||||
return JSON.parse(content);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async function setIgnisMeta(vaultPath, data) {
|
||||
const ignisDir = path.join(vaultPath, ".ignis");
|
||||
const metaFile = path.join(ignisDir, "meta.json");
|
||||
|
||||
await fs.promises.mkdir(ignisDir, { recursive: true });
|
||||
await fs.promises.writeFile(metaFile, JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
// Bridge plugin install/check
|
||||
|
||||
async function isBridgePluginInstalled(vaultPath) {
|
||||
return isObsidianPluginInstalled(BRIDGE_PLUGIN_ID, vaultPath);
|
||||
}
|
||||
|
||||
async function installBridgePlugin(vaultPath) {
|
||||
const result = await installObsidianPlugin(BRIDGE_PLUGIN_DIR, vaultPath);
|
||||
return result.installed;
|
||||
}
|
||||
|
||||
async function updateBridgePluginInAllVaults(vaultRoot) {
|
||||
if (!(await fs.promises.stat(vaultRoot).catch(() => null))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entries = await fs.promises.readdir(vaultRoot, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const vaultPath = path.join(vaultRoot, entry.name);
|
||||
const installed = await installBridgePlugin(vaultPath);
|
||||
|
||||
if (installed) {
|
||||
console.log(`[ignis] Installed bridge plugin in vault: ${entry.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
installBridgePlugin,
|
||||
updateBridgePluginInAllVaults,
|
||||
isBridgePluginInstalled,
|
||||
getIgnisMeta,
|
||||
setIgnisMeta,
|
||||
};
|
||||
@@ -3,7 +3,8 @@ const path = require("path");
|
||||
const compression = require("compression");
|
||||
const config = require("./config");
|
||||
const { setupWebSocket } = require("./ws");
|
||||
const { installPluginInAllVaults } = require("./install-plugin");
|
||||
const watcher = require("./watcher");
|
||||
const { updateBridgePluginInAllVaults } = require("./bridge-plugin");
|
||||
|
||||
const ANSI_RED = "\x1b[31m";
|
||||
const ANSI_YELLOW = "\x1b[33m";
|
||||
@@ -97,7 +98,26 @@ const server = app.listen(config.port, async () => {
|
||||
console.log(`[ignis] Vault root: ${config.vaultRoot}`);
|
||||
console.log(`[ignis] Vaults: ${Object.keys(config.vaults).join(", ")}`);
|
||||
|
||||
await installPluginInAllVaults(config.vaultRoot);
|
||||
await updateBridgePluginInAllVaults(config.vaultRoot);
|
||||
});
|
||||
|
||||
setupWebSocket(server);
|
||||
const wss = setupWebSocket(server);
|
||||
|
||||
async function gracefulShutdown(signal) {
|
||||
console.log(`\n[ignis] Received ${signal}, shutting down gracefully...`);
|
||||
|
||||
await shutdownPlugins();
|
||||
|
||||
server.close(() => {
|
||||
console.log("[ignis] Server closed");
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.error("[ignis] Forced shutdown after timeout");
|
||||
process.exit(1);
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
|
||||
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
// .ignis metadata helpers
|
||||
async function getIgnisMeta(vaultPath) {
|
||||
const ignisDir = path.join(vaultPath, ".ignis");
|
||||
const metaFile = path.join(ignisDir, "meta.json");
|
||||
|
||||
try {
|
||||
const content = await fs.promises.readFile(metaFile, "utf-8");
|
||||
return JSON.parse(content);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async function setIgnisMeta(vaultPath, data) {
|
||||
const ignisDir = path.join(vaultPath, ".ignis");
|
||||
const metaFile = path.join(ignisDir, "meta.json");
|
||||
|
||||
await fs.promises.mkdir(ignisDir, { recursive: true });
|
||||
await fs.promises.writeFile(metaFile, JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
async function checkPluginInstalled(vaultPath) {
|
||||
const pluginDir = path.join(
|
||||
vaultPath,
|
||||
".obsidian",
|
||||
"plugins",
|
||||
"ignis-bridge",
|
||||
);
|
||||
const manifestPath = path.join(pluginDir, "manifest.json");
|
||||
const mainPath = path.join(pluginDir, "main.js");
|
||||
|
||||
try {
|
||||
await fs.promises.access(manifestPath);
|
||||
await fs.promises.access(mainPath);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function installPluginInVault(vaultPath) {
|
||||
const obsidianDir = path.join(vaultPath, ".obsidian");
|
||||
const pluginDir = path.join(obsidianDir, "plugins", "ignis-bridge");
|
||||
|
||||
if (!(await fs.promises.stat(obsidianDir).catch(() => null))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await fs.promises.mkdir(pluginDir, { recursive: true });
|
||||
|
||||
const pluginSrcDir = path.join(__dirname, "..", "plugin");
|
||||
await fs.promises.copyFile(
|
||||
path.join(pluginSrcDir, "manifest.json"),
|
||||
path.join(pluginDir, "manifest.json"),
|
||||
);
|
||||
await fs.promises.copyFile(
|
||||
path.join(pluginSrcDir, "main.js"),
|
||||
path.join(pluginDir, "main.js"),
|
||||
);
|
||||
|
||||
const pluginsConfig = path.join(obsidianDir, "community-plugins.json");
|
||||
let plugins = [];
|
||||
|
||||
if (await fs.promises.stat(pluginsConfig).catch(() => null)) {
|
||||
try {
|
||||
plugins = JSON.parse(await fs.promises.readFile(pluginsConfig, "utf8"));
|
||||
} catch (e) {
|
||||
plugins = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (!plugins.includes("ignis-bridge")) {
|
||||
plugins.push("ignis-bridge");
|
||||
await fs.promises.writeFile(pluginsConfig, JSON.stringify(plugins));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async function installPluginInAllVaults(vaultRoot) {
|
||||
if (!(await fs.promises.stat(vaultRoot).catch(() => null))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entries = await fs.promises.readdir(vaultRoot, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
const vaultPath = path.join(vaultRoot, entry.name);
|
||||
const installed = await installPluginInVault(vaultPath);
|
||||
|
||||
if (installed) {
|
||||
console.log(`[ignis] Installed plugin in vault: ${entry.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
installPluginInVault,
|
||||
installPluginInAllVaults,
|
||||
getIgnisMeta,
|
||||
setIgnisMeta,
|
||||
checkPluginInstalled,
|
||||
};
|
||||
110
server/plugin-system/obsidian-plugin.js
Normal file
110
server/plugin-system/obsidian-plugin.js
Normal file
@@ -0,0 +1,110 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
async function readManifestId(sourceDir) {
|
||||
const manifestPath = path.join(sourceDir, "manifest.json");
|
||||
const content = await fs.promises.readFile(manifestPath, "utf-8");
|
||||
const manifest = JSON.parse(content);
|
||||
|
||||
if (!manifest.id) {
|
||||
throw new Error(`No "id" in manifest.json at ${sourceDir}`);
|
||||
}
|
||||
|
||||
return manifest.id;
|
||||
}
|
||||
|
||||
async function installObsidianPlugin(sourceDir, vaultPath) {
|
||||
const pluginId = await readManifestId(sourceDir);
|
||||
|
||||
const obsidianDir = path.join(vaultPath, ".obsidian");
|
||||
|
||||
try {
|
||||
await fs.promises.access(obsidianDir);
|
||||
} catch {
|
||||
return { installed: false, pluginId };
|
||||
}
|
||||
|
||||
const targetDir = path.join(obsidianDir, "plugins", pluginId);
|
||||
await fs.promises.mkdir(targetDir, { recursive: true });
|
||||
|
||||
const files = await fs.promises.readdir(sourceDir);
|
||||
|
||||
for (const file of files) {
|
||||
const srcPath = path.join(sourceDir, file);
|
||||
const stat = await fs.promises.stat(srcPath);
|
||||
|
||||
if (stat.isFile()) {
|
||||
await fs.promises.copyFile(srcPath, path.join(targetDir, file));
|
||||
}
|
||||
}
|
||||
|
||||
const pluginsConfigFile = path.join(obsidianDir, "community-plugins.json");
|
||||
let plugins = [];
|
||||
|
||||
try {
|
||||
const content = await fs.promises.readFile(pluginsConfigFile, "utf-8");
|
||||
plugins = JSON.parse(content);
|
||||
} catch {
|
||||
plugins = [];
|
||||
}
|
||||
|
||||
if (!plugins.includes(pluginId)) {
|
||||
plugins.push(pluginId);
|
||||
await fs.promises.writeFile(pluginsConfigFile, JSON.stringify(plugins));
|
||||
}
|
||||
|
||||
return { installed: true, pluginId };
|
||||
}
|
||||
|
||||
async function removeObsidianPlugin(sourceDir, vaultPath) {
|
||||
const pluginId = await readManifestId(sourceDir);
|
||||
|
||||
const obsidianDir = path.join(vaultPath, ".obsidian");
|
||||
|
||||
try {
|
||||
await fs.promises.access(obsidianDir);
|
||||
} catch {
|
||||
return { removed: false, pluginId };
|
||||
}
|
||||
|
||||
const targetDir = path.join(obsidianDir, "plugins", pluginId);
|
||||
|
||||
try {
|
||||
await fs.promises.rm(targetDir, { recursive: true });
|
||||
} catch {
|
||||
// Already gone
|
||||
}
|
||||
|
||||
const pluginsConfigFile = path.join(obsidianDir, "community-plugins.json");
|
||||
|
||||
try {
|
||||
const content = await fs.promises.readFile(pluginsConfigFile, "utf-8");
|
||||
let plugins = JSON.parse(content);
|
||||
plugins = plugins.filter((id) => id !== pluginId);
|
||||
await fs.promises.writeFile(pluginsConfigFile, JSON.stringify(plugins));
|
||||
} catch {
|
||||
// No config file or parse error - nothing to remove from
|
||||
}
|
||||
|
||||
return { removed: true, pluginId };
|
||||
}
|
||||
|
||||
async function isObsidianPluginInstalled(pluginId, vaultPath) {
|
||||
const pluginDir = path.join(vaultPath, ".obsidian", "plugins", pluginId);
|
||||
const manifestPath = path.join(pluginDir, "manifest.json");
|
||||
const mainPath = path.join(pluginDir, "main.js");
|
||||
|
||||
try {
|
||||
await fs.promises.access(manifestPath);
|
||||
await fs.promises.access(mainPath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
installObsidianPlugin,
|
||||
removeObsidianPlugin,
|
||||
isObsidianPluginInstalled,
|
||||
};
|
||||
@@ -3,11 +3,11 @@ const fs = require("fs");
|
||||
const config = require("../config");
|
||||
const path = require("path");
|
||||
const {
|
||||
checkPluginInstalled,
|
||||
isBridgePluginInstalled,
|
||||
getIgnisMeta,
|
||||
setIgnisMeta,
|
||||
installPluginInVault,
|
||||
} = require("../install-plugin");
|
||||
installBridgePlugin,
|
||||
} = require("../bridge-plugin");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -33,7 +33,7 @@ router.get("/info", async (req, res) => {
|
||||
return res.status(404).json({ error: "Vault not found", id: vaultId });
|
||||
}
|
||||
|
||||
const pluginInstalled = await checkPluginInstalled(vaultPath);
|
||||
const pluginInstalled = await isBridgePluginInstalled(vaultPath);
|
||||
const ignisMeta = await getIgnisMeta(vaultPath);
|
||||
|
||||
res.json({
|
||||
@@ -65,30 +65,7 @@ router.post("/create", async (req, res) => {
|
||||
recursive: false,
|
||||
});
|
||||
|
||||
// Install ignis-bridge plugin
|
||||
const pluginDir = path.join(
|
||||
vaultPath,
|
||||
".obsidian",
|
||||
"plugins",
|
||||
"ignis-bridge",
|
||||
);
|
||||
await fs.promises.mkdir(pluginDir, { recursive: true });
|
||||
|
||||
const pluginSrcDir = path.join(__dirname, "..", "..", "plugin");
|
||||
await fs.promises.copyFile(
|
||||
path.join(pluginSrcDir, "manifest.json"),
|
||||
path.join(pluginDir, "manifest.json"),
|
||||
);
|
||||
await fs.promises.copyFile(
|
||||
path.join(pluginSrcDir, "main.js"),
|
||||
path.join(pluginDir, "main.js"),
|
||||
);
|
||||
|
||||
// Enable the plugin
|
||||
await fs.promises.writeFile(
|
||||
path.join(vaultPath, ".obsidian", "community-plugins.json"),
|
||||
JSON.stringify(["ignis-bridge"]),
|
||||
);
|
||||
await installBridgePlugin(vaultPath);
|
||||
|
||||
config.refreshVaults();
|
||||
|
||||
@@ -182,7 +159,7 @@ router.post("/install-plugin", async (req, res) => {
|
||||
return res.json({ ok: true, prompted: true });
|
||||
} else {
|
||||
// User wants to install the plugin
|
||||
const installed = await installPluginInVault(vaultPath);
|
||||
const installed = await installBridgePlugin(vaultPath);
|
||||
|
||||
meta.pluginPrompted = true;
|
||||
await setIgnisMeta(vaultPath, meta);
|
||||
|
||||
Reference in New Issue
Block a user