firepeer
Version:
secure p2p signalling and authentication for simple-peer using firebase realtime database
218 lines • 17.5 kB
JavaScript
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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
;