2015-07-20 00:09:47 -07:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
2023-10-01 13:52:19 +05:30
|
|
|
const constants = require('./src/constants.js'),
|
2025-08-14 11:17:38 +05:30
|
|
|
fs = require('node:fs'),
|
2024-01-06 13:29:23 +01:00
|
|
|
ldapServer = require('./src/ldapserver.js'),
|
2025-08-14 11:17:38 +05:30
|
|
|
net = require('node:net'),
|
2025-06-11 22:00:09 +02:00
|
|
|
oidcServer = require('./src/oidcserver.js'),
|
2020-08-04 09:34:03 -07:00
|
|
|
paths = require('./src/paths.js'),
|
2020-11-10 09:59:28 -08:00
|
|
|
proxyAuth = require('./src/proxyauth.js'),
|
2021-09-07 09:57:49 -07:00
|
|
|
safe = require('safetydance'),
|
2021-11-24 17:59:21 +01:00
|
|
|
server = require('./src/server.js'),
|
2022-08-15 19:14:02 +02:00
|
|
|
directoryServer = require('./src/directoryserver.js');
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2021-09-07 11:23:54 -07:00
|
|
|
let logFd;
|
|
|
|
|
|
|
|
|
|
async function setupLogging() {
|
2023-10-01 13:52:19 +05:30
|
|
|
if (constants.TEST) return;
|
2020-08-04 22:16:38 -07:00
|
|
|
|
2021-09-07 11:23:54 -07:00
|
|
|
logFd = fs.openSync(paths.BOX_LOG_FILE, 'a');
|
|
|
|
|
// we used to write using a stream before but it caches internally and there is no way to flush it when things crash
|
|
|
|
|
process.stdout.write = process.stderr.write = function (...args) {
|
|
|
|
|
const callback = typeof args[args.length-1] === 'function' ? args.pop() : function () {}; // callback is required for fs.write
|
|
|
|
|
fs.write.apply(fs, [logFd, ...args, callback]);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
network: fix premature connection closures with node 20 and above
the happy eyeballs implementation in node is buggy. ipv4 and ipv6 connections
are made in parallel and whichever responds first is chosen. when there is no
ipv6 (immediately errors with ENETUNREACH/EHOSTUNREACH) and when ipv4 is > 250ms,
the code erroneously times out.
see also https://github.com/nodejs/node/issues/54359
reproduction for those servers:
const options = {
hostname: 'www.cloudron.io', port: 80, path: '/', method: 'HEAD',
// family: 4, // uncomment to make it work
};
const req = require('http').request(options, (res) => {
console.log('statusCode:', res.statusCode);
res.on('data', () => {}); // drain
});
req.on('socket', (socket) => console.log('Socket assigned to request', socket););
req.on('error', (e) => console.error(e));
req.end();
2024-10-31 09:38:40 +01:00
|
|
|
// happy eyeballs workaround. when there is no ipv6, nodejs timesout prematurely since the default for ipv4 is just 250ms
|
|
|
|
|
// https://github.com/nodejs/node/issues/54359
|
|
|
|
|
async function setupNetworking() {
|
2024-10-31 10:07:11 +01:00
|
|
|
net.setDefaultAutoSelectFamilyAttemptTimeout(2500);
|
network: fix premature connection closures with node 20 and above
the happy eyeballs implementation in node is buggy. ipv4 and ipv6 connections
are made in parallel and whichever responds first is chosen. when there is no
ipv6 (immediately errors with ENETUNREACH/EHOSTUNREACH) and when ipv4 is > 250ms,
the code erroneously times out.
see also https://github.com/nodejs/node/issues/54359
reproduction for those servers:
const options = {
hostname: 'www.cloudron.io', port: 80, path: '/', method: 'HEAD',
// family: 4, // uncomment to make it work
};
const req = require('http').request(options, (res) => {
console.log('statusCode:', res.statusCode);
res.on('data', () => {}); // drain
});
req.on('socket', (socket) => console.log('Socket assigned to request', socket););
req.on('error', (e) => console.error(e));
req.end();
2024-10-31 09:38:40 +01:00
|
|
|
}
|
|
|
|
|
|
2021-09-07 11:23:54 -07:00
|
|
|
// this is also used as the 'uncaughtException' handler which can only have synchronous functions
|
|
|
|
|
function exitSync(status) {
|
2024-10-14 16:21:25 +02:00
|
|
|
const ts = new Date().toISOString();
|
2025-06-30 17:34:55 +02:00
|
|
|
if (status.message) fs.write(logFd, `${ts} ${status.message}\n`, function () {});
|
2024-10-14 16:21:25 +02:00
|
|
|
const msg = status.error.stack.replace(/\n/g, `\n${ts} `); // prefix each line with ts
|
|
|
|
|
if (status.error) fs.write(logFd, `${ts} ${msg}\n`, function () {});
|
2021-09-07 11:23:54 -07:00
|
|
|
fs.fsyncSync(logFd);
|
|
|
|
|
fs.closeSync(logFd);
|
|
|
|
|
process.exit(status.code);
|
2021-09-07 09:57:49 -07:00
|
|
|
}
|
2020-08-04 09:34:03 -07:00
|
|
|
|
2021-09-07 09:57:49 -07:00
|
|
|
async function startServers() {
|
2021-09-07 11:23:54 -07:00
|
|
|
await setupLogging();
|
network: fix premature connection closures with node 20 and above
the happy eyeballs implementation in node is buggy. ipv4 and ipv6 connections
are made in parallel and whichever responds first is chosen. when there is no
ipv6 (immediately errors with ENETUNREACH/EHOSTUNREACH) and when ipv4 is > 250ms,
the code erroneously times out.
see also https://github.com/nodejs/node/issues/54359
reproduction for those servers:
const options = {
hostname: 'www.cloudron.io', port: 80, path: '/', method: 'HEAD',
// family: 4, // uncomment to make it work
};
const req = require('http').request(options, (res) => {
console.log('statusCode:', res.statusCode);
res.on('data', () => {}); // drain
});
req.on('socket', (socket) => console.log('Socket assigned to request', socket););
req.on('error', (e) => console.error(e));
req.end();
2024-10-31 09:38:40 +01:00
|
|
|
await setupNetworking();
|
2021-09-07 09:57:49 -07:00
|
|
|
await server.start(); // do this first since it also inits the database
|
|
|
|
|
await proxyAuth.start();
|
2024-01-06 13:29:23 +01:00
|
|
|
await ldapServer.start();
|
2021-11-24 17:59:21 +01:00
|
|
|
|
2023-08-03 06:34:55 +05:30
|
|
|
const conf = await directoryServer.getConfig();
|
2022-08-15 19:14:02 +02:00
|
|
|
if (conf.enabled) await directoryServer.start();
|
2020-08-04 09:34:03 -07:00
|
|
|
}
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2021-09-07 09:57:49 -07:00
|
|
|
async function main() {
|
|
|
|
|
const [error] = await safe(startServers());
|
2025-06-30 17:34:55 +02:00
|
|
|
if (error) return exitSync({ error, code: 1, message: 'Error starting servers' });
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2022-02-21 17:34:51 -08:00
|
|
|
// require this here so that logging handler is already setup
|
2020-08-21 09:45:03 +02:00
|
|
|
const debug = require('debug')('box:box');
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2022-11-16 08:10:57 +01:00
|
|
|
process.on('SIGHUP', async function () {
|
|
|
|
|
debug('Received SIGHUP. Re-reading configs.');
|
2023-08-03 06:34:55 +05:30
|
|
|
const conf = await directoryServer.getConfig();
|
2022-11-30 15:16:16 +01:00
|
|
|
if (conf.enabled) await directoryServer.checkCertificate();
|
2022-11-16 08:10:57 +01:00
|
|
|
});
|
|
|
|
|
|
2021-09-07 09:57:49 -07:00
|
|
|
process.on('SIGINT', async function () {
|
2020-08-04 09:34:03 -07:00
|
|
|
debug('Received SIGINT. Shutting down.');
|
2018-11-08 14:35:22 +01:00
|
|
|
|
2021-09-07 09:57:49 -07:00
|
|
|
await proxyAuth.stop();
|
|
|
|
|
await server.stop();
|
2022-08-15 19:14:02 +02:00
|
|
|
await directoryServer.stop();
|
2024-01-06 13:29:23 +01:00
|
|
|
await ldapServer.stop();
|
2025-06-11 22:00:09 +02:00
|
|
|
await oidcServer.stop();
|
2025-06-17 22:28:11 +02:00
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
debug('Shutdown complete');
|
|
|
|
|
process.exit();
|
|
|
|
|
}, 2000); // need to wait for the task processes to die
|
2020-08-04 09:34:03 -07:00
|
|
|
});
|
|
|
|
|
|
2021-09-07 09:57:49 -07:00
|
|
|
process.on('SIGTERM', async function () {
|
2020-08-04 09:34:03 -07:00
|
|
|
debug('Received SIGTERM. Shutting down.');
|
2015-09-09 16:57:41 -07:00
|
|
|
|
2021-09-07 09:57:49 -07:00
|
|
|
await proxyAuth.stop();
|
|
|
|
|
await server.stop();
|
2022-08-15 19:14:02 +02:00
|
|
|
await directoryServer.stop();
|
2024-01-06 13:29:23 +01:00
|
|
|
await ldapServer.stop();
|
2025-06-11 22:00:09 +02:00
|
|
|
await oidcServer.stop();
|
2025-06-17 22:28:11 +02:00
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
debug('Shutdown complete');
|
|
|
|
|
process.exit();
|
|
|
|
|
}, 2000); // need to wait for the task processes to die
|
2020-08-04 09:34:03 -07:00
|
|
|
});
|
2018-11-08 14:35:22 +01:00
|
|
|
|
2025-06-30 17:34:55 +02:00
|
|
|
process.on('uncaughtException', (error) => exitSync({ error, code: 1, message: 'From uncaughtException handler.' }));
|
2021-09-07 09:57:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main();
|