Compare commits

...

2 Commits

7 changed files with 15 additions and 179 deletions

View File

@ -19,21 +19,20 @@ switch (os.platform()) {
case "linux": case "linux":
default: default:
configDir = "/etc/lumeweb/relay/config.d"; configDir = "/etc/lumeweb/relay/conf.d";
break; break;
} }
config.inject({ config.inject({
configDir, "core.confdir": configDir,
port: 8080, "core.port": 8080,
logLevel: "info", "core.loglevel": "info",
pluginDir: path.resolve(configDir, "..", "plugins"), "core.plugindir": path.resolve(configDir, "..", "plugins"),
cache: true,
}); });
config.load(); config.load();
configDir = config.str("configdir"); configDir = config.str("core.confdir");
if (fs.existsSync(configDir)) { if (fs.existsSync(configDir)) {
try { try {
@ -45,6 +44,6 @@ if (fs.existsSync(configDir)) {
config.load(); config.load();
log.level = config.get("loglevel"); log.level = config.get("core.loglevel");
export default config; export default config;

View File

@ -8,7 +8,7 @@ import b4a from "b4a";
const BIP44_PATH = "m/44'/1627'/0'/0'/0'"; const BIP44_PATH = "m/44'/1627'/0'/0'/0'";
export function getSeed() { export function getSeed() {
const seed = config.str("seed"); const seed = config.str("core.seed");
let valid = bip39.validateMnemonic(seed, wordlist); let valid = bip39.validateMnemonic(seed, wordlist);
if (!valid) { if (!valid) {

View File

@ -130,7 +130,7 @@ export class PluginAPIManager {
const paths = []; const paths = [];
for (const modulePath of [`${moduleName}.js`, `${moduleName}.mjs`]) { for (const modulePath of [`${moduleName}.js`, `${moduleName}.mjs`]) {
const fullPath = path.join(config.get("plugindir"), modulePath); const fullPath = path.join(config.get("core.plugindir"), modulePath);
if (fs.existsSync(fullPath)) { if (fs.existsSync(fullPath)) {
paths.push(fullPath); paths.push(fullPath);
break; break;
@ -239,7 +239,7 @@ export async function loadPlugins() {
await apiManager.loadPluginInstance(plugin); await apiManager.loadPluginInstance(plugin);
} }
for (const plugin of [...new Set(config.array("plugins", []))] as []) { for (const plugin of [...new Set(config.array("core.plugins", []))] as []) {
await apiManager.loadPlugin(plugin); await apiManager.loadPlugin(plugin);
} }

View File

@ -38,5 +38,5 @@ export async function start() {
relay(dht, new Stream(false, connection.socket)); relay(dht, new Stream(false, connection.socket));
}); });
await relayServer.listen({ port: config.uint("port"), host: "0.0.0.0" }); await relayServer.listen({ port: config.uint("core.port"), host: "0.0.0.0" });
} }

View File

@ -1,119 +0,0 @@
import EventEmitter from "events";
import DHTCache from "@lumeweb/dht-cache";
import { RPCCacheItem, RPCRequest, RPCResponse } from "@lumeweb/relay-types";
import { get as getSwarm } from "../swarm";
import { RPCServer } from "./server";
// @ts-ignore
import jsonStringify from "json-stringify-deterministic";
// @ts-ignore
import crypto from "hypercore-crypto";
import NodeCache from "node-cache";
import log from "../../log.js";
export class RPCCache extends EventEmitter {
private server: RPCServer;
constructor(server: RPCServer) {
super();
this.server = server;
this._swarm = getSwarm();
this._dhtCache = new DHTCache(this._swarm, {
protocol: "lumeweb.rpccache",
logger: log.child({ module: "dht-cache" }),
});
this._data.on("del", (key: string) => {
try {
this.deleteItem(key);
} catch {}
});
}
private _dhtCache?: DHTCache;
get dhtCache(): DHTCache {
return this._dhtCache as DHTCache;
}
private _swarm?: any;
get swarm(): any {
return this._swarm;
}
private _data: NodeCache = new NodeCache({ stdTTL: 60 * 60 * 24 });
get data(): NodeCache {
return this._data;
}
public signResponse(item: RPCCacheItem): string {
const field = item.value.signedField || "data";
const updated = item.value.updated;
// @ts-ignore
let json = item.value[field];
if (typeof json !== "string") {
json = jsonStringify(json);
}
return this.server.signData(`${updated}${json}`);
}
public verifyResponse(pubkey: Buffer, item: RPCCacheItem): boolean | Buffer {
const field = item.value.signedField || "data";
const updated = item.value.updated;
// @ts-ignore
let json = item.value[field];
if (typeof json !== "string") {
json = jsonStringify(json);
}
try {
if (
!crypto.verify(
Buffer.from(`${updated}${json}`),
Buffer.from(item?.signature as string, "hex"),
pubkey
)
) {
return false;
}
} catch {
return false;
}
return true;
}
public addItem(query: RPCRequest, response: RPCResponse) {
const queryHash = RPCServer.hashQuery(query);
const clonedResponse = { ...response };
clonedResponse.updated = Date.now();
const item = {
value: clonedResponse,
signature: "",
};
item.signature = this.signResponse(item);
this._dhtCache?.addItem(queryHash);
this._data.set(queryHash, item);
}
public deleteItem(queryHash: string): boolean {
const cache = this._dhtCache?.cache;
if (!cache?.includes(queryHash)) {
throw Error("item does not exist");
}
this._dhtCache?.removeItem(queryHash);
this._data.del(queryHash);
return true;
}
}

View File

@ -8,17 +8,15 @@ import EventEmitter from "events";
// @ts-ignore // @ts-ignore
import ProtomuxRPC from "protomux-rpc"; import ProtomuxRPC from "protomux-rpc";
import b4a from "b4a"; import b4a from "b4a";
import { SecretStream } from "../swarm"; import { get as getSwarm, SecretStream } from "../swarm";
// @ts-ignore // @ts-ignore
import c from "compact-encoding"; import c from "compact-encoding";
// @ts-ignore // @ts-ignore
import crypto from "hypercore-crypto"; import crypto from "hypercore-crypto";
// @ts-ignore // @ts-ignore
import { Mutex } from "async-mutex"; import { Mutex } from "async-mutex";
import { RPCCache } from "./cache";
// @ts-ignore // @ts-ignore
import jsonStringify from "json-stringify-deterministic"; import jsonStringify from "json-stringify-deterministic";
import config from "../../config";
const sodium = require("sodium-universal"); const sodium = require("sodium-universal");
let server: RPCServer; let server: RPCServer;
@ -55,19 +53,6 @@ export class RPCServer extends EventEmitter {
>(); >();
private pendingRequests: Map<string, Mutex> = new Map<string, Mutex>(); private pendingRequests: Map<string, Mutex> = new Map<string, Mutex>();
private _cache?: RPCCache;
constructor() {
super();
if (config.bool("cache")) {
this._cache = new RPCCache(this);
}
}
get cache(): RPCCache | undefined {
return this._cache;
}
public static hashQuery(query: RPCRequest): string { public static hashQuery(query: RPCRequest): string {
const clonedQuery: RPCRequest = { const clonedQuery: RPCRequest = {
module: query.module, module: query.module,
@ -145,7 +130,7 @@ export class RPCServer extends EventEmitter {
} }
return crypto return crypto
.sign(Buffer.from(raw), this._cache?.swarm.keyPair.secretKey) .sign(Buffer.from(raw), getSwarm().keyPair.secretKey)
.toString("hex"); .toString("hex");
} }
@ -156,13 +141,6 @@ export class RPCServer extends EventEmitter {
return lockedRequest; return lockedRequest;
} }
let cachedRequest = this.getCachedRequest(request) as RPCCacheItem;
if (cachedRequest) {
this.getRequestLock(request)?.release();
return { ...cachedRequest.value, signature: cachedRequest.signature };
}
let method = this.getMethodByRequest(request); let method = this.getMethodByRequest(request);
let ret; let ret;
@ -208,30 +186,11 @@ export class RPCServer extends EventEmitter {
}; };
} }
method = method as RPCMethod;
if (config.bool("cache") && method.cacheable) {
this.cache?.addItem(request, rpcResult);
}
this.getRequestLock(request)?.release(); this.getRequestLock(request)?.release();
return rpcResult; return rpcResult;
} }
private getCachedRequest(request: RPCRequest): RPCCacheItem | boolean {
if (!config.bool("cache")) {
return false;
}
const req = RPCServer.hashQuery(request);
if (this._cache?.data.has(req)) {
return this._cache?.data.get<RPCCacheItem>(req) as RPCCacheItem;
}
return false;
}
private getMethodByRequest(request: RPCRequest): Error | RPCMethod { private getMethodByRequest(request: RPCRequest): Error | RPCMethod {
return this.getMethod(request.module, request.method); return this.getMethod(request.module, request.method);
} }
@ -269,9 +228,6 @@ export class RPCServer extends EventEmitter {
if (lock.isLocked()) { if (lock.isLocked()) {
await lock.waitForUnlock(); await lock.waitForUnlock();
if (this._cache?.data.has(reqId)) {
return this._cache?.data.get<RPCCacheItem>(reqId) as RPCCacheItem;
}
} }
await lock.acquire(); await lock.acquire();

View File

@ -51,7 +51,7 @@ export class SSLManager {
} }
get enabled() { get enabled() {
return config.bool("ssl") && this._renewHandler; return config.bool("core.ssl") && this._renewHandler;
} }
} }
@ -59,7 +59,7 @@ let sslManager: SSLManager;
export function get(): SSLManager { export function get(): SSLManager {
if (!sslManager) { if (!sslManager) {
sslManager = new SSLManager(config.get("domain")); sslManager = new SSLManager(config.get("core.domain"));
} }
return sslManager; return sslManager;