UNPKG

firepeer

Version:

secure p2p signalling and authentication for simple-peer using firebase realtime database

218 lines 17.5 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var events_1 = require("events"); var shortid_1 = __importDefault(require("shortid")); var simple_peer_1 = __importDefault(require("simple-peer")); var debug_1 = __importDefault(require("./debug")); /** * Secure p2p signalling and authentication for [simple-peer](https://github.com/feross/simple-peer) * using [firebase realtime database](https://firebase.google.com/docs/database/). * * @noInheritDoc */ var FirePeer = /** @class */ (function (_super) { __extends(FirePeer, _super); /** * * @param fbaseOrApp Configured firebase instance or a specific firebase app if you have configured multiple apps. */ function FirePeer(fbaseOrApp, options) { if (options === void 0) { options = {}; } var _this = _super.call(this) || this; _this.app = fbaseOrApp; _this.id = options.id ? options.id : shortid_1.default.generate(); _this.spOpts = options.spOpts ? options.spOpts : {}; _this.onOffer = options.onOffer ? options.onOffer : function (signal) { return signal; }; _this.onAnswer = options.onAnswer ? options.onAnswer : function (signal) { return signal; }; _this.sendOffer = options.sendOffer ? options.sendOffer : function (signal) { return signal; }; _this.sendAnswer = options.sendAnswer ? options.sendAnswer : function (signal) { return signal; }; _this.app.auth().onAuthStateChanged(function (user) { if (user) { _this.uid = user.uid; _this.listen(); _this.emit('loggedin'); } else { _this.uid = null; _this.unlisten(); _this.emit('loggedout'); } }); return _this; } /** * Connect to a peer identified by a user id and peer id. Returns a promise that resolves to a [[FirePeerInstance]]. */ FirePeer.prototype.connect = function (uid, id) { var _this = this; return new Promise(function (resolve, reject) { var connectPeer = function () { var ref = _this.app .database() .ref("peers/" + uid + "/" + id + "/" + _this.uid + "/" + _this.id); debug_1.default(_this.id)('connecting to %s', ref); var peer = _this.createPeer(ref, true); peer.on('connect', function () { resolve(peer); }); peer.on('_connect_error', function (err) { reject(err); }); }; if (_this.uid) { connectPeer(); } else { _this.on('loggedin', connectPeer); } }); }; FirePeer.prototype.listen = function () { var _this = this; this.ref = this.app.database().ref("peers/" + this.uid + "/" + this.id); this.ref.on('child_added', function (ss) { if (ss) { ss.ref.on('child_added', function (css) { if (css) { _this.createPeer(css.ref, false); } }); } }); this.ref.on('child_removed', function (ss) { if (ss) { debug_1.default(_this.id)('unlistening to %s', ss.ref); ss.ref.off('child_added'); } }); }; FirePeer.prototype.unlisten = function () { if (this.ref) { debug_1.default(this.id)('unlisten to %s', this.ref); this.ref.off('child_added'); } }; FirePeer.prototype.createPeer = function (ref, initiator) { var _this = this; debug_1.default(this.id)('createPeer(): initiator: %s, %s', initiator, ref); var initiatorId = ref.key; var initiatorUid = (ref.parent && ref.parent.key); var receiverId = (ref.parent && ref.parent.parent && ref.parent.parent.key); var receiverUid = (ref.parent && ref.parent.parent && ref.parent.parent.parent && ref.parent.parent.parent.key); var peer = new simple_peer_1.default(__assign({ initiator: initiator }, this.spOpts, { trickle: false })); peer.on('signal', function (signal) { var result = signal.type === 'offer' ? _this.sendOffer(signal) : _this.sendAnswer(signal); Promise.resolve(result ? result : Promise.reject()).then(function (sig) { if (sig) { debug_1.default(_this.id)('local signal: %s', signal.type); ref.set(sig); } }, function () { debug_1.default(_this.id)('local signal rejected: %s', signal.type); }); }); ref.on('value', function (ss) { if (ss) { var signal_1 = ss.val(); if (signal_1) { if ((signal_1.type === 'offer' && !initiator) || (signal_1.type === 'answer' && initiator)) { if (signal_1.type === 'offer') { signal_1.uid = initiatorUid; signal_1.id = initiatorId; } else { signal_1.uid = receiverUid; signal_1.id = receiverId; } var result = signal_1.type === 'offer' ? _this.onOffer(signal_1) : _this.onAnswer(signal_1); Promise.resolve(result ? result : Promise.reject()).then(function (sig) { if (sig) { debug_1.default(_this.id)('remote signal: %s', signal_1.type); peer.signal(sig); } }, function () { debug_1.default(_this.id)('remote signal rejected: %s', signal_1.type); ref.set({ sdp: 'signal rejected by remote peer', type: 'error' }); }); } else if (signal_1.type === 'error') { ref.set(null); peer.emit('_connect_error', signal_1.sdp); _this.emit('connection_failed', new Error(signal_1.sdp)); } } } }); var cleanup = function () { ref.off('value'); ref.set(null); debug_1.default(_this.id)('cleanup'); }; peer.on('error', function (err) { debug_1.default(err && err.message); cleanup(); }); peer.on('close', cleanup); peer.on('connect', function () { debug_1.default(_this.id)('connection established'); cleanup(); peer.initiatorId = initiatorId; peer.initiatorUid = initiatorUid; peer.receiverId = receiverId; peer.receiverUid = receiverUid; _this.emit('connection', peer); }); return peer; }; return FirePeer; }(events_1.EventEmitter)); exports.FirePeer = FirePeer; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlyZXBlZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZmlyZXBlZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxpQ0FBc0M7QUFFdEMsb0RBQThCO0FBQzlCLDREQUFxQztBQUNyQyxrREFBNEI7QUFpRzVCOzs7OztHQUtHO0FBQ0g7SUFBOEIsNEJBQVk7SUFrQnhDOzs7T0FHRztJQUNILGtCQUNFLFVBQWtDLEVBQ2xDLE9BQTZCO1FBQTdCLHdCQUFBLEVBQUEsWUFBNkI7UUFGL0IsWUFJRSxpQkFBTyxTQTZCUjtRQTVCQyxLQUFJLENBQUMsR0FBRyxHQUFHLFVBQThCLENBQUM7UUFFMUMsS0FBSSxDQUFDLEVBQUUsR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxpQkFBTyxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ3ZELEtBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ25ELEtBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDLE9BQU87WUFDNUIsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxPQUFPO1lBQ2pCLENBQUMsQ0FBQyxVQUFDLE1BQWMsSUFBSyxPQUFBLE1BQU0sRUFBTixDQUFNLENBQUM7UUFDL0IsS0FBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUTtZQUM5QixDQUFDLENBQUMsT0FBTyxDQUFDLFFBQVE7WUFDbEIsQ0FBQyxDQUFDLFVBQUMsTUFBYyxJQUFLLE9BQUEsTUFBTSxFQUFOLENBQU0sQ0FBQztRQUMvQixLQUFJLENBQUMsU0FBUyxHQUFHLE9BQU8sQ0FBQyxTQUFTO1lBQ2hDLENBQUMsQ0FBQyxPQUFPLENBQUMsU0FBUztZQUNuQixDQUFDLENBQUMsVUFB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