UNPKG

open-collaboration-server

Version:

Open Collaboration Server implementation, part of the Open Collaboration Tools project

196 lines 7.37 kB
// ****************************************************************************** // Copyright 2024 TypeFox GmbH // This program and the accompanying materials are made available under the // terms of the MIT License, which is available in the project root. // ****************************************************************************** var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { inject, injectable, postConstruct } from 'inversify'; import { nanoid } from 'nanoid'; import * as protocol from 'open-collaboration-protocol'; import { Channel } from './channel.js'; import { MessageRelay } from './message-relay.js'; import { RoomManager } from './room-manager.js'; import { PeerInfo } from './types.js'; import { Logger } from './utils/logging.js'; import { parse } from 'semver'; export const PeerFactory = Symbol('PeerFactory'); let PeerImpl = class PeerImpl { logger; id = nanoid(24); get jwt() { return this.peerInfo.jwt; } get publicKey() { return this.peerInfo.publicKey; } get client() { return this.peerInfo.client; } get supportedCompression() { return this.peerInfo.supportedCompression; } get user() { return this.peerInfo.user; } get host() { return this.peerInfo.host; } get room() { const value = this.roomManager.getRoomByPeerId(this.id); if (!value) { throw this.logger.createErrorAndLog(`Peer '${this.id}' does not belong to any room`); } return value; } messageRelay; peerInfo; roomManager; _channel; disposed = false; onDisposeEmitter = new protocol.Emitter(); get onDispose() { return this.onDisposeEmitter.event; } get channel() { if (!this._channel) { throw new Error('Not initialized'); } return this._channel; } initialize() { this._channel = new Channel(this.peerInfo.channel); this._channel.onMessage(message => this.receiveMessage(message)); this._channel.onClose(() => this.dispose()); } async receiveMessage(message) { const messageVersion = parse(message.version); if (!messageVersion) { this.logger.warn(`Received message from '${this.id}' with invalid version: ${message.version}. Ignoring message.`); return; } if (!protocol.compatibleVersions(messageVersion)) { this.logger.warn(`Received message from '${this.id}' with incompatible version: ${message.version}; expected: ${protocol.VERSION}. Ignoring message.`); return; } if (protocol.ResponseMessage.isAny(message) || protocol.ResponseErrorMessage.isAny(message)) { this.messageRelay.pushResponse(this, message); } else if (protocol.RequestMessage.isAny(message)) { if (!protocol.Message.isEncrypted(message)) { this.logger.warn(`Received unencrypted request from: '${this.id}'`); return; } // Override whatever we know about the origin of the message message.origin = this.id; try { const response = await this.messageRelay.sendRequest(this.getTargetPeer(message.target), message); // Adjust the response to the original message id response.id = message.id; this.channel.sendMessage(response); } catch (_error) { const errorResponseMessage = protocol.ResponseErrorMessage.create(message.id, 'Failed to retrieve the requested data.'); this.channel.sendMessage(errorResponseMessage); } } else if (protocol.NotificationMessage.isAny(message)) { message.origin = this.id; if (message.target === '') { this.handleServerMessage(message); return; } else if (!protocol.Message.isEncrypted(message)) { this.logger.warn(`Received unencrypted notification from: '${this.id}'`); return; } try { this.messageRelay.sendNotification(this.getTargetPeer(message.target), message); } catch (_error) { this.logger.error(`Failed sending notification to: ${message.target || '<empty>'}`); } } else if (protocol.BroadcastMessage.isAny(message)) { if (!protocol.Message.isEncrypted(message)) { this.logger.warn(`Received unencrypted broadcast from: '${this.id}'`); return; } this.messageRelay.sendBroadcast(this, message); } } async handleServerMessage(notification) { if (!protocol.NotificationMessage.is(notification)) { this.logger.error('Received encrypted server message'); return; } try { if (notification.content.method === protocol.Messages.Room.Leave.method) { this.dispose(); } else { throw new Error('Unknown server message method: ' + notification.content.method); } } catch (err) { this.logger.error('Failed to handle server message', err); } } getTargetPeer(targetId) { const peer = targetId ? this.room.getPeer(targetId) : undefined; if (!peer) { throw this.logger.createErrorAndLog(`Could not find the target peer: ${targetId || '<empty>'}`); } return peer; } toProtocol() { return { id: this.id, host: this.host, name: this.user.name, email: this.user.email, metadata: { compression: { supported: this.peerInfo.supportedCompression }, encryption: { publicKey: this.publicKey } } }; } dispose() { if (this.disposed) { return; } this.disposed = true; this.onDisposeEmitter.fire(undefined); this.onDisposeEmitter.dispose(); this._channel?.close(); this.logger.info(`Peer '${this.id}' disposed`); } }; __decorate([ inject(Logger) ], PeerImpl.prototype, "logger", void 0); __decorate([ inject(MessageRelay) ], PeerImpl.prototype, "messageRelay", void 0); __decorate([ inject(PeerInfo) ], PeerImpl.prototype, "peerInfo", void 0); __decorate([ inject(RoomManager) ], PeerImpl.prototype, "roomManager", void 0); __decorate([ postConstruct() ], PeerImpl.prototype, "initialize", null); PeerImpl = __decorate([ injectable() ], PeerImpl); export { PeerImpl }; //# sourceMappingURL=peer.js.map