diff options
| author | Sam Wilkins <abdullah_ahmed@brown.edu> | 2019-03-12 16:28:28 -0400 |
|---|---|---|
| committer | Sam Wilkins <abdullah_ahmed@brown.edu> | 2019-03-12 16:28:28 -0400 |
| commit | 884647be52cf6b3c7e3132dce7d9133875d9a9cd (patch) | |
| tree | af5041ec3ec76fb45944c2a81e3716b3a866c3dd /src/server | |
| parent | d2383acb7123c0c032822745171f7df8baa77518 (diff) | |
| parent | 91338b2cea4f006e5813145009cb471b17679cd7 (diff) | |
Merged with master, refactor with route store
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/Message.ts | 2 | ||||
| -rw-r--r-- | src/server/RouteStore.ts | 28 | ||||
| -rw-r--r-- | src/server/ServerUtil.ts | 35 | ||||
| -rw-r--r-- | src/server/authentication/config/passport.ts | 3 | ||||
| -rw-r--r-- | src/server/authentication/controllers/user_controller.ts | 47 | ||||
| -rw-r--r-- | src/server/authentication/models/user_model.ts | 2 | ||||
| -rw-r--r-- | src/server/database.ts | 4 | ||||
| -rw-r--r-- | src/server/index.ts | 137 |
8 files changed, 153 insertions, 105 deletions
diff --git a/src/server/Message.ts b/src/server/Message.ts index 5e97a5edf..8a00f6b59 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -45,7 +45,7 @@ export class GetFieldArgs { } export enum Types { - Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference, Html, Ink, PDF + Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference, Html, Video, Audio, Ink, PDF } export class DocumentTransfer implements Transferable { diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts new file mode 100644 index 000000000..ace2152d7 --- /dev/null +++ b/src/server/RouteStore.ts @@ -0,0 +1,28 @@ +// PREPEND ALL ROUTES WITH FORWARD SLASHES! + +export enum RouteStore { + // GENERAL + root = "/root", + home = "/home", + corsProxy = "/corsProxy", + delete = "/delete", + + // UPLOAD AND STATIC FILE SERVING + public = "/public", + upload = "/upload", + images = "/images", + + // USER AND WORKSPACES + addWorkspace = "/addWorkspaceId", + getAllWorkspaces = "/getAllWorkspaceIds", + getActiveWorkspace = "/getActiveWorkspaceId", + setActiveWorkspace = "/setActiveWorkspaceId", + + // AUTHENTICATION + signup = "/signup", + login = "/login", + logout = "/logout", + forgot = "/forgotpassword", + reset = "/reset/:token", + +}
\ No newline at end of file diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts index 3b9d14891..5331c9e30 100644 --- a/src/server/ServerUtil.ts +++ b/src/server/ServerUtil.ts @@ -1,17 +1,24 @@ -import {HtmlField} from '../fields/HtmlField'; + +import { Field } from './../fields/Field'; +import { TextField } from './../fields/TextField'; +import { NumberField } from './../fields/NumberField'; +import { RichTextField } from './../fields/RichTextField'; +import { Key } from './../fields/Key'; +import { ImageField } from './../fields/ImageField'; +import { ListField } from './../fields/ListField'; +import { Document } from './../fields/Document'; +import { Server } from './../client/Server'; +import { Types } from './Message'; +import { Utils } from '../Utils'; +import { HtmlField } from '../fields/HtmlField'; +import { WebField } from '../fields/WebField'; +import { AudioField } from '../fields/AudioField'; +import { VideoField } from '../fields/VideoField'; import {InkField} from '../fields/InkField'; import {PDFField} from '../fields/PDFField'; -import {WebField} from '../fields/WebField'; -import {Utils} from '../Utils'; -import {Document} from './../fields/Document'; -import {Field} from './../fields/Field'; -import {ImageField} from './../fields/ImageField'; -import {Key} from './../fields/Key'; -import {ListField} from './../fields/ListField'; -import {NumberField} from './../fields/NumberField'; -import {RichTextField} from './../fields/RichTextField'; -import {TextField} from './../fields/TextField'; -import {Types} from './Message'; + + + export class ServerUtils { public static FromJson(json: any): Field { @@ -44,6 +51,10 @@ export class ServerUtils { return new PDFField(new URL(data), id, false) case Types.List: return ListField.FromJson(id, data) + case Types.Audio: + return new AudioField(new URL(data), id, false) + case Types.Video: + return new VideoField(new URL(data), id, false) case Types.Ink: return InkField.FromJson(id, data); case Types.Document: diff --git a/src/server/authentication/config/passport.ts b/src/server/authentication/config/passport.ts index d90bedb18..b6fe15655 100644 --- a/src/server/authentication/config/passport.ts +++ b/src/server/authentication/config/passport.ts @@ -4,6 +4,7 @@ import * as mongodb from 'mongodb'; import * as _ from "lodash"; import { default as User } from '../models/user_model'; import { Request, Response, NextFunction } from "express"; +import { RouteStore } from '../../RouteStore'; const LocalStrategy = passportLocal.Strategy; @@ -35,7 +36,7 @@ export let isAuthenticated = (req: Request, res: Response, next: NextFunction) = if (req.isAuthenticated()) { return next(); } - return res.redirect("/login"); + return res.redirect(RouteStore.login); } export let isAuthorized = (req: Request, res: Response, next: NextFunction) => { diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts index 7b89b5152..2cef958e8 100644 --- a/src/server/authentication/controllers/user_controller.ts +++ b/src/server/authentication/controllers/user_controller.ts @@ -10,18 +10,7 @@ import * as pug from 'pug'; import * as async from 'async'; import * as nodemailer from 'nodemailer'; import c = require("crypto"); - - -/** - * GET / - * Whenever a user navigates to the root of Dash - * (doesn't specify a sub-route), redirect to login. - * If the user is already signed in, it will effectively - * automatically redirect them to /home instead - */ -export let getEntry = (req: Request, res: Response) => { - res.redirect("/login"); -} +import { RouteStore } from "../../RouteStore"; /** * GET /signup @@ -31,7 +20,7 @@ export let getEntry = (req: Request, res: Response) => { export let getSignup = (req: Request, res: Response) => { if (req.user) { let user = req.user; - return res.redirect("/home"); + return res.redirect(RouteStore.home); } res.render("signup.pug", { title: "Sign Up", @@ -56,7 +45,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { title: "Sign Up", user: req.user, }); - return res.redirect("/signup"); + return res.redirect(RouteStore.signup); } const email = req.body.email; @@ -71,7 +60,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { User.findOne({ email }, (err, existingUser) => { if (err) { return next(err); } if (existingUser) { - return res.redirect("/login"); + return res.redirect(RouteStore.login); } user.save((err) => { if (err) { return next(err); } @@ -79,7 +68,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { if (err) { return next(err); } - res.redirect("/home"); + res.redirect(RouteStore.home); }); }); }); @@ -93,7 +82,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { */ export let getLogin = (req: Request, res: Response) => { if (req.user) { - return res.redirect("/home"); + return res.redirect(RouteStore.home); } res.render("login.pug", { title: "Log In", @@ -104,7 +93,7 @@ export let getLogin = (req: Request, res: Response) => { /** * POST /login * Sign in using email and password. - * On failure, redirect to login page + * On failure, redirect to signup page */ export let postLogin = (req: Request, res: Response, next: NextFunction) => { req.assert("email", "Email is not valid").isEmail(); @@ -115,17 +104,17 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => { if (errors) { req.flash("errors", "Unable to login at this time. Please try again."); - return res.redirect("/signup"); + return res.redirect(RouteStore.signup); } passport.authenticate("local", (err: Error, user: DashUserModel, info: IVerifyOptions) => { if (err) { return next(err); } if (!user) { - return res.redirect("/signup"); + return res.redirect(RouteStore.signup); } req.logIn(user, (err) => { if (err) { return next(err); } - res.redirect("/home"); + res.redirect(RouteStore.home); }); })(req, res, next); }; @@ -136,16 +125,12 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => { * and destroys the user's current session. */ export let getLogout = (req: Request, res: Response) => { - const dashUser: DashUserModel | undefined = req.user; - if (dashUser) { - dashUser.update({ $set: { didSelectSessionWorkspace: false } }, () => { }) - } req.logout(); const sess = req.session; if (sess) { sess.destroy((err) => { if (err) { console.log(err); } }); } - res.redirect('/login'); + res.redirect(RouteStore.login); } export let getForgot = function (req: Request, res: Response) { @@ -172,7 +157,7 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio User.findOne({ email }, function (err, user: DashUserModel) { if (!user) { // NO ACCOUNT WITH SUBMITTED EMAIL - return res.redirect('/forgot'); + return res.redirect(RouteStore.forgot); } user.passwordResetToken = token; user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR @@ -205,14 +190,14 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio } ], function (err) { if (err) return next(err); - res.redirect('/forgot'); + res.redirect(RouteStore.forgot); }) } export let getReset = function (req: Request, res: Response) { User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) { if (!user || err) { - return res.redirect('/forgot'); + return res.redirect(RouteStore.forgot); } res.render("reset.pug", { title: "Reset Password", @@ -242,7 +227,7 @@ export let postReset = function (req: Request, res: Response) { user.save(function (err) { if (err) { - return res.redirect("/login"); + return res.redirect(RouteStore.login); } req.logIn(user, function (err) { if (err) { @@ -273,6 +258,6 @@ export let postReset = function (req: Request, res: Response) { }); } ], function (err) { - res.redirect('/login'); + res.redirect(RouteStore.login); }); }
\ No newline at end of file diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/models/user_model.ts index 29076ba19..3d4ed6896 100644 --- a/src/server/authentication/models/user_model.ts +++ b/src/server/authentication/models/user_model.ts @@ -23,6 +23,7 @@ export type DashUserModel = mongoose.Document & { allWorkspaceIds: Array<String>, activeWorkspaceId: String, + activeUsersId: String, profile: { name: string, @@ -53,6 +54,7 @@ const userSchema = new mongoose.Schema({ default: [] }, activeWorkspaceId: String, + activeUsersId: String, facebook: String, twitter: String, diff --git a/src/server/database.ts b/src/server/database.ts index 1553dd94e..f414266e2 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -16,12 +16,12 @@ export class Database { }) } - public update(id: string, value: any) { + public update(id: string, value: any, callback: () => void) { if (this.db) { let collection = this.db.collection('documents'); collection.update({ _id: id }, { $set: value }, { upsert: true - }); + }, callback); } } diff --git a/src/server/index.ts b/src/server/index.ts index fad30f3ad..6f6f620d8 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -3,6 +3,8 @@ const app = express() import * as webpack from 'webpack' import * as wdm from 'webpack-dev-middleware'; import * as whm from 'webpack-hot-middleware'; +import * as path from 'path' +import * as formidable from 'formidable' import * as passport from 'passport'; import { MessageStore, Message, SetFieldArgs, GetFieldArgs, Transferable } from "./Message"; import { Client } from './Client'; @@ -17,7 +19,7 @@ import * as bcrypt from "bcrypt-nodejs"; import { Document } from '../fields/Document'; import * as io from 'socket.io' import * as passportConfig from './authentication/config/passport'; -import { getLogin, postLogin, getSignup, postSignup, getLogout, getEntry, postReset, getForgot, postForgot, getReset } from './authentication/controllers/user_controller'; +import { getLogin, postLogin, getSignup, postSignup, getLogout, postReset, getForgot, postForgot, getReset } from './authentication/controllers/user_controller'; const config = require('../../webpack.config'); const compiler = webpack(config); const port = 1050; // default port to listen @@ -33,10 +35,10 @@ import c = require("crypto"); const MongoStore = require('connect-mongo')(session); const mongoose = require('mongoose'); import { performance } from 'perf_hooks' -import * as path from 'path' import User, { DashUserModel } from './authentication/models/user_model'; import * as fs from 'fs'; import * as request from 'request' +import { RouteStore } from './RouteStore'; const download = (url: string, dest: fs.PathLike) => { request.get(url).pipe(fs.createWriteStream(dest)); @@ -61,11 +63,7 @@ app.use(session({ url: 'mongodb://localhost:27017/Dash' }) })); -// app.use(cookieSession({ -// name: 'authentication', -// keys: [`${c.randomBytes(8)}`, `${c.randomBytes(8)}`, `${c.randomBytes(8)}`], -// maxAge: 7 * 24 * 60 * 60 * 1000 -// })); + app.use(flash()); app.use(expressFlash()); app.use(bodyParser.json()); @@ -78,97 +76,123 @@ app.use((req, res, next) => { next(); }); -// AUTHENTICATION ROUTING - enum Method { - Get, - Post + GET, + POST } +/** + * Please invoke this function when adding a new route to Dash's server. + * It ensures that any requests leading to or containing user-sensitive information + * does not execute unless Passport authentication detects a user logged in. + * @param method whether or not the request is a GET or a POST + * @param route the forward slash prepended path name (reference and add to RouteStore.ts) + * @param handler the action to invoke, recieving a DashUserModel and the expected request and response + * @param onRejection an optional callback invoked on return if no user is found to be logged in + */ function addSecureRoute(method: Method, route: string, handler: (user: DashUserModel, req: express.Request, res: express.Response) => void, - nope: (res: express.Response) => any) { - route = "/" + route; + onRejection: (res: express.Response) => any = (res) => res.redirect(RouteStore.logout)) { switch (method) { - case Method.Get: + case Method.GET: app.get(route, (req, res) => { const dashUser: DashUserModel = req.user; - if (!dashUser) return nope(res); + if (!dashUser) return onRejection(res); handler(dashUser, req, res); }); break; - case Method.Post: + case Method.POST: app.post(route, (req, res) => { const dashUser: DashUserModel = req.user; - if (!dashUser) return nope(res); + if (!dashUser) return onRejection(res); handler(dashUser, req, res); }); break; } } -// *** -// Look for the definitions of these get and post -// functions in the exports of user.ts +// STATIC FILE SERVING -addSecureRoute(Method.Get, "home", (user, req, res) => { +let FieldStore: ObservableMap<FieldId, Field> = new ObservableMap(); + +app.use(express.static(__dirname + RouteStore.public)); +app.use(RouteStore.images, express.static(__dirname + RouteStore.public)) + +addSecureRoute(Method.POST, RouteStore.upload, (user, req, res) => { + let form = new formidable.IncomingForm() + form.uploadDir = __dirname + "/public/files/" + form.keepExtensions = true + // let path = req.body.path; + console.log("upload") + form.parse(req, (err, fields, files) => { + console.log("parsing") + let names: any[] = []; + for (const name in files) { + let file = files[name]; + names.push(`/files/` + path.basename(file.path)); + } + res.send(names); + }); +}); + +// anyone attempting to navigate to localhost at this port will +// first have to login +addSecureRoute(Method.GET, RouteStore.root, (user, req, res) => { + +}, res => { + res.send() +}); + +// YAY! SHOW THEM THEIR WORKSPACES NOW +addSecureRoute(Method.GET, RouteStore.home, (user, req, res) => { res.sendFile(path.join(__dirname, '../../deploy/index.html')); -}, res => res.redirect("/login")) +}); -addSecureRoute(Method.Get, "getActiveWorkspaceId", (user, req, res) => { +addSecureRoute(Method.GET, RouteStore.getActiveWorkspace, (user, req, res) => { res.send(user.activeWorkspaceId || ""); -}, () => { }); +}); -addSecureRoute(Method.Get, "getAllWorkspaceIds", (user, req, res) => { +addSecureRoute(Method.GET, RouteStore.getAllWorkspaces, (user, req, res) => { res.send(JSON.stringify(user.allWorkspaceIds as Array<String>)); -}, () => { }); +}); -addSecureRoute(Method.Post, "setActiveWorkspaceId", (user, req) => { +addSecureRoute(Method.POST, RouteStore.setActiveWorkspace, (user, req) => { user.update({ $set: { activeWorkspaceId: req.body.target } }, () => { }); -}, () => { }); +}); -addSecureRoute(Method.Post, "addWorkspaceId", (user, req) => { +addSecureRoute(Method.POST, RouteStore.addWorkspace, (user, req) => { user.update({ $push: { allWorkspaceIds: req.body.target } }, () => { }); -}, () => { }); +}); -// anyone attempting to navigate to localhost at this port will -// first have to login -app.get("/", getEntry); +// AUTHENTICATION // Sign Up -app.get("/signup", getSignup); -app.post("/signup", postSignup); +app.get(RouteStore.signup, getSignup); +app.post(RouteStore.signup, postSignup); // Log In -app.get("/login", getLogin); -app.post("/login", postLogin); +app.get(RouteStore.login, getLogin); +app.post(RouteStore.login, postLogin); // Log Out -app.get('/logout', getLogout); - -// *** +app.get(RouteStore.logout, getLogout); // FORGOT PASSWORD EMAIL HANDLING -app.get('/forgot', getForgot) -app.post('/forgot', postForgot) +app.get(RouteStore.forgot, getForgot) +app.post(RouteStore.forgot, postForgot) // RESET PASSWORD EMAIL HANDLING -app.get('/reset/:token', getReset); -app.post('/reset/:token', postReset); +app.get(RouteStore.reset, getReset); +app.post(RouteStore.reset, postReset); -let FieldStore: ObservableMap<FieldId, Field> = new ObservableMap(); -app.get("/hello", (req, res) => { - res.send("<p>Hello</p>"); -}) - -app.use("/corsProxy", (req, res) => { +app.use(RouteStore.corsProxy, (req, res) => { req.pipe(request(req.url.substring(1))).pipe(res); }); -app.get("/delete", (req, res) => { +app.get(RouteStore.delete, (req, res) => { deleteAll(); - res.redirect("/"); + res.redirect(RouteStore.home); }); app.use(wdm(compiler, { @@ -208,10 +232,6 @@ function barReceived(guid: String) { clients[guid.toString()] = new Client(guid.toString()); } -function addDocument(document: Document) { - -} - function getField([id, callback]: [string, (result: any) => void]) { Database.Instance.getDocument(id, (result: any) => { if (result) { @@ -228,8 +248,9 @@ function getFields([ids, callback]: [string[], (result: any) => void]) { } function setField(socket: Socket, newValue: Transferable) { - Database.Instance.update(newValue._id, newValue) - socket.broadcast.emit(MessageStore.SetField.Message, newValue) + Database.Instance.update(newValue._id, newValue, () => { + socket.broadcast.emit(MessageStore.SetField.Message, newValue); + }) } server.listen(serverPort); |
