UNPKG

@copperjs/copper

Version:
155 lines 5.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.grid = exports.Grid = exports.Node = void 0; const node_fetch_1 = require("node-fetch"); const errors_1 = require("../common/errors"); const utils_1 = require("../common/utils"); const logger_1 = require("../logger"); const config_1 = require("../standalone/config"); class Node { constructor(config) { var _a; this.config = config; this.sessions = new Set(); this.shouldCheckIsAlive = true; this.isAlive = false; this.config.urlPrefix = config_1.copperConfig.value.routesPrefix; this.config.maxSession = (_a = this.config.maxSession) !== null && _a !== void 0 ? _a : Number.POSITIVE_INFINITY; this.config.nodePolling = this.config.nodePolling || 10000; if (this.config.maxSession < 1) { this.config.maxSession = Number.POSITIVE_INFINITY; } this.checkIsAlive(); } static getId(config) { return `${config.host}:${config.port}`; } get id() { return Node.getId(this.config); } get URL() { return `http://${this.config.host}:${this.config.port}`; } get webSocketURL() { return `ws://${this.config.host}:${this.config.port}`; } get urlPrefix() { return this.config.urlPrefix; } get freeSlots() { return this.config.maxSession - this.sessions.size; } get canCreateSession() { return this.freeSlots > 0 && this.isAlive; } getSessions() { return Array.from(this.sessions); } registerSession(id) { this.sessions.add(id); } deregisterSession(id) { this.sessions.delete(id); } deregister() { this.isAlive = false; this.shouldCheckIsAlive = false; } async checkIsAlive() { if (!this.shouldCheckIsAlive) { return; } try { await node_fetch_1.default(`${this.URL}${this.config.urlPrefix}status`, { timeout: 1000 }); if (!this.isAlive) { logger_1.logger.warn(`node ${this.id} connected`); } this.isAlive = true; } catch (err) { if (this.isAlive) { logger_1.logger.warn(`node ${this.id} disconnected`); } this.isAlive = false; } await utils_1.delay(this.config.nodePolling); process.nextTick(() => this.checkIsAlive()); } } exports.Node = Node; class Grid { constructor() { this.nodes = new Map(); this.sessionNodeMap = new Map(); this.sessions = new Map(); } registerNode(config) { const node = new Node(config); this.nodes.set(node.id, node); logger_1.logger.info(`registered node ${node.id}`); return node; } deregisterNode(host, port) { const nodeId = Node.getId({ host, port }); if (!this.nodes.has(nodeId)) { logger_1.logger.error(`failed deregistering node ${nodeId}`); throw new errors_1.NoMatchingNode(`node ${nodeId} not registered`); } const node = this.nodes.get(nodeId); this.nodes.delete(nodeId); node.deregister(); node.getSessions().map((sessionId) => this._removeSession(sessionId)); logger_1.logger.info(`deregistered node ${node.id}`); } async createSession(body = {}) { const candidates = Array.from(this.nodes.values()).filter((node) => node.canCreateSession); const node = candidates.length ? candidates.reduce((prev, curr) => (curr.freeSlots > prev.freeSlots ? curr : prev)) : null; if (!node) { throw new errors_1.NoMatchingNode('cannot find a free node to create a session on'); } const session = await node_fetch_1.default(`${node.URL}${node.urlPrefix}session`, { method: 'POST', body: JSON.stringify(body), headers: { 'Content-Type': 'application/json' }, }).then((res) => res.json()); node.registerSession(session.sessionId); this.sessionNodeMap.set(session.sessionId, node.id); this.sessions.set(session.sessionId, utils_1.removeWsUrl(session.value)); return session.value; } getWebSocketUrl(sessionId) { return `${this.getNode(sessionId).webSocketURL}/ws/${sessionId}`; } listSessions() { return Array.from(this.sessions.values()); } getSession(id) { if (!this.sessions.has(id)) { throw new errors_1.SessionNotFound(id); } return this.sessions.get(id); } getNode(sessionId) { const nodeId = this.sessionNodeMap.get(this.getSession(sessionId).id); return this.nodes.get(nodeId); } _removeSession(sessionId) { this.getSession(sessionId); // throw if no session this.sessionNodeMap.delete(sessionId); this.sessions.delete(sessionId); } async removeSession(sessionId) { const node = this.getNode(sessionId); node.deregisterSession(sessionId); this._removeSession(sessionId); return await node_fetch_1.default(`${node.URL}${node.urlPrefix}session/${sessionId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, }).then((res) => res.json()); } } exports.Grid = Grid; exports.grid = new Grid(); //# sourceMappingURL=grid.js.map