UNPKG

@towercg2/server

Version:

The server runtime for the TowerCG2 video graphics system.

132 lines (131 loc) 5.85 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const lodash_1 = __importDefault(require("lodash")); const express_1 = __importDefault(require("express")); const socket_io_1 = __importDefault(require("socket.io")); const sleep_promise_1 = __importDefault(require("sleep-promise")); const v4_1 = __importDefault(require("uuid/v4")); const client_1 = require("@towercg2/client"); const auth_1 = require("../auth"); const utils_1 = require("../utils"); const AUTHENTICATED_CLIENTS = "AUTHENTICATED_CLIENTS"; class Server { constructor(baseConfig) { this._started = false; this._stopping = false; this.plugins = []; this.config = baseConfig; this.logger = this.baseLogger.child({ component: this.constructor.name }); this.logger.info({ port: this.config.port, dataDirectory: this.config.dataDirectory }, "Instantiating server."); } get started() { return this._started; } get baseLogger() { return this.config.logger; } get dataDirectory() { return this.config.dataDirectory; } get state() { return lodash_1.default.fromPairs(this.plugins.map((plugin) => [plugin.name, plugin.state])); } get authenticatedSockets() { return Object.values(this.io.in(AUTHENTICATED_CLIENTS).sockets); } async start(returnImmediately = false) { if (this._started) { throw new Error("Server cannot be started a second time. Create a new one."); } this.logger.info("Starting server..."); this._started = true; for (const pluginRecord of this.config.plugins) { const pluginClassName = pluginRecord.type.name; this.logger.debug({ pluginClassName }, "Instantiating plugin."); const plugin = new pluginRecord.type(pluginRecord.config, this); this.plugins.push(plugin); } this.http = express_1.default(); const localIdentifiers = {}; const pluginAuthLookup = auth_1.LookupAuth(localIdentifiers); const localUri = `http://127.0.0.1:${this.config.port}`; for (const plugin of this.plugins) { this.logger.debug({ pluginName: plugin.name, pluginClassName: plugin.constructor.name }, "Initializing plugin."); const pluginApp = express_1.default(); const clientId = `LOCAL-${plugin.name}`; const clientSecret = v4_1.default(); localIdentifiers[clientId] = clientSecret; plugin.doSetClientConnection(localUri, clientId, clientSecret); await plugin.doInitialize(pluginApp); this.http.use(`/${plugin.name}`, pluginApp); } this.http.get("/state", (_req, res) => res.json(this.state)); const port = this.config.port; this.logger.info({ port }, `Starting HTTP server on port ${port}.`); this.httpServer = this.http.listen(port); this.io = this.initializeSocketIO(localIdentifiers); this.logger.info("Server started."); if (!returnImmediately) { while (!this._stopping) { await sleep_promise_1.default(1000); } } } async stop() { if (!this._started) { throw new Error("Server cannot be stopped before starting."); } this.logger.info("Stopping server..."); this._stopping = true; if (this.httpServer) { this.logger.debug("Stopping HTTP."); this.httpServer.close(); } for (const plugin of this.plugins) { this.logger.debug({ pluginName: plugin.name }, "Cleaning up plugin."); await plugin.cleanup(); } this.logger.info("Server stopped."); } messageToAuthenticated(messageName, messageBody) { if (!this.io) { throw new Error("Attempted to emit before socket.io enabled."); } this.io.to(AUTHENTICATED_CLIENTS).emit(client_1.ProtocolNames.MESSAGE, { messageName, messageBody }); } notifyClientsOfStateChange(pluginName) { } initializeSocketIO(localIdentifiers) { const io = socket_io_1.default(); const pluginAuthLookup = auth_1.LookupAuth(localIdentifiers); io.on("connect", (socket) => { const logger = this.logger.child({ socket: socket.id }); logger.info("Connection request received."); socket.on(client_1.ProtocolNames.CLIENT_AUTHENTICATE, async (msg) => { const auth = (await this.config.authnFn(msg.payload.clientId, msg.payload.clientSecret)) || await pluginAuthLookup(msg.payload.clientId, msg.payload.clientSecret); if (auth) { logger.info("Socket has authenticated."); const ev = { type: client_1.ProtocolNames.NEW_CLIENT_CONNECTED, payload: utils_1.socketToClientInfo(socket) }; this.messageToAuthenticated(ev.type, ev); socket.join(AUTHENTICATED_CLIENTS); this.attachSocketIOHandlers(socket); socket.emit(client_1.ProtocolNames.YOU_ARE_CONNECTED); } else { logger.info("Socket failed to authenticate; closing."); socket.disconnect(); } }); socket.emit(client_1.ProtocolNames.PLEASE_AUTHENTICATE); }); io.attach(this.httpServer); return io; } attachSocketIOHandlers(socket) { for (const plugin of this.plugins) { plugin.doBindSocketIOHandlers(socket); } } } exports.Server = Server;