*Move dns and ssl control to plugin apis
*Add files plugin api *Add logger to api *Add seed getter to api *Add app router to api
This commit is contained in:
parent
8e881a7dc1
commit
08fdc88874
|
@ -33,6 +33,7 @@ config.inject({
|
||||||
logLevel: "info",
|
logLevel: "info",
|
||||||
pluginFolder: path.join(configDir, "plugins"),
|
pluginFolder: path.join(configDir, "plugins"),
|
||||||
plugins: ["core"],
|
plugins: ["core"],
|
||||||
|
ssl: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
config.load({
|
config.load({
|
||||||
|
|
60
src/dns.ts
60
src/dns.ts
|
@ -1,14 +1,11 @@
|
||||||
import cron from "node-cron";
|
import cron from "node-cron";
|
||||||
import { get as getDHT } from "./dht.js";
|
import { get as getDHT } from "./dht.js";
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
import { Parser } from "xml2js";
|
|
||||||
import { URL } from "url";
|
|
||||||
import { pack } from "msgpackr";
|
import { pack } from "msgpackr";
|
||||||
import config from "./config.js";
|
import config from "./config.js";
|
||||||
import { errorExit } from "./error.js";
|
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import { createHash } from "crypto";
|
|
||||||
import { dynImport } from "./util.js";
|
import { dynImport } from "./util.js";
|
||||||
|
import type { DnsProvider } from "@lumeweb/relay-types";
|
||||||
|
|
||||||
let activeIp: string;
|
let activeIp: string;
|
||||||
let fetch: typeof import("node-fetch").default;
|
let fetch: typeof import("node-fetch").default;
|
||||||
|
@ -17,6 +14,12 @@ let hashDataKey: typeof import("@lumeweb/kernel-utils").hashDataKey;
|
||||||
|
|
||||||
const REGISTRY_NODE_KEY = "lumeweb-dht-node";
|
const REGISTRY_NODE_KEY = "lumeweb-dht-node";
|
||||||
|
|
||||||
|
let dnsProvider: DnsProvider = async (ip) => {};
|
||||||
|
|
||||||
|
export function setDnsProvider(provider: DnsProvider) {
|
||||||
|
dnsProvider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
async function ipUpdate() {
|
async function ipUpdate() {
|
||||||
let currentIp = await getCurrentIp();
|
let currentIp = await getCurrentIp();
|
||||||
|
|
||||||
|
@ -24,11 +27,7 @@ async function ipUpdate() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let domain = await getDomainInfo();
|
await dnsProvider(currentIp);
|
||||||
|
|
||||||
await fetch(domain.url[0].toString());
|
|
||||||
|
|
||||||
activeIp = domain.address[0];
|
|
||||||
|
|
||||||
log.info(`Updated DynDNS hostname ${config.str("domain")} to ${activeIp}`);
|
log.info(`Updated DynDNS hostname ${config.str("domain")} to ${activeIp}`);
|
||||||
}
|
}
|
||||||
|
@ -57,49 +56,6 @@ export async function start() {
|
||||||
cron.schedule("0 * * * *", ipUpdate);
|
cron.schedule("0 * * * *", ipUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getDomainInfo() {
|
|
||||||
const relayDomain = config.str("domain");
|
|
||||||
const parser = new Parser();
|
|
||||||
|
|
||||||
const url = new URL("https://freedns.afraid.org/api/");
|
|
||||||
|
|
||||||
const params = url.searchParams;
|
|
||||||
|
|
||||||
params.append("action", "getdyndns");
|
|
||||||
params.append("v", "2");
|
|
||||||
params.append("style", "xml");
|
|
||||||
|
|
||||||
const hash = createHash("sha1");
|
|
||||||
hash.update(
|
|
||||||
`${config.str("afraid-username")}|${config.str("afraid-password")}`
|
|
||||||
);
|
|
||||||
|
|
||||||
params.append("sha", hash.digest().toString("hex"));
|
|
||||||
|
|
||||||
const response = await (await fetch(url.toString())).text();
|
|
||||||
|
|
||||||
if (/could not authenticate/i.test(response)) {
|
|
||||||
errorExit("Failed to authenticate to afraid.org");
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = await parser.parseStringPromise(response);
|
|
||||||
|
|
||||||
let domain = null;
|
|
||||||
|
|
||||||
for (const item of json.xml.item) {
|
|
||||||
if (item.host[0] === relayDomain) {
|
|
||||||
domain = item;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!domain) {
|
|
||||||
errorExit(`Domain ${relayDomain} not found in afraid.org account`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCurrentIp(): Promise<string> {
|
async function getCurrentIp(): Promise<string> {
|
||||||
return await (await fetch("http://ip1.dynupdate.no-ip.com/")).text();
|
return await (await fetch("http://ip1.dynupdate.no-ip.com/")).text();
|
||||||
}
|
}
|
||||||
|
|
40
src/file.ts
40
src/file.ts
|
@ -1,45 +1,15 @@
|
||||||
import type { Ed25519Keypair, Err, progressiveFetchResult } from "libskynet";
|
import type { Err, progressiveFetchResult } from "libskynet";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { SkynetClient } from "@skynetlabs/skynet-nodejs";
|
import { SkynetClient } from "@skynetlabs/skynet-nodejs";
|
||||||
import { dynImport } from "./util.js";
|
import { dynImport } from "./util.js";
|
||||||
|
import type {
|
||||||
|
IndependentFileSmall,
|
||||||
|
IndependentFileSmallMetadata,
|
||||||
|
} from "@lumeweb/relay-types";
|
||||||
|
|
||||||
const ERR_EXISTS = "exists";
|
|
||||||
const ERR_NOT_EXISTS = "DNE";
|
const ERR_NOT_EXISTS = "DNE";
|
||||||
const STD_FILENAME = "file";
|
const STD_FILENAME = "file";
|
||||||
|
|
||||||
type OverwriteDataFn = (newData: Uint8Array) => Promise<Err>;
|
|
||||||
|
|
||||||
type ReadDataFn = () => Promise<[Uint8Array, Err]>;
|
|
||||||
|
|
||||||
export interface IndependentFileSmallMetadata {
|
|
||||||
largestHistoricSize: bigint;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IndependentFileSmall {
|
|
||||||
dataKey: Uint8Array;
|
|
||||||
fileData: Uint8Array;
|
|
||||||
inode: string;
|
|
||||||
keypair: Ed25519Keypair;
|
|
||||||
metadata: IndependentFileSmallMetadata;
|
|
||||||
revision: bigint;
|
|
||||||
seed: Uint8Array;
|
|
||||||
|
|
||||||
skylink: string;
|
|
||||||
viewKey: string;
|
|
||||||
|
|
||||||
overwriteData: OverwriteDataFn;
|
|
||||||
|
|
||||||
readData: ReadDataFn;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IndependentFileSmallViewer {
|
|
||||||
fileData: Uint8Array;
|
|
||||||
skylink: string;
|
|
||||||
viewKey: string;
|
|
||||||
|
|
||||||
readData: ReadDataFn;
|
|
||||||
}
|
|
||||||
|
|
||||||
let addContextToErr: typeof import("libskynet").addContextToErr,
|
let addContextToErr: typeof import("libskynet").addContextToErr,
|
||||||
blake2b: typeof import("libskynet").blake2b,
|
blake2b: typeof import("libskynet").blake2b,
|
||||||
bufToHex: typeof import("libskynet").bufToHex,
|
bufToHex: typeof import("libskynet").bufToHex,
|
||||||
|
|
|
@ -4,6 +4,23 @@ import type { PluginAPI, RPCMethod, Plugin } from "@lumeweb/relay-types";
|
||||||
import slugify from "slugify";
|
import slugify from "slugify";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import {
|
||||||
|
getSavedSsl,
|
||||||
|
getSsl,
|
||||||
|
getSslContext,
|
||||||
|
saveSSl,
|
||||||
|
setSsl,
|
||||||
|
setSslContext,
|
||||||
|
} from "./ssl.js";
|
||||||
|
import log from "loglevel";
|
||||||
|
import { getSeed } from "./util.js";
|
||||||
|
import { getRouter, resetRouter, setRouter } from "./relay";
|
||||||
|
import {
|
||||||
|
createIndependentFileSmall,
|
||||||
|
openIndependentFileSmall,
|
||||||
|
overwriteIndependentFileSmall,
|
||||||
|
} from "./file";
|
||||||
|
import { setDnsProvider } from "./dns";
|
||||||
|
|
||||||
let pluginApi: PluginApiManager;
|
let pluginApi: PluginApiManager;
|
||||||
|
|
||||||
|
@ -64,6 +81,30 @@ export class PluginApiManager {
|
||||||
},
|
},
|
||||||
loadPlugin: getPluginAPI().loadPlugin.bind(getPluginAPI()),
|
loadPlugin: getPluginAPI().loadPlugin.bind(getPluginAPI()),
|
||||||
getMethods: getRpcServer().getMethods.bind(getRpcServer()),
|
getMethods: getRpcServer().getMethods.bind(getRpcServer()),
|
||||||
|
|
||||||
|
ssl: {
|
||||||
|
setContext: setSslContext,
|
||||||
|
getContext: getSslContext,
|
||||||
|
getSaved: getSavedSsl,
|
||||||
|
set: setSsl,
|
||||||
|
get: getSsl,
|
||||||
|
save: saveSSl,
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
createIndependentFileSmall,
|
||||||
|
openIndependentFileSmall,
|
||||||
|
overwriteIndependentFileSmall,
|
||||||
|
},
|
||||||
|
dns: {
|
||||||
|
setProvider: setDnsProvider,
|
||||||
|
},
|
||||||
|
logger: log,
|
||||||
|
getSeed,
|
||||||
|
appRouter: {
|
||||||
|
get: getRouter,
|
||||||
|
set: setRouter,
|
||||||
|
reset: resetRouter,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
267
src/relay.ts
267
src/relay.ts
|
@ -8,44 +8,31 @@ import express, { Express } from "express";
|
||||||
import config from "./config.js";
|
import config from "./config.js";
|
||||||
import * as http from "http";
|
import * as http from "http";
|
||||||
import * as https from "https";
|
import * as https from "https";
|
||||||
import * as tls from "tls";
|
|
||||||
import * as acme from "acme-client";
|
|
||||||
import { Buffer } from "buffer";
|
|
||||||
import { intervalToDuration } from "date-fns";
|
|
||||||
import cron from "node-cron";
|
|
||||||
import { get as getDHT } from "./dht.js";
|
import { get as getDHT } from "./dht.js";
|
||||||
import WS from "ws";
|
import WS from "ws";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import {
|
|
||||||
createIndependentFileSmall,
|
|
||||||
IndependentFileSmall,
|
|
||||||
openIndependentFileSmall,
|
|
||||||
overwriteIndependentFileSmall,
|
|
||||||
} from "./file.js";
|
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import { AddressInfo } from "net";
|
import { AddressInfo } from "net";
|
||||||
import { sprintf } from "sprintf-js";
|
|
||||||
import { dynImport } from "./util.js";
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import promiseRetry from "promise-retry";
|
import promiseRetry from "promise-retry";
|
||||||
|
import { getSslContext } from "./ssl.js";
|
||||||
let sslCtx: tls.SecureContext = tls.createSecureContext();
|
|
||||||
const sslParams: tls.SecureContextOptions = { cert: "", key: "" };
|
|
||||||
|
|
||||||
let acmeClient: acme.Client;
|
|
||||||
|
|
||||||
let app: Express;
|
let app: Express;
|
||||||
let router = express.Router();
|
let router = express.Router();
|
||||||
|
|
||||||
let seedPhraseToSeed: typeof import("libskynet").seedPhraseToSeed;
|
export function getRouter(): express.Router {
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
|
||||||
const FILE_CERT_NAME = "/lumeweb/relay/ssl.crt";
|
export function setRouter(newRouter: express.Router): void {
|
||||||
const FILE_KEY_NAME = "/lumeweb/relay/ssl.key";
|
router = newRouter;
|
||||||
const FILE_ACCOUNT_KEY_NAME = "/lumeweb/relay/account.key";
|
}
|
||||||
|
|
||||||
|
export function resetRouter(): void {
|
||||||
|
setRouter(express.Router());
|
||||||
|
}
|
||||||
|
|
||||||
export async function start() {
|
export async function start() {
|
||||||
seedPhraseToSeed = (await dynImport("libskynet")).seedPhraseToSeed;
|
|
||||||
|
|
||||||
const relayPort = config.uint("port");
|
const relayPort = config.uint("port");
|
||||||
app = express();
|
app = express();
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
|
@ -82,23 +69,27 @@ export async function start() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await setupSSl(true);
|
let relayServer: https.Server | http.Server;
|
||||||
|
|
||||||
let httpsServer = https.createServer({
|
if (config.bool("ssl")) {
|
||||||
|
relayServer = https.createServer({
|
||||||
SNICallback(servername, cb) {
|
SNICallback(servername, cb) {
|
||||||
cb(null, sslCtx);
|
cb(null, getSslContext());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
relayServer = http.createServer();
|
||||||
|
}
|
||||||
|
|
||||||
let wsServer = new WS.Server({ server: httpsServer });
|
let wsServer = new WS.Server({ server: relayServer });
|
||||||
|
|
||||||
wsServer.on("connection", (socket: any) => {
|
wsServer.on("connection", (socket: any) => {
|
||||||
relay(dht, new Stream(false, socket));
|
relay(dht, new Stream(false, socket));
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
httpsServer.listen(relayPort, "0.0.0.0", function () {
|
relayServer.listen(relayPort, "0.0.0.0", function () {
|
||||||
const address = httpsServer.address() as AddressInfo;
|
const address = relayServer.address() as AddressInfo;
|
||||||
log.info(
|
log.info(
|
||||||
"DHT Relay Server started on ",
|
"DHT Relay Server started on ",
|
||||||
`${address.address}:${address.port}`
|
`${address.address}:${address.port}`
|
||||||
|
@ -106,220 +97,4 @@ export async function start() {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
cron.schedule("0 * * * *", setupSSl.bind(null, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setupSSl(bootup: boolean) {
|
|
||||||
let sslCert: IndependentFileSmall | boolean = false;
|
|
||||||
let sslKey: IndependentFileSmall | boolean = false;
|
|
||||||
let certInfo;
|
|
||||||
let exists = false;
|
|
||||||
let domainValid = false;
|
|
||||||
let dateValid = false;
|
|
||||||
let configDomain = config.str("domain");
|
|
||||||
|
|
||||||
let retryOptions = bootup ? {} : { retry: 0 };
|
|
||||||
|
|
||||||
try {
|
|
||||||
await promiseRetry(async (retry: any) => {
|
|
||||||
sslCert = await getSslCert();
|
|
||||||
if (!sslCert) {
|
|
||||||
retry();
|
|
||||||
}
|
|
||||||
}, retryOptions);
|
|
||||||
|
|
||||||
await promiseRetry(async (retry: any) => {
|
|
||||||
sslKey = await getSslKey();
|
|
||||||
if (!sslKey) {
|
|
||||||
retry();
|
|
||||||
}
|
|
||||||
}, retryOptions);
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
if (sslCert && sslKey) {
|
|
||||||
sslParams.cert = Buffer.from((sslCert as IndependentFileSmall).fileData);
|
|
||||||
sslParams.key = Buffer.from((sslKey as IndependentFileSmall).fileData);
|
|
||||||
certInfo = await getCertInfo();
|
|
||||||
exists = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exists) {
|
|
||||||
const expires = certInfo?.notAfter as Date;
|
|
||||||
let duration = intervalToDuration({ start: new Date(), end: expires });
|
|
||||||
let daysLeft = (duration.months as number) * 30 + (duration.days as number);
|
|
||||||
|
|
||||||
if (daysLeft > 30) {
|
|
||||||
dateValid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (certInfo?.domains.commonName === configDomain) {
|
|
||||||
domainValid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Boolean(isSSlStaging()) !==
|
|
||||||
Boolean(certInfo?.issuer.commonName.toLowerCase().includes("staging"))
|
|
||||||
) {
|
|
||||||
domainValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dateValid && domainValid) {
|
|
||||||
if (bootup) {
|
|
||||||
sslCtx = tls.createSecureContext(sslParams);
|
|
||||||
log.info(`Loaded SSL Certificate for ${configDomain}`);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await createOrRenewSSl(
|
|
||||||
sslCert as unknown as IndependentFileSmall,
|
|
||||||
sslKey as unknown as IndependentFileSmall
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createOrRenewSSl(
|
|
||||||
oldCert?: IndependentFileSmall,
|
|
||||||
oldKey?: IndependentFileSmall
|
|
||||||
) {
|
|
||||||
const existing = oldCert && oldKey;
|
|
||||||
|
|
||||||
log.info(
|
|
||||||
sprintf(
|
|
||||||
"%s SSL Certificate for %s",
|
|
||||||
existing ? "Renewing" : "Creating",
|
|
||||||
config.str("domain")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
let accountKey: boolean | IndependentFileSmall | Buffer = await getSslFile(
|
|
||||||
FILE_ACCOUNT_KEY_NAME
|
|
||||||
);
|
|
||||||
|
|
||||||
if (accountKey) {
|
|
||||||
accountKey = Buffer.from((accountKey as IndependentFileSmall).fileData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!accountKey) {
|
|
||||||
accountKey = await acme.forge.createPrivateKey();
|
|
||||||
await createIndependentFileSmall(
|
|
||||||
getSeed(),
|
|
||||||
FILE_ACCOUNT_KEY_NAME,
|
|
||||||
accountKey
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
acmeClient = new acme.Client({
|
|
||||||
accountKey: accountKey as Buffer,
|
|
||||||
directoryUrl: isSSlStaging()
|
|
||||||
? acme.directory.letsencrypt.staging
|
|
||||||
: acme.directory.letsencrypt.production,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [certificateKey, certificateRequest] = await acme.forge.createCsr({
|
|
||||||
commonName: config.str("domain"),
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
sslParams.cert = await acmeClient.auto({
|
|
||||||
csr: certificateRequest,
|
|
||||||
termsOfServiceAgreed: true,
|
|
||||||
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
|
|
||||||
router.get(
|
|
||||||
`/.well-known/acme-challenge/${challenge.token}`,
|
|
||||||
(req, res) => {
|
|
||||||
res.send(keyAuthorization);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
challengeRemoveFn: async () => {
|
|
||||||
router = express.Router();
|
|
||||||
},
|
|
||||||
challengePriority: ["http-01"],
|
|
||||||
});
|
|
||||||
sslParams.cert = Buffer.from(sslParams.cert);
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error((e as Error).message);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
sslParams.key = certificateKey;
|
|
||||||
sslCtx = tls.createSecureContext(sslParams);
|
|
||||||
|
|
||||||
await saveSsl(oldCert, oldKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveSsl(
|
|
||||||
oldCert?: IndependentFileSmall,
|
|
||||||
oldKey?: IndependentFileSmall
|
|
||||||
): Promise<void> {
|
|
||||||
const seed = getSeed();
|
|
||||||
|
|
||||||
log.info(`Saving SSL Certificate for ${config.str("domain")}`);
|
|
||||||
|
|
||||||
if (oldCert) {
|
|
||||||
await overwriteIndependentFileSmall(
|
|
||||||
oldCert,
|
|
||||||
Buffer.from(sslParams.cert as any)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await createIndependentFileSmall(
|
|
||||||
seed,
|
|
||||||
FILE_CERT_NAME,
|
|
||||||
Buffer.from(sslParams.cert as any)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldKey) {
|
|
||||||
await overwriteIndependentFileSmall(
|
|
||||||
oldKey,
|
|
||||||
Buffer.from(sslParams.key as any)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await createIndependentFileSmall(
|
|
||||||
seed,
|
|
||||||
FILE_KEY_NAME,
|
|
||||||
Buffer.from(sslParams.key as any)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getCertInfo() {
|
|
||||||
return acme.forge.readCertificateInfo(sslParams.cert as Buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSslCert(): Promise<IndependentFileSmall | boolean> {
|
|
||||||
return getSslFile(FILE_CERT_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSslKey(): Promise<IndependentFileSmall | boolean> {
|
|
||||||
return getSslFile(FILE_KEY_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSslFile(
|
|
||||||
name: string
|
|
||||||
): Promise<IndependentFileSmall | boolean> {
|
|
||||||
let seed = getSeed();
|
|
||||||
|
|
||||||
let [file, err] = await openIndependentFileSmall(seed, name);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSeed(): Uint8Array {
|
|
||||||
let [seed, err] = seedPhraseToSeed(config.str("seed"));
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSSlStaging() {
|
|
||||||
return config.str("ssl-mode") === "staging";
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
import tls from "tls";
|
||||||
|
import {
|
||||||
|
createIndependentFileSmall,
|
||||||
|
openIndependentFileSmall,
|
||||||
|
overwriteIndependentFileSmall,
|
||||||
|
} from "./file.js";
|
||||||
|
// @ts-ignore
|
||||||
|
import promiseRetry from "promise-retry";
|
||||||
|
import config from "./config.js";
|
||||||
|
import log from "loglevel";
|
||||||
|
import { getSeed } from "./util.js";
|
||||||
|
import type {
|
||||||
|
IndependentFileSmall,
|
||||||
|
SavedSslData,
|
||||||
|
SslData,
|
||||||
|
} from "@lumeweb/relay-types";
|
||||||
|
|
||||||
|
let sslCtx: tls.SecureContext = tls.createSecureContext();
|
||||||
|
let sslObject: SslData = {};
|
||||||
|
|
||||||
|
const FILE_CERT_NAME = "/lumeweb/relay/ssl.crt";
|
||||||
|
const FILE_KEY_NAME = "/lumeweb/relay/ssl.key";
|
||||||
|
|
||||||
|
export function setSslContext(context: tls.SecureContext) {
|
||||||
|
sslCtx = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSslContext(): tls.SecureContext {
|
||||||
|
return sslCtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setSsl(
|
||||||
|
cert: IndependentFileSmall | Uint8Array,
|
||||||
|
key: IndependentFileSmall | Uint8Array
|
||||||
|
): void {
|
||||||
|
cert = (cert as IndependentFileSmall)?.fileData || cert;
|
||||||
|
key = (key as IndependentFileSmall)?.fileData || cert;
|
||||||
|
sslObject.cert = cert as Uint8Array;
|
||||||
|
sslObject.key = key as Uint8Array;
|
||||||
|
setSslContext(
|
||||||
|
tls.createSecureContext({
|
||||||
|
cert: Buffer.from(cert),
|
||||||
|
key: Buffer.from(key),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSsl(): SslData {
|
||||||
|
return sslObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveSSl(): Promise<void> {
|
||||||
|
const seed = getSeed();
|
||||||
|
|
||||||
|
log.info(`Saving SSL Certificate for ${config.str("domain")}`);
|
||||||
|
|
||||||
|
let oldCert = await getSslCert();
|
||||||
|
let cert: any = getSsl()?.cert;
|
||||||
|
cert = cert?.fileData;
|
||||||
|
if (oldCert) {
|
||||||
|
await overwriteIndependentFileSmall(
|
||||||
|
oldCert as IndependentFileSmall,
|
||||||
|
Buffer.from(cert)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await createIndependentFileSmall(seed, FILE_CERT_NAME, Buffer.from(cert));
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldKey = await getSslKey();
|
||||||
|
let key: any = getSsl()?.cert;
|
||||||
|
key = key?.fileData;
|
||||||
|
|
||||||
|
if (oldKey) {
|
||||||
|
await overwriteIndependentFileSmall(
|
||||||
|
oldKey as IndependentFileSmall,
|
||||||
|
Buffer.from(key)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await createIndependentFileSmall(seed, FILE_KEY_NAME, Buffer.from(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSavedSsl(
|
||||||
|
retry = true
|
||||||
|
): Promise<boolean | SavedSslData> {
|
||||||
|
let retryOptions = retry ? {} : { retry: 0 };
|
||||||
|
let sslCert: IndependentFileSmall | boolean = false;
|
||||||
|
let sslKey: IndependentFileSmall | boolean = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await promiseRetry(async (retry: any) => {
|
||||||
|
sslCert = await getSslCert();
|
||||||
|
if (!sslCert) {
|
||||||
|
retry();
|
||||||
|
}
|
||||||
|
}, retryOptions);
|
||||||
|
|
||||||
|
await promiseRetry(async (retry: any) => {
|
||||||
|
sslKey = await getSslKey();
|
||||||
|
if (!sslKey) {
|
||||||
|
retry();
|
||||||
|
}
|
||||||
|
}, retryOptions);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
if (!sslCert || !sslKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
cert: sslCert as IndependentFileSmall,
|
||||||
|
key: sslKey as IndependentFileSmall,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSslCert(): Promise<IndependentFileSmall | boolean> {
|
||||||
|
return getSslFile(FILE_CERT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSslKey(): Promise<IndependentFileSmall | boolean> {
|
||||||
|
return getSslFile(FILE_KEY_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSslFile(
|
||||||
|
name: string
|
||||||
|
): Promise<IndependentFileSmall | boolean> {
|
||||||
|
let seed = getSeed();
|
||||||
|
|
||||||
|
let [file, err] = await openIndependentFileSmall(seed, name);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
14
src/util.ts
14
src/util.ts
|
@ -1,3 +1,17 @@
|
||||||
|
import config from "./config";
|
||||||
|
import { seedPhraseToSeed } from "libskynet";
|
||||||
|
|
||||||
export function dynImport(module: string) {
|
export function dynImport(module: string) {
|
||||||
return Function(`return import("${module}")`)() as Promise<any>;
|
return Function(`return import("${module}")`)() as Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSeed(): Uint8Array {
|
||||||
|
let [seed, err] = seedPhraseToSeed(config.str("seed"));
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue