aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/server')
-rw-r--r--src/server/ActionUtilities.ts6
-rw-r--r--src/server/ApiManagers/SearchManager.ts19
-rw-r--r--src/server/ApiManagers/SessionManager.ts6
-rw-r--r--src/server/ApiManagers/UserManager.ts41
-rw-r--r--src/server/DashSession/DashSessionAgent.ts10
-rw-r--r--src/server/DashSession/Session/agents/monitor.ts10
-rw-r--r--src/server/DashSession/Session/agents/promisified_ipc_manager.ts8
-rw-r--r--src/server/DashSession/Session/agents/server_worker.ts8
-rw-r--r--src/server/DashSession/Session/utilities/session_config.ts2
-rw-r--r--src/server/DashUploadUtils.ts3
-rw-r--r--src/server/GarbageCollector.ts2
-rw-r--r--src/server/SharedMediaTypes.ts2
-rw-r--r--src/server/authentication/AuthenticationManager.ts17
-rw-r--r--src/server/authentication/DashUserModel.ts8
-rw-r--r--src/server/index.ts11
-rw-r--r--src/server/websocket.ts120
16 files changed, 200 insertions, 73 deletions
diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts
index fd9bc0c83..d237869ed 100644
--- a/src/server/ActionUtilities.ts
+++ b/src/server/ActionUtilities.ts
@@ -114,8 +114,8 @@ export namespace Email {
const smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
- user: 'brownptcdash@gmail.com',
- pass: 'browngfx1'
+ user: 'browndashptc@gmail.com',
+ pass: 'TsarNicholas#2'
}
});
@@ -149,7 +149,7 @@ export namespace Email {
export async function dispatch({ to, subject, content, attachments }: DispatchOptions<string>): Promise<Error | null> {
const mailOptions = {
to,
- from: 'brownptcdash@gmail.com',
+ from: 'browndashptc@gmail.com',
subject,
text: `Hello ${to.split("@")[0]},\n\n${content}`,
attachments
diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts
index a52430ee8..775e90520 100644
--- a/src/server/ApiManagers/SearchManager.ts
+++ b/src/server/ApiManagers/SearchManager.ts
@@ -47,13 +47,20 @@ export class SearchManager extends ApiManager {
const resObj: { ids: string[], numFound: number, lines: string[] } = { ids: [], numFound: 0, lines: [] };
let results: any;
const dir = pathToDirectory(Directory.text);
- results = await findInFiles.find({ 'term': q, 'flags': 'ig' }, dir, ".txt$");
- for (const result in results) {
- resObj.ids.push(path.basename(result, ".txt").replace(/upload_/, ""));
- resObj.lines.push(results[result].line);
- resObj.numFound++;
+ try {
+ const regex = new RegExp(q.toString());
+ results = await findInFiles.find({ 'term': q, 'flags': 'ig' }, dir, ".txt$");
+ for (const result in results) {
+ resObj.ids.push(path.basename(result, ".txt").replace(/upload_/, ""));
+ resObj.lines.push(results[result].line);
+ resObj.numFound++;
+ }
+ res.send(resObj);
+ } catch (e) {
+ console.log(red("textsearch:bad RegExp" + q.toString()));
+ res.send([]);
+ return;
}
- res.send(resObj);
}
});
diff --git a/src/server/ApiManagers/SessionManager.ts b/src/server/ApiManagers/SessionManager.ts
index fa2f6002a..e37f8c6db 100644
--- a/src/server/ApiManagers/SessionManager.ts
+++ b/src/server/ApiManagers/SessionManager.ts
@@ -12,9 +12,9 @@ export default class SessionManager extends ApiManager {
private authorizedAction = (handler: SecureHandler) => {
return (core: AuthorizedCore) => {
- const { req: { params }, res, isRelease } = core;
- if (!isRelease) {
- return res.send("This can be run only on the release server.");
+ const { req: { params }, res } = core;
+ if (!process.env.MONITORED) {
+ return res.send("This command only makes sense in the context of a monitored session.");
}
if (params.session_key !== process.env.session_key) {
return _permission_denied(res, permissionError);
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index 0d1d8f218..f36506b14 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -19,22 +19,53 @@ export default class UserManager extends ApiManager {
method: Method.GET,
subscription: "/getUsers",
secureHandler: async ({ res }) => {
- const cursor = await Database.Instance.query({}, { email: 1, userDocumentId: 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 => ({ email: user.email, userDocumentId: user.userDocumentId })));
+ res.send(results.map(user => ({ email: user.email, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })));
}
});
register({
+ method: Method.POST,
+ 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" }];
+ }
+ });
+
+ // Database.Instance.update(id, { "$set": { "fields.cacheDocumentIds": cacheDocumentIds } }, e => {
+ // 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 })
+ });
+
+ register({
+ method: Method.GET,
+ subscription: "/getSharingDocumentId",
+ secureHandler: ({ res, user }) => res.send(user.sharingDocumentId)
+ });
+
+ register({
method: Method.GET,
- subscription: "/getUserDocumentId",
- secureHandler: ({ res, user }) => res.send(user.userDocumentId)
+ subscription: "/getLinkDatabaseId",
+ secureHandler: ({ res, user }) => res.send(user.linkDatabaseId)
});
register({
method: Method.GET,
subscription: "/getCurrentUser",
- secureHandler: ({ res, user: { _id, email } }) => res.send(JSON.stringify({ id: _id, email })),
+ secureHandler: ({ res, user: { _id, email, cacheDocumentIds } }) => res.send(JSON.stringify({ id: _id, email, cacheDocumentIds })),
publicHandler: ({ res }) => res.send(JSON.stringify({ id: "__guest__", email: "" }))
});
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index ab3dfffcc..03ba33fee 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -12,7 +12,7 @@ import rimraf = require("rimraf");
import { AppliedSessionAgent, ExitHandler } from "./Session/agents/applied_session_agent";
import { ServerWorker } from "./Session/agents/server_worker";
import { Monitor } from "./Session/agents/monitor";
-import { MessageHandler } from "./Session/agents/promisified_ipc_manager";
+import { MessageHandler, ErrorLike } from "./Session/agents/promisified_ipc_manager";
/**
* If we're the monitor (master) thread, we should launch the monitor logic for the session.
@@ -70,7 +70,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
* Prepares the body of the email with information regarding a crash event.
*/
private _crashInstructions: string | undefined;
- private generateCrashInstructions({ name, message, stack }: Error): string {
+ private generateCrashInstructions({ name, message, stack }: ErrorLike): string {
if (!this._crashInstructions) {
this._crashInstructions = readFileSync(resolve(__dirname, "./templates/crash_instructions.txt"), { encoding: "utf8" });
}
@@ -109,7 +109,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
/**
* This sends an email with the generated crash report.
*/
- private dispatchCrashReport: MessageHandler<{ error: Error }> = async ({ error: crashCause }) => {
+ private dispatchCrashReport: MessageHandler<{ error: ErrorLike }> = async ({ error: crashCause }) => {
const { mainLog } = this.sessionMonitor;
const { notificationRecipient } = DashSessionAgent;
const error = await Email.dispatch({
@@ -127,7 +127,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
/**
* Logic for interfacing with Solr. Either starts it,
- * stops it, or rebuilds its indicies.
+ * stops it, or rebuilds its indices.
*/
private executeSolrCommand = async (args: string[]): Promise<void> => {
const { exec, mainLog } = this.sessionMonitor;
@@ -224,6 +224,6 @@ export class DashSessionAgent extends AppliedSessionAgent {
export namespace DashSessionAgent {
- export const notificationRecipient = "brownptcdash@gmail.com";
+ export const notificationRecipient = "browndashptc@gmail.com";
}
diff --git a/src/server/DashSession/Session/agents/monitor.ts b/src/server/DashSession/Session/agents/monitor.ts
index ee8afee65..0fdaf07ff 100644
--- a/src/server/DashSession/Session/agents/monitor.ts
+++ b/src/server/DashSession/Session/agents/monitor.ts
@@ -2,7 +2,7 @@ import { ExitHandler } from "./applied_session_agent";
import { Configuration, configurationSchema, defaultConfig, Identifiers, colorMapping } from "../utilities/session_config";
import Repl, { ReplAction } from "../utilities/repl";
import { isWorker, setupMaster, on, Worker, fork } from "cluster";
-import { manage, MessageHandler } from "./promisified_ipc_manager";
+import { manage, MessageHandler, ErrorLike } from "./promisified_ipc_manager";
import { red, cyan, white, yellow, blue } from "colors";
import { exec, ExecOptions } from "child_process";
import { validate, ValidationError } from "jsonschema";
@@ -22,7 +22,7 @@ export class Monitor extends IPCMessageReceiver {
private readonly config: Configuration;
private activeWorker: Worker | undefined;
private key: string | undefined;
- // private repl: Repl;
+ private repl: Repl;
public static Create() {
if (isWorker) {
@@ -46,7 +46,7 @@ export class Monitor extends IPCMessageReceiver {
this.configureInternalHandlers();
this.config = this.loadAndValidateConfiguration();
this.initializeClusterFunctions();
- // this.repl = this.initializeRepl();
+ this.repl = this.initializeRepl();
}
protected configureInternalHandlers = () => {
@@ -90,7 +90,7 @@ export class Monitor extends IPCMessageReceiver {
}
public readonly coreHooks = Object.freeze({
- onCrashDetected: (listener: MessageHandler<{ error: Error }>) => this.on(Monitor.IntrinsicEvents.CrashDetected, listener),
+ onCrashDetected: (listener: MessageHandler<{ error: ErrorLike }>) => this.on(Monitor.IntrinsicEvents.CrashDetected, listener),
onServerRunning: (listener: MessageHandler<{ isFirstTime: boolean }>) => this.on(Monitor.IntrinsicEvents.ServerRunning, listener)
});
@@ -119,7 +119,7 @@ export class Monitor extends IPCMessageReceiver {
* that can invoke application logic external to this module
*/
public addReplCommand = (basename: string, argPatterns: (RegExp | string)[], action: ReplAction) => {
- // this.repl.registerCommand(basename, argPatterns, action);
+ this.repl.registerCommand(basename, argPatterns, action);
}
public exec = (command: string, options?: ExecOptions) => {
diff --git a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
index feff568e1..95aa686e6 100644
--- a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
+++ b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
@@ -43,8 +43,8 @@ type InternalMessageHandler = (message: InternalMessage) => (any | Promise<any>)
* Allows for the transmission of the error's key features over IPC.
*/
export interface ErrorLike {
- name?: string;
- message?: string;
+ name: string;
+ message: string;
stack?: string;
}
@@ -162,8 +162,8 @@ export class PromisifiedIPCManager {
}
if (!this.isDestroyed && this.target.send) {
const metadata = { id, isResponse: true };
- const response: Response = { results , error };
- const message = { name, args: response , metadata };
+ const response: Response = { results, error };
+ const message = { name, args: response, metadata };
delete this.pendingMessages[id];
this.target.send(message);
}
diff --git a/src/server/DashSession/Session/agents/server_worker.ts b/src/server/DashSession/Session/agents/server_worker.ts
index 976d27226..6a19bfa5d 100644
--- a/src/server/DashSession/Session/agents/server_worker.ts
+++ b/src/server/DashSession/Session/agents/server_worker.ts
@@ -1,6 +1,6 @@
import { ExitHandler } from "./applied_session_agent";
import { isMaster } from "cluster";
-import { manage } from "./promisified_ipc_manager";
+import { manage, ErrorLike } from "./promisified_ipc_manager";
import IPCMessageReceiver from "./process_message_router";
import { red, green, white, yellow } from "colors";
import { get } from "request-promise";
@@ -112,7 +112,9 @@ export class ServerWorker extends IPCMessageReceiver {
private proactiveUnplannedExit = async (error: Error): Promise<void> => {
this.shouldServerBeResponsive = false;
// communicates via IPC to the master thread that it should dispatch a crash notification email
- this.emit(Monitor.IntrinsicEvents.CrashDetected, { error });
+ const { name, message, stack } = error;
+ const errorLike: ErrorLike = { name, message, stack };
+ this.emit(Monitor.IntrinsicEvents.CrashDetected, { error: errorLike });
await this.executeExitHandlers(error);
// notify master thread (which will log update in the console) of crash event via IPC
this.lifecycleNotification(red(`crash event detected @ ${new Date().toUTCString()}`));
@@ -157,4 +159,4 @@ export class ServerWorker extends IPCMessageReceiver {
this.pollServer();
}
-} \ No newline at end of file
+}
diff --git a/src/server/DashSession/Session/utilities/session_config.ts b/src/server/DashSession/Session/utilities/session_config.ts
index b0e65dde4..bde98e9d2 100644
--- a/src/server/DashSession/Session/utilities/session_config.ts
+++ b/src/server/DashSession/Session/utilities/session_config.ts
@@ -19,7 +19,7 @@ const identifierProperties: Schema = {
const portProperties: Schema = {
type: "number",
- minimum: 1024,
+ minimum: 443,
maximum: 65535
};
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index e4d0d1f5f..d9b38c014 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -1,4 +1,4 @@
-import { red } from 'colors';
+import { red, green } from 'colors';
import { ExifImage } from 'exif';
import { File } from 'formidable';
import { createWriteStream, existsSync, readFileSync, rename, unlinkSync, writeFile } from 'fs';
@@ -62,6 +62,7 @@ export namespace DashUploadUtils {
const category = types[0];
let format = `.${types[1]}`;
+ console.log(green(`Processing upload of file (${name}) with upload type (${type}) in category (${category}).`));
switch (category) {
case "image":
diff --git a/src/server/GarbageCollector.ts b/src/server/GarbageCollector.ts
index a9a3b0481..7c441e3c0 100644
--- a/src/server/GarbageCollector.ts
+++ b/src/server/GarbageCollector.ts
@@ -65,7 +65,7 @@ async function GarbageCollect(full: boolean = true) {
// await new Promise(res => setTimeout(res, 3000));
const cursor = await Database.Instance.query({}, { userDocumentId: 1 }, 'users');
const users = await cursor.toArray();
- const ids: string[] = users.map(user => user.userDocumentId);
+ const ids: string[] = [...users.map(user => user.userDocumentId), ...users.map(user => user.sharingDocumentId), ...users.map(user => user.linkDatabaseId)];
const visited = new Set<string>();
const files: { [name: string]: string[] } = {};
diff --git a/src/server/SharedMediaTypes.ts b/src/server/SharedMediaTypes.ts
index a341fd1c2..f1fe582e5 100644
--- a/src/server/SharedMediaTypes.ts
+++ b/src/server/SharedMediaTypes.ts
@@ -8,7 +8,7 @@ export namespace AcceptableMedia {
export const webps = [".webp"];
export const tiffs = [".tiff"];
export const imageFormats = [...pngs, ...jpgs, ...gifs, ...webps, ...tiffs];
- export const videoFormats = [".mov", ".mp4"];
+ export const videoFormats = [".mov", ".mp4", ".quicktime"];
export const applicationFormats = [".pdf"];
export const audioFormats = [".wav", ".mp3", ".mpeg", ".flac", ".au", ".aiff", ".m4a", ".webm"];
}
diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index 00f1fe44e..3fbd4b3a7 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -47,7 +47,10 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
const model = {
email,
password,
- userDocumentId: Utils.GenerateGuid()
+ userDocumentId: Utils.GenerateGuid(),
+ sharingDocumentId: Utils.GenerateGuid(),
+ linkDatabaseId: Utils.GenerateGuid(),
+ cacheDocumentIds: ""
} as Partial<DashUserModel>;
const user = new User(model);
@@ -174,13 +177,13 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
const smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
- user: 'brownptcdash@gmail.com',
- pass: 'browngfx1'
+ user: 'browndashptc@gmail.com',
+ pass: 'TsarNicholas#2'
}
});
const mailOptions = {
to: user.email,
- from: 'brownptcdash@gmail.com',
+ from: 'browndashptc@gmail.com',
subject: 'Dash Password Reset',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
@@ -247,13 +250,13 @@ export let postReset = function (req: Request, res: Response) {
const smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
- user: 'brownptcdash@gmail.com',
- pass: 'browngfx1'
+ user: 'browndashptc@gmail.com',
+ pass: 'TsarNicholas#2'
}
});
const mailOptions = {
to: user.email,
- from: 'brownptcdash@gmail.com',
+ from: 'browndashptc@gmail.com',
subject: 'Your password has been changed',
text: 'Hello,\n\n' +
'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts
index 51d920a8f..bee28b96d 100644
--- a/src/server/authentication/DashUserModel.ts
+++ b/src/server/authentication/DashUserModel.ts
@@ -10,6 +10,9 @@ export type DashUserModel = mongoose.Document & {
passwordResetExpires?: Date,
userDocumentId: string;
+ sharingDocumentId: string;
+ linkDatabaseId: string;
+ cacheDocumentIds: string;
profile: {
name: string,
@@ -35,7 +38,10 @@ const userSchema = new mongoose.Schema({
passwordResetToken: String,
passwordResetExpires: Date,
- userDocumentId: String,
+ 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,
diff --git a/src/server/index.ts b/src/server/index.ts
index c4e6be8a2..9687c3b23 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -23,6 +23,7 @@ import { Logger } from "./ProcessFactory";
import RouteManager, { Method, PublicHandler } from './RouteManager';
import RouteSubscriber from './RouteSubscriber';
import initializeServer, { resolvedPorts } from './server_Initialization';
+import { DashSessionAgent } from "./DashSession/DashSessionAgent";
export const AdminPriviliges: Map<string, boolean> = new Map();
export const onWindows = process.platform === "win32";
@@ -186,9 +187,9 @@ export async function launchServer() {
* log the output of the server process, so it's not ideal for development.
* So, the 'else' clause is exactly what we've always run when executing npm start.
*/
-// if (process.env.RELEASE) {
-// (sessionAgent = new DashSessionAgent()).launch();
-// } else {
(Database.Instance as Database.Database).doConnect();
-launchServer();
-// }
+if (process.env.MONITORED) {
+ (sessionAgent = new DashSessionAgent()).launch();
+} else {
+ launchServer();
+}
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index b33e76c0b..7d111f359 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -201,15 +201,18 @@ export namespace WebSocket {
function setField(socket: Socket, newValue: Transferable) {
Database.Instance.update(newValue.id, newValue, () =>
- socket.broadcast.emit(MessageStore.SetField.Message, 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 } });
}
}
+ function GetRefFieldLocal([id, callback]: [string, (result?: Transferable) => void]) {
+ return Database.Instance.getDocument(id, callback);
+ }
function GetRefField([id, callback]: [string, (result?: Transferable) => void]) {
process.stdout.write(`.`);
- Database.Instance.getDocument(id, callback);
+ GetRefFieldLocal([id, callback]);
}
function GetRefFields([ids, callback]: [string[], (result?: Transferable[]) => void]) {
@@ -271,33 +274,106 @@ export namespace WebSocket {
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
+ const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
+ const newListItems = diff.diff.$set[updatefield].fields;
+ const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields.filter((item: any) => item !== undefined) || [];
+ diff.diff.$set[updatefield].fields = [...curList, ...newListItems.filter((newItem: any) => newItem && !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,
+ () => {
+ if (sendBack) {
+ console.log("RET BACK");
+ const 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);
+ }
+
+ function remFromListField(socket: Socket, diff: Diff, curListItems?: Transferable): void {
+ 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 || [];
+ 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,
+ () => {
+ if (sendBack) {
+ console.log("SEND BACK");
+ const 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);
+ }
+
+ const pendingOps = new Map<string, { diff: Diff, socket: Socket }[]>();
+
+ function dispatchNextOp(id: string) {
+ const next = pendingOps.get(id)!.shift();
+ if (next) {
+ const { diff, socket } = next;
+ if (diff.diff.$addToSet) {
+ return GetRefFieldLocal([diff.id, (result?: Transferable) => addToListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
+ }
+ if (diff.diff.$remFromSet) {
+ return GetRefFieldLocal([diff.id, (result?: Transferable) => remFromListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
+ }
+ return GetRefFieldLocal([diff.id, (result?: Transferable) => SetField(socket, diff, result)]);
+ }
+ if (!pendingOps.get(id)!.length) pendingOps.delete(id);
+ }
+
function UpdateField(socket: Socket, diff: Diff) {
+ if (pendingOps.has(diff.id)) {
+ pendingOps.get(diff.id)!.push({ diff, socket });
+ return true;
+ }
+ pendingOps.set(diff.id, [{ diff, socket }]);
+ if (diff.diff.$addToSet) {
+ return GetRefFieldLocal([diff.id, (result?: Transferable) => addToListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
+ }
+ if (diff.diff.$remFromSet) {
+ return GetRefFieldLocal([diff.id, (result?: Transferable) => remFromListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
+ }
+ 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);
const docfield = diff.diff.$set || diff.diff.$unset;
- if (!docfield) {
- return;
- }
- const update: any = { id: diff.id };
- let dynfield = false;
- for (let key in docfield) {
- 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 }; });
- const term = ToSearchTerm(val);
- if (term !== undefined) {
- const { suffix, value } = term;
- update[key + suffix] = { set: value };
- if (key.endsWith('lastModified')) {
- update["lastModified" + suffix] = value;
+ if (docfield) {
+ const update: any = { id: diff.id };
+ let dynfield = false;
+ for (let key in docfield) {
+ 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 }; });
+ const term = ToSearchTerm(val);
+ if (term !== undefined) {
+ const { suffix, value } = term;
+ update[key + suffix] = { set: value };
+ if (key.endsWith('lastModified')) {
+ update["lastModified" + suffix] = value;
+ }
}
}
+ if (dynfield) {
+ Search.updateDocument(update);
+ }
}
- if (dynfield) {
- Search.updateDocument(update);
- }
+ dispatchNextOp(diff.id);
}
function DeleteField(socket: Socket, id: string) {