aboutsummaryrefslogtreecommitdiff
path: root/src/server/websocket.ts
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-04-17 12:27:21 -0400
committerbobzel <zzzman@gmail.com>2024-04-17 12:27:21 -0400
commit2a313f28fcb8675223708b0657de7517a3281095 (patch)
treeed6db226cc7d323aee378eddee43dc5f3bdb1ef9 /src/server/websocket.ts
parent62937027183dc8acf14e489fbb4590aff6fce2cd (diff)
restoring eslint - updates not complete yet
Diffstat (limited to 'src/server/websocket.ts')
-rw-r--r--src/server/websocket.ts380
1 files changed, 190 insertions, 190 deletions
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 38134f2c1..cb16bce72 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -3,152 +3,26 @@ import { createServer } from 'https';
import * as _ from 'lodash';
import { networkInterfaces } from 'os';
import { Server, Socket } from 'socket.io';
-import { Utils } from '../Utils';
+import { ServerUtils } from '../ServerUtils';
import { logPort } from './ActionUtilities';
-import { timeMap } from './ApiManagers/UserManager';
import { Client } from './Client';
import { DashStats } from './DashStats';
import { DocumentsCollection } from './IDatabase';
import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent, YoutubeQueryInput, YoutubeQueryTypes } from './Message';
import { Search } from './Search';
+import { resolvedPorts, socketMap, timeMap, userOperations } from './SocketData';
import { GoogleCredentialsLoader } from './apis/google/CredentialsLoader';
import YoutubeApi from './apis/youtube/youtubeApiSample';
import { initializeGuest } from './authentication/DashUserModel';
import { Database } from './database';
-import { resolvedPorts } from './server_Initialization';
export namespace WebSocket {
+ let CurUser: string | undefined;
+ // eslint-disable-next-line import/no-mutable-exports
export let _socket: Socket;
+ // eslint-disable-next-line import/no-mutable-exports
+ export let _disconnect: Function;
export const clients: { [key: string]: Client } = {};
- export const socketMap = new Map<Socket, string>();
- export const userOperations = new Map<string, number>();
- export let disconnect: Function;
-
- export async function initialize(isRelease: boolean, credentials:any) {
- let io: Server;
- if (isRelease) {
- const { socketPort } = process.env;
- if (socketPort) {
- resolvedPorts.socket = Number(socketPort);
- }
- const httpsServer = createServer(credentials);
- io = new Server(httpsServer, {})
- httpsServer.listen(resolvedPorts.socket);
- } else {
- io = new Server();
- io.listen(resolvedPorts.socket);
- }
- logPort('websocket', resolvedPorts.socket);
-
- io.on('connection', socket => {
- _socket = socket;
- socket.use((_packet, next) => {
- const userEmail = socketMap.get(socket);
- if (userEmail) {
- timeMap[userEmail] = Date.now();
- }
- next();
- });
-
- socket.emit(MessageStore.UpdateStats.Message, DashStats.getUpdatedStatsBundle());
-
- // convenience function to log server messages on the client
- function log(message?: any, ...optionalParams: any[]) {
- socket.emit('log', ['Message from server:', message, ...optionalParams]);
- }
-
- socket.on('message', function (message, room) {
- console.log('Client said: ', message);
- socket.in(room).emit('message', message);
- });
-
- socket.on('create or join', function (room) {
- console.log('Received request to create or join room ' + room);
-
- const clientsInRoom = socket.rooms.has(room);
- const numClients = clientsInRoom ? Object.keys(room.sockets).length : 0;
- console.log('Room ' + room + ' now has ' + numClients + ' client(s)');
-
- if (numClients === 0) {
- 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
- socket.emit('full', room);
- }
- });
-
- socket.on('ipaddr', function () {
- const ifaces = networkInterfaces();
- for (const dev in ifaces) {
- ifaces[dev]?.forEach(function (details) {
- if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
- socket.emit('ipaddr', details.address);
- }
- });
- }
- });
-
- socket.on('bye', function () {
- console.log('received bye');
- });
-
- socket.on('disconnect', function () {
- let currentUser = socketMap.get(socket);
- if (!(currentUser === undefined)) {
- let currentUsername = currentUser.split(' ')[0];
- DashStats.logUserLogout(currentUsername, socket);
- delete timeMap[currentUsername];
- }
- });
-
- 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.AddServerHandlerCallback(socket, MessageStore.GetField, getField);
- Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields);
- if (isRelease) {
- Utils.AddServerHandler(socket, MessageStore.DeleteAll, () => doDelete(false));
- }
-
- Utils.AddServerHandler(socket, MessageStore.CreateField, CreateField);
- Utils.AddServerHandlerCallback(socket, MessageStore.YoutubeApiQuery, HandleYoutubeQuery);
- Utils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff));
- Utils.AddServerHandler(socket, MessageStore.DeleteField, id => DeleteField(socket, id));
- Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids));
- Utils.AddServerHandler(socket, MessageStore.GesturePoints, content => processGesturePoints(socket, content));
- Utils.AddServerHandler(socket, MessageStore.MobileInkOverlayTrigger, content => processOverlayTrigger(socket, content));
- Utils.AddServerHandler(socket, MessageStore.UpdateMobileInkOverlayPosition, content => processUpdateOverlayPosition(socket, content));
- Utils.AddServerHandler(socket, MessageStore.MobileDocumentUpload, content => processMobileDocumentUpload(socket, content));
- Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField);
- Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields);
-
- /**
- * Whenever we receive the go-ahead, invoke the import script and pass in
- * as an emitter and a terminator the functions that simply broadcast a result
- * or indicate termination to the client via the web socket
- */
-
- disconnect = () => {
- socket.broadcast.emit('connection_terminated', Date.now());
- socket.disconnect(true);
- };
- });
-
- setInterval(function () {
- // Utils.Emit(socket, MessageStore.UpdateStats, DashStats.getUpdatedStatsBundle());
-
- io.emit(MessageStore.UpdateStats.Message, DashStats.getUpdatedStatsBundle());
- }, DashStats.SAMPLING_INTERVAL);
- }
function processGesturePoints(socket: Socket, content: GestureContent) {
socket.broadcast.emit('receiveGesturePoints', content);
@@ -174,8 +48,11 @@ export namespace WebSocket {
break;
case YoutubeQueryTypes.SearchVideo:
YoutubeApi.authorizedGetVideos(ProjectCredentials, query.userInput, callback);
+ break;
case YoutubeQueryTypes.VideoDetails:
YoutubeApi.authorizedGetVideoDetails(ProjectCredentials, query.videoIds, callback);
+ break;
+ default:
}
}
@@ -189,6 +66,9 @@ export namespace WebSocket {
initializeGuest();
}
+ function printActiveUsers() {
+ socketMap.forEach((user, socket) => !socket.disconnected && console.log(user));
+ }
function barReceived(socket: Socket, userEmail: string) {
clients[userEmail] = new Client(userEmail.toString());
const currentdate = new Date();
@@ -203,7 +83,7 @@ export namespace WebSocket {
}
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));
}
function getFields([ids, callback]: [string[], (result: Transferable[]) => void]) {
@@ -248,27 +128,24 @@ export namespace WebSocket {
list: [
'_l',
list => {
- const results = [];
- for (const value of list.fields) {
- const term = ToSearchTerm(value);
- if (term) {
- results.push(term.value);
- }
- }
+ const results: any[] = [];
+ // eslint-disable-next-line no-use-before-define
+ list.fields.forEach((value: any) => ToSearchTerm(value) && results.push(ToSearchTerm(value)!.value));
return results.length ? results : null;
},
],
};
- function ToSearchTerm(val: any): { suffix: string; value: any } | undefined {
+ function ToSearchTerm(valIn: any): { suffix: string; value: any } | undefined {
+ let val = valIn;
if (val === null || val === undefined) {
- return;
+ return undefined;
}
const type = val.__type || typeof val;
let suffix = suffixMap[type];
if (!suffix) {
- return;
+ return undefined;
}
if (Array.isArray(suffix)) {
const accessor = suffix[1];
@@ -277,7 +154,7 @@ export namespace WebSocket {
} else {
val = val[accessor];
}
- suffix = suffix[0];
+ [suffix] = suffix;
}
return { suffix, value: val };
}
@@ -285,8 +162,28 @@ export namespace WebSocket {
function getSuffix(value: string | [string, any]): string {
return typeof value === 'string' ? value : value[0];
}
+ const pendingOps = new Map<string, { diff: Diff; socket: Socket }[]>();
- function addToListField(socket: Socket, diff: Diff, curListItems?: Transferable): void {
+ function dispatchNextOp(id: string) {
+ const next = pendingOps.get(id)!.shift();
+ if (next) {
+ 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
+ }
+ 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
+ }
+ // eslint-disable-next-line no-use-before-define
+ return SetField(socket, diff);
+ }
+ return !pendingOps.get(id)!.length && pendingOps.delete(id);
+ }
+
+ function addToListField(socket: Socket, diffIn: Diff, curListItems?: Transferable): 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];
@@ -296,7 +193,7 @@ export namespace WebSocket {
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))];
+ 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(
@@ -305,11 +202,13 @@ export namespace WebSocket {
() => {
if (sendBack) {
console.log('Warning: list modified during update. Composite list is being returned.');
- const id = socket.id;
- (socket as any).id = '';
+ 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);
+ } else {
+ socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
+ }
dispatchNextOp(diff.id);
},
false
@@ -352,28 +251,28 @@ export namespace WebSocket {
* items to delete)
* @param curListItems the server's current copy of the data
*/
- function remFromListField(socket: Socket, diff: Diff, curListItems?: Transferable): void {
+ function remFromListField(socket: Socket, diffIn: Diff, curListItems?: Transferable): void {
+ const diff = diffIn;
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) || [];
- const hint = diff.diff.$set.hint;
+ 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)
- let indexesToRemove: number[] = [];
+ 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);
- continue;
- }
-
- let 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');
+ 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');
+ }
}
}
@@ -398,45 +297,23 @@ export namespace WebSocket {
if (sendBack) {
// the two copies are different, so the server sends its copy.
console.log('SEND BACK');
- const id = socket.id;
- (socket as any).id = '';
+ 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);
+ } 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 printActiveUsers() {
- socketMap.forEach((user, socket) => {
- !socket.disconnected && console.log(user);
- });
- }
- var CurUser: string | undefined = undefined;
-
function UpdateField(socket: Socket, diff: Diff) {
const curUser = socketMap.get(socket);
- if (!curUser) return;
- let currentUsername = curUser.split(' ')[0];
+ if (!curUser) return false;
+ const currentUsername = curUser.split(' ')[0];
userOperations.set(currentUsername, userOperations.get(currentUsername) !== undefined ? userOperations.get(currentUsername)! + 1 : 0);
if (CurUser !== socketMap.get(socket)) {
@@ -454,15 +331,18 @@ export namespace WebSocket {
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)]);
+ // eslint-disable-next-line no-use-before-define
+ return SetField(socket, diff);
}
- function SetField(socket: Socket, diff: Diff, curListItems?: Transferable) {
+ 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];
@@ -504,4 +384,124 @@ export namespace WebSocket {
function CreateField(newValue: any) {
Database.Instance.insert(newValue);
}
+ export async function initialize(isRelease: boolean, credentials: any) {
+ let io: Server;
+ if (isRelease) {
+ const { socketPort } = process.env;
+ if (socketPort) {
+ resolvedPorts.socket = Number(socketPort);
+ }
+ const httpsServer = createServer(credentials);
+ io = new Server(httpsServer, {});
+ httpsServer.listen(resolvedPorts.socket);
+ } else {
+ io = new Server();
+ io.listen(resolvedPorts.socket);
+ }
+ logPort('websocket', resolvedPorts.socket);
+
+ io.on('connection', socket => {
+ _socket = socket;
+ socket.use((_packet, next) => {
+ const userEmail = socketMap.get(socket);
+ if (userEmail) {
+ timeMap[userEmail] = Date.now();
+ }
+ next();
+ });
+
+ socket.emit(MessageStore.UpdateStats.Message, DashStats.getUpdatedStatsBundle());
+
+ socket.on('message', (message, room) => {
+ console.log('Client said: ', message);
+ socket.in(room).emit('message', message);
+ });
+
+ socket.on('create or join', room => {
+ console.log('Received request to create or join room ' + room);
+
+ const clientsInRoom = socket.rooms.has(room);
+ const numClients = clientsInRoom ? Object.keys(room.sockets).length : 0;
+ console.log('Room ' + room + ' now has ' + numClients + ' client(s)');
+
+ if (numClients === 0) {
+ 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
+ socket.emit('full', room);
+ }
+ });
+
+ socket.on('ipaddr', () => {
+ const ifaces = networkInterfaces();
+ for (const dev in ifaces) {
+ ifaces[dev]?.forEach(details => {
+ if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
+ socket.emit('ipaddr', details.address);
+ }
+ });
+ }
+ });
+
+ socket.on('bye', () => {
+ console.log('received bye');
+ });
+
+ socket.on('disconnect', () => {
+ const currentUser = socketMap.get(socket);
+ if (!(currentUser === undefined)) {
+ const currentUsername = currentUser.split(' ')[0];
+ DashStats.logUserLogout(currentUsername, socket);
+ delete timeMap[currentUsername];
+ }
+ });
+
+ 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.AddServerHandlerCallback(socket, MessageStore.YoutubeApiQuery, HandleYoutubeQuery);
+ 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);
+
+ /**
+ * Whenever we receive the go-ahead, invoke the import script and pass in
+ * as an emitter and a terminator the functions that simply broadcast a result
+ * or indicate termination to the client via the web socket
+ */
+
+ _disconnect = () => {
+ socket.broadcast.emit('connection_terminated', Date.now());
+ socket.disconnect(true);
+ };
+ });
+
+ setInterval(() => {
+ // Utils.Emit(socket, MessageStore.UpdateStats, DashStats.getUpdatedStatsBundle());
+
+ io.emit(MessageStore.UpdateStats.Message, DashStats.getUpdatedStatsBundle());
+ }, DashStats.SAMPLING_INTERVAL);
+ }
}