aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/ApiManagers/SessionManager.ts57
-rw-r--r--src/server/DashSession.ts83
-rw-r--r--src/server/RouteManager.ts3
-rw-r--r--src/server/index.ts17
4 files changed, 105 insertions, 55 deletions
diff --git a/src/server/ApiManagers/SessionManager.ts b/src/server/ApiManagers/SessionManager.ts
new file mode 100644
index 000000000..eb17ff567
--- /dev/null
+++ b/src/server/ApiManagers/SessionManager.ts
@@ -0,0 +1,57 @@
+import ApiManager, { Registration } from "./ApiManager";
+import { Method, _permission_denied, AuthorizedCore, SecureHandler } from "../RouteManager";
+import RouteSubscriber from "../RouteSubscriber";
+import { sessionAgent } from "..";
+
+const permissionError = "You are not authorized!";
+
+export default class SessionManager extends ApiManager {
+
+ private secureSubscriber = (root: string, ...params: string[]) => new RouteSubscriber(root).add("password", ...params);
+
+ private authorizedAction = (handler: SecureHandler) => {
+ return (core: AuthorizedCore) => {
+ const { req, res, isRelease } = core;
+ const { password } = req.params;
+ if (!isRelease) {
+ return res.send("This can be run only on the release server.");
+ }
+ if (password !== process.env.session_key) {
+ return _permission_denied(res, permissionError);
+ }
+ handler(core);
+ res.redirect("/home");
+ };
+ }
+
+ protected initialize(register: Registration): void {
+
+ register({
+ method: Method.GET,
+ subscription: this.secureSubscriber("debug", "mode", "recipient"),
+ secureHandler: this.authorizedAction(({ req }) => {
+ const { mode, recipient } = req.params;
+ if (["passive", "active"].includes(mode)) {
+ sessionAgent.serverWorker.sendMonitorAction("debug", { mode, recipient });
+ }
+ })
+ });
+
+ register({
+ method: Method.GET,
+ subscription: this.secureSubscriber("backup"),
+ secureHandler: this.authorizedAction(() => sessionAgent.serverWorker.sendMonitorAction("backup"))
+ });
+
+ register({
+ method: Method.GET,
+ subscription: this.secureSubscriber("kill"),
+ secureHandler: this.authorizedAction(({ res }) => {
+ res.send("<img src='https://media.giphy.com/media/NGIfqtcS81qi4/giphy.gif' style='width:100%;height:100%;'/>");
+ sessionAgent.killSession("an authorized user has manually ended the server session via the /kill route", true);
+ })
+ });
+
+ }
+
+} \ No newline at end of file
diff --git a/src/server/DashSession.ts b/src/server/DashSession.ts
index e03487a61..c893f4b64 100644
--- a/src/server/DashSession.ts
+++ b/src/server/DashSession.ts
@@ -19,7 +19,7 @@ export class DashSessionAgent extends Session.AppliedSessionAgent {
private readonly notificationRecipients = ["samuel_wilkins@brown.edu"];
private readonly signature = "-Dash Server Session Manager";
-
+ private readonly releaseDesktop = pathFromRoot("../../Desktop");
protected async launchMonitor() {
const monitor = Session.Monitor.Create({
@@ -67,44 +67,10 @@ export class DashSessionAgent extends Session.AppliedSessionAgent {
}
}
});
- const releaseDesktop = pathFromRoot("../../Desktop");
- const backup = () => monitor.exec("backup.bat", { cwd: releaseDesktop });
- monitor.addReplCommand("backup", [], backup);
- monitor.addReplCommand("debug", [/active|passive/, /\S+\@\S+/], async args => {
- const [mode, recipient] = args;
- if (mode === "active") {
- await backup();
- }
- monitor.mainLog("backup complete");
- const backupsDirectory = `${releaseDesktop}/backups`;
- const compressedDirectory = `${releaseDesktop}/compressed`;
- if (!existsSync(compressedDirectory)) {
- mkdirSync(compressedDirectory);
- }
- const target = readdirSync(backupsDirectory).map(filename => ({
- modifiedTime: statSync(`${backupsDirectory}/${filename}`).mtimeMs,
- filename
- })).sort((a, b) => b.modifiedTime - a.modifiedTime)[0].filename;
- monitor.mainLog(`targeting ${target}...`);
- const zipName = `${target}.zip`;
- const zipPath = `${compressedDirectory}/${zipName}`;
- const output = createWriteStream(zipPath);
- const zip = Archiver('zip');
- zip.pipe(output);
- zip.directory(`${backupsDirectory}/${target}/Dash`, false);
- await zip.finalize();
- monitor.mainLog(`zip finalized with size ${statSync(zipPath).size} bytes, saved to ${zipPath}`);
- let instructions = readFileSync(resolve(__dirname, "./remote_debug_instructions.txt"), { encoding: "utf8" });
- instructions = instructions.replace(/__zipname__/, zipName).replace(/__target__/, target).replace(/__signature__/, this.signature);
- const error = await Email.dispatch(recipient, `Compressed backup of ${target}...`, instructions, [
- {
- filename: zipName,
- path: zipPath
- }
- ]);
- monitor.mainLog(`${error === null ? green("successfully dispatched") : red("failed to dispatch")} ${zipName} to ${cyan(recipient)}`);
- error && monitor.mainLog(red(error.message));
- });
+ 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));
return monitor;
}
@@ -120,4 +86,43 @@ export class DashSessionAgent extends Session.AppliedSessionAgent {
return worker;
}
+ public async backup() {
+ return this.sessionMonitor.exec("backup.bat", { cwd: this.releaseDesktop });
+ }
+
+ public async dispatchZippedDebugBackup(mode: string, recipient: string) {
+ if (mode === "active") {
+ await this.backup();
+ }
+ this.sessionMonitor.mainLog("backup complete");
+ const backupsDirectory = `${this.releaseDesktop}/backups`;
+ const compressedDirectory = `${this.releaseDesktop}/compressed`;
+ if (!existsSync(compressedDirectory)) {
+ mkdirSync(compressedDirectory);
+ }
+ const target = readdirSync(backupsDirectory).map(filename => ({
+ modifiedTime: statSync(`${backupsDirectory}/${filename}`).mtimeMs,
+ filename
+ })).sort((a, b) => b.modifiedTime - a.modifiedTime)[0].filename;
+ this.sessionMonitor.mainLog(`targeting ${target}...`);
+ const zipName = `${target}.zip`;
+ const zipPath = `${compressedDirectory}/${zipName}`;
+ const output = createWriteStream(zipPath);
+ const zip = Archiver('zip');
+ zip.pipe(output);
+ zip.directory(`${backupsDirectory}/${target}/Dash`, false);
+ await zip.finalize();
+ this.sessionMonitor.mainLog(`zip finalized with size ${statSync(zipPath).size} bytes, saved to ${zipPath}`);
+ let instructions = readFileSync(resolve(__dirname, "./remote_debug_instructions.txt"), { encoding: "utf8" });
+ instructions = instructions.replace(/__zipname__/, zipName).replace(/__target__/, target).replace(/__signature__/, this.signature);
+ const error = await Email.dispatch(recipient, `Compressed backup of ${target}...`, instructions, [
+ {
+ filename: zipName,
+ path: zipPath
+ }
+ ]);
+ this.sessionMonitor.mainLog(`${error === null ? green("successfully dispatched") : red("failed to dispatch")} ${zipName} to ${cyan(recipient)}`);
+ error && this.sessionMonitor.mainLog(red(error.message));
+ }
+
} \ No newline at end of file
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 25259bd88..a8ad81bf7 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -14,7 +14,8 @@ export interface CoreArguments {
isRelease: boolean;
}
-export type SecureHandler = (core: CoreArguments & { user: DashUserModel }) => any | Promise<any>;
+export type AuthorizedCore = CoreArguments & { user: DashUserModel };
+export type SecureHandler = (core: AuthorizedCore) => any | Promise<any>;
export type PublicHandler = (core: CoreArguments) => any | Promise<any>;
export type ErrorHandler = (core: CoreArguments & { error: any }) => any | Promise<any>;
diff --git a/src/server/index.ts b/src/server/index.ts
index 6b3dfd614..d73e48d8a 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -24,6 +24,7 @@ import { Logger } from "./ProcessFactory";
import { yellow, red } from "colors";
import { Session } from "./Session/session";
import { DashSessionAgent } from "./DashSession";
+import SessionManager from "./ApiManagers/SessionManager";
export const onWindows = process.platform === "win32";
export let sessionAgent: Session.AppliedSessionAgent;
@@ -58,6 +59,7 @@ async function preliminaryFunctions() {
*/
function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: RouteManager) {
const managers = [
+ new SessionManager(),
new UserManager(),
new UploadManager(),
new DownloadManager(),
@@ -88,21 +90,6 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
secureHandler: ({ res }) => res.send(true)
});
- addSupervisedRoute({
- method: Method.GET,
- subscription: new RouteSubscriber("kill").add("key"),
- secureHandler: ({ req, res }) => {
- if (req.params.key === process.env.session_key) {
- res.send("<img src='https://media.giphy.com/media/NGIfqtcS81qi4/giphy.gif' style='width:100%;height:100%;'/>");
- setTimeout(() => {
- sessionAgent.killSession("an authorized user has manually ended the server session via the /kill route", false);
- }, 5000);
- } else {
- res.redirect("/home");
- }
- }
- });
-
const serve: PublicHandler = ({ req, res }) => {
const detector = new mobileDetect(req.headers['user-agent'] || "");
const filename = detector.mobile() !== null ? 'mobile/image.html' : 'index.html';