Compare commits

...

7 Commits

Author SHA1 Message Date
Alex Christoffer Rasmussen 261e71e8c3 Merge branch 'master' into port-colision 2024-12-20 18:05:22 +01:00
arc-alex 9b464ff2db use escalateErrorFn 2024-12-16 22:18:22 +01:00
arc-alex 544f01630b Merge branch 'master' into port-colision 2024-12-16 22:00:23 +01:00
Alex Christoffer Rasmussen 022778f892 Merge branch 'master' into port-colision 2024-11-29 21:12:49 +01:00
arc-alex c9545d06e2 send portError to electron 2024-11-29 21:10:52 +01:00
arc-alex c9013fc8cf @ts-expect-error 2024-11-28 17:38:20 +01:00
arc-alex 4b49d13366 detect EADDRINUSE on server start 2024-11-28 17:33:20 +01:00
2 changed files with 57 additions and 14 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ if (!isProduction) {
}
/** Flag holds server loading state */
let loaded = 'Ontime running';
let loaded = 'Ontime starting';
/**
* Flag whether user has requested a quit
+56 -13
View File
@@ -3,7 +3,7 @@ import { LogOrigin, Playback, SimpleDirection, SimplePlayback } from 'ontime-typ
import 'dotenv/config';
import express from 'express';
import expressStaticGzip from 'express-static-gzip';
import http, { type Server } from 'http';
import http, { Server } from 'http';
import cors from 'cors';
import serverTiming from 'server-timing';
import { extname } from 'node:path';
@@ -179,9 +179,15 @@ export const startServer = async (
escalateErrorFn?: (error: string) => void,
): Promise<{ message: string; serverPort: number }> => {
checkStart(OntimeStartOrder.InitServer);
const { serverPort } = getDataProvider().getSettings();
const settings = getDataProvider().getSettings();
const { serverPort: desiredPort } = settings;
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, desiredPort);
await getDataProvider().setSettings({ ...settings, serverPort: resultPort });
socket.init(expressServer, prefix);
/**
@@ -225,19 +231,20 @@ export const startServer = async (
// TODO: pass event store to rundownservice
runtimeService.init(maybeRestorePoint);
expressServer.listen(serverPort, '0.0.0.0', () => {
const nif = getNetworkInterfaces();
consoleSuccess(`Local: http://localhost:${serverPort}${prefix}/editor`);
for (const key in nif) {
const address = nif[key].address;
consoleSuccess(`Network: http://${address}:${serverPort}${prefix}/editor`);
}
});
const nif = getNetworkInterfaces();
consoleSuccess(`Local: http://localhost:${resultPort}${prefix}/editor`);
for (const key in nif) {
const address = nif[key].address;
consoleSuccess(`Network: http://${address}:${resultPort}${prefix}/editor`);
}
const returnMessage = `Ontime is listening on port ${serverPort}`;
const returnMessage = `Ontime is listening on port ${resultPort}`;
logger.info(LogOrigin.Server, returnMessage);
return { message: returnMessage, serverPort };
return {
message: returnMessage,
serverPort: resultPort,
};
};
/**
@@ -307,7 +314,7 @@ process.on('unhandledRejection', async (error) => {
consoleError(error.stack);
}
generateCrashReport(error);
logger.crash(LogOrigin.Server, `Uncaught exception | ${error}`);
logger.crash(LogOrigin.Server, `Uncaught rejection | ${error}`);
await shutdown(1);
});
@@ -324,3 +331,39 @@ process.on('uncaughtException', async (error) => {
process.once('SIGHUP', async () => shutdown(0));
process.once('SIGINT', 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.crash(LogOrigin.Server, `Failed open the desired port: ${desiredPort} | to moving to Ephemeral port`);
server.listen(0, '0.0.0.0', () => {
// @ts-expect-error TODO: find proper documentation for this api
const port: number = server.address().port;
res(port);
});
} else {
throw e;
}
});
server.listen(desiredPort, '0.0.0.0', () => {
// @ts-expect-error TODO: find proper documentation for this api
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;
}