aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/server')
-rw-r--r--src/server/ApiManagers/UserManager.ts77
-rw-r--r--src/server/RouteManager.ts58
-rw-r--r--src/server/authentication/AuthenticationManager.ts31
-rw-r--r--src/server/authentication/DashUserModel.ts106
-rw-r--r--src/server/index.ts108
-rw-r--r--src/server/websocket.ts194
6 files changed, 296 insertions, 278 deletions
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index 7be8a1e9f..53e55c1c3 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -1,10 +1,10 @@
-import ApiManager, { Registration } from "./ApiManager";
-import { Method } from "../RouteManager";
-import { Database } from "../database";
-import { msToTime } from "../ActionUtilities";
-import * as bcrypt from "bcrypt-nodejs";
-import { Opt } from "../../fields/Doc";
-import { WebSocket } from "../websocket";
+import ApiManager, { Registration } from './ApiManager';
+import { Method } from '../RouteManager';
+import { Database } from '../database';
+import { msToTime } from '../ActionUtilities';
+import * as bcrypt from 'bcrypt-nodejs';
+import { Opt } from '../../fields/Doc';
+import { WebSocket } from '../websocket';
export const timeMap: { [id: string]: number } = {};
interface ActivityUnit {
@@ -13,28 +13,26 @@ interface ActivityUnit {
}
export default class UserManager extends ApiManager {
-
protected initialize(register: Registration): void {
-
register({
method: Method.GET,
- subscription: "/getUsers",
+ subscription: '/getUsers',
secureHandler: async ({ res }) => {
- const cursor = await Database.Instance.query({}, { email: 1, linkDatabaseId: 1, sharingDocumentId: 1 }, "users");
+ const cursor = await Database.Instance.query({}, { email: 1, linkDatabaseId: 1, sharingDocumentId: 1 }, 'users');
const results = await cursor.toArray();
res.send(results.map((user: any) => ({ email: user.email, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })));
- }
+ },
});
register({
method: Method.POST,
- subscription: "/setCacheDocumentIds",
+ subscription: '/setCacheDocumentIds',
secureHandler: async ({ user, req, res }) => {
const result: any = {};
user.cacheDocumentIds = req.body.cacheDocumentIds;
user.save(err => {
if (err) {
- result.error = [{ msg: "Error while caching documents" }];
+ result.error = [{ msg: 'Error while caching documents' }];
}
});
@@ -42,32 +40,35 @@ export default class UserManager extends ApiManager {
// console.log(e);
// });
res.send(result);
- }
+ },
});
register({
method: Method.GET,
- subscription: "/getUserDocumentIds",
- secureHandler: ({ res, user }) => res.send({ userDocumentId: user.userDocumentId, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })
+ subscription: '/getUserDocumentIds',
+ secureHandler: ({ res, user }) => res.send({ userDocumentId: user.userDocumentId, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId }),
+ publicHandler: ({ res }) => res.send({ userDocumentId: '__guest__', linkDatabaseId: 3, sharingDocumentId: 2 }),
});
register({
method: Method.GET,
- subscription: "/getSharingDocumentId",
- secureHandler: ({ res, user }) => res.send(user.sharingDocumentId)
+ subscription: '/getSharingDocumentId',
+ secureHandler: ({ res, user }) => res.send(user.sharingDocumentId),
+ publicHandler: ({ res }) => res.send(2),
});
register({
method: Method.GET,
- subscription: "/getLinkDatabaseId",
- secureHandler: ({ res, user }) => res.send(user.linkDatabaseId)
+ subscription: '/getLinkDatabaseId',
+ secureHandler: ({ res, user }) => res.send(user.linkDatabaseId),
+ publicHandler: ({ res }) => res.send(3),
});
register({
method: Method.GET,
- subscription: "/getCurrentUser",
+ subscription: '/getCurrentUser',
secureHandler: ({ res, user: { _id, email, cacheDocumentIds } }) => res.send(JSON.stringify({ id: _id, email, cacheDocumentIds })),
- publicHandler: ({ res }) => res.send(JSON.stringify({ id: "__guest__", email: "" }))
+ publicHandler: ({ res }) => res.send(JSON.stringify({ id: '__guest__', email: 'guest' })),
});
register({
@@ -80,7 +81,7 @@ export default class UserManager extends ApiManager {
const validated = await new Promise<Opt<boolean>>(resolve => {
bcrypt.compare(curr_pass, user.password, (err, passwords_match) => {
if (err || !passwords_match) {
- result.error = [{ msg: "Incorrect current password" }];
+ result.error = [{ msg: 'Incorrect current password' }];
res.send(result);
resolve(undefined);
} else {
@@ -93,10 +94,10 @@ export default class UserManager extends ApiManager {
return;
}
- req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 });
- req.assert("new_confirm", "Passwords do not match").equals(new_pass);
+ req.assert('new_pass', 'Password must be at least 4 characters long').len({ min: 4 });
+ req.assert('new_confirm', 'Passwords do not match').equals(new_pass);
if (curr_pass === new_pass) {
- result.error = [{ msg: "Current and new password are the same" }];
+ result.error = [{ msg: 'Current and new password are the same' }];
}
// was there error in validating new passwords?
if (req.validationErrors()) {
@@ -113,17 +114,17 @@ export default class UserManager extends ApiManager {
user.save(err => {
if (err) {
- result.error = [{ msg: "Error while saving new password" }];
+ result.error = [{ msg: 'Error while saving new password' }];
}
});
res.send(result);
- }
+ },
});
register({
method: Method.GET,
- subscription: "/activity",
+ subscription: '/activity',
secureHandler: ({ res }) => {
const now = Date.now();
@@ -135,25 +136,23 @@ export default class UserManager extends ApiManager {
const socketPair = Array.from(WebSocket.socketMap).find(pair => pair[1] === user);
if (socketPair && !socketPair[0].disconnected) {
const duration = now - time;
- const target = (duration / 1000) < (60 * 5) ? activeTimes : inactiveTimes;
+ const target = duration / 1000 < 60 * 5 ? activeTimes : inactiveTimes;
target.push({ user, duration });
}
}
- const process = (target: { user: string, duration: number }[]) => {
+ const process = (target: { user: string; duration: number }[]) => {
const comparator = (first: ActivityUnit, second: ActivityUnit) => first.duration - second.duration;
const sorted = target.sort(comparator);
return sorted.map(({ user, duration }) => `${user} (${msToTime(duration)})`);
};
- res.render("user_activity.pug", {
- title: "User Activity",
+ res.render('user_activity.pug', {
+ title: 'User Activity',
active: process(activeTimes),
- inactive: process(inactiveTimes)
+ inactive: process(inactiveTimes),
});
- }
+ },
});
-
}
-
-} \ No newline at end of file
+}
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index aa9bfcfa7..5683cd539 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -1,12 +1,12 @@
import { cyan, green, red } from 'colors';
import { Express, Request, Response } from 'express';
-import { AdminPriviliges } from ".";
-import { DashUserModel } from "./authentication/DashUserModel";
-import RouteSubscriber from "./RouteSubscriber";
+import { AdminPriviliges } from '.';
+import { DashUserModel } from './authentication/DashUserModel';
+import RouteSubscriber from './RouteSubscriber';
export enum Method {
GET,
- POST
+ POST,
}
export interface CoreArguments {
@@ -33,13 +33,13 @@ const registered = new Map<string, Set<Method>>();
enum RegistrationError {
Malformed,
- Duplicate
+ Duplicate,
}
export default class RouteManager {
private server: Express;
private _isRelease: boolean;
- private failedRegistrations: { route: string, reason: RegistrationError }[] = [];
+ private failedRegistrations: { route: string; reason: RegistrationError }[] = [];
public get isRelease() {
return this._isRelease;
@@ -74,39 +74,42 @@ export default class RouteManager {
}
process.exit(1);
} else {
- console.log(green("all server routes have been successfully registered:"));
- Array.from(registered.keys()).sort().forEach(route => console.log(cyan(route)));
+ console.log(green('all server routes have been successfully registered:'));
+ Array.from(registered.keys())
+ .sort()
+ .forEach(route => console.log(cyan(route)));
console.log();
}
- }
+ };
static routes: string[] = [];
/**
- *
- * @param initializer
+ *
+ * @param initializer
*/
addSupervisedRoute = (initializer: RouteInitializer): void => {
const { method, subscription, secureHandler, publicHandler, errorHandler, requireAdminInRelease: requireAdmin } = initializer;
- typeof (initializer.subscription) === "string" && RouteManager.routes.push(initializer.subscription);
+ typeof initializer.subscription === 'string' && RouteManager.routes.push(initializer.subscription);
initializer.subscription instanceof RouteSubscriber && RouteManager.routes.push(initializer.subscription.root);
- initializer.subscription instanceof Array && initializer.subscription.map(sub => {
- typeof (sub) === "string" && RouteManager.routes.push(sub);
- sub instanceof RouteSubscriber && RouteManager.routes.push(sub.root);
- });
+ initializer.subscription instanceof Array &&
+ initializer.subscription.map(sub => {
+ typeof sub === 'string' && RouteManager.routes.push(sub);
+ sub instanceof RouteSubscriber && RouteManager.routes.push(sub.root);
+ });
const isRelease = this._isRelease;
const supervised = async (req: Request, res: Response) => {
let user = req.user as Partial<DashUserModel> | undefined;
const { originalUrl: target } = req;
- if (process.env.DB === "MEM" && !user) {
- user = { id: "guest", email: "", userDocumentId: "guestDocId" };
+ if (process.env.DB === 'MEM' && !user) {
+ user = { id: 'guest', email: 'guest', userDocumentId: '__guest__' };
}
const core = { req, res, isRelease };
const tryExecute = async (toExecute: (args: any) => any | Promise<any>, args: any) => {
try {
await toExecute(args);
} catch (e) {
- console.log(red(target), user && ("email" in user) ? "<user logged out>" : undefined);
+ console.log(red(target), user && 'email' in user ? '<user logged out>' : undefined);
if (errorHandler) {
errorHandler({ ...core, error: e });
} else {
@@ -119,7 +122,7 @@ export default class RouteManager {
if (AdminPriviliges.get(user.id)) {
AdminPriviliges.delete(user.id);
} else {
- return res.redirect(`/admin/${req.originalUrl.substring(1).replace("/", ":")}`);
+ return res.redirect(`/admin/${req.originalUrl.substring(1).replace('/', ':')}`);
}
}
await tryExecute(secureHandler, { ...core, user });
@@ -128,10 +131,10 @@ export default class RouteManager {
if (publicHandler) {
await tryExecute(publicHandler, core);
if (!res.headersSent) {
- res.redirect("/login");
+ // res.redirect("/login");
}
} else {
- res.redirect("/login");
+ res.redirect('/login');
}
}
setTimeout(() => {
@@ -144,7 +147,7 @@ export default class RouteManager {
};
const subscribe = (subscriber: RouteSubscriber | string) => {
let route: string;
- if (typeof subscriber === "string") {
+ if (typeof subscriber === 'string') {
route = subscriber;
} else {
route = subscriber.build;
@@ -152,7 +155,7 @@ export default class RouteManager {
if (!/^\/$|^\/[A-Za-z\*]+(\/\:[A-Za-z?_\*]+)*$/g.test(route)) {
this.failedRegistrations.push({
reason: RegistrationError.Malformed,
- route
+ route,
});
} else {
const existing = registered.get(route);
@@ -160,7 +163,7 @@ export default class RouteManager {
if (existing.has(method)) {
this.failedRegistrations.push({
reason: RegistrationError.Duplicate,
- route
+ route,
});
return;
}
@@ -184,15 +187,14 @@ export default class RouteManager {
} else {
subscribe(subscription);
}
- }
-
+ };
}
export const STATUS = {
OK: 200,
BAD_REQUEST: 400,
EXECUTION_ERROR: 500,
- PERMISSION_DENIED: 403
+ PERMISSION_DENIED: 403,
};
export function _error(res: Response, message: string, error?: any) {
diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index 3622be4c5..52d876e95 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -1,4 +1,4 @@
-import { default as User, DashUserModel } from './DashUserModel';
+import { default as User, DashUserModel, initializeGuest } from './DashUserModel';
import { Request, Response, NextFunction } from 'express';
import * as passport from 'passport';
import { IVerifyOptions } from 'passport-local';
@@ -30,6 +30,7 @@ export let getSignup = (req: Request, res: Response) => {
* Create a new local account.
*/
export let postSignup = (req: Request, res: Response, next: NextFunction) => {
+ const email = req.body.email as String;
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password must be at least 4 characters long').len({ min: 4 });
req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
@@ -41,15 +42,14 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
return res.redirect('/signup');
}
- const email = req.body.email as String;
const password = req.body.password;
const model = {
email,
password,
- userDocumentId: Utils.GenerateGuid(),
- sharingDocumentId: Utils.GenerateGuid(),
- linkDatabaseId: Utils.GenerateGuid(),
+ userDocumentId: email === 'guest' ? '__guest__' : Utils.GenerateGuid(),
+ sharingDocumentId: email === 'guest' ? 2 : Utils.GenerateGuid(),
+ linkDatabaseId: email === 'guest' ? 3 : Utils.GenerateGuid(),
cacheDocumentIds: '',
} as Partial<DashUserModel>;
@@ -106,18 +106,22 @@ export let getLogin = (req: Request, res: Response) => {
* On failure, redirect to signup page
*/
export let postLogin = (req: Request, res: Response, next: NextFunction) => {
- req.assert('email', 'Email is not valid').isEmail();
- req.assert('password', 'Password cannot be blank').notEmpty();
- req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
-
- const errors = req.validationErrors();
+ if (req.body.email === '') {
+ User.findOne({ email: 'guest' }, (err: any, user: DashUserModel) => !user && initializeGuest());
+ req.body.email = 'guest';
+ req.body.password = 'guest';
+ } else {
+ req.assert('email', 'Email is not valid').isEmail();
+ req.assert('password', 'Password cannot be blank').notEmpty();
+ req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
+ }
- if (errors) {
+ if (req.validationErrors()) {
req.flash('errors', 'Unable to login at this time. Please try again.');
return res.redirect('/signup');
}
- passport.authenticate('local', (err: Error, user: DashUserModel, _info: IVerifyOptions) => {
+ const callback = (err: Error, user: DashUserModel, _info: IVerifyOptions) => {
if (err) {
next(err);
return;
@@ -132,7 +136,8 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
}
tryRedirectToTarget(req, res);
});
- })(req, res, next);
+ };
+ setTimeout(() => passport.authenticate('local', callback)(req, res, next), 500);
};
/**
diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts
index bee28b96d..a1883beab 100644
--- a/src/server/authentication/DashUserModel.ts
+++ b/src/server/authentication/DashUserModel.ts
@@ -1,13 +1,13 @@
//@ts-ignore
-import * as bcrypt from "bcrypt-nodejs";
+import * as bcrypt from 'bcrypt-nodejs';
//@ts-ignore
import * as mongoose from 'mongoose';
export type DashUserModel = mongoose.Document & {
- email: String,
- password: string,
- passwordResetToken?: string,
- passwordResetExpires?: Date,
+ email: String;
+ password: string;
+ passwordResetToken?: string;
+ passwordResetExpires?: Date;
userDocumentId: string;
sharingDocumentId: string;
@@ -15,66 +15,74 @@ export type DashUserModel = mongoose.Document & {
cacheDocumentIds: string;
profile: {
- name: string,
- gender: string,
- location: string,
- website: string,
- picture: string
- },
+ name: string;
+ gender: string;
+ location: string;
+ website: string;
+ picture: string;
+ };
- comparePassword: comparePasswordFunction,
+ comparePassword: comparePasswordFunction;
};
type comparePasswordFunction = (candidatePassword: string, cb: (err: any, isMatch: any) => {}) => void;
export type AuthToken = {
- accessToken: string,
- kind: string
+ accessToken: string;
+ kind: string;
};
-const userSchema = new mongoose.Schema({
- email: String,
- password: String,
- passwordResetToken: String,
- passwordResetExpires: Date,
+const userSchema = new mongoose.Schema(
+ {
+ email: String,
+ password: String,
+ passwordResetToken: String,
+ passwordResetExpires: Date,
- userDocumentId: String, // id that identifies a document which hosts all of a user's account data
- sharingDocumentId: String, // id that identifies a document that stores documents shared to a user, their user color, and any additional info needed to communicate between users
- linkDatabaseId: String,
- cacheDocumentIds: String, // set of document ids to retreive on startup
+ userDocumentId: String, // id that identifies a document which hosts all of a user's account data
+ sharingDocumentId: String, // id that identifies a document that stores documents shared to a user, their user color, and any additional info needed to communicate between users
+ linkDatabaseId: String,
+ cacheDocumentIds: String, // set of document ids to retreive on startup
- facebook: String,
- twitter: String,
- google: String,
+ facebook: String,
+ twitter: String,
+ google: String,
- profile: {
- name: String,
- gender: String,
- location: String,
- website: String,
- picture: String
- }
-}, { timestamps: true });
+ profile: {
+ name: String,
+ gender: String,
+ location: String,
+ website: String,
+ picture: String,
+ },
+ },
+ { timestamps: true }
+);
/**
* Password hash middleware.
*/
-userSchema.pre("save", function save(next) {
+userSchema.pre('save', function save(next) {
const user = this as DashUserModel;
- if (!user.isModified("password")) {
+ if (!user.isModified('password')) {
return next();
}
bcrypt.genSalt(10, (err: any, salt: string) => {
if (err) {
return next(err);
}
- bcrypt.hash(user.password, salt, () => void {}, (err: mongoose.Error, hash: string) => {
- if (err) {
- return next(err);
+ bcrypt.hash(
+ user.password,
+ salt,
+ () => void {},
+ (err: mongoose.Error, hash: string) => {
+ if (err) {
+ return next(err);
+ }
+ user.password = hash;
+ next();
}
- user.password = hash;
- next();
- });
+ );
});
});
@@ -88,5 +96,15 @@ const comparePassword: comparePasswordFunction = function (this: DashUserModel,
userSchema.methods.comparePassword = comparePassword;
-const User = mongoose.model("User", userSchema);
-export default User; \ No newline at end of file
+const User = mongoose.model('User', userSchema);
+export function initializeGuest() {
+ new User({
+ email: 'guest',
+ password: 'guest',
+ userDocumentId: '__guest__',
+ sharingDocumentId: '2',
+ linkDatabaseId: '3',
+ cacheDocumentIds: '',
+ }).save();
+}
+export default User;
diff --git a/src/server/index.ts b/src/server/index.ts
index f8c32103b..6e6bde3cb 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1,35 +1,35 @@
require('dotenv').config();
-import { yellow } from "colors";
+import { yellow } from 'colors';
import * as mobileDetect from 'mobile-detect';
import * as path from 'path';
import * as qs from 'query-string';
-import { log_execution } from "./ActionUtilities";
-import DeleteManager from "./ApiManagers/DeleteManager";
+import { log_execution } from './ActionUtilities';
+import DeleteManager from './ApiManagers/DeleteManager';
import DownloadManager from './ApiManagers/DownloadManager';
-import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager";
-import GooglePhotosManager from "./ApiManagers/GooglePhotosManager";
-import PDFManager from "./ApiManagers/PDFManager";
+import GeneralGoogleManager from './ApiManagers/GeneralGoogleManager';
+import GooglePhotosManager from './ApiManagers/GooglePhotosManager';
+import PDFManager from './ApiManagers/PDFManager';
import { SearchManager } from './ApiManagers/SearchManager';
-import SessionManager from "./ApiManagers/SessionManager";
-import UploadManager from "./ApiManagers/UploadManager";
+import SessionManager from './ApiManagers/SessionManager';
+import UploadManager from './ApiManagers/UploadManager';
import UserManager from './ApiManagers/UserManager';
import UtilManager from './ApiManagers/UtilManager';
import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader';
-import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils";
-import { DashSessionAgent } from "./DashSession/DashSessionAgent";
-import { AppliedSessionAgent } from "./DashSession/Session/agents/applied_session_agent";
+import { GoogleApiServerUtils } from './apis/google/GoogleApiServerUtils';
+import { DashSessionAgent } from './DashSession/DashSessionAgent';
+import { AppliedSessionAgent } from './DashSession/Session/agents/applied_session_agent';
import { DashUploadUtils } from './DashUploadUtils';
import { Database } from './database';
-import { Logger } from "./ProcessFactory";
+import { Logger } from './ProcessFactory';
import RouteManager, { Method, PublicHandler } from './RouteManager';
import RouteSubscriber from './RouteSubscriber';
import initializeServer, { resolvedPorts } from './server_Initialization';
export const AdminPriviliges: Map<string, boolean> = new Map();
-export const onWindows = process.platform === "win32";
+export const onWindows = process.platform === 'win32';
export let sessionAgent: AppliedSessionAgent;
-export const publicDirectory = path.resolve(__dirname, "public");
-export const filesDirectory = path.resolve(publicDirectory, "files");
+export const publicDirectory = path.resolve(__dirname, 'public');
+export const filesDirectory = path.resolve(publicDirectory, 'files');
/**
* These are the functions run before the server starts
@@ -43,11 +43,11 @@ async function preliminaryFunctions() {
await GoogleCredentialsLoader.loadCredentials();
SSL.loadCredentials();
GoogleApiServerUtils.processProjectCredentials();
- if (process.env.DB !== "MEM") {
+ if (process.env.DB !== 'MEM') {
await log_execution({
- startMessage: "attempting to initialize mongodb connection",
- endMessage: "connection outcome determined",
- action: Database.tryInitializeConnection
+ startMessage: 'attempting to initialize mongodb connection',
+ endMessage: 'connection outcome determined',
+ action: Database.tryInitializeConnection,
});
}
}
@@ -56,27 +56,16 @@ async function preliminaryFunctions() {
* Either clustered together as an API manager
* or individually referenced below, by the completion
* of this function's execution, all routes will
- * be registered on the server
+ * be registered on the server
* @param router the instance of the route manager
* that will manage the registration of new routes
* with the server
*/
function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: RouteManager) {
- const managers = [
- new SessionManager(),
- new UserManager(),
- new UploadManager(),
- new DownloadManager(),
- new SearchManager(),
- new PDFManager(),
- new DeleteManager(),
- new UtilManager(),
- new GeneralGoogleManager(),
- new GooglePhotosManager(),
- ];
+ const managers = [new SessionManager(), new UserManager(), new UploadManager(), new DownloadManager(), new SearchManager(), new PDFManager(), new DeleteManager(), new UtilManager(), new GeneralGoogleManager(), new GooglePhotosManager()];
// initialize API Managers
- console.log(yellow("\nregistering server routes..."));
+ console.log(yellow('\nregistering server routes...'));
managers.forEach(manager => manager.register(addSupervisedRoute));
/**
@@ -84,88 +73,87 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
*/
addSupervisedRoute({
method: Method.GET,
- subscription: "/",
- secureHandler: ({ res }) => res.redirect("/home")
+ subscription: '/',
+ secureHandler: ({ res }) => res.redirect('/home'),
});
-
addSupervisedRoute({
method: Method.GET,
- subscription: "/serverHeartbeat",
- secureHandler: ({ res }) => res.send(true)
+ subscription: '/serverHeartbeat',
+ secureHandler: ({ res }) => res.send(true),
});
addSupervisedRoute({
method: Method.GET,
- subscription: "/resolvedPorts",
- secureHandler: ({ res }) => res.send(resolvedPorts)
+ subscription: '/resolvedPorts',
+ secureHandler: ({ res }) => res.send(resolvedPorts),
+ publicHandler: ({ res }) => res.send(resolvedPorts),
});
const serve: PublicHandler = ({ req, res }) => {
- const detector = new mobileDetect(req.headers['user-agent'] || "");
+ const detector = new mobileDetect(req.headers['user-agent'] || '');
const filename = detector.mobile() !== null ? 'mobile/image.html' : 'index.html';
res.sendFile(path.join(__dirname, '../../deploy/' + filename));
};
/**
- * Serves a simple password input box for any
+ * Serves a simple password input box for any
*/
addSupervisedRoute({
method: Method.GET,
- subscription: new RouteSubscriber("admin").add("previous_target"),
+ subscription: new RouteSubscriber('admin').add('previous_target'),
secureHandler: ({ res, isRelease }) => {
const { PASSWORD } = process.env;
if (!(isRelease && PASSWORD)) {
- return res.redirect("/home");
+ return res.redirect('/home');
}
- res.render("admin.pug", { title: "Enter Administrator Password" });
- }
+ res.render('admin.pug', { title: 'Enter Administrator Password' });
+ },
});
addSupervisedRoute({
method: Method.POST,
- subscription: new RouteSubscriber("admin").add("previous_target"),
+ subscription: new RouteSubscriber('admin').add('previous_target'),
secureHandler: async ({ req, res, isRelease, user: { id } }) => {
const { PASSWORD } = process.env;
if (!(isRelease && PASSWORD)) {
- return res.redirect("/home");
+ return res.redirect('/home');
}
const { password } = req.body;
const { previous_target } = req.params;
let redirect: string;
if (password === PASSWORD) {
AdminPriviliges.set(id, true);
- redirect = `/${previous_target.replace(":", "/")}`;
+ redirect = `/${previous_target.replace(':', '/')}`;
} else {
redirect = `/admin/${previous_target}`;
}
res.redirect(redirect);
- }
+ },
});
addSupervisedRoute({
method: Method.GET,
- subscription: ["/home", new RouteSubscriber("doc").add("docId")],
+ subscription: ['/home', new RouteSubscriber('doc').add('docId')],
secureHandler: serve,
publicHandler: ({ req, res, ...remaining }) => {
const { originalUrl: target } = req;
- const sharing = qs.parse(qs.extract(req.originalUrl), { sort: false }).sharing === "true";
- const docAccess = target.startsWith("/doc/");
+ const sharing = qs.parse(qs.extract(req.originalUrl), { sort: false }).sharing === 'true';
+ const docAccess = target.startsWith('/doc/');
// since this is the public handler, there's no meaning of '/home' to speak of
// since there's no user logged in, so the only viable operation
// for a guest is to look at a shared document
- if (sharing && docAccess) {
+ if (docAccess) {
serve({ req, res, ...remaining });
} else {
- res.redirect("/login");
+ res.redirect('/login');
}
- }
+ },
});
logRegistrationOutcome();
}
-
/**
* 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,
@@ -174,9 +162,9 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
*/
export async function launchServer() {
await log_execution({
- startMessage: "\nstarting execution of preliminary functions",
- endMessage: "completed preliminary functions\n",
- action: preliminaryFunctions
+ startMessage: '\nstarting execution of preliminary functions',
+ endMessage: 'completed preliminary functions\n',
+ action: preliminaryFunctions,
});
await initializeServer(routeSetter);
}
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 1b7f5919f..9b91a35a6 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -1,24 +1,24 @@
-import * as express from "express";
-import { blue, green } from "colors";
-import { createServer, Server } from "https";
-import { networkInterfaces } from "os";
+import { blue } from 'colors';
+import * as express from 'express';
+import { createServer, Server } from 'https';
+import { networkInterfaces } from 'os';
import * as sio from 'socket.io';
-import { Socket } from "socket.io";
-import { Utils } from "../Utils";
+import { Socket } from 'socket.io';
+import { Opt } from '../fields/Doc';
+import { Utils } from '../Utils';
import { logPort } from './ActionUtilities';
-import { timeMap } from "./ApiManagers/UserManager";
-import { GoogleCredentialsLoader, SSL } from "./apis/google/CredentialsLoader";
-import YoutubeApi from "./apis/youtube/youtubeApiSample";
-import { Client } from "./Client";
-import { Database } from "./database";
-import { DocumentsCollection } from "./IDatabase";
-import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent, YoutubeQueryInput, YoutubeQueryTypes } from "./Message";
-import { Search } from "./Search";
+import { timeMap } from './ApiManagers/UserManager';
+import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader';
+import YoutubeApi from './apis/youtube/youtubeApiSample';
+import { initializeGuest } from './authentication/DashUserModel';
+import { Client } from './Client';
+import { Database } from './database';
+import { DocumentsCollection } from './IDatabase';
+import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent, YoutubeQueryInput, YoutubeQueryTypes } from './Message';
+import { Search } from './Search';
import { resolvedPorts } from './server_Initialization';
-import { Opt } from "../fields/Doc";
export namespace WebSocket {
-
export let _socket: Socket;
const clients: { [key: string]: Client } = {};
export const socketMap = new Map<SocketIO.Socket, string>();
@@ -32,14 +32,14 @@ export namespace WebSocket {
resolvedPorts.socket = Number(socketPort);
}
let socketEndpoint: Opt<Server>;
- await new Promise<void>(resolve => socketEndpoint = createServer(SSL.Credentials, app).listen(resolvedPorts.socket, resolve));
+ await new Promise<void>(resolve => (socketEndpoint = createServer(SSL.Credentials, app).listen(resolvedPorts.socket, resolve)));
io = sio(socketEndpoint!, SSL.Credentials as any);
} else {
io = sio().listen(resolvedPorts.socket);
}
- logPort("websocket", resolvedPorts.socket);
+ logPort('websocket', resolvedPorts.socket);
- io.on("connection", function (socket: Socket) {
+ io.on('connection', function (socket: Socket) {
_socket = socket;
socket.use((_packet, next) => {
const userEmail = socketMap.get(socket);
@@ -70,14 +70,14 @@ export namespace WebSocket {
socket.join(room);
console.log('Client ID ' + socket.id + ' created room ' + room);
socket.emit('created', room, socket.id);
-
} else if (numClients === 1) {
console.log('Client ID ' + socket.id + ' joined room ' + room);
socket.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room, socket.id);
socket.in(room).emit('ready');
- } else { // max two clients
+ } else {
+ // max two clients
socket.emit('full', room);
}
});
@@ -97,10 +97,10 @@ export namespace WebSocket {
console.log('received bye');
});
- Utils.Emit(socket, MessageStore.Foo, "handshooken");
+ Utils.Emit(socket, MessageStore.Foo, 'handshooken');
Utils.AddServerHandler(socket, MessageStore.Bar, guid => barReceived(socket, guid));
- Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args));
+ Utils.AddServerHandler(socket, MessageStore.SetField, args => setField(socket, args));
Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField);
Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields);
if (isRelease) {
@@ -126,26 +126,26 @@ export namespace WebSocket {
*/
disconnect = () => {
- socket.broadcast.emit("connection_terminated", Date.now());
+ socket.broadcast.emit('connection_terminated', Date.now());
socket.disconnect(true);
};
});
}
function processGesturePoints(socket: Socket, content: GestureContent) {
- socket.broadcast.emit("receiveGesturePoints", content);
+ socket.broadcast.emit('receiveGesturePoints', content);
}
function processOverlayTrigger(socket: Socket, content: MobileInkOverlayContent) {
- socket.broadcast.emit("receiveOverlayTrigger", content);
+ socket.broadcast.emit('receiveOverlayTrigger', content);
}
function processUpdateOverlayPosition(socket: Socket, content: UpdateMobileInkOverlayPositionContent) {
- socket.broadcast.emit("receiveUpdateOverlayPosition", content);
+ socket.broadcast.emit('receiveUpdateOverlayPosition', content);
}
function processMobileDocumentUpload(socket: Socket, content: MobileDocumentUploadContent) {
- socket.broadcast.emit("receiveMobileDocumentUpload", content);
+ socket.broadcast.emit('receiveMobileDocumentUpload', content);
}
function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any[]) => void]) {
@@ -165,27 +165,22 @@ export namespace WebSocket {
const target: string[] = [];
onlyFields && target.push(DocumentsCollection);
await Database.Instance.dropSchema(...target);
- if (process.env.DISABLE_SEARCH !== "true") {
+ if (process.env.DISABLE_SEARCH !== 'true') {
await Search.clear();
}
+ initializeGuest();
}
function barReceived(socket: SocketIO.Socket, userEmail: string) {
clients[userEmail] = new Client(userEmail.toString());
const currentdate = new Date();
- const datetime = currentdate.getDate() + "/"
- + (currentdate.getMonth() + 1) + "/"
- + currentdate.getFullYear() + " @ "
- + currentdate.getHours() + ":"
- + currentdate.getMinutes() + ":"
- + currentdate.getSeconds();
+ const datetime = currentdate.getDate() + '/' + (currentdate.getMonth() + 1) + '/' + currentdate.getFullYear() + ' @ ' + currentdate.getHours() + ':' + currentdate.getMinutes() + ':' + currentdate.getSeconds();
console.log(blue(`user ${userEmail} has connected to the web socket at: ${datetime}`));
- socketMap.set(socket, userEmail + " at " + datetime);
+ socketMap.set(socket, userEmail + ' at ' + datetime);
}
function getField([id, callback]: [string, (result?: Transferable) => void]) {
- Database.Instance.getDocument(id, (result?: Transferable) =>
- callback(result ? result : undefined));
+ Database.Instance.getDocument(id, (result?: Transferable) => callback(result ? result : undefined));
}
function getFields([ids, callback]: [string[], (result: Transferable[]) => void]) {
@@ -193,9 +188,9 @@ export namespace WebSocket {
}
function setField(socket: Socket, newValue: Transferable) {
- Database.Instance.update(newValue.id, newValue, () =>
- socket.broadcast.emit(MessageStore.SetField.Message, newValue)); // broadcast set value to all other clients
- if (newValue.type === Types.Text) { // if the newValue has sring type, then it's suitable for searching -- pass it to SOLR
+ Database.Instance.update(newValue.id, newValue, () => socket.broadcast.emit(MessageStore.SetField.Message, newValue)); // broadcast set value to all other clients
+ if (newValue.type === Types.Text) {
+ // if the newValue has sring type, then it's suitable for searching -- pass it to SOLR
Search.updateDocument({ id: newValue.id, data: { set: (newValue as any).data } });
}
}
@@ -213,34 +208,36 @@ export namespace WebSocket {
Database.Instance.getDocuments(ids, callback);
}
- const suffixMap: { [type: string]: (string | [string, string | ((json: any) => any)]) } = {
- "number": "_n",
- "string": "_t",
- "boolean": "_b",
- "image": ["_t", "url"],
- "video": ["_t", "url"],
- "pdf": ["_t", "url"],
- "audio": ["_t", "url"],
- "web": ["_t", "url"],
- "map": ["_t", "url"],
- "script": ["_t", value => value.script.originalScript],
- "RichTextField": ["_t", value => value.Text],
- "date": ["_d", value => new Date(value.date).toISOString()],
- "proxy": ["_i", "fieldId"],
- "list": ["_l", list => {
- const results = [];
- for (const value of list.fields) {
- const term = ToSearchTerm(value);
- if (term) {
- results.push(term.value);
+ const suffixMap: { [type: string]: string | [string, string | ((json: any) => any)] } = {
+ number: '_n',
+ string: '_t',
+ boolean: '_b',
+ image: ['_t', 'url'],
+ video: ['_t', 'url'],
+ pdf: ['_t', 'url'],
+ audio: ['_t', 'url'],
+ web: ['_t', 'url'],
+ map: ['_t', 'url'],
+ script: ['_t', value => value.script.originalScript],
+ RichTextField: ['_t', value => value.Text],
+ date: ['_d', value => new Date(value.date).toISOString()],
+ proxy: ['_i', 'fieldId'],
+ list: [
+ '_l',
+ list => {
+ const results = [];
+ for (const value of list.fields) {
+ const term = ToSearchTerm(value);
+ if (term) {
+ results.push(term.value);
+ }
}
- }
- return results.length ? results : null;
- }]
+ return results.length ? results : null;
+ },
+ ],
};
- function ToSearchTerm(val: any): { suffix: string, value: any } | undefined {
-
+ function ToSearchTerm(val: any): { suffix: string; value: any } | undefined {
if (val === null || val === undefined) {
return;
}
@@ -252,69 +249,79 @@ export namespace WebSocket {
}
if (Array.isArray(suffix)) {
const accessor = suffix[1];
- if (typeof accessor === "function") {
+ if (typeof accessor === 'function') {
val = accessor(val);
} else {
val = val[accessor];
-
}
suffix = suffix[0];
-
}
return { suffix, value: val };
}
function getSuffix(value: string | [string, any]): string {
- return typeof value === "string" ? value : value[0];
+ return typeof value === 'string' ? value : value[0];
}
function addToListField(socket: Socket, diff: Diff, curListItems?: Transferable): void {
- diff.diff.$set = diff.diff.$addToSet; delete diff.diff.$addToSet;// convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones
+ diff.diff.$set = diff.diff.$addToSet;
+ delete diff.diff.$addToSet; // convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones
const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
const newListItems = diff.diff.$set[updatefield]?.fields;
if (!newListItems) {
- console.log("Error: addToListField - no new list items");
+ console.log('Error: addToListField - no new list items');
return;
}
- const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields.filter((item: any) => item !== undefined) || [];
- diff.diff.$set[updatefield].fields = [...curList, ...newListItems];//, ...newListItems.filter((newItem: any) => newItem === null || !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
+ const curList = (curListItems as any)?.fields?.[updatefield.replace('fields.', '')]?.fields.filter((item: any) => item !== undefined) || [];
+ diff.diff.$set[updatefield].fields = [...curList, ...newListItems]; //, ...newListItems.filter((newItem: any) => newItem === null || !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
delete diff.diff.length;
- Database.Instance.update(diff.id, diff.diff,
+ Database.Instance.update(
+ diff.id,
+ diff.diff,
() => {
if (sendBack) {
- console.log("Warning: list modified during update. Composite list is being returned.");
+ console.log('Warning: list modified during update. Composite list is being returned.');
const id = socket.id;
- socket.id = "";
+ socket.id = '';
socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
socket.id = id;
} else socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
dispatchNextOp(diff.id);
- }, false);
+ },
+ false
+ );
}
function remFromListField(socket: Socket, diff: Diff, curListItems?: Transferable): void {
- diff.diff.$set = diff.diff.$remFromSet; delete diff.diff.$remFromSet;
+ diff.diff.$set = diff.diff.$remFromSet;
+ delete diff.diff.$remFromSet;
const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
const remListItems = diff.diff.$set[updatefield].fields;
- const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields.filter((f: any) => f !== null) || [];
- diff.diff.$set[updatefield].fields = curList?.filter((curItem: any) => !remListItems.some((remItem: any) => remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem));
+ const curList = (curListItems as any)?.fields?.[updatefield.replace('fields.', '')]?.fields.filter((f: any) => f !== null) || [];
+ diff.diff.$set[updatefield].fields = curList?.filter(
+ (curItem: any) => !remListItems.some((remItem: any) => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem))
+ );
const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
delete diff.diff.length;
- Database.Instance.update(diff.id, diff.diff,
+ Database.Instance.update(
+ diff.id,
+ diff.diff,
() => {
if (sendBack) {
- console.log("SEND BACK");
+ console.log('SEND BACK');
const id = socket.id;
- socket.id = "";
+ socket.id = '';
socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
socket.id = id;
} else socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
dispatchNextOp(diff.id);
- }, false);
+ },
+ false
+ );
}
- const pendingOps = new Map<string, { diff: Diff, socket: Socket }[]>();
+ const pendingOps = new Map<string, { diff: Diff; socket: Socket }[]>();
function dispatchNextOp(id: string) {
const next = pendingOps.get(id)!.shift();
@@ -341,7 +348,7 @@ export namespace WebSocket {
function UpdateField(socket: Socket, diff: Diff) {
if (CurUser !== socketMap.get(socket)) {
CurUser = socketMap.get(socket);
- console.log("Switch User: " + CurUser);
+ console.log('Switch User: ' + CurUser);
}
if (pendingOps.has(diff.id)) {
pendingOps.get(diff.id)!.push({ diff, socket });
@@ -357,24 +364,25 @@ export namespace WebSocket {
return GetRefFieldLocal([diff.id, (result?: Transferable) => SetField(socket, diff, result)]);
}
function SetField(socket: Socket, diff: Diff, curListItems?: Transferable) {
- Database.Instance.update(diff.id, diff.diff,
- () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false);
+ Database.Instance.update(diff.id, diff.diff, () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false);
const docfield = diff.diff.$set || diff.diff.$unset;
if (docfield) {
const update: any = { id: diff.id };
let dynfield = false;
for (let key in docfield) {
- if (!key.startsWith("fields.")) continue;
+ if (!key.startsWith('fields.')) continue;
dynfield = true;
const val = docfield[key];
key = key.substring(7);
- Object.values(suffixMap).forEach(suf => { update[key + getSuffix(suf)] = { set: null }; });
+ Object.values(suffixMap).forEach(suf => {
+ update[key + getSuffix(suf)] = { set: null };
+ });
const term = ToSearchTerm(val);
if (term !== undefined) {
const { suffix, value } = term;
update[key + suffix] = { set: value };
if (key.endsWith('lastModified')) {
- update["lastModified" + suffix] = value;
+ update['lastModified' + suffix] = value;
}
}
}
@@ -403,6 +411,4 @@ export namespace WebSocket {
function CreateField(newValue: any) {
Database.Instance.insert(newValue);
}
-
}
-