diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/Websocket/Websocket.ts | 3 | ||||
-rw-r--r-- | src/server/index.ts | 36 | ||||
-rw-r--r-- | src/server/session.ts | 101 |
3 files changed, 107 insertions, 33 deletions
diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index e1e157fc4..0b58ca344 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -13,6 +13,7 @@ import { green } from "colors"; export namespace WebSocket { + export let _socket: Socket; const clients: { [key: string]: Client } = {}; export const socketMap = new Map<SocketIO.Socket, string>(); export let disconnect: Function; @@ -28,6 +29,8 @@ export namespace WebSocket { export function initialize(socketPort: number, isRelease: boolean) { const endpoint = io(); endpoint.on("connection", function (socket: Socket) { + _socket = socket; + socket.use((_packet, next) => { const userEmail = socketMap.get(socket); if (userEmail) { diff --git a/src/server/index.ts b/src/server/index.ts index 83413c23c..c26e0ec19 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -21,17 +21,11 @@ import UploadManager from "./ApiManagers/UploadManager"; import { log_execution } from "./ActionUtilities"; import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager"; import GooglePhotosManager from "./ApiManagers/GooglePhotosManager"; -import { yellow, red, cyan } from "colors"; import { disconnect } from "../server/Initialization"; import { Logger } from "./ProcessFactory"; -import { isMaster, on, fork, workers } from "cluster"; -import { identifier } from "./session_manager/config"; -import InputManager from "./session_manager/input_manager"; -import { execSync } from "child_process"; -import { CrashEmail } from "./session_manager/crash_email"; -const killport = require("kill-port"); +import { yellow } from "colors"; +import { Session } from "./session"; -export const onWindows = process.platform === "win32"; export const publicDirectory = path.resolve(__dirname, "public"); export const filesDirectory = path.resolve(publicDirectory, "files"); @@ -144,28 +138,4 @@ async function start() { await initializeServer({ serverPort: 1050, routeSetter }); } -const admin = ["samuel_wilkins@brown.edu"]; -if (isMaster) { - fork(); - on("exit", ({ process: { pid } }, code, signal) => { - const prompt = `Server worker with process id ${pid} has died with code ${code}${signal === null ? "" : `, having encountered signal ${signal}`}.\n`; - console.log(cyan(prompt)); - fork(); - }); - const { registerCommand } = new InputManager({ identifier }); - registerCommand("exit", [], () => execSync(onWindows ? "taskkill /f /im node.exe" : "killall -9 node")); - registerCommand("restart", [], () => { - for (const id in workers) { - workers[id]?.kill(); - } - fork(); - }); -} else { - process.on('uncaughtException', async error => { - await CrashEmail.dispatch(error, admin); - console.error(red(`Crash event detected @ ${new Date().toUTCString()}`)); - console.error(error.message); - process.exit(1); - }); - start(); -}
\ No newline at end of file +Session.initialize(start);
\ No newline at end of file diff --git a/src/server/session.ts b/src/server/session.ts new file mode 100644 index 000000000..53e1be1b1 --- /dev/null +++ b/src/server/session.ts @@ -0,0 +1,101 @@ +import { yellow, red, cyan, magenta, green } from "colors"; +import { isMaster, on, fork, setupMaster, Worker } from "cluster"; +import { identifier } from "./session_manager/config"; +import InputManager from "./session_manager/input_manager"; +import { execSync } from "child_process"; +import { CrashEmail } from "./session_manager/crash_email"; +import { get } from "request-promise"; +import { WebSocket } from "./Websocket/Websocket"; +import { Utils } from "../Utils"; +import { MessageStore } from "./Message"; + +const onWindows = process.platform === "win32"; +const heartbeat = `http://localhost:1050/serverHeartbeat`; +const admin = ["samuel_wilkins@brown.edu"]; + +export namespace Session { + + const masterIdentifier = yellow("__master__"); + const workerIdentifier = magenta("__worker__"); + + export async function initialize(work: Function) { + let listening = false; + let active: Worker; + if (isMaster) { + process.on("uncaughtException", error => { + if (error.message !== "Channel closed") { + console.log(`${masterIdentifier}: ${red(error.message)}`); + if (error.stack) { + console.log(`${masterIdentifier}:\n${red(error.stack)}`); + } + } + }); + setupMaster({ silent: true }); + const spawn = () => { + if (active && !active.isDead()) { + active.process.kill(); + } + active = fork(); + active.on("message", ({ update }) => { + if (update) { + console.log(`${workerIdentifier}: ${update}`); + } + }); + }; + spawn(); + on("exit", ({ process: { pid } }, code, signal) => { + const prompt = `Server worker with process id ${pid} has exited with code ${code}${signal === null ? "" : `, having encountered signal ${signal}`}.`; + console.log(`${masterIdentifier}: ${cyan(prompt)}`); + spawn(); + }); + const restart = () => { + listening = false; + const prompt = `Server worker with process id ${active.process.pid} has been manually killed.`; + console.log(`${masterIdentifier}: ${cyan(prompt)}`); + spawn(); + }; + const { registerCommand } = new InputManager({ identifier }); + registerCommand("exit", [], () => execSync(onWindows ? "taskkill /f /im node.exe" : "killall -9 node")); + registerCommand("restart", [], restart); + } else { + const notifyMaster = (update: string) => process.send?.({ update }); + notifyMaster(green("initializing...")); + const gracefulExit = async (error: Error) => { + if (!listening) { + return; + } + listening = false; + await CrashEmail.dispatch(error, admin); + const { _socket } = WebSocket; + if (_socket) { + Utils.Emit(_socket, MessageStore.ConnectionTerminated, "Manual"); + } + notifyMaster(red(`Crash event detected @ ${new Date().toUTCString()}`)); + notifyMaster(red(error.message)); + process.exit(1); + }; + process.on('uncaughtException', gracefulExit); + const checkHeartbeat = async () => { + await new Promise<void>(resolve => { + setTimeout(async () => { + try { + await get(heartbeat); + if (!listening) { + notifyMaster(green("server is now successfully listening...")); + } + listening = true; + } catch (error) { + await gracefulExit(error); + } finally { + resolve(); + } + }, 1000 * 15); + }); + checkHeartbeat(); + }; + work(); + checkHeartbeat(); + } + } + +}
\ No newline at end of file |