2022-09-21 12:59:22 +00:00
|
|
|
import config from "../config.js";
|
2022-12-18 16:09:29 +00:00
|
|
|
import type { RPCServer } from "./rpc/server.js";
|
2022-11-26 07:59:07 +00:00
|
|
|
import { getRpcServer } from "./rpc/server.js";
|
2022-12-18 16:09:29 +00:00
|
|
|
import type { Plugin, RPCMethod } from "@lumeweb/relay-types";
|
2022-08-27 01:52:19 +00:00
|
|
|
import slugify from "slugify";
|
2022-08-28 04:26:24 +00:00
|
|
|
import * as fs from "fs";
|
|
|
|
import path from "path";
|
2022-12-19 16:47:09 +00:00
|
|
|
import type { Logger } from "pino";
|
2022-12-18 16:09:29 +00:00
|
|
|
|
2022-12-19 17:08:33 +00:00
|
|
|
import { getHDKey, getSeed } from "../lib/seed.js";
|
2022-12-18 16:09:29 +00:00
|
|
|
import type Config from "@lumeweb/cfg";
|
|
|
|
import EventEmitter2 from "eventemitter2";
|
2022-12-19 16:47:09 +00:00
|
|
|
import log from "../log.js";
|
2022-12-30 06:00:45 +00:00
|
|
|
import {
|
|
|
|
get as getSwarm,
|
|
|
|
getProtocolManager,
|
|
|
|
ProtocolManager,
|
|
|
|
} from "./swarm.js";
|
2022-12-19 17:17:33 +00:00
|
|
|
import { get as getSSl, SSLManager } from "./ssl.js";
|
2022-12-19 17:08:33 +00:00
|
|
|
import type { HDKey } from "micro-ed25519-hdkey";
|
2023-01-08 04:02:13 +00:00
|
|
|
import corePlugins from "../plugins";
|
2023-01-08 04:34:06 +00:00
|
|
|
import Util from "./plugin/util";
|
2022-08-27 01:52:19 +00:00
|
|
|
|
2022-12-18 16:09:29 +00:00
|
|
|
let pluginAPIManager: PluginAPIManager;
|
|
|
|
let pluginAPI: PluginAPI;
|
2022-08-27 01:52:19 +00:00
|
|
|
|
|
|
|
const sanitizeName = (name: string) =>
|
|
|
|
slugify(name, { lower: true, strict: true });
|
|
|
|
|
2022-12-18 16:09:29 +00:00
|
|
|
class PluginAPI extends EventEmitter2 {
|
|
|
|
private _server: RPCServer;
|
|
|
|
|
|
|
|
constructor({
|
|
|
|
config,
|
|
|
|
server,
|
2022-12-18 20:01:27 +00:00
|
|
|
swarm,
|
2022-12-18 16:09:29 +00:00
|
|
|
}: {
|
|
|
|
config: Config;
|
|
|
|
server: RPCServer;
|
2022-12-18 20:01:27 +00:00
|
|
|
swarm: any;
|
2022-12-18 16:09:29 +00:00
|
|
|
}) {
|
|
|
|
super({
|
|
|
|
wildcard: true,
|
|
|
|
verboseMemoryLeak: true,
|
|
|
|
maxListeners: 0,
|
|
|
|
});
|
|
|
|
this._config = config;
|
|
|
|
this._server = server;
|
2022-12-18 20:01:27 +00:00
|
|
|
this._swarm = swarm;
|
|
|
|
}
|
|
|
|
|
2023-01-08 04:34:06 +00:00
|
|
|
private _util: Util = new Util();
|
|
|
|
|
|
|
|
get util(): Util {
|
|
|
|
return this._util;
|
|
|
|
}
|
|
|
|
|
2022-12-18 20:01:27 +00:00
|
|
|
private _swarm: any;
|
|
|
|
|
|
|
|
get swarm(): any {
|
|
|
|
return this._swarm;
|
2022-12-18 16:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private _config: Config;
|
|
|
|
|
|
|
|
get config(): Config {
|
|
|
|
return this._config;
|
|
|
|
}
|
|
|
|
|
2022-12-21 20:12:20 +00:00
|
|
|
get pluginConfig(): Config {
|
|
|
|
throw new Error("not implemented and should not be called");
|
|
|
|
}
|
|
|
|
|
2022-12-18 16:09:29 +00:00
|
|
|
get logger(): Logger {
|
2023-01-13 22:46:53 +00:00
|
|
|
throw new Error("not implemented and should not be called");
|
2022-12-18 16:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get rpcServer(): RPCServer {
|
|
|
|
return this._server;
|
|
|
|
}
|
|
|
|
|
2022-12-18 20:01:27 +00:00
|
|
|
get seed(): Uint8Array {
|
|
|
|
return getSeed();
|
|
|
|
}
|
|
|
|
|
2022-12-19 17:08:33 +00:00
|
|
|
get identity(): HDKey {
|
|
|
|
return getHDKey();
|
|
|
|
}
|
|
|
|
|
2022-12-19 17:17:33 +00:00
|
|
|
get ssl(): SSLManager {
|
2022-12-19 17:08:33 +00:00
|
|
|
return getSSl();
|
|
|
|
}
|
|
|
|
|
2022-12-30 06:00:45 +00:00
|
|
|
get protocols(): ProtocolManager {
|
|
|
|
return getProtocolManager();
|
|
|
|
}
|
|
|
|
|
2022-12-18 16:09:29 +00:00
|
|
|
public loadPlugin(
|
|
|
|
moduleName: string
|
|
|
|
): (moduleName: string) => Promise<Plugin> {
|
|
|
|
return getPluginAPIManager().loadPlugin;
|
|
|
|
}
|
|
|
|
|
|
|
|
registerMethod(methodName: string, method: RPCMethod): void {
|
|
|
|
throw new Error("not implemented and should not be called");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getPluginAPI(): PluginAPI {
|
|
|
|
if (!pluginAPI) {
|
2022-12-18 20:01:27 +00:00
|
|
|
pluginAPI = new PluginAPI({
|
|
|
|
config,
|
|
|
|
server: getRpcServer(),
|
|
|
|
swarm: getSwarm(),
|
|
|
|
});
|
2022-12-18 16:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return pluginAPI as PluginAPI;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class PluginAPIManager {
|
2022-08-27 01:52:19 +00:00
|
|
|
private registeredPlugins: Map<string, Plugin> = new Map<string, Plugin>();
|
|
|
|
|
|
|
|
public async loadPlugin(moduleName: string): Promise<Plugin> {
|
|
|
|
moduleName = sanitizeName(moduleName);
|
|
|
|
|
|
|
|
if (this.registeredPlugins.has(moduleName)) {
|
|
|
|
return this.registeredPlugins.get(moduleName) as Plugin;
|
|
|
|
}
|
|
|
|
|
2022-08-28 04:26:24 +00:00
|
|
|
const paths = [];
|
|
|
|
for (const modulePath of [`${moduleName}.js`, `${moduleName}.mjs`]) {
|
2022-09-21 20:11:02 +00:00
|
|
|
const fullPath = path.join(config.get("plugindir"), modulePath);
|
2022-08-28 04:26:24 +00:00
|
|
|
if (fs.existsSync(fullPath)) {
|
|
|
|
paths.push(fullPath);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-08-27 01:52:19 +00:00
|
|
|
|
|
|
|
if (!paths.length) {
|
|
|
|
throw new Error(`Plugin ${moduleName} does not exist`);
|
|
|
|
}
|
|
|
|
|
|
|
|
let plugin: Plugin;
|
|
|
|
try {
|
2022-12-13 12:23:18 +00:00
|
|
|
plugin = require(paths.shift() as string) as Plugin;
|
2022-08-27 01:52:19 +00:00
|
|
|
} catch (e) {
|
|
|
|
throw e;
|
|
|
|
}
|
2022-11-26 07:59:07 +00:00
|
|
|
|
2023-01-08 19:13:11 +00:00
|
|
|
log.debug("Loaded plugin %s", moduleName);
|
|
|
|
|
2022-11-26 07:59:07 +00:00
|
|
|
return this.loadPluginInstance(plugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async loadPluginInstance(plugin: Plugin): Promise<Plugin> {
|
2022-08-28 04:27:20 +00:00
|
|
|
if ("default" in plugin) {
|
|
|
|
plugin = plugin?.default as Plugin;
|
|
|
|
}
|
2022-08-27 01:52:19 +00:00
|
|
|
|
|
|
|
plugin.name = sanitizeName(plugin.name);
|
|
|
|
|
|
|
|
this.registeredPlugins.set(plugin.name, plugin);
|
|
|
|
|
|
|
|
try {
|
2022-12-18 16:09:29 +00:00
|
|
|
plugin.plugin(
|
|
|
|
// @ts-ignore
|
|
|
|
new Proxy<PluginAPI>(getPluginAPI(), {
|
|
|
|
get(target: PluginAPI, prop: string): any {
|
|
|
|
if (prop === "registerMethod") {
|
|
|
|
return (methodName: string, method: RPCMethod): void => {
|
|
|
|
return getRpcServer().registerMethod(
|
|
|
|
plugin.name,
|
|
|
|
methodName,
|
|
|
|
method
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-21 20:12:20 +00:00
|
|
|
if (prop === "pluginConfig") {
|
|
|
|
return new Proxy<Config>(config, {
|
|
|
|
get(target: Config, prop: string): any {
|
|
|
|
if (prop === "set") {
|
|
|
|
return (key: string, value: any): void => {
|
|
|
|
target.set(`plugin.${plugin.name}.${key}`, value);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prop === "get") {
|
|
|
|
return (key: string, fallback = null): any => {
|
|
|
|
return target.get(
|
|
|
|
`plugin.${plugin.name}.${key}`,
|
|
|
|
fallback
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prop === "has") {
|
|
|
|
return (key: string): any => {
|
|
|
|
return target.has(`plugin.${plugin.name}.${key}`);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-22 14:54:05 +00:00
|
|
|
return (target as any)[prop];
|
2022-12-21 20:12:20 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-01-13 22:46:53 +00:00
|
|
|
if (prop === "logger") {
|
2023-01-13 22:53:54 +00:00
|
|
|
return log.child({ plugin: plugin.name });
|
2023-01-13 22:46:53 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 16:09:29 +00:00
|
|
|
return (target as any)[prop];
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
2022-08-27 01:52:19 +00:00
|
|
|
} catch (e) {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
|
2023-01-08 19:13:11 +00:00
|
|
|
log.debug("Initialized plugin %s", plugin.name);
|
|
|
|
|
2022-08-27 01:52:19 +00:00
|
|
|
return plugin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-18 16:09:29 +00:00
|
|
|
export function getPluginAPIManager(): PluginAPIManager {
|
|
|
|
if (!pluginAPIManager) {
|
|
|
|
pluginAPIManager = new PluginAPIManager();
|
2022-08-27 01:52:19 +00:00
|
|
|
}
|
|
|
|
|
2022-12-18 16:09:29 +00:00
|
|
|
return pluginAPIManager as PluginAPIManager;
|
2022-08-27 01:52:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export async function loadPlugins() {
|
2022-12-18 16:42:50 +00:00
|
|
|
const apiManager = getPluginAPIManager();
|
2022-11-26 07:59:07 +00:00
|
|
|
|
2023-01-08 04:02:13 +00:00
|
|
|
for (const plugin of corePlugins) {
|
|
|
|
await apiManager.loadPluginInstance(plugin);
|
|
|
|
}
|
2022-11-26 07:59:07 +00:00
|
|
|
|
2022-09-21 20:03:12 +00:00
|
|
|
for (const plugin of [...new Set(config.array("plugins", []))] as []) {
|
2022-12-18 16:42:50 +00:00
|
|
|
await apiManager.loadPlugin(plugin);
|
2022-08-27 01:52:19 +00:00
|
|
|
}
|
2022-12-18 16:42:50 +00:00
|
|
|
|
|
|
|
getPluginAPI().emit("core.pluginsLoaded");
|
2022-08-27 01:52:19 +00:00
|
|
|
}
|