1
Fork 0
This repository has been archived on 2022-02-28. You can view files and clone it, but cannot push or open issues or pull requests.
web-drs-backend/server.node.ts

206 lines
5.2 KiB
TypeScript
Raw Normal View History

import * as fs from "fs";
import * as https from "https";
import { WebSocketServer } from "ws";
import * as crypto from "crypto";
2022-01-08 20:23:03 +01:00
import type {
AckMessage,
IdResponseMessage,
TextMessage,
2022-01-17 16:20:31 +01:00
TypingMessage,
2022-01-21 14:51:26 +01:00
ConnectedUser,
ConnectedUsersMessage,
2022-01-08 20:23:03 +01:00
} from "./lib/ServerMessage";
2022-01-07 17:36:19 +01:00
import {
2022-01-07 18:59:25 +01:00
isServerMessage,
isTextMessage,
2022-01-17 16:20:31 +01:00
isTypingMessage,
2022-01-21 14:51:26 +01:00
isDesiredNameMessage,
2022-01-07 18:59:25 +01:00
MessageType,
} from "./lib/ServerMessage";
const port = 8085;
2022-01-21 14:51:26 +01:00
const timeout = 5000;
2022-01-17 16:20:31 +01:00
const typingTimeout = 2000;
2022-01-21 14:51:26 +01:00
const serverId = "00000000-0000-0000-0000-000000000000";
2022-01-07 17:36:19 +01:00
const httpsServer = https.createServer({
key: fs.readFileSync("./key.pem"),
cert: fs.readFileSync("./cert.pem"),
});
const webSocketServer = new WebSocketServer({ server: httpsServer });
2022-01-07 17:36:19 +01:00
2022-01-17 16:20:31 +01:00
/* If not using SSL/TLS
const webSocketServer = new WebSocketServer({ port });
*/
2022-01-07 17:36:19 +01:00
console.log("listening on port: " + port);
async function handleTextMessage(message: TextMessage, from: string) {
for (const to of webSocketServer.clients) {
to.send(
JSON.stringify(
Object.assign({}, message, {
author: from,
})
)
);
2022-01-07 17:36:19 +01:00
}
}
2022-01-07 17:36:19 +01:00
2022-01-17 16:20:31 +01:00
const activeConnections = new Set<string>();
const currentlyTyping = new Set<string>();
const currentlyTypingTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
2022-01-21 14:51:26 +01:00
const desiredNames = new Map<string, string>();
activeConnections.add(serverId);
desiredNames.set(serverId, "[SYSTEM]");
2022-01-17 16:20:31 +01:00
async function handleTypingMessage(_message: TypingMessage, from: string) {
currentlyTyping.add(from);
if(currentlyTypingTimeouts.has(from)) {
clearTimeout(currentlyTypingTimeouts.get(from)!);
}
currentlyTypingTimeouts.set(from, setTimeout(() => {
currentlyTyping.delete(from);
currentlyTypingTimeouts.delete(from);
sendCurrentlyTypingMessage(from, true);
}, typingTimeout));
sendCurrentlyTypingMessage(from);
}
async function sendCurrentlyTypingMessage(from?: string, stopped = false) {
for (const to of webSocketServer.clients) {
to.send(
JSON.stringify(
Object.assign({
type: MessageType.CURRENTLY_TYPING,
date: Date.now(),
}, {
currently: Array.from(currentlyTyping.values()),
}, from === undefined ? {
__ctx: "regular update",
} : {
__ctx: from + (stopped ? " stopped typing" : " started typing"),
})
)
)
}
}
2022-01-08 20:23:03 +01:00
async function handleCloseConnection(id: string) {
activeConnections.delete(id);
2022-01-21 14:51:26 +01:00
if (desiredNames.has(id)) {
desiredNames.delete(id);
}
for (const to of webSocketServer.clients) {
to.send(
JSON.stringify({
type: MessageType.CONNECTED_USERS,
__ctx: `${id} left`,
date: Date.now(),
connected:
Array.from(activeConnections,
(id) => {
return {
id,
desiredName: desiredNames.get(id),
} as ConnectedUser;
}),
} as ConnectedUsersMessage)
);
}
}
function handleNewConnection(id: string) {
activeConnections.add(id);
for (const to of webSocketServer.clients) {
to.send(
JSON.stringify({
type: MessageType.CONNECTED_USERS,
__ctx: `${id} joined`,
date: Date.now(),
connected:
Array.from(activeConnections,
(id) => {
return {
id,
desiredName: desiredNames.get(id),
} as ConnectedUser;
}),
} as ConnectedUsersMessage)
);
}
2022-01-08 20:23:03 +01:00
}
webSocketServer.on("connection", function connection(socket) {
const close = (reason: string, code: number = 1000) => {
socket.send(
JSON.stringify({
type: MessageType.ACK,
date: Date.now(),
__ctx: `closing connection. reason: ${reason}`,
} as AckMessage)
);
socket.close(code, `closing connection. reason: ${reason}`);
2022-01-08 20:23:03 +01:00
handleCloseConnection(authorId);
};
2022-01-07 18:59:25 +01:00
2022-01-14 13:33:03 +01:00
const authorId: string = crypto.randomUUID();
socket.on("message", function (rawMessage: string) {
2022-01-07 18:59:25 +01:00
const message = JSON.parse(rawMessage);
if (!isServerMessage(message)) {
2022-01-14 13:33:03 +01:00
console.error(`Unexpected message received from client "${authorId}": \`${message}\``);
2022-01-08 20:23:03 +01:00
return;
2022-01-21 14:51:26 +01:00
} else {
clearTimeout(closeTimeout);
closeTimeout = setTimeout(close, timeout);
2022-01-07 18:59:25 +01:00
}
2022-01-08 20:23:03 +01:00
2022-01-07 18:59:25 +01:00
if (isTextMessage(message)) {
2022-01-08 20:23:03 +01:00
if (message.author === authorId) {
handleTextMessage(message, authorId);
}
2022-01-17 16:20:31 +01:00
} else if (isTypingMessage(message)) {
handleTypingMessage(message, authorId);
2022-01-21 14:51:26 +01:00
} else if (isDesiredNameMessage(message)) {
desiredNames.set(authorId, message.desiredName);
2022-01-07 18:59:25 +01:00
}
2022-01-07 17:36:19 +01:00
});
socket.on("close", function close() {
console.log("closed a connection");
});
2022-01-08 20:23:03 +01:00
console.log("new client connected! ID:", authorId);
socket.send(
JSON.stringify({
2022-01-08 20:23:03 +01:00
type: MessageType.ID_RESPONSE,
__ctx: "connected successfully",
2022-01-08 20:23:03 +01:00
date: Date.now(),
authorId,
} as IdResponseMessage)
);
2022-01-21 14:51:26 +01:00
handleNewConnection(authorId);
socket.send(
JSON.stringify({
type: MessageType.TEXT,
__ctx: "server welcome message",
date: Date.now(),
author: serverId,
content: "Successfully connected. Welcome!\nSend a message to talk to other connected users.",
} as TextMessage)
);
2022-01-07 17:36:19 +01:00
let closeTimeout = setTimeout(close, timeout);
});
httpsServer.listen(port);