aboutsummaryrefslogtreecommitdiff
path: root/src/server/session.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/session.ts')
-rw-r--r--src/server/session.ts93
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();