aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/ActionUtilities.ts13
-rw-r--r--src/server/ApiManagers/UserManager.ts46
-rw-r--r--src/server/Initialization.ts8
-rw-r--r--src/server/Websocket/Websocket.ts22
-rw-r--r--src/server/authentication/controllers/user_controller.ts3
-rw-r--r--views/login.pug2
-rw-r--r--views/stylesheets/authentication.css81
-rw-r--r--views/user_activity.pug19
8 files changed, 155 insertions, 39 deletions
diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts
index a5f33833d..7f493dd70 100644
--- a/src/server/ActionUtilities.ts
+++ b/src/server/ActionUtilities.ts
@@ -55,4 +55,17 @@ export enum ConsoleColors {
export function logPort(listener: string, port: number) {
process.stdout.write(`${listener} listening on port `);
console.log(ConsoleColors.Yellow, port);
+}
+
+export 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
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
diff --git a/src/server/Initialization.ts b/src/server/Initialization.ts
index 08b476822..7fad5556d 100644
--- a/src/server/Initialization.ts
+++ b/src/server/Initialization.ts
@@ -20,6 +20,8 @@ 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
from being exposed. */
@@ -37,7 +39,11 @@ export default async function InitializeServer(options: InitializationOptions) {
server.use("/images", express.static(publicDirectory));
server.use("*", (req, _res, next) => {
- console.log(ConsoleColors.Cyan, req.originalUrl, req.user.email);
+ const userEmail = req.user?.email;
+ console.log(ConsoleColors.Cyan, req.originalUrl, userEmail ?? "<user logged out>");
+ if (userEmail) {
+ timeMap[userEmail] = Date.now();
+ }
next();
});
diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts
index 74a6b4263..fbf71f707 100644
--- a/src/server/Websocket/Websocket.ts
+++ b/src/server/Websocket/Websocket.ts
@@ -8,16 +8,12 @@ import * as io from 'socket.io';
import YoutubeApi from "../apis/youtube/youtubeApiSample";
import { GoogleCredentialsLoader } from "../credentials/CredentialsLoader";
import { ConsoleColors, logPort } from "../ActionUtilities";
+import { timeMap } from "../ApiManagers/UserManager";
export namespace WebSocket {
- interface Map {
- [key: string]: Client;
- }
- let clients: Map = {};
-
+ let clients: { [key: string]: Client } = {};
export const socketMap = new Map<SocketIO.Socket, string>();
- export const timeMap: { [id: string]: number } = {};
export async function start(serverPort: number, isRelease: boolean) {
await preliminaryFunctions();
@@ -31,9 +27,9 @@ export namespace WebSocket {
const endpoint = io();
endpoint.on("connection", function (socket: Socket) {
socket.use((_packet, next) => {
- let id = socketMap.get(socket);
- if (id) {
- timeMap[id] = Date.now();
+ let userEmail = socketMap.get(socket);
+ if (userEmail) {
+ timeMap[userEmail] = Date.now();
}
next();
});
@@ -87,10 +83,10 @@ export namespace WebSocket {
await Search.Instance.clear();
}
- function barReceived(socket: SocketIO.Socket, guid: string) {
- clients[guid] = new Client(guid.toString());
- console.log(ConsoleColors.Green, `user ${guid} has connected to the web socket`);
- socketMap.set(socket, guid);
+ function barReceived(socket: SocketIO.Socket, userEmail: string) {
+ clients[userEmail] = new Client(userEmail.toString());
+ console.log(ConsoleColors.Green, `user ${userEmail} has connected to the web socket`);
+ socketMap.set(socket, userEmail);
}
function getField([id, callback]: [string, (result?: Transferable) => void]) {
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts
index b2b9d33f6..517353479 100644
--- a/src/server/authentication/controllers/user_controller.ts
+++ b/src/server/authentication/controllers/user_controller.ts
@@ -3,10 +3,7 @@ import { Request, Response, NextFunction } from "express";
import * as passport from "passport";
import { IVerifyOptions } from "passport-local";
import "../config/passport";
-import * as request from "express-validator";
import flash = require("express-flash");
-import * as session from "express-session";
-import * as pug from 'pug';
import * as async from 'async';
import * as nodemailer from 'nodemailer';
import c = require("crypto");
diff --git a/views/login.pug b/views/login.pug
index 9bc40a495..26da5e29e 100644
--- a/views/login.pug
+++ b/views/login.pug
@@ -14,11 +14,9 @@ block content
.inner.login
h3.auth_header Log In
.form-group
- //- label.col-sm-3.control-label(for='email', id='email_label') Email
.col-sm-7
input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus, required)
.form-group
- //- label.col-sm-3.control-label(for='password') Password
.col-sm-7
input.form-control(type='password', name='password', id='password', placeholder='Password', required)
.form-group
diff --git a/views/stylesheets/authentication.css b/views/stylesheets/authentication.css
index 36bb880af..ff1f4aace 100644
--- a/views/stylesheets/authentication.css
+++ b/views/stylesheets/authentication.css
@@ -139,4 +139,85 @@ body {
padding-right: 10px;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
+}
+
+.outermost, .online-container {
+ display: flex;
+ flex-direction: row;
+ height: 98vh;
+ justify-content: center;
+}
+
+.online-container {
+ background: white;
+ display: flex;
+ flex-direction: row;
+ height: 80%;
+ width: 80%;
+ align-self: center;
+ justify-content: center;
+ border-radius: 8px;
+ box-shadow: 10px 10px 10px #00000099;
+}
+
+.partition {
+ width: 50%;
+ display: flex;
+ flex-direction: column;
+ border: 1px solid black;
+}
+
+.inner-activity {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ height: 100%;
+ border-top: 2px solid black;
+ background: white;
+ padding: 20px;
+ overflow: scroll;
+}
+
+ol {
+ align-self: center;
+}
+
+li {
+ font-family: Arial, Helvetica, sans-serif;
+ border: 1px solid black;
+ padding: 10px;
+ border-radius: 5px;
+ margin-bottom: 5px;
+}
+
+.duration {
+ font-style: italic;
+}
+
+span.user-type {
+ align-self: center;
+ font-family: Arial, Helvetica, sans-serif;
+ font-weight: bold;
+ font-size: 20px;
+ margin: 50px;
+}
+
+#active-partition {
+ background: green;
+ border-top-left-radius: 8px;
+ border-bottom-left-radius: 8px;
+}
+
+#active-inner {
+ border-bottom-left-radius: 8px;
+}
+
+#inactive-partition {
+ background: red;
+ border-top-right-radius: 8px;
+ border-bottom-right-radius: 8px;
+}
+
+#inactive-inner {
+ border-bottom-right-radius: 8px;
} \ No newline at end of file
diff --git a/views/user_activity.pug b/views/user_activity.pug
new file mode 100644
index 000000000..68e42140d
--- /dev/null
+++ b/views/user_activity.pug
@@ -0,0 +1,19 @@
+extends ./layout
+
+block content
+ style
+ include ./stylesheets/authentication.css
+ .outermost
+ .online-container
+ .partition(id="active-partition")
+ span.user-type Active Users
+ .inner-activity(id="active-inner")
+ ol
+ each val in active
+ li= val
+ .partition(id="inactive-partition")
+ span.user-type Inactive Users
+ .inner-activity(id="inactive-inner")
+ ol
+ each val in inactive
+ li= val \ No newline at end of file