Files
matrix-backend/src/websocket.ts
T

134 lines
5.0 KiB
TypeScript

import {Server} from "http";
import {Server as WebSocketServer, WebSocket} from "ws";
import {verifyClient} from "./utils/verifyClient";
import {ExtendedWebSocket} from "./interfaces/extendedWebsocket";
import {WebsocketServerEventHandler} from "./utils/websocket/websocketServerEventHandler";
import {WebsocketEventHandler} from "./utils/websocket/websocketEventHandler";
import {WebsocketEventType} from "./utils/websocket/websocketCustomEvents/websocketEventType";
import {
appEventBus,
SPOTIFY_STATE_UPDATED_EVENT,
USER_UPDATED_EVENT,
WEATHER_STATE_UPDATED_EVENT
} from "./utils/eventBus";
import {IUser} from "./db/models/user";
import {SpotifyPollingService} from "./services/spotifyPollingService";
import {UserService} from "./services/db/UserService";
import {WeatherPollingService} from "./services/weatherPollingService";
import {JwtAuthenticator} from "./utils/jwtAuthenticator";
export class ExtendedWebSocketServer {
private readonly _wss: WebSocketServer;
private readonly userService: UserService;
private readonly spotifyPollingService: SpotifyPollingService;
private readonly weatherPollingService: WeatherPollingService;
constructor(server: Server, userService: UserService, spotifyPollingService: SpotifyPollingService,
weatherPollingService: WeatherPollingService, jwtAuthenticator: JwtAuthenticator) {
this.userService = userService;
this.spotifyPollingService = spotifyPollingService;
this.weatherPollingService = weatherPollingService;
this._wss = new WebSocketServer({
server,
verifyClient: (info, callback) => verifyClient(info.req, jwtAuthenticator, callback),
});
this._setupConnectionHandling();
this._listenForAppEvents();
}
public broadcast(message: string): void {
this.getConnectedClients().forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message, {binary: false});
}
});
}
public sendMessageToUser(uuid: string, message: string): void {
const client = this._findClientByUUID(uuid);
if (client && client.readyState === WebSocket.OPEN) {
client.send(message, {binary: false});
}
}
public getConnectedClients(): Set<ExtendedWebSocket> {
return this._wss.clients as Set<ExtendedWebSocket>;
}
private _setupConnectionHandling(): void {
const serverEventHandler = new WebsocketServerEventHandler(this._wss, this.userService);
serverEventHandler.enableConnectionEvent((ws) => {
this._onNewClientReady(ws);
});
const interval = serverEventHandler.enableHeartbeat(30000);
serverEventHandler.enableCloseEvent(() => {
clearInterval(interval);
});
}
private _onNewClientReady(ws: ExtendedWebSocket): void {
console.log("WebSocket client connected and authenticated");
const socketEventHandler = new WebsocketEventHandler(ws, this.spotifyPollingService, this.weatherPollingService);
socketEventHandler.enableErrorEvent();
socketEventHandler.enablePongEvent();
socketEventHandler.enableMessageEvent();
socketEventHandler.registerCustomEvents();
socketEventHandler.enableDisconnectEvent(() => {
console.log("User disconnected");
});
// send initial state and settings
ws.emit(WebsocketEventType.GET_SETTINGS, {});
ws.emit(WebsocketEventType.GET_STATE, {});
}
private _listenForAppEvents(): void {
appEventBus.on(USER_UPDATED_EVENT, (user: IUser) => {
console.log(`Received update for user ${user.uuid}`);
const client = this._findClientByUUID(user.uuid);
if (client) {
console.log(`Pushing update to user ${user.uuid}`);
client.emit(WebsocketEventType.UPDATE_USER_SINGLE, user);
}
});
appEventBus.on(SPOTIFY_STATE_UPDATED_EVENT, ({uuid, state}) => {
const client = this._findClientByUUID(uuid);
console.log(`Received update for user ${uuid}`);
if (client) {
client.send(JSON.stringify({
type: "SPOTIFY_UPDATE",
payload: state,
}), {binary: false});
}
});
appEventBus.on(WEATHER_STATE_UPDATED_EVENT, ({weatherData, subscribers}) => {
for (const uuid of subscribers) {
const client = this._findClientByUUID(uuid);
if (client) {
client.send(JSON.stringify({
type: "WEATHER_UPDATE",
payload: weatherData,
}), {binary: false});
}
}
});
}
private _findClientByUUID(uuid: string): ExtendedWebSocket | undefined {
for (const client of this.getConnectedClients()) {
if (client.payload?.uuid === uuid) {
return client;
}
}
return undefined;
}
}