aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/server')
-rw-r--r--src/server/ApiManagers/UploadManager.ts556
-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.ts97
-rw-r--r--src/server/websocket.ts194
7 files changed, 582 insertions, 537 deletions
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 332ba3d35..787e331c5 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -1,291 +1,308 @@
-import ApiManager, { Registration } from "./ApiManager";
-import { Method, _success } from "../RouteManager";
+import ApiManager, { Registration } from './ApiManager';
+import { Method, _success } from '../RouteManager';
import * as formidable from 'formidable';
import v4 = require('uuid/v4');
const AdmZip = require('adm-zip');
-import { extname, basename, dirname, } from 'path';
-import { createReadStream, createWriteStream, unlink, writeFile } from "fs";
-import { publicDirectory, filesDirectory } from "..";
-import { Database } from "../database";
-import { DashUploadUtils, InjectSize, SizeSuffix } from "../DashUploadUtils";
+import { extname, basename, dirname } from 'path';
+import { createReadStream, createWriteStream, unlink, writeFile } from 'fs';
+import { publicDirectory, filesDirectory } from '..';
+import { Database } from '../database';
+import { DashUploadUtils, InjectSize, SizeSuffix } from '../DashUploadUtils';
import * as sharp from 'sharp';
-import { AcceptableMedia, Upload } from "../SharedMediaTypes";
-import { normalize } from "path";
-import RouteSubscriber from "../RouteSubscriber";
+import { AcceptableMedia, Upload } from '../SharedMediaTypes';
+import { normalize } from 'path';
+import RouteSubscriber from '../RouteSubscriber';
const imageDataUri = require('image-data-uri');
-import { SolrManager } from "./SearchManager";
+import { SolrManager } from './SearchManager';
const fs = require('fs');
export enum Directory {
- parsed_files = "parsed_files",
- images = "images",
- videos = "videos",
- pdfs = "pdfs",
- text = "text",
- pdf_thumbnails = "pdf_thumbnails",
- audio = "audio",
- csv = "csv",
+ parsed_files = 'parsed_files',
+ images = 'images',
+ videos = 'videos',
+ pdfs = 'pdfs',
+ text = 'text',
+ pdf_thumbnails = 'pdf_thumbnails',
+ audio = 'audio',
+ csv = 'csv',
}
export function serverPathToFile(directory: Directory, filename: string) {
- return normalize(`${filesDirectory}/${directory}/${filename}`);
+ return normalize(`${filesDirectory}/${directory}/${filename}`);
}
export function pathToDirectory(directory: Directory) {
- return normalize(`${filesDirectory}/${directory}`);
+ return normalize(`${filesDirectory}/${directory}`);
}
export function clientPathToFile(directory: Directory, filename: string) {
- return `/files/${directory}/${filename}`;
+ return `/files/${directory}/${filename}`;
}
export default class UploadManager extends ApiManager {
-
protected initialize(register: Registration): void {
-
- register({
- method: Method.POST,
- subscription: "/concatVideos",
- secureHandler: async ({ req, res }) => {
- // req.body contains the array of server paths to the videos
- _success(res, await DashUploadUtils.concatVideos(req.body));
- }
- });
-
- register({
- method: Method.POST,
- subscription: "/uploadFormData",
- secureHandler: async ({ req, res }) => {
- const form = new formidable.IncomingForm();
- form.keepExtensions = true;
- form.uploadDir = pathToDirectory(Directory.parsed_files);
- return new Promise<void>(resolve => {
- form.parse(req, async (_err, _fields, files) => {
- const results: Upload.FileResponse[] = [];
- for (const key in files) {
- const f = files[key];
- if (!Array.isArray(f)) {
- const result = await DashUploadUtils.upload(f);
- result && !(result.result instanceof Error) && results.push(result);
- }
- }
- _success(res, results);
- resolve();
- });
- });
- }
- });
-
- register({
- method: Method.POST,
- subscription: "/uploadYoutubeVideo",
- secureHandler: async ({ req, res }) => {
- //req.readableBuffer.head.data
- return new Promise<void>(async resolve => {
- req.addListener("data", async (args) => {
- console.log(args);
- const payload = String.fromCharCode.apply(String, args);
- const videoId = JSON.parse(payload).videoId;
- const results: Upload.FileResponse[] = [];
- const result = await DashUploadUtils.uploadYoutube(videoId);
- result && !(result.result instanceof Error) && results.push(result);
- _success(res, results);
- resolve();
- });
- });
- }
- });
+ register({
+ method: Method.POST,
+ subscription: '/concatVideos',
+ secureHandler: async ({ req, res }) => {
+ // req.body contains the array of server paths to the videos
+ _success(res, await DashUploadUtils.concatVideos(req.body));
+ },
+ });
- register({
- method: Method.POST,
- subscription: new RouteSubscriber("youtubeScreenshot"),
- secureHandler: async ({ req, res }) => {
- const { id, timecode } = req.body;
- const convert = (raw: string) => {
- const number = Math.floor(Number(raw));
- const seconds = number % 60;
- const minutes = (number - seconds) / 60;
- return `${minutes}m${seconds}s`;
- };
- const suffix = timecode ? `&t=${convert(timecode)}` : ``;
- const targetUrl = `https://www.youtube.com/watch?v=${id}${suffix}`;
- const buffer = await captureYoutubeScreenshot(targetUrl);
- if (!buffer) {
- return res.send();
+ register({
+ method: Method.POST,
+ subscription: '/uploadFormData',
+ secureHandler: async ({ req, res }) => {
+ const form = new formidable.IncomingForm();
+ form.keepExtensions = true;
+ form.uploadDir = pathToDirectory(Directory.parsed_files);
+ return new Promise<void>(resolve => {
+ form.parse(req, async (_err, _fields, files) => {
+ const results: Upload.FileResponse[] = [];
+ for (const key in files) {
+ const f = files[key];
+ if (!Array.isArray(f)) {
+ const result = await DashUploadUtils.upload(f);
+ result && !(result.result instanceof Error) && results.push(result);
+ }
}
- const resolvedName = `youtube_capture_${id}_${suffix}.png`;
- const resolvedPath = serverPathToFile(Directory.images, resolvedName);
- return new Promise<void>(resolve => {
- writeFile(resolvedPath, buffer, async error => {
- if (error) {
- return res.send();
- }
- await DashUploadUtils.outputResizedImages(() => createReadStream(resolvedPath), resolvedName, pathToDirectory(Directory.images));
- res.send({
- accessPaths: {
- agnostic: DashUploadUtils.getAccessPaths(Directory.images, resolvedName)
- }
- } as Upload.FileInformation);
- resolve();
- });
- });
- }
- });
+ _success(res, results);
+ resolve();
+ });
+ });
+ },
+ });
- register({
- method: Method.POST,
- subscription: "/uploadRemoteImage",
- secureHandler: async ({ req, res }) => {
+ register({
+ method: Method.POST,
+ subscription: '/uploadYoutubeVideo',
+ secureHandler: async ({ req, res }) => {
+ //req.readableBuffer.head.data
+ return new Promise<void>(async resolve => {
+ req.addListener('data', async args => {
+ console.log(args);
+ const payload = String.fromCharCode.apply(String, args);
+ const videoId = JSON.parse(payload).videoId;
+ const results: Upload.FileResponse[] = [];
+ const result = await DashUploadUtils.uploadYoutube(videoId);
+ result && !(result.result instanceof Error) && results.push(result);
+ _success(res, results);
+ resolve();
+ });
+ });
+ },
+ });
- const { sources } = req.body;
- if (Array.isArray(sources)) {
- const results = await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source)));
- return res.send(results);
+ register({
+ method: Method.POST,
+ subscription: new RouteSubscriber('youtubeScreenshot'),
+ secureHandler: async ({ req, res }) => {
+ const { id, timecode } = req.body;
+ const convert = (raw: string) => {
+ const number = Math.floor(Number(raw));
+ const seconds = number % 60;
+ const minutes = (number - seconds) / 60;
+ return `${minutes}m${seconds}s`;
+ };
+ const suffix = timecode ? `&t=${convert(timecode)}` : ``;
+ const targetUrl = `https://www.youtube.com/watch?v=${id}${suffix}`;
+ const buffer = await captureYoutubeScreenshot(targetUrl);
+ if (!buffer) {
+ return res.send();
+ }
+ const resolvedName = `youtube_capture_${id}_${suffix}.png`;
+ const resolvedPath = serverPathToFile(Directory.images, resolvedName);
+ return new Promise<void>(resolve => {
+ writeFile(resolvedPath, buffer, async error => {
+ if (error) {
+ return res.send();
}
- res.send();
- }
- });
+ await DashUploadUtils.outputResizedImages(() => createReadStream(resolvedPath), resolvedName, pathToDirectory(Directory.images));
+ res.send({
+ accessPaths: {
+ agnostic: DashUploadUtils.getAccessPaths(Directory.images, resolvedName),
+ },
+ } as Upload.FileInformation);
+ resolve();
+ });
+ });
+ },
+ });
- register({
- method: Method.POST,
- subscription: "/uploadDoc",
- secureHandler: ({ req, res }) => {
+ register({
+ method: Method.POST,
+ subscription: '/uploadRemoteImage',
+ secureHandler: async ({ req, res }) => {
+ const { sources } = req.body;
+ if (Array.isArray(sources)) {
+ const results = await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source)));
+ return res.send(results);
+ }
+ res.send();
+ },
+ });
- const form = new formidable.IncomingForm();
- form.keepExtensions = true;
- // let path = req.body.path;
- const ids: { [id: string]: string } = {};
- let remap = true;
- const getId = (id: string): string => {
- if (!remap) return id;
- if (id.endsWith("Proto")) return id;
- if (id in ids) {
- return ids[id];
- } else {
- return ids[id] = v4();
- }
- };
- const mapFn = (doc: any) => {
- if (doc.id) {
- doc.id = getId(doc.id);
- }
- for (const key in doc.fields) {
- if (!doc.fields.hasOwnProperty(key)) { continue; }
- const field = doc.fields[key];
- if (field === undefined || field === null) { continue; }
+ register({
+ method: Method.POST,
+ subscription: '/uploadDoc',
+ secureHandler: ({ req, res }) => {
+ const form = new formidable.IncomingForm();
+ form.keepExtensions = true;
+ // let path = req.body.path;
+ const ids: { [id: string]: string } = {};
+ let remap = true;
+ const getId = (id: string): string => {
+ if (!remap) return id;
+ if (id.endsWith('Proto')) return id;
+ if (id in ids) {
+ return ids[id];
+ } else {
+ return (ids[id] = v4());
+ }
+ };
+ const mapFn = (doc: any) => {
+ if (doc.id) {
+ doc.id = getId(doc.id);
+ }
+ for (const key in doc.fields) {
+ if (!doc.fields.hasOwnProperty(key)) {
+ continue;
+ }
+ const field = doc.fields[key];
+ if (field === undefined || field === null) {
+ continue;
+ }
- if (field.__type === "Doc") {
- mapFn(field);
- } else if (field.__type === "proxy" || field.__type === "prefetch_proxy") {
- field.fieldId = getId(field.fieldId);
- } else if (field.__type === "script" || field.__type === "computed") {
- if (field.captures) {
- field.captures.fieldId = getId(field.captures.fieldId);
- }
- } else if (field.__type === "list") {
- mapFn(field);
- } else if (typeof field === "string") {
- const re = /("(?:dataD|d)ocumentId"\s*:\s*")([\w\-]*)"/g;
- doc.fields[key] = (field as any).replace(re, (match: any, p1: string, p2: string) => {
- return `${p1}${getId(p2)}"`;
- });
- } else if (field.__type === "RichTextField") {
- const re = /("href"\s*:\s*")(.*?)"/g;
- field.Data = field.Data.replace(re, (match: any, p1: string, p2: string) => {
- return `${p1}${getId(p2)}"`;
- });
- }
- }
- };
- return new Promise<void>(resolve => {
- form.parse(req, async (_err, fields, files) => {
- remap = fields.remap !== "false";
- let id: string = "";
+ if (field.__type === 'Doc') {
+ mapFn(field);
+ } else if (field.__type === 'proxy' || field.__type === 'prefetch_proxy') {
+ field.fieldId = getId(field.fieldId);
+ } else if (field.__type === 'script' || field.__type === 'computed') {
+ if (field.captures) {
+ field.captures.fieldId = getId(field.captures.fieldId);
+ }
+ } else if (field.__type === 'list') {
+ mapFn(field);
+ } else if (typeof field === 'string') {
+ const re = /("(?:dataD|d)ocumentId"\s*:\s*")([\w\-]*)"/g;
+ doc.fields[key] = (field as any).replace(re, (match: any, p1: string, p2: string) => {
+ return `${p1}${getId(p2)}"`;
+ });
+ } else if (field.__type === 'RichTextField') {
+ const re = /("href"\s*:\s*")(.*?)"/g;
+ field.Data = field.Data.replace(re, (match: any, p1: string, p2: string) => {
+ return `${p1}${getId(p2)}"`;
+ });
+ }
+ }
+ };
+ return new Promise<void>(resolve => {
+ form.parse(req, async (_err, fields, files) => {
+ remap = fields.remap !== 'false';
+ let id: string = '';
+ try {
+ for (const name in files) {
+ const f = files[name];
+ const path_2 = Array.isArray(f) ? '' : f.path;
+ const zip = new AdmZip(path_2);
+ zip.getEntries().forEach((entry: any) => {
+ if (!entry.entryName.startsWith('files/')) return;
+ let directory = dirname(entry.entryName) + '/';
+ const extension = extname(entry.entryName);
+ const base = basename(entry.entryName).split('.')[0];
try {
- for (const name in files) {
- const f = files[name];
- const path_2 = Array.isArray(f) ? "" : f.path;
- const zip = new AdmZip(path_2);
- zip.getEntries().forEach((entry: any) => {
- if (!entry.entryName.startsWith("files/")) return;
- let directory = dirname(entry.entryName) + "/";
- const extension = extname(entry.entryName);
- const base = basename(entry.entryName).split(".")[0];
- try {
- zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
- directory = "/" + directory;
-
- createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_o" + extension));
- createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_s" + extension));
- createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_m" + extension));
- createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_l" + extension));
- } catch (e) {
- console.log(e);
- }
- });
- const json = zip.getEntry("doc.json");
- try {
- const data = JSON.parse(json.getData().toString("utf8"));
- const datadocs = data.docs;
- id = getId(data.id);
- const docs = Object.keys(datadocs).map(key => datadocs[key]);
- docs.forEach(mapFn);
- await Promise.all(docs.map((doc: any) => new Promise<void>(res => {
- Database.Instance.replace(doc.id, doc, (err, r) => {
- err && console.log(err);
- res();
- }, true);
- })));
- } catch (e) { console.log(e); }
- unlink(path_2, () => { });
- }
- SolrManager.update();
- res.send(JSON.stringify(id || "error"));
- } catch (e) { console.log(e); }
- resolve();
- });
- });
- }
- });
-
- register({
- method: Method.POST,
- subscription: "/inspectImage",
- secureHandler: async ({ req, res }) => {
+ zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
+ directory = '/' + directory;
- const { source } = req.body;
- if (typeof source === "string") {
- return res.send(await DashUploadUtils.InspectImage(source));
+ createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + '_o' + extension));
+ createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + '_s' + extension));
+ createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + '_m' + extension));
+ createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + '_l' + extension));
+ } catch (e) {
+ console.log(e);
+ }
+ });
+ const json = zip.getEntry('doc.json');
+ try {
+ const data = JSON.parse(json.getData().toString('utf8'));
+ const datadocs = data.docs;
+ id = getId(data.id);
+ const docs = Object.keys(datadocs).map(key => datadocs[key]);
+ docs.forEach(mapFn);
+ await Promise.all(
+ docs.map(
+ (doc: any) =>
+ new Promise<void>(res => {
+ Database.Instance.replace(
+ doc.id,
+ doc,
+ (err, r) => {
+ err && console.log(err);
+ res();
+ },
+ true
+ );
+ })
+ )
+ );
+ } catch (e) {
+ console.log(e);
+ }
+ unlink(path_2, () => {});
+ }
+ SolrManager.update();
+ res.send(JSON.stringify(id || 'error'));
+ } catch (e) {
+ console.log(e);
}
- res.send({});
- }
- });
+ resolve();
+ });
+ });
+ },
+ });
+
+ register({
+ method: Method.POST,
+ subscription: '/inspectImage',
+ secureHandler: async ({ req, res }) => {
+ const { source } = req.body;
+ if (typeof source === 'string') {
+ return res.send(await DashUploadUtils.InspectImage(source));
+ }
+ res.send({});
+ },
+ });
register({
method: Method.POST,
- subscription: "/uploadURI",
+ subscription: '/uploadURI',
secureHandler: ({ req, res }) => {
const uri = req.body.uri;
const filename = req.body.name;
const origSuffix = req.body.nosuffix ? SizeSuffix.None : SizeSuffix.Original;
const deleteFiles = req.body.replaceRootFilename;
if (!uri || !filename) {
- res.status(401).send("incorrect parameters specified");
+ res.status(401).send('incorrect parameters specified');
return;
}
if (deleteFiles) {
- const path = serverPathToFile(Directory.images, "");
+ const path = serverPathToFile(Directory.images, '');
const regex = new RegExp(`${deleteFiles}.*`);
- fs.readdirSync(path).filter((f: any) => regex.test(f)).map((f: any) => fs.unlinkSync(path + f));
+ fs.readdirSync(path)
+ .filter((f: any) => regex.test(f))
+ .map((f: any) => fs.unlinkSync(path + f));
}
return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, origSuffix))).then((savedName: string) => {
const ext = extname(savedName).toLowerCase();
const { pngs, jpgs } = AcceptableMedia;
- const resizers = !origSuffix ? [{ resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium }] : [
- { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Small },
- { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium },
- { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Large },
- ];
+ const resizers = !origSuffix
+ ? [{ resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium }]
+ : [
+ { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Small },
+ { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium },
+ { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Large },
+ ];
let isImage = false;
if (pngs.includes(ext)) {
resizers.forEach(element => {
@@ -301,49 +318,48 @@ export default class UploadManager extends ApiManager {
if (isImage) {
resizers.forEach(resizer => {
const path = serverPathToFile(Directory.images, InjectSize(filename, resizer.suffix) + ext);
- createReadStream(savedName).on("error", e => console.log("Resizing read:" + e))
+ createReadStream(savedName)
+ .on('error', e => console.log('Resizing read:' + e))
.pipe(resizer.resizer)
- .pipe(createWriteStream(path).on("error", e => console.log("Resizing write: " + e)));
+ .on('error', e => console.log('Resizing write: ' + e))
+ .pipe(createWriteStream(path).on('error', e => console.log('Resizing write: ' + e)));
});
-
- }
- res.send(clientPathToFile(Directory.images, filename + ext));
- });
- }
- });
-
- }
-
+ }
+ res.send(clientPathToFile(Directory.images, filename + ext));
+ });
+ },
+ });
+ }
}
function delay(ms: number) {
- return new Promise(resolve => setTimeout(resolve, ms));
+ return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* On success, returns a buffer containing the bytes of a screenshot
* of the video (optionally, at a timecode) specified by @param targetUrl.
- *
+ *
* On failure, returns undefined.
*/
async function captureYoutubeScreenshot(targetUrl: string) {
- // const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
- // const page = await browser.newPage();
- // // await page.setViewport({ width: 1920, height: 1080 });
+ // const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
+ // const page = await browser.newPage();
+ // // await page.setViewport({ width: 1920, height: 1080 });
- // // await page.goto(targetUrl, { waitUntil: 'domcontentloaded' as any });
+ // // await page.goto(targetUrl, { waitUntil: 'domcontentloaded' as any });
- // const videoPlayer = await page.$('.html5-video-player');
- // videoPlayer && await page.focus("video");
- // await delay(7000);
- // const ad = await page.$('.ytp-ad-skip-button-text');
- // await ad?.click();
- // await videoPlayer?.click();
- // await delay(1000);
- // // hide youtube player controls.
- // await page.evaluate(() => (document.querySelector('.ytp-chrome-bottom') as HTMLElement).style.display = 'none');
+ // const videoPlayer = await page.$('.html5-video-player');
+ // videoPlayer && await page.focus("video");
+ // await delay(7000);
+ // const ad = await page.$('.ytp-ad-skip-button-text');
+ // await ad?.click();
+ // await videoPlayer?.click();
+ // await delay(1000);
+ // // hide youtube player controls.
+ // await page.evaluate(() => (document.querySelector('.ytp-chrome-bottom') as HTMLElement).style.display = 'none');
- // const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
- // await browser.close();
+ // const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
+ // await browser.close();
- // return buffer;
- return null;
-} \ No newline at end of file
+ // return buffer;
+ return null;
+}
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 0acb7f20f..6562860fe 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1,36 +1,36 @@
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 DataVizManager from "./ApiManagers/DataVizManager";
-import DeleteManager from "./ApiManagers/DeleteManager";
+import { log_execution } from './ActionUtilities';
+import DataVizManager from './ApiManagers/DataVizManager';
+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
@@ -44,11 +44,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,
});
}
}
@@ -57,7 +57,7 @@ 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
@@ -78,7 +78,7 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
];
// initialize API Managers
- console.log(yellow("\nregistering server routes..."));
+ console.log(yellow('\nregistering server routes...'));
managers.forEach(manager => manager.register(addSupervisedRoute));
/**
@@ -86,88 +86,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,
@@ -176,9 +175,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);
}
-
}
-