aboutsummaryrefslogtreecommitdiff
path: root/src/server/DashSession/DashSessionAgent.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/DashSession/DashSessionAgent.ts')
-rw-r--r--src/server/DashSession/DashSessionAgent.ts65
1 files changed, 34 insertions, 31 deletions
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index f3f0a3c3d..c55e01243 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -5,12 +5,11 @@ import { Utils } from "../../Utils";
import { WebSocket } from "../Websocket/Websocket";
import { MessageStore } from "../Message";
import { launchServer, onWindows } from "..";
-import { existsSync, mkdirSync, readdirSync, statSync, createWriteStream, readFileSync } from "fs";
+import { readdirSync, statSync, createWriteStream, readFileSync, unlinkSync } from "fs";
import * as Archiver from "archiver";
import { resolve } from "path";
-import { AppliedSessionAgent, ExitHandler } from "../session/agents/applied_session_agent";
-import { Monitor } from "../session/agents/monitor";
-import { ServerWorker } from "../session/agents/server_worker";
+import { AppliedSessionAgent, MessageHandler, ExitHandler, Monitor, ServerWorker } from "resilient-server-session";
+import rimraf = require("rimraf");
/**
* If we're the monitor (master) thread, we should launch the monitor logic for the session.
@@ -26,22 +25,22 @@ export class DashSessionAgent extends AppliedSessionAgent {
* The core method invoked when the single master thread is initialized.
* Installs event hooks, repl commands and additional IPC listeners.
*/
- protected async initializeMonitor(monitor: Monitor) {
+ protected async initializeMonitor(monitor: Monitor, sessionKey: string): Promise<void> {
+ await this.dispatchSessionPassword(sessionKey);
monitor.addReplCommand("pull", [], () => monitor.exec("git pull"));
monitor.addReplCommand("solr", [/start|stop|index/], this.executeSolrCommand);
monitor.addReplCommand("backup", [], this.backup);
- monitor.addReplCommand("debug", [/active|passive/, /\S+\@\S+/], async ([mode, recipient]) => this.dispatchZippedDebugBackup(mode, recipient));
- monitor.addServerMessageListener("backup", this.backup);
- monitor.addServerMessageListener("debug", ({ args: { mode, recipient } }) => this.dispatchZippedDebugBackup(mode, recipient));
- monitor.on(Monitor.IntrinsicEvents.KeyGenerated, this.dispatchSessionPassword);
- monitor.on(Monitor.IntrinsicEvents.CrashDetected, this.dispatchCrashReport);
+ monitor.addReplCommand("debug", [/\S+\@\S+/], async ([to]) => this.dispatchZippedDebugBackup(to));
+ monitor.on("backup", this.backup);
+ monitor.on("debug", async ({ to }) => this.dispatchZippedDebugBackup(to));
+ monitor.coreHooks.onCrashDetected(this.dispatchCrashReport);
}
/**
* The core method invoked when a server worker thread is initialized.
* Installs logic to be executed when the server worker dies.
*/
- protected async initializeServerWorker() {
+ protected async initializeServerWorker(): Promise<ServerWorker> {
const worker = ServerWorker.Create(launchServer); // server initialization delegated to worker
worker.addExitHandler(this.notifyClient);
return worker;
@@ -51,7 +50,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* Prepares the body of the email with instructions on restoring the transmitted remote database backup locally.
*/
private _remoteDebugInstructions: string | undefined;
- private generateDebugInstructions = (zipName: string, target: string) => {
+ private generateDebugInstructions = (zipName: string, target: string): string => {
if (!this._remoteDebugInstructions) {
this._remoteDebugInstructions = readFileSync(resolve(__dirname, "./templates/remote_debug_instructions.txt"), { encoding: "utf8" });
}
@@ -65,7 +64,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* Prepares the body of the email with information regarding a crash event.
*/
private _crashInstructions: string | undefined;
- private generateCrashInstructions({ name, message, stack }: Error) {
+ private generateCrashInstructions({ name, message, stack }: Error): string {
if (!this._crashInstructions) {
this._crashInstructions = readFileSync(resolve(__dirname, "./templates/crash_instructions.txt"), { encoding: "utf8" });
}
@@ -80,14 +79,18 @@ export class DashSessionAgent extends AppliedSessionAgent {
* This sends a pseudorandomly generated guid to the configuration's recipients, allowing them alone
* to kill the server via the /kill/:key route.
*/
- private dispatchSessionPassword = async (key: string) => {
+ private dispatchSessionPassword = async (sessionKey: string): Promise<void> => {
const { mainLog } = this.sessionMonitor;
const { notificationRecipient } = DashSessionAgent;
mainLog(green("dispatching session key..."));
const error = await Email.dispatch({
to: notificationRecipient,
subject: "Dash Release Session Admin Authentication Key",
- content: `The key for this session (started @ ${new Date().toUTCString()}) is ${key}.\n\n${this.signature}`
+ content: [
+ `Here's the key for this session (started @ ${new Date().toUTCString()}):`,
+ sessionKey,
+ this.signature
+ ].join("\n\n")
});
if (error) {
this.sessionMonitor.mainLog(red(`dispatch failure @ ${notificationRecipient} (${yellow(error.message)})`));
@@ -100,7 +103,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
/**
* This sends an email with the generated crash report.
*/
- private dispatchCrashReport = async (crashCause: Error) => {
+ private dispatchCrashReport: MessageHandler<{ error: Error }> = async ({ error: crashCause }) => {
const { mainLog } = this.sessionMonitor;
const { notificationRecipient } = DashSessionAgent;
const error = await Email.dispatch({
@@ -109,7 +112,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
content: this.generateCrashInstructions(crashCause)
});
if (error) {
- this.sessionMonitor.mainLog(red(`dispatch failure @ ${notificationRecipient} (${yellow(error.message)})`));
+ this.sessionMonitor.mainLog(red(`dispatch failure @ ${notificationRecipient} ${yellow(`(${error.message})`)}`));
mainLog(red("distribution of crash notification experienced errors"));
} else {
mainLog(green("successfully distributed crash notification to recipients"));
@@ -120,7 +123,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* Logic for interfacing with Solr. Either starts it,
* stops it, or rebuilds its indicies.
*/
- private executeSolrCommand = async (args: string[]) => {
+ private executeSolrCommand = async (args: string[]): Promise<void> => {
const { exec, mainLog } = this.sessionMonitor;
const action = args[0];
if (action === "index") {
@@ -153,7 +156,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* Performs a backup of the database, saved to the desktop subdirectory.
* This should work as is only on our specific release server.
*/
- private backup = async () => this.sessionMonitor.exec("backup.bat", { cwd: this.releaseDesktop });
+ private backup = async (): Promise<void> => this.sessionMonitor.exec("backup.bat", { cwd: this.releaseDesktop });
/**
* Compress either a brand new backup or the most recent backup and send it
@@ -161,21 +164,14 @@ export class DashSessionAgent extends AppliedSessionAgent {
* @param mode specifies whether or not to make a new backup before exporting
* @param to the recipient of the email
*/
- private async dispatchZippedDebugBackup(mode: string, to: string) {
+ private async dispatchZippedDebugBackup(to: string): Promise<void> {
const { mainLog } = this.sessionMonitor;
try {
// if desired, complete an immediate backup to send
- if (mode === "active") {
- await this.backup();
- mainLog("backup complete");
- }
+ await this.backup();
+ mainLog("backup complete");
- // ensure the directory for compressed backups exists
const backupsDirectory = `${this.releaseDesktop}/backups`;
- const compressedDirectory = `${this.releaseDesktop}/compressed`;
- if (!existsSync(compressedDirectory)) {
- mkdirSync(compressedDirectory);
- }
// sort all backups by their modified time, and choose the most recent one
const target = readdirSync(backupsDirectory).map(filename => ({
@@ -186,11 +182,12 @@ export class DashSessionAgent extends AppliedSessionAgent {
// create a zip file and to it, write the contents of the backup directory
const zipName = `${target}.zip`;
- const zipPath = `${compressedDirectory}/${zipName}`;
+ const zipPath = `${this.releaseDesktop}/${zipName}`;
+ const targetPath = `${backupsDirectory}/${target}`;
const output = createWriteStream(zipPath);
const zip = Archiver('zip');
zip.pipe(output);
- zip.directory(`${backupsDirectory}/${target}/Dash`, false);
+ zip.directory(`${targetPath}/Dash`, false);
await zip.finalize();
mainLog(`zip finalized with size ${statSync(zipPath).size} bytes, saved to ${zipPath}`);
@@ -202,6 +199,12 @@ export class DashSessionAgent extends AppliedSessionAgent {
attachments: [{ filename: zipName, path: zipPath }]
});
+ // since this is intended to be a zero-footprint operation, clean up
+ // by unlinking both the backup generated earlier in the function and the compressed zip file.
+ // to generate a persistent backup, just run backup.
+ unlinkSync(zipPath);
+ rimraf.sync(targetPath);
+
// indicate success or failure
mainLog(`${error === null ? green("successfully dispatched") : red("failed to dispatch")} ${zipName} to ${cyan(to)}`);
error && mainLog(red(error.message));