mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-01 04:19:12 +00:00
detect EADDRINUSE on server start
This commit is contained in:
+54
-13
@@ -3,7 +3,7 @@ import { LogOrigin, Playback, SimpleDirection, SimplePlayback } from 'ontime-typ
|
|||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import expressStaticGzip from 'express-static-gzip';
|
import expressStaticGzip from 'express-static-gzip';
|
||||||
import http, { type Server } from 'http';
|
import http, { Server } from 'http';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import serverTiming from 'server-timing';
|
import serverTiming from 'server-timing';
|
||||||
|
|
||||||
@@ -158,9 +158,15 @@ export const startServer = async (
|
|||||||
escalateErrorFn?: (error: string) => void,
|
escalateErrorFn?: (error: string) => void,
|
||||||
): Promise<{ message: string; serverPort: number }> => {
|
): Promise<{ message: string; serverPort: number }> => {
|
||||||
checkStart(OntimeStartOrder.InitServer);
|
checkStart(OntimeStartOrder.InitServer);
|
||||||
const { serverPort } = getDataProvider().getSettings();
|
const settings = getDataProvider().getSettings();
|
||||||
|
const { serverPort } = settings;
|
||||||
|
|
||||||
expressServer = http.createServer(app);
|
expressServer = http.createServer(app);
|
||||||
|
|
||||||
|
// the express server must be started before the socket otherwise the on error eventlissner will not attach properly
|
||||||
|
const resultPort = await serverTryDesiredPort(expressServer, serverPort);
|
||||||
|
await getDataProvider().setSettings({ ...settings, serverPort: resultPort });
|
||||||
|
|
||||||
socket.init(expressServer);
|
socket.init(expressServer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -205,19 +211,20 @@ export const startServer = async (
|
|||||||
// TODO: pass event store to rundownservice
|
// TODO: pass event store to rundownservice
|
||||||
runtimeService.init(maybeRestorePoint);
|
runtimeService.init(maybeRestorePoint);
|
||||||
|
|
||||||
expressServer.listen(serverPort, '0.0.0.0', () => {
|
const nif = getNetworkInterfaces();
|
||||||
const nif = getNetworkInterfaces();
|
consoleSuccess(`Local: http://localhost:${resultPort}/editor`);
|
||||||
consoleSuccess(`Local: http://localhost:${serverPort}/editor`);
|
for (const key in nif) {
|
||||||
for (const key in nif) {
|
const address = nif[key].address;
|
||||||
const address = nif[key].address;
|
consoleSuccess(`Network: http://${address}:${resultPort}/editor`);
|
||||||
consoleSuccess(`Network: http://${address}:${serverPort}/editor`);
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const returnMessage = `Ontime is listening on port ${serverPort}`;
|
const returnMessage = `Ontime is listening on port ${resultPort}`;
|
||||||
logger.info(LogOrigin.Server, returnMessage);
|
logger.info(LogOrigin.Server, returnMessage);
|
||||||
|
|
||||||
return { message: returnMessage, serverPort };
|
return {
|
||||||
|
message: returnMessage,
|
||||||
|
serverPort: resultPort,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -282,7 +289,7 @@ process.on('unhandledRejection', async (error) => {
|
|||||||
consoleError(error.stack);
|
consoleError(error.stack);
|
||||||
}
|
}
|
||||||
generateCrashReport(error);
|
generateCrashReport(error);
|
||||||
logger.crash(LogOrigin.Server, `Uncaught exception | ${error}`);
|
logger.crash(LogOrigin.Server, `Uncaught rejection | ${error}`);
|
||||||
await shutdown(1);
|
await shutdown(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -299,3 +306,37 @@ process.on('uncaughtException', async (error) => {
|
|||||||
process.once('SIGHUP', async () => shutdown(0));
|
process.once('SIGHUP', async () => shutdown(0));
|
||||||
process.once('SIGINT', async () => shutdown(0));
|
process.once('SIGINT', async () => shutdown(0));
|
||||||
process.once('SIGTERM', async () => shutdown(0));
|
process.once('SIGTERM', async () => shutdown(0));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description tries to open the server with the desired port, and if getting a `EADDRINUSE` will change to an efemeral port
|
||||||
|
* @param {http.Server}server http server object
|
||||||
|
* @param {number}desiredPort the desired port
|
||||||
|
* @returns {number} the resulting port number
|
||||||
|
* @throws any other server errors will result in a throw
|
||||||
|
*/
|
||||||
|
async function serverTryDesiredPort(server: http.Server, desiredPort: number): Promise<number> {
|
||||||
|
return new Promise((res) => {
|
||||||
|
expressServer.once('error', (e) => {
|
||||||
|
if (testForPortInUser(e)) {
|
||||||
|
logger.error(LogOrigin.Server, `Failed open the desired port: ${desiredPort} | to moving to Ephemeral port`);
|
||||||
|
server.listen(0, '0.0.0.0', () => {
|
||||||
|
const port: number = server.address().port;
|
||||||
|
res(port);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
server.listen(desiredPort, '0.0.0.0', () => {
|
||||||
|
const port: number = server.address().port;
|
||||||
|
res(port);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testForPortInUser(err: unknown) {
|
||||||
|
if (typeof err === 'object' && 'code' in err && err.code === 'EADDRINUSE') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user