cryptpad/server.js

188 lines
5.5 KiB
JavaScript
Raw Permalink Normal View History

2023-10-20 14:35:26 +00:00
// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
//
// SPDX-License-Identifier: AGPL-3.0-or-later
2014-10-31 15:42:58 +00:00
var Express = require('express');
var Http = require('http');
2014-12-04 09:53:47 +00:00
var Fs = require('fs');
2018-01-26 14:24:07 +00:00
var nThen = require("nthen");
var Util = require("./lib/common-util");
2022-12-20 09:19:47 +00:00
var OS = require("node:os");
var Cluster = require("node:cluster");
2014-10-31 15:42:58 +00:00
var config = require("./lib/load-config");
var Environment = require("./lib/env");
var Env = Environment.create(config);
var Default = require("./lib/defaults");
2019-12-23 22:15:07 +00:00
var app = Express();
(function () {
// you absolutely must provide an 'httpUnsafeOrigin' (a truthy string)
2022-03-14 11:39:22 +00:00
if (typeof(Env.httpUnsafeOrigin) !== 'string' || !Env.httpUnsafeOrigin.trim()) {
throw new Error("No 'httpUnsafeOrigin' provided");
}
}());
var COMMANDS = {};
COMMANDS.LOG = function (msg, cb) {
var level = msg.level;
Env.Log[level](msg.tag, msg.info);
cb();
2022-03-14 11:39:22 +00:00
};
COMMANDS.UPDATE_QUOTA = function (msg, cb) {
var Quota = require("./lib/commands/quota");
2022-12-20 09:19:47 +00:00
Quota.updateCachedLimits(Env, (err) => {
if (err) {
Env.Log.warn('UPDATE_QUOTA_ERR', err);
return void cb(err);
}
Env.Log.info('QUOTA_UPDATED', {});
cb();
});
};
COMMANDS.GET_PROFILING_DATA = function (msg, cb) {
cb(void 0, Env.bytesWritten);
};
nThen(function (w) {
require("./lib/log").create(config, w(function (_log) {
Env.Log = _log;
config.log = _log;
}));
}).nThen(function (w) {
Fs.exists("customize", w(function (e) {
if (e) { return; }
Env.Log.info('NO_CUSTOMIZE_FOLDER', {
message: "CryptPad is customizable, see customize.dist/readme.md for details",
});
}));
2023-05-06 09:56:21 +00:00
}).nThen(function (w) {
// check that a valid origin was provided in the config
try {
var url = new URL('', Env.httpUnsafeOrigin).href;
Env.Log.info("WEBSERVER_LISTENING", {
origin: url,
});
} catch (err) {
Env.Log.error("INVALID_ORIGIN", {
httpUnsafeOrigin: Env.httpUnsafeOrigin,
});
process.exit(1);
}
Env.httpServer = Http.createServer(app);
Env.httpServer.listen(Env.websocketPort, Env.httpAddress, w(function () {
Env.Log.info('WEBSOCKET_LISTENING', {
port: Env.websocketPort,
});
}));
}).nThen(function (w) {
var limit = Env.maxWorkers;
var workerState = {
Env: Environment.serialize(Env),
};
Cluster.setupPrimary({
exec: './lib/http-worker.js',
args: [],
});
var launchWorker = (online) => {
var worker = Cluster.fork(workerState);
worker.on('online', () => {
online();
});
worker.on('message', msg => {
if (!msg) { return; }
var txid = msg.txid;
var content = msg.content;
if (!content) { return; }
var command = COMMANDS[content.command];
if (typeof(command) !== 'function') {
return void Env.Log.error('UNHANDLED_HTTP_WORKER_COMMAND', msg);
2020-11-24 15:38:31 +00:00
}
const cb = Util.once(Util.mkAsync(function (err, value) {
worker.send({
type: 'REPLY',
error: Util.serializeError(err),
txid: txid,
pid: msg.pid,
value: value,
});
}));
command(content, cb);
});
worker.on('exit', (code, signal) => {
if (!signal && code === 0) { return; }
// relaunch http workers if they crash
Env.Log.error('HTTP_WORKER_EXIT', {
signal,
code,
});
// update the environment with the latest state before relaunching
workerState.Env = Environment.serialize(Env);
launchWorker(function () {
Env.Log.info('HTTP_WORKER_RELAUNCH', {});
});
});
};
var txids = {};
var sendCommand = (worker, command, data /*, cb */) => {
worker.send({
type: 'EVENT',
txid: Util.guid(txids),
command: command,
data: data,
});
};
2024-03-07 13:58:14 +00:00
var broadcast = (command, data/*, cb*/) => {
for (const worker of Object.values(Cluster.workers)) {
sendCommand(worker, command, data /*, cb */);
}
};
var throttledEnvChange = Util.throttle(function () {
Env.Log.info('WORKER_ENV_UPDATE', 'Updating HTTP workers with latest state');
broadcast('ENV_UPDATE', Environment.serialize(Env));
}, 250); // NOTE: changing this value will impact lib/commands/admin-rpc.js#adminDecree callback
var throttledCacheFlush = Util.throttle(function () {
Env.Log.info('WORKER_CACHE_FLUSH', 'Instructing HTTP workers to flush cache');
broadcast('FLUSH_CACHE', Env.FRESH_KEY);
}, 250);
Env.envUpdated.reg(throttledEnvChange);
Env.cacheFlushed.reg(throttledCacheFlush);
OS.cpus().forEach((cpu, index) => {
if (limit && index >= limit) {
return;
}
launchWorker(w());
2022-03-22 11:08:42 +00:00
});
2018-01-26 14:24:07 +00:00
}).nThen(function () {
if (Env.shouldUpdateNode) {
Env.Log.warn("NODEJS_OLD_VERSION", {
message: `The CryptPad development team recommends using at least NodeJS v${Default.recommendedVersion.join('.')}`,
currentVersion: process.version,
});
}
if (Env.OFFLINE_MODE) { return; }
2023-07-13 12:12:47 +00:00
if (Env.websocketPath) { return; }
require("./lib/api").create(Env);
2018-01-26 14:24:07 +00:00
});