aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/websocket.ts244
1 files changed, 89 insertions, 155 deletions
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 905dbcf57..f10455680 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -3,13 +3,14 @@ import { createServer } from 'https';
import * as _ from 'lodash';
import { networkInterfaces } from 'os';
import { Server, Socket } from 'socket.io';
+import { SecureContextOptions } from 'tls';
import { ServerUtils } from '../ServerUtils';
+import { serializedDoctype, serializedFieldsType } from '../fields/ObjectField';
import { logPort } from './ActionUtilities';
import { Client } from './Client';
import { DashStats } from './DashStats';
import { DocumentsCollection } from './IDatabase';
-import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent } from './Message';
-import { Search } from './Search';
+import { Diff, GestureContent, MessageStore } from './Message';
import { resolvedPorts, socketMap, timeMap, userOperations } from './SocketData';
import { initializeGuest } from './authentication/DashUserModel';
import { Database } from './database';
@@ -25,25 +26,10 @@ export namespace WebSocket {
socket.broadcast.emit('receiveGesturePoints', content);
}
- function processOverlayTrigger(socket: Socket, content: MobileInkOverlayContent) {
- socket.broadcast.emit('receiveOverlayTrigger', content);
- }
-
- function processUpdateOverlayPosition(socket: Socket, content: UpdateMobileInkOverlayPositionContent) {
- socket.broadcast.emit('receiveUpdateOverlayPosition', content);
- }
-
- function processMobileDocumentUpload(socket: Socket, content: MobileDocumentUploadContent) {
- socket.broadcast.emit('receiveMobileDocumentUpload', content);
- }
-
export async function doDelete(onlyFields = true) {
const target: string[] = [];
onlyFields && target.push(DocumentsCollection);
await Database.Instance.dropSchema(...target);
- if (process.env.DISABLE_SEARCH !== 'true') {
- await Search.clear();
- }
initializeGuest();
}
@@ -63,31 +49,15 @@ export namespace WebSocket {
DashStats.logUserLogin(userEmail);
}
- function getField([id, callback]: [string, (result?: Transferable) => void]) {
- Database.Instance.getDocument(id, (result?: Transferable) => callback(result));
- }
-
- function getFields([ids, callback]: [string[], (result: Transferable[]) => void]) {
- Database.Instance.getDocuments(ids, callback);
- }
-
- 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
- Search.updateDocument({ id: newValue.id, data: { set: newValue.data } });
- }
- }
-
- function GetRefFieldLocal([id, callback]: [string, (result?: Transferable) => void]) {
+ function GetRefFieldLocal([id, callback]: [string, (result?: serializedDoctype) => void]) {
return Database.Instance.getDocument(id, callback);
}
- function GetRefField([id, callback]: [string, (result?: Transferable) => void]) {
+ function GetRefField([id, callback]: [string, (result?: serializedDoctype) => void]) {
process.stdout.write(`+`);
GetRefFieldLocal([id, callback]);
}
- function GetRefFields([ids, callback]: [string[], (result?: Transferable[]) => void]) {
+ function GetRefFields([ids, callback]: [string[], (result?: serializedDoctype[]) => void]) {
process.stdout.write(`${ids.length}…`);
Database.Instance.getDocuments(ids, callback);
}
@@ -151,11 +121,11 @@ export namespace WebSocket {
const { diff, socket } = next;
if (diff.diff.$addToSet) {
// eslint-disable-next-line no-use-before-define
- 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
+ return GetRefFieldLocal([diff.id, (result?: serializedDoctype) => 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) {
// eslint-disable-next-line no-use-before-define
- 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?: serializedDoctype) => remFromListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
}
// eslint-disable-next-line no-use-before-define
return SetField(socket, diff);
@@ -163,37 +133,41 @@ export namespace WebSocket {
return !pendingOps.get(id)!.length && pendingOps.delete(id);
}
- function addToListField(socket: Socket, diffIn: Diff, curListItems?: Transferable): void {
+ function addToListField(socket: Socket, diffIn: Diff, listDoc?: serializedDoctype): void {
const diff = diffIn;
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 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');
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 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('Warning: list modified during update. Composite list is being returned.');
- const { id } = socket;
- (socket as any).id = ''; // bcz: HACK. this prevents the update message from going back to the client that made the change.
- socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- (socket as any).id = id;
- } else {
- socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- }
- dispatchNextOp(diff.id);
- },
- false
- );
+ const listItems = listDoc?.fields?.[updatefield.replace('fields.', '')]?.fields.filter(item => item !== undefined) ?? [];
+ if (diff.diff.$set?.[updatefield]?.fields !== undefined) {
+ diff.diff.$set[updatefield]!.fields = [...listItems, ...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,
+ () => {
+ if (sendBack) {
+ console.log('Warning: list modified during update. Composite list is being returned.');
+ const { id } = socket;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (socket as any).id = ''; // bcz: HACK to reference private variable. this allows the update message to go back to the client that made the change.
+ socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (socket as any).id = id;
+ } else {
+ socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
+ }
+ dispatchNextOp(diff.id);
+ },
+ false
+ );
+ }
}
/**
@@ -208,7 +182,7 @@ export namespace WebSocket {
* the data
* @returns the closest index with the same value or -1 if the element was not found.
*/
- function findClosestIndex(list: any, indexesToDelete: number[], value: any, hintIndex: number) {
+ function findClosestIndex(list: { fieldId: string; __type: string }[], indexesToDelete: number[], value: { fieldId: string; __type: string }, hintIndex: number) {
let closestIndex = -1;
for (let i = 0; i < list.length; i++) {
if (list[i] === value && !indexesToDelete.includes(i)) {
@@ -232,63 +206,63 @@ export namespace WebSocket {
* items to delete)
* @param curListItems the server's current copy of the data
*/
- function remFromListField(socket: Socket, diffIn: Diff, curListItems?: Transferable): void {
+ function remFromListField(socket: Socket, diffIn: Diff, curListItems?: serializedDoctype): void {
const diff = diffIn;
- diff.diff.$set = diff.diff.$remFromSet;
+ diff.diff.$set = diff.diff.$remFromSet as serializedFieldsType;
+ const hint = diff.diff.$remFromSet?.hint;
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) || [];
- const { hint } = diff.diff.$set;
-
- if (hint) {
- // indexesToRemove stores the indexes that we mark for deletion, which is later used to filter the list (delete the elements)
- const indexesToRemove: number[] = [];
- for (let i = 0; i < hint.deleteCount; i++) {
- if (curList.length > i + hint.start && _.isEqual(curList[i + hint.start], remListItems[i])) {
- indexesToRemove.push(i + hint.start);
- } else {
- const closestIndex = findClosestIndex(curList, indexesToRemove, remListItems[i], i + hint.start);
- if (closestIndex !== -1) {
- indexesToRemove.push(closestIndex);
+ const updatefield = Array.from(Object.keys(diff.diff.$set ?? {}))[0];
+ const remListItems = diff.diff.$set[updatefield]?.fields;
+ if (diff.diff.$set[updatefield] !== undefined && remListItems) {
+ const curList = curListItems?.fields?.[updatefield.replace('fields.', '')]?.fields.filter(f => f !== null) || [];
+
+ if (hint) {
+ // indexesToRemove stores the indexes that we mark for deletion, which is later used to filter the list (delete the elements)
+ const indexesToRemove: number[] = [];
+ for (let i = 0; i < hint.deleteCount; i++) {
+ if (curList.length > i + hint.start && _.isEqual(curList[i + hint.start], remListItems[i])) {
+ indexesToRemove.push(i + hint.start);
} else {
- console.log('Item to delete was not found - index = -1');
+ const closestIndex = findClosestIndex(curList, indexesToRemove, remListItems[i], i + hint.start);
+ if (closestIndex !== -1) {
+ indexesToRemove.push(closestIndex);
+ } else {
+ console.log('Item to delete was not found - index = -1');
+ }
}
}
+ diff.diff.$set[updatefield]!.fields = curList.filter((curItem, index) => !indexesToRemove.includes(index));
+ } else {
+ // go back to the original way to delete if we didn't receive
+ // a hint from the client
+ diff.diff.$set[updatefield]!.fields = curList?.filter(curItem => !remListItems.some(remItem => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem)));
}
-
- diff.diff.$set[updatefield].fields = curList?.filter((curItem: any, index: number) => !indexesToRemove.includes(index));
- } else {
- // go back to the original way to delete if we didn't receive
- // a hint from the client
- 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))
+ // if the client and server have different versions of the data after
+ // deletion, they will have different lengths and the server will
+ // send its version of the data to the client
+ const sendBack = diff.diff.length !== remListItems.length;
+ delete diff.diff.length;
+ Database.Instance.update(
+ diff.id,
+ diff.diff,
+ () => {
+ if (sendBack) {
+ // the two copies are different, so the server sends its copy.
+ console.log('SEND BACK');
+ const { id } = socket;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (socket as any).id = ''; // bcz: HACK to access private variable this allows the update message to go back to the client that made the change.
+ socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (socket as any).id = id;
+ } else {
+ socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
+ }
+ dispatchNextOp(diff.id);
+ },
+ false
);
}
-
- // if the client and server have different versions of the data after
- // deletion, they will have different lengths and the server will
- // send its version of the data to the client
- const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
- delete diff.diff.length;
- Database.Instance.update(
- diff.id,
- diff.diff,
- () => {
- if (sendBack) {
- // the two copies are different, so the server sends its copy.
- console.log('SEND BACK');
- const { id } = socket;
- (socket as any).id = ''; // bcz: HACK. this prevents the update message from going back to the client that made the change.
- socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- (socket as any).id = id;
- } else {
- socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- }
- dispatchNextOp(diff.id);
- },
- false
- );
}
function UpdateField(socket: Socket, diff: Diff) {
@@ -307,43 +281,16 @@ export namespace WebSocket {
}
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
+ return GetRefFieldLocal([diff.id, (result?: serializedDoctype) => 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?: serializedDoctype) => remFromListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own
}
// eslint-disable-next-line no-use-before-define
return SetField(socket, diff);
}
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) {
- const update: any = { id: diff.id };
- let dynfield = false;
- // eslint-disable-next-line no-restricted-syntax
- for (let key in docfield) {
- // eslint-disable-next-line no-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 };
- });
- const term = ToSearchTerm(val);
- if (term !== undefined) {
- const { suffix, value } = term;
- update[key + suffix] = { set: value };
- if (key.endsWith('modificationDate')) {
- update['modificationDate' + suffix] = value;
- }
- }
- }
- if (dynfield) {
- Search.updateDocument(update);
- }
- }
dispatchNextOp(diff.id);
}
@@ -351,21 +298,15 @@ export namespace WebSocket {
Database.Instance.delete({ _id: id }).then(() => {
socket.broadcast.emit(MessageStore.DeleteField.Message, id);
});
-
- Search.deleteDocuments([id]);
}
function DeleteFields(socket: Socket, ids: string[]) {
Database.Instance.delete({ _id: { $in: ids } }).then(() => {
socket.broadcast.emit(MessageStore.DeleteFields.Message, ids);
});
- Search.deleteDocuments(ids);
}
- function CreateField(newValue: any) {
- Database.Instance.insert(newValue);
- }
- export async function initialize(isRelease: boolean, credentials: any) {
+ export async function initialize(isRelease: boolean, credentials: SecureContextOptions) {
let io: Server;
if (isRelease) {
const { socketPort } = process.env;
@@ -422,21 +363,14 @@ export namespace WebSocket {
ServerUtils.Emit(socket, MessageStore.Foo, 'handshooken');
ServerUtils.AddServerHandler(socket, MessageStore.Bar, guid => barReceived(socket, guid));
- ServerUtils.AddServerHandler(socket, MessageStore.SetField, args => setField(socket, args));
- ServerUtils.AddServerHandlerCallback(socket, MessageStore.GetField, getField);
- ServerUtils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields);
if (isRelease) {
ServerUtils.AddServerHandler(socket, MessageStore.DeleteAll, () => doDelete(false));
}
- ServerUtils.AddServerHandler(socket, MessageStore.CreateField, CreateField);
ServerUtils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff));
ServerUtils.AddServerHandler(socket, MessageStore.DeleteField, id => DeleteField(socket, id));
ServerUtils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids));
ServerUtils.AddServerHandler(socket, MessageStore.GesturePoints, content => processGesturePoints(socket, content));
- ServerUtils.AddServerHandler(socket, MessageStore.MobileInkOverlayTrigger, content => processOverlayTrigger(socket, content));
- ServerUtils.AddServerHandler(socket, MessageStore.UpdateMobileInkOverlayPosition, content => processUpdateOverlayPosition(socket, content));
- ServerUtils.AddServerHandler(socket, MessageStore.MobileDocumentUpload, content => processMobileDocumentUpload(socket, content));
ServerUtils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField);
ServerUtils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields);