diff options
Diffstat (limited to 'src/server/session.ts')
-rw-r--r-- | src/server/session.ts | 93 |
1 files changed, 58 insertions, 35 deletions
diff --git a/src/server/session.ts b/src/server/session.ts index 1fed9746e..c66461d03 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1,9 +1,8 @@ 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 { Email } from "./session_manager/email"; import { get } from "request-promise"; import { WebSocket } from "./Websocket/Websocket"; import { Utils } from "../Utils"; @@ -15,21 +14,59 @@ const admin = ["samuel_wilkins@brown.edu"]; export namespace Session { - const masterIdentifier = yellow("__master__"); - const workerIdentifier = magenta("__worker__"); - - function killAll() { - execSync(onWindows ? "taskkill /f /im node.exe" : "killall -9 node"); - } + export let key: string; + export const signature = "Best,\nServer Session Manager"; + let activeWorker: Worker; + const masterIdentifier = `${yellow("__master__")}:`; + const workerIdentifier = `${magenta("__worker__")}:`; function log(message?: any, ...optionalParams: any[]) { - const identifier = `${isMaster ? masterIdentifier : workerIdentifier}:`; + const identifier = isMaster ? masterIdentifier : workerIdentifier; console.log(identifier, message, ...optionalParams); } + export async function distributeKey() { + key = Utils.GenerateGuid(); + const timestamp = new Date().toUTCString(); + const content = `The key for this session (started @ ${timestamp}) is ${key}.\n\n${signature}`; + return Promise.all(admin.map(recipient => Email.dispatch(recipient, "Server Termination Key", content))); + } + + function tryKillActiveWorker() { + if (activeWorker && !activeWorker.isDead()) { + activeWorker.process.kill(); + return true; + } + return false; + } + + function messageHandler({ lifecycle, action }: any) { + if (action) { + console.log(`${workerIdentifier} action requested (${action})`); + switch (action) { + case "kill": + log(red("An authorized user has ended the server from the /kill route")); + tryKillActiveWorker(); + process.exit(0); + } + } else if (lifecycle) { + console.log(`${workerIdentifier} lifecycle phase (${lifecycle})`); + } + } + + function crashReport({ name, message, stack }: Error) { + return [ + "You, as a Dash Administrator, are being notified of a server crash event. Here's what we know:", + `name:\n${name}`, + `message:\n${message}`, + `stack:\n${stack}`, + "The server is already restarting itself, but if you're concerned, use the Remote Desktop Connection to monitor progress.", + signature + ].join("\n\n"); + } + export async function initialize(work: Function) { let listening = false; - let active: Worker; if (isMaster) { process.on("uncaughtException", error => { if (error.message !== "Channel closed") { @@ -41,21 +78,9 @@ export namespace Session { }); setupMaster({ silent: true }); const spawn = () => { - if (active && !active.isDead()) { - active.process.kill(); - } - active = fork(); - active.on("message", ({ lifecycle, action }) => { - if (action) { - console.log(`${workerIdentifier}: action requested (${action})`); - switch (action) { - case "kill": - log(red("An authorized user has ended the server from the /kill route")); - } - } else if (lifecycle) { - console.log(`${workerIdentifier}: lifecycle phase (${lifecycle})`); - } - }); + tryKillActiveWorker(); + activeWorker = fork(); + activeWorker.on("message", messageHandler); }; spawn(); on("exit", ({ process: { pid } }, code, signal) => { @@ -63,15 +88,13 @@ export namespace Session { log(cyan(prompt)); spawn(); }); - const restart = () => { + const { registerCommand } = new InputManager({ identifier: masterIdentifier }); + registerCommand("exit", [], () => execSync(onWindows ? "taskkill /f /im node.exe" : "killall -9 node")); + registerCommand("pull", [], () => execSync("git pull", { stdio: ["ignore", "inherit", "inherit"] })); + registerCommand("restart", [], () => { listening = false; - const prompt = `Server worker with process id ${active.process.pid} has been manually killed.`; - log(cyan(prompt)); - spawn(); - }; - const { registerCommand } = new InputManager({ identifier }); - registerCommand("exit", [], killAll); - registerCommand("restart", [], restart); + tryKillActiveWorker(); + }); } else { const logLifecycleEvent = (lifecycle: string) => process.send?.({ lifecycle }); logLifecycleEvent(green("initializing...")); @@ -80,7 +103,7 @@ export namespace Session { return; } listening = false; - await CrashEmail.dispatch(error, admin); + await Promise.all(admin.map(recipient => Email.dispatch(recipient, "Dash Web Server Crash", crashReport(error)))); const { _socket } = WebSocket; if (_socket) { Utils.Emit(_socket, MessageStore.ConnectionTerminated, "Manual"); @@ -96,7 +119,7 @@ export namespace Session { try { await get(heartbeat); if (!listening) { - logLifecycleEvent(green("server is now successfully listening...")); + logLifecycleEvent(green("listening...")); } listening = true; resolve(); |