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 compression = require("compression");
|
||||||
const config = require("./config");
|
const config = require("./config");
|
||||||
const { setupWebSocket } = require("./ws");
|
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_RED = "\x1b[31m";
|
||||||
const ANSI_YELLOW = "\x1b[33m";
|
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] Vault root: ${config.vaultRoot}`);
|
||||||
console.log(`[ignis] Vaults: ${Object.keys(config.vaults).join(", ")}`);
|
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 config = require("../config");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const {
|
const {
|
||||||
checkPluginInstalled,
|
isBridgePluginInstalled,
|
||||||
getIgnisMeta,
|
getIgnisMeta,
|
||||||
setIgnisMeta,
|
setIgnisMeta,
|
||||||
installPluginInVault,
|
installBridgePlugin,
|
||||||
} = require("../install-plugin");
|
} = require("../bridge-plugin");
|
||||||
|
|
||||||
const router = express.Router();
|
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 });
|
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);
|
const ignisMeta = await getIgnisMeta(vaultPath);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
@@ -65,30 +65,7 @@ router.post("/create", async (req, res) => {
|
|||||||
recursive: false,
|
recursive: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Install ignis-bridge plugin
|
await installBridgePlugin(vaultPath);
|
||||||
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"]),
|
|
||||||
);
|
|
||||||
|
|
||||||
config.refreshVaults();
|
config.refreshVaults();
|
||||||
|
|
||||||
@@ -182,7 +159,7 @@ router.post("/install-plugin", async (req, res) => {
|
|||||||
return res.json({ ok: true, prompted: true });
|
return res.json({ ok: true, prompted: true });
|
||||||
} else {
|
} else {
|
||||||
// User wants to install the plugin
|
// User wants to install the plugin
|
||||||
const installed = await installPluginInVault(vaultPath);
|
const installed = await installBridgePlugin(vaultPath);
|
||||||
|
|
||||||
meta.pluginPrompted = true;
|
meta.pluginPrompted = true;
|
||||||
await setIgnisMeta(vaultPath, meta);
|
await setIgnisMeta(vaultPath, meta);
|
||||||
|
|||||||
Reference in New Issue
Block a user