web3.db-fileconnector
Version:
GraphQL System Built on web3 technologies. Web3.DB is an open database built on top of web3 technologies. It is a decentralized, open-source database that allows users to store and query data in a secure and efficient manner. The system is designed to be
318 lines (270 loc) • 10 kB
JavaScript
import { adminDidAuthMiddleware } from "../../../middleware/didAuthMiddleware.js";
import fs from "fs";
import path from "path";
import { v4 as uuidv4 } from "uuid";
import {
getOrbisDBSettings,
updateOrAddPlugin,
updateOrbisDBSettings,
} from "../../../utils/helpers.js";
import {
installPlugin,
loadPlugin,
loadPlugins,
} from "../../../utils/plugins.js";
import { __dirname } from "../../../utils/helpers.js";
import logger from "../../../logger/index.js";
/** prefixed with /api/plugins/ */
export default async function (server, opts) {
/** List all plugins */
server.get("/", async (req, res) => {
try {
const plugins = await loadPlugins();
return {
plugins,
};
} catch (e) {
return res.internalServerError(`Error loading plugins: ${e.message}`);
}
});
server.get("/readme/:pluginId", (req, res) => {
const pluginId = req.params.pluginId;
const readmePath = path.join(
__dirname,
"../plugins",
pluginId,
"README.md"
);
fs.readFile(readmePath, "utf8", (err, data) => {
if (err) {
if (err.code === "ENOENT") {
console.log(
"README file not found for " + pluginId + " / path:" + readmePath
);
return res.status(404).send();
} else {
return res.status(500).send("Server error");
}
}
res.send(data);
});
});
/** Authenticated scope */
await server.register(async function (server) {
await server.addHook("onRequest", adminDidAuthMiddleware);
/** Will return the details for a specific plugin (not uuid but plugin like "evm-blockchain-listener") */
server.get("/:plugin_id", async (req, res) => {
const { plugin_id } = req.params;
try {
const plugins = await loadPlugins(); // This loads all available plugins
const plugin = plugins.find((p) => p.id === plugin_id); // Find the plugin with the corresponding id
// If no plugin matches the provided id, send an appropriate response
if (!plugin) {
return res.notFound(`Plugin with id "${plugin_id}" not found.`);
}
return {
plugin,
};
} catch (error) {
logger.error(error);
return res.internalServerError(
`Internal server error while loading plugin ${plugin_id}.`
);
}
});
/** Will return the dynamic variables for a plugin */
server.get("/:plugin_uuid/dynamic-variables", async (req, res) => {
const { plugin_uuid } = req.params;
try {
const pluginDetails = await loadPlugin(plugin_uuid);
// If no plugin matches the provided id, send an appropriate response
if (!pluginDetails) {
return res.notFound(
`Plugin instance with id "${plugin_uuid}" not found.`
);
}
// Retrieve plugin dynamic variables
if (pluginDetails.getDynamicVariables) {
let { results: dynamic_variables } =
await pluginDetails.getDynamicVariables();
return {
dynamic_variables,
};
} else {
return [];
}
} catch (error) {
logger.error(error);
return res.internalServerError(
`Internal server error while loading plugin ${plugin_uuid}.`
);
}
});
// Install a plugin
server.post("/", async (req, res) => {
const { plugin } = req.body;
const adminDid = req.adminDid;
const { updatedSettings, pluginMessage, error } = await installPlugin(
plugin,
adminDid
);
if (error) {
return res.internalServerError(error);
}
return {
updatedSettings,
pluginMessage,
result: "New plugin added to the settings file.",
};
});
// Assign a plugin to a specified context
server.post("/:plugin_id/context", async (req, res) => {
const { plugin_id, path, variables, uuid } = req.body;
console.log("Enter assign-context with variables:", variables);
console.log("Enter assign-context with plugin_id:", plugin_id);
// Retrieve global settings
const globalSettings = getOrbisDBSettings();
const adminDid = req.adminDid;
try {
const settings = getOrbisDBSettings(adminDid);
// Find the plugin by plugin_id
const pluginIndex = settings.plugins?.findIndex(
(plugin) => plugin.plugin_id === plugin_id
);
if (pluginIndex === -1) {
throw new Error("Plugin not found");
}
// Update or add the context
let contextIndex = -1;
if (
settings.plugins &&
settings.plugins.length > 0 &&
settings.plugins[pluginIndex]?.contexts
) {
contextIndex = settings.plugins[pluginIndex].contexts.findIndex(
(c) => c.uuid === uuid
);
}
/** Add a new plugin instance if there wasn't any uuid passed, otherwise update the referenced context */
if (!uuid) {
logger.debug("Assigning plugin to a new context.");
let val = {
path: path,
slot: adminDid && globalSettings.is_shared ? adminDid : "global",
context: path[path.length - 1],
uuid: uuidv4(), // Assign a unique identifier to this plugin instance on install
};
if (variables && Object.keys(variables).length > 0) {
// Check if variables is not empty
val.variables = variables;
}
// Update settings
if (settings.plugins) {
if (settings.plugins[pluginIndex].contexts) {
settings.plugins[pluginIndex].contexts.push(val);
} else {
settings.plugins[pluginIndex].contexts = [val];
}
} else {
settings.plugins = [
{
contexts: [val],
},
];
}
} else {
// Update variable for existing plugin instance
settings.plugins[pluginIndex].contexts[contextIndex].variables =
variables;
// TODO: Update runtime variables for this plugin
let pluginsInstances = await global.indexingService.plugins;
for (let plugin of pluginsInstances) {
if (plugin.uuid === uuid) {
for (let key in variables) {
plugin[key] = variables[key];
}
logger.debug("Plugin " + uuid + " updated with:", variables);
}
}
}
// Write the updated settings back to the file
updateOrbisDBSettings(settings, adminDid);
// Restart plugins
global.indexingService.restartPlugins(uuid);
// Return results
return {
message: "Context updated successfully",
settings: settings,
};
} catch (err) {
logger.error(err);
return res.internalServerError("Failed to update settings.");
}
});
// Delete a plugin from a context
server.post("/delete", async (req, res) => {
const { plugin_id, uuid } = req.body;
logger.debug("Enter /delete plugin with uuid:", uuid);
// Retrieve global settings
const globalSettings = getOrbisDBSettings();
const adminDid = req.adminDid;
try {
const settings = getOrbisDBSettings(adminDid);
// TODO: Find plugin in settings.plugins using the plugin_id and uuid and delete it
const pluginIndex = settings.plugins.findIndex(
(plugin) => plugin.plugin_id === plugin_id
);
if (pluginIndex !== -1) {
const contextIndex = settings.plugins[pluginIndex].contexts.findIndex(
(context) => context.uuid === uuid
);
if (contextIndex !== -1) {
// Remove the context from the plugin
settings.plugins[pluginIndex].contexts.splice(contextIndex, 1);
logger.debug("settings:", settings);
// Write the updated settings back to the file
updateOrbisDBSettings(settings, adminDid);
// Reset plugins
global.indexingService.restartPlugins();
// Return results
return {
message: "Plugin removed successfully",
settings: settings,
};
} else {
return { message: "Context with given uuid not found" };
}
} else {
return { message: "Plugin with given plugin_id not found" };
}
} catch (err) {
logger.error(err);
return res.internalServerError(
"Failed to remove plugin and update settings."
);
}
});
});
/** Dynamic route to handle GET / POST / PUT / PATCH / DELETE routes exposed by installed plugins */
server.route({
method: ["GET", "POST", "PUT", "PATCH", "DELETE"],
exposesHeadRoute: false,
url: "/:plugin_uuid/routes/:plugin_route/:plugin_params?",
handler: async (req, res) => {
const reqMethod = req.method.toUpperCase();
const { plugin_uuid, plugin_route } = req.params;
// Retrieve the plugin installed using uuid
const plugin = await loadPlugin(plugin_uuid);
const { ROUTES } = await plugin.init();
if (reqMethod in ROUTES) {
const method = ROUTES[reqMethod][plugin_route];
if (method) {
return await method(req, res);
}
}
return res.notFound(
`Couldn't access route (${reqMethod}:${plugin_route}), make sure that the plugin (${plugin_uuid}) is properly installed and the route is exposed.`
);
},
});
}