From b831be86743e329cce441b3d7ae2aa5321e7fb9c Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sat, 23 Nov 2019 17:09:13 -0500 Subject: improved user activity log --- src/server/ApiManagers/UserManager.ts | 46 ++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'src/server/ApiManagers') diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 51a434fcf..4ee5a2b85 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -1,7 +1,14 @@ import ApiManager, { Registration } from "./ApiManager"; import { Method } from "../RouteManager"; -import { WebSocket } from "../Websocket/Websocket"; import { Database } from "../database"; +import { msToTime } from "../ActionUtilities"; + +export const timeMap: { [id: string]: number } = {}; +interface ActivityUnit { + user: string; + duration: number; +} + export default class UserManager extends ApiManager { @@ -32,35 +39,34 @@ export default class UserManager extends ApiManager { register({ method: Method.GET, - subscription: "/whosOnline", + subscription: "/activity", onValidation: ({ res }) => { - let users: any = { active: {}, inactive: {} }; const now = Date.now(); - const { timeMap } = WebSocket; + const activeTimes: ActivityUnit[] = []; + const inactiveTimes: ActivityUnit[] = []; + for (const user in timeMap) { const time = timeMap[user]; - const key = ((now - time) / 1000) < (60 * 5) ? "active" : "inactive"; - users[key][user] = `Last active ${msToTime(now - time)} ago`; + const duration = now - time; + const target = (duration / 1000) < (60 * 5) ? activeTimes : inactiveTimes; + target.push({ user, duration }); } - res.send(users); + 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} (last active ${msToTime(duration)} ago)`); + }; + + res.render("user_activity.pug", { + title: "User Activity", + active: process(activeTimes), + inactive: process(inactiveTimes) + }); } }); } -} - -function msToTime(duration: number) { - let milliseconds = Math.floor((duration % 1000) / 100), - seconds = Math.floor((duration / 1000) % 60), - minutes = Math.floor((duration / (1000 * 60)) % 60), - hours = Math.floor((duration / (1000 * 60 * 60)) % 24); - - let hoursS = (hours < 10) ? "0" + hours : hours; - let minutesS = (minutes < 10) ? "0" + minutes : minutes; - let secondsS = (seconds < 10) ? "0" + seconds : seconds; - - return hoursS + ":" + minutesS + ":" + secondsS + "." + milliseconds; } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From e324248724a130a84b459a072dc846f500f8d9b0 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sat, 23 Nov 2019 18:19:27 -0500 Subject: heartbeats --- src/client/DocServer.ts | 2 ++ src/client/util/ClientDiagnostics.ts | 29 +++++++++++++++++++++++++++++ src/client/views/Main.tsx | 2 ++ src/client/views/MainView.tsx | 6 ++++-- src/server/ApiManagers/DiagnosticManager.ts | 26 ++++++++++++++++++++++++++ src/server/ApiManagers/UserManager.ts | 2 +- src/server/Initialization.ts | 13 +++++++------ src/server/index.ts | 2 ++ 8 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 src/client/util/ClientDiagnostics.ts create mode 100644 src/server/ApiManagers/DiagnosticManager.ts (limited to 'src/server/ApiManagers') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 2cec1046b..14479694c 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -64,6 +64,8 @@ export namespace DocServer { } } + let connection_error = false; + export function init(protocol: string, hostname: string, port: number, identifier: string) { _cache = {}; GUID = identifier; diff --git a/src/client/util/ClientDiagnostics.ts b/src/client/util/ClientDiagnostics.ts new file mode 100644 index 000000000..e454cdecb --- /dev/null +++ b/src/client/util/ClientDiagnostics.ts @@ -0,0 +1,29 @@ +import { observable, runInAction } from "mobx"; +import { MainView } from "../views/MainView"; + +export namespace ClientDiagnostics { + + export function start() { + + let serverPolls = 0; + const serverHandle = setInterval(async () => { + if (++serverPolls === 20) { + alert("Your connection to the server has been terminated."); + clearInterval(serverHandle); + } + await fetch("/serverHeartbeat"); + serverPolls--; + }, 100); + + + const solrHandle = setInterval(async () => { + const response = await fetch("/solrHeartbeat"); + if (!response) { + alert("Looks like SOLR is not running on your machine."); + clearInterval(solrHandle); + } + }, 100); + + } + +} \ No newline at end of file diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index b21eb9c8f..dec4a24e4 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -5,10 +5,12 @@ import * as ReactDOM from 'react-dom'; import * as React from 'react'; import { DocServer } from "../DocServer"; import { AssignAllExtensions } from "../../extensions/General/Extensions"; +import { ClientDiagnostics } from "../util/ClientDiagnostics"; AssignAllExtensions(); (async () => { + ClientDiagnostics.start(); const info = await CurrentUserUtils.loadCurrentUser(); DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email); await Docs.Prototypes.initialize(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 031926604..d352ad776 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -4,7 +4,7 @@ import { faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; +import { action, computed, configure, observable, reaction, runInAction, autorun } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; @@ -414,7 +414,8 @@ export class MainView extends React.Component { - ; + + ; } @computed get mainContent() { @@ -422,6 +423,7 @@ export class MainView extends React.Component { return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
+
HEY!
diff --git a/src/server/ApiManagers/DiagnosticManager.ts b/src/server/ApiManagers/DiagnosticManager.ts new file mode 100644 index 000000000..b775167b6 --- /dev/null +++ b/src/server/ApiManagers/DiagnosticManager.ts @@ -0,0 +1,26 @@ +import ApiManager, { Registration } from "./ApiManager"; +import { Method } from "../RouteManager"; +import request = require('request-promise'); + +export default class DiagnosticManager extends ApiManager { + + protected initialize(register: Registration): void { + + register({ + method: Method.GET, + subscription: "/serverHeartbeat", + onValidation: ({ res }) => res.send(true) + }); + + register({ + method: Method.GET, + subscription: "/solrHeartbeat", + onValidation: async ({ res }) => { + const response = await request("http://localhost:8983"); + res.send(response !== undefined); + } + }); + + } + +} \ No newline at end of file diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 4ee5a2b85..8edeab16d 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -56,7 +56,7 @@ export default class UserManager extends ApiManager { 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} (last active ${msToTime(duration)} ago)`); + return sorted.map(({ user, duration }) => `${user} (${msToTime(duration)})`); }; res.render("user_activity.pug", { diff --git a/src/server/Initialization.ts b/src/server/Initialization.ts index 7fad5556d..76acb4363 100644 --- a/src/server/Initialization.ts +++ b/src/server/Initialization.ts @@ -20,7 +20,6 @@ import * as request from 'request'; import RouteSubscriber from './RouteSubscriber'; import { publicDirectory } from '.'; import { ConsoleColors, logPort } from './ActionUtilities'; -import { WebSocket } from './Websocket/Websocket'; import { timeMap } from './ApiManagers/UserManager'; /* RouteSetter is a wrapper around the server that prevents the server @@ -38,11 +37,13 @@ export default async function InitializeServer(options: InitializationOptions) { server.use(express.static(publicDirectory)); server.use("/images", express.static(publicDirectory)); - server.use("*", (req, _res, next) => { - const userEmail = req.user?.email; - console.log(ConsoleColors.Cyan, req.originalUrl, userEmail ?? ""); - if (userEmail) { - timeMap[userEmail] = Date.now(); + server.use("*", ({ user, originalUrl }, _res, next) => { + if (!originalUrl.includes("Heartbeat")) { + const userEmail = user?.email; + console.log(ConsoleColors.Cyan, originalUrl, userEmail ?? ""); + if (userEmail) { + timeMap[userEmail] = Date.now(); + } } next(); }); diff --git a/src/server/index.ts b/src/server/index.ts index 9c48aca45..d02a6005e 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -21,6 +21,7 @@ import UploadManager from "./ApiManagers/UploadManager"; import { log_execution } from "./ActionUtilities"; import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager"; import GooglePhotosManager from "./ApiManagers/GooglePhotosManager"; +import DiagnosticManager from "./ApiManagers/DiagnosticManager"; export const publicDirectory = path.resolve(__dirname, "public"); export const filesDirectory = path.resolve(publicDirectory, "files") + "/"; @@ -55,6 +56,7 @@ function routeSetter({ isRelease, addSupervisedRoute }: RouteManager) { new UserManager(), new UploadManager(), new DownloadManager(), + new DiagnosticManager(), new SearchManager(), new PDFManager(), new DeleteManager(), -- cgit v1.2.3-70-g09d2