From f1c4bdb6c0f80dfe486a589623bd6fd864c1e686 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 5 Jan 2020 17:37:22 -0800 Subject: commenting --- src/server/Initialization.ts | 147 ------------------------------------ src/server/Websocket/Websocket.ts | 2 +- src/server/index.ts | 70 +++++++++++------ src/server/server_Initialization.ts | 147 ++++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 173 deletions(-) delete mode 100644 src/server/Initialization.ts create mode 100644 src/server/server_Initialization.ts (limited to 'src') diff --git a/src/server/Initialization.ts b/src/server/Initialization.ts deleted file mode 100644 index d99aaa3f0..000000000 --- a/src/server/Initialization.ts +++ /dev/null @@ -1,147 +0,0 @@ -import * as express from 'express'; -import * as expressValidator from 'express-validator'; -import * as session from 'express-session'; -import * as passport from 'passport'; -import * as bodyParser from 'body-parser'; -import * as cookieParser from 'cookie-parser'; -import expressFlash = require('express-flash'); -import flash = require('connect-flash'); -import { Database } from './database'; -import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/controllers/user_controller'; -const MongoStore = require('connect-mongo')(session); -import RouteManager from './RouteManager'; -import * as webpack from 'webpack'; -const config = require('../../webpack.config'); -const compiler = webpack(config); -import * as wdm from 'webpack-dev-middleware'; -import * as whm from 'webpack-hot-middleware'; -import * as fs from 'fs'; -import * as request from 'request'; -import RouteSubscriber from './RouteSubscriber'; -import { publicDirectory } from '.'; -import { logPort, } from './ActionUtilities'; -import { timeMap } from './ApiManagers/UserManager'; -import { blue, yellow } from 'colors'; - -/* RouteSetter is a wrapper around the server that prevents the server - from being exposed. */ -export type RouteSetter = (server: RouteManager) => void; -export let disconnect: Function; - -export default async function InitializeServer(routeSetter: RouteSetter) { - const app = buildWithMiddleware(express()); - - app.use(express.static(publicDirectory)); - app.use("/images", express.static(publicDirectory)); - - app.use("*", ({ user, originalUrl }, res, next) => { - if (user && !originalUrl.includes("Heartbeat")) { - const userEmail = (user as any).email; - if (userEmail) { - timeMap[userEmail] = Date.now(); - } - } - if (!user && originalUrl === "/") { - return res.redirect("/login"); - } - next(); - }); - - app.use(wdm(compiler, { publicPath: config.output.publicPath })); - app.use(whm(compiler)); - - registerAuthenticationRoutes(app); - registerCorsProxy(app); - - const isRelease = determineEnvironment(); - - routeSetter(new RouteManager(app, isRelease)); - - const serverPort = process.env.serverPort ? Number(process.env.serverPort) : 1050; - const server = app.listen(serverPort, () => { - logPort("server", Number(serverPort)); - console.log(); - }); - disconnect = async () => new Promise(resolve => server.close(resolve)); - - return isRelease; -} - -const week = 7 * 24 * 60 * 60 * 1000; -const secret = "64d6866242d3b5a5503c675b32c9605e4e90478e9b77bcf2bc"; - -function buildWithMiddleware(server: express.Express) { - [ - cookieParser(), - session({ - secret, - resave: true, - cookie: { maxAge: week }, - saveUninitialized: true, - store: new MongoStore({ url: Database.url }) - }), - flash(), - expressFlash(), - bodyParser.json({ limit: "10mb" }), - bodyParser.urlencoded({ extended: true }), - expressValidator(), - passport.initialize(), - passport.session(), - (req: express.Request, res: express.Response, next: express.NextFunction) => { - res.locals.user = req.user; - next(); - } - ].forEach(next => server.use(next)); - return server; -} - -/* Determine if the enviroment is dev mode or release mode. */ -function determineEnvironment() { - const isRelease = process.env.RELEASE === "true"; - - const color = isRelease ? blue : yellow; - const label = isRelease ? "release" : "development"; - console.log(`\nrunning server in ${color(label)} mode`); - - let clientUtils = fs.readFileSync("./src/client/util/ClientUtils.ts.temp", "utf8"); - clientUtils = `//AUTO-GENERATED FILE: DO NOT EDIT\n${clientUtils.replace('"mode"', String(isRelease))}`; - fs.writeFileSync("./src/client/util/ClientUtils.ts", clientUtils, "utf8"); - - return isRelease; -} - -function registerAuthenticationRoutes(server: express.Express) { - server.get("/signup", getSignup); - server.post("/signup", postSignup); - - server.get("/login", getLogin); - server.post("/login", postLogin); - - server.get("/logout", getLogout); - - server.get("/forgotPassword", getForgot); - server.post("/forgotPassword", postForgot); - - const reset = new RouteSubscriber("resetPassword").add("token").build; - server.get(reset, getReset); - server.post(reset, postReset); -} - -function registerCorsProxy(server: express.Express) { - const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; - server.use("/corsProxy", (req, res) => { - req.pipe(request(decodeURIComponent(req.url.substring(1)))).on("response", res => { - const headers = Object.keys(res.headers); - headers.forEach(headerName => { - const header = res.headers[headerName]; - if (Array.isArray(header)) { - res.headers[headerName] = header.filter(h => !headerCharRegex.test(h)); - } else if (header) { - if (headerCharRegex.test(header as any)) { - delete res.headers[headerName]; - } - } - }); - }).pipe(res); - }); -} \ No newline at end of file diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index e574cd111..578147d60 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -63,7 +63,7 @@ export namespace WebSocket { }; }); - const socketPort = process.env.socketPort ? Number(process.env.socketPort) : 4321; + const socketPort = isRelease ? Number(process.env.socketPort) : 4321; endpoint.listen(socketPort); logPort("websocket", socketPort); } diff --git a/src/server/index.ts b/src/server/index.ts index 18b31e7a4..d1480e51e 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -6,7 +6,7 @@ import { Database } from './database'; const serverPort = 4321; import { DashUploadUtils } from './DashUploadUtils'; import RouteSubscriber from './RouteSubscriber'; -import initializeServer from './Initialization'; +import initializeServer from './server_Initialization'; import RouteManager, { Method, _success, _permission_denied, _error, _invalid, PublicHandler } from './RouteManager'; import * as qs from 'query-string'; import UtilManager from './ApiManagers/UtilManager'; @@ -130,6 +130,12 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: WebSocket.start(isRelease); } +/** + * This function can be used in two different ways. If not in release mode, + * this is simply the logic that is invoked to start the server. In release mode, + * however, this becomes the logic invoked by a single worker thread spawned by + * the main monitor (master) thread. + */ async function launchServer() { await log_execution({ startMessage: "\nstarting execution of preliminary functions", @@ -139,31 +145,45 @@ async function launchServer() { await initializeServer(routeSetter); } -if (process.env.RELEASE) { - /** - * Thread dependent session initialization +/** + * A function to dictate the format of the message sent on crash + * @param error the error that caused the crash */ - (async function launchSession() { - if (isMaster) { - const emailGenerator = (error: Error) => { - const subject = "Dash Web Server Crash"; - const { name, message, stack } = error; - const body = [ - "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.", - ].join("\n\n"); - return { subject, body }; - }; - const customizer = await Session.initializeMonitorThread(emailGenerator); - customizer.addReplCommand("pull", [], () => execSync("git pull", { stdio: ["ignore", "inherit", "inherit"] })); - } else { - const addExitHandler = await Session.initializeWorkerThread(launchServer); - addExitHandler(() => Utils.Emit(WebSocket._socket, MessageStore.ConnectionTerminated, "Manual")); - } - })(); +function crashEmailGenerator(error: Error) { + const subject = "Dash Web Server Crash"; + const { name, message, stack } = error; + const body = [ + "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.", + ].join("\n\n"); + return { subject, body }; +} + +/** + * If on the master thread, launches the monitor for the session. + * Otherwise, the thread must have been spawned *by* the monitor, and thus + * should run the server as a worker. + */ +async function launchMonitoredSession() { + if (isMaster) { + const customizer = await Session.initializeMonitorThread(crashEmailGenerator); + customizer.addReplCommand("pull", [], () => execSync("git pull", { stdio: ["ignore", "inherit", "inherit"] })); + } else { + const addExitHandler = await Session.initializeWorkerThread(launchServer); // server initialization delegated to worker + addExitHandler(() => Utils.Emit(WebSocket._socket, MessageStore.ConnectionTerminated, "Manual")); + } +} + +/** + * Ensures that development mode avoids + * the overhead and lack of default output + * found in a release session. + */ +if (process.env.RELEASE) { + launchMonitoredSession(); } else { launchServer(); } \ No newline at end of file diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts new file mode 100644 index 000000000..4cb1fca47 --- /dev/null +++ b/src/server/server_Initialization.ts @@ -0,0 +1,147 @@ +import * as express from 'express'; +import * as expressValidator from 'express-validator'; +import * as session from 'express-session'; +import * as passport from 'passport'; +import * as bodyParser from 'body-parser'; +import * as cookieParser from 'cookie-parser'; +import expressFlash = require('express-flash'); +import flash = require('connect-flash'); +import { Database } from './database'; +import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/controllers/user_controller'; +const MongoStore = require('connect-mongo')(session); +import RouteManager from './RouteManager'; +import * as webpack from 'webpack'; +const config = require('../../webpack.config'); +const compiler = webpack(config); +import * as wdm from 'webpack-dev-middleware'; +import * as whm from 'webpack-hot-middleware'; +import * as fs from 'fs'; +import * as request from 'request'; +import RouteSubscriber from './RouteSubscriber'; +import { publicDirectory } from '.'; +import { logPort, } from './ActionUtilities'; +import { timeMap } from './ApiManagers/UserManager'; +import { blue, yellow } from 'colors'; + +/* RouteSetter is a wrapper around the server that prevents the server + from being exposed. */ +export type RouteSetter = (server: RouteManager) => void; +export let disconnect: Function; + +export default async function InitializeServer(routeSetter: RouteSetter) { + const app = buildWithMiddleware(express()); + + app.use(express.static(publicDirectory)); + app.use("/images", express.static(publicDirectory)); + + app.use("*", ({ user, originalUrl }, res, next) => { + if (user && !originalUrl.includes("Heartbeat")) { + const userEmail = (user as any).email; + if (userEmail) { + timeMap[userEmail] = Date.now(); + } + } + if (!user && originalUrl === "/") { + return res.redirect("/login"); + } + next(); + }); + + app.use(wdm(compiler, { publicPath: config.output.publicPath })); + app.use(whm(compiler)); + + registerAuthenticationRoutes(app); + registerCorsProxy(app); + + const isRelease = determineEnvironment(); + + routeSetter(new RouteManager(app, isRelease)); + + const serverPort = isRelease ? Number(process.env.serverPort) : 1050; + const server = app.listen(serverPort, () => { + logPort("server", Number(serverPort)); + console.log(); + }); + disconnect = async () => new Promise(resolve => server.close(resolve)); + + return isRelease; +} + +const week = 7 * 24 * 60 * 60 * 1000; +const secret = "64d6866242d3b5a5503c675b32c9605e4e90478e9b77bcf2bc"; + +function buildWithMiddleware(server: express.Express) { + [ + cookieParser(), + session({ + secret, + resave: true, + cookie: { maxAge: week }, + saveUninitialized: true, + store: new MongoStore({ url: Database.url }) + }), + flash(), + expressFlash(), + bodyParser.json({ limit: "10mb" }), + bodyParser.urlencoded({ extended: true }), + expressValidator(), + passport.initialize(), + passport.session(), + (req: express.Request, res: express.Response, next: express.NextFunction) => { + res.locals.user = req.user; + next(); + } + ].forEach(next => server.use(next)); + return server; +} + +/* Determine if the enviroment is dev mode or release mode. */ +function determineEnvironment() { + const isRelease = process.env.RELEASE === "true"; + + const color = isRelease ? blue : yellow; + const label = isRelease ? "release" : "development"; + console.log(`\nrunning server in ${color(label)} mode`); + + let clientUtils = fs.readFileSync("./src/client/util/ClientUtils.ts.temp", "utf8"); + clientUtils = `//AUTO-GENERATED FILE: DO NOT EDIT\n${clientUtils.replace('"mode"', String(isRelease))}`; + fs.writeFileSync("./src/client/util/ClientUtils.ts", clientUtils, "utf8"); + + return isRelease; +} + +function registerAuthenticationRoutes(server: express.Express) { + server.get("/signup", getSignup); + server.post("/signup", postSignup); + + server.get("/login", getLogin); + server.post("/login", postLogin); + + server.get("/logout", getLogout); + + server.get("/forgotPassword", getForgot); + server.post("/forgotPassword", postForgot); + + const reset = new RouteSubscriber("resetPassword").add("token").build; + server.get(reset, getReset); + server.post(reset, postReset); +} + +function registerCorsProxy(server: express.Express) { + const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; + server.use("/corsProxy", (req, res) => { + req.pipe(request(decodeURIComponent(req.url.substring(1)))).on("response", res => { + const headers = Object.keys(res.headers); + headers.forEach(headerName => { + const header = res.headers[headerName]; + if (Array.isArray(header)) { + res.headers[headerName] = header.filter(h => !headerCharRegex.test(h)); + } else if (header) { + if (headerCharRegex.test(header as any)) { + delete res.headers[headerName]; + } + } + }); + }).pipe(res); + }); +} \ No newline at end of file -- cgit v1.2.3-70-g09d2