mirror of
https://github.com/szimek/sharedrop.git
synced 2026-06-13 21:03:07 +08:00
Merge branch 'firebase'
This commit is contained in:
commit
dc91e3653b
@ -2,3 +2,6 @@ HOST=localhost
|
||||
PORT=8000
|
||||
WEB_PORT=8000
|
||||
SECRET=35b725ff3c3b48fc889c55d886f21aa1
|
||||
FIREBASE_SECRET=qwerty
|
||||
NEW_RELIC_ENABLED=false
|
||||
NEW_RELIC_LICENSE_KEY=qwerty
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,3 +4,5 @@
|
||||
.env
|
||||
.tmp
|
||||
dist
|
||||
|
||||
newrelic_agent.log
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>P2P file sharing</title>
|
||||
<title>ShareDrop</title>
|
||||
<meta name="description" content="ShareDrop is a peer-to-peer file sharing app powered by HTML5 WebRTC.">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
@ -38,6 +39,7 @@
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/ember.js/1.4.0/ember.min.js"></script>
|
||||
<!-- @endif -->
|
||||
|
||||
<script src="https://cdn.firebase.com/js/client/1.0.6/firebase.js"></script>
|
||||
<script src="https://login.persona.org/include.js"></script>
|
||||
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
window.ShareDrop.App = Ember.Application.create();
|
||||
|
||||
ShareDrop.App.config = {
|
||||
FIREBASE_URL: "https://sharedrop.firebaseio.com/"
|
||||
};
|
||||
|
||||
ShareDrop.App.deferReadiness();
|
||||
|
||||
// Check if everything we need is available
|
||||
@ -9,6 +13,7 @@ ShareDrop.App.deferReadiness();
|
||||
.catch(function (error) {
|
||||
ShareDrop.App.error = error;
|
||||
})
|
||||
.then(authenticateToFirebase)
|
||||
.then(function () {
|
||||
ShareDrop.App.advanceReadiness();
|
||||
});
|
||||
@ -34,6 +39,21 @@ ShareDrop.App.deferReadiness();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function authenticateToFirebase() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = Ember.$.getJSON('/auth');
|
||||
xhr.then(function (data) {
|
||||
var ref = new Firebase(ShareDrop.App.config.FIREBASE_URL);
|
||||
ShareDrop.App.ref = ref;
|
||||
ShareDrop.App.userId = data.id;
|
||||
|
||||
ref.auth(data.token, function (error) {
|
||||
error ? reject(error) : resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
ShareDrop.App.IndexRoute = Ember.Route.extend({
|
||||
|
||||
@ -2,9 +2,12 @@ ShareDrop.App.ApplicationController = Ember.Controller.extend({
|
||||
init: function () {
|
||||
this._super();
|
||||
|
||||
var you = ShareDrop.App.User.create({
|
||||
var id = ShareDrop.App.userId,
|
||||
you = ShareDrop.App.User.create({
|
||||
uuid: id,
|
||||
email: localStorage.email || null
|
||||
});
|
||||
you.set('peer.id', id);
|
||||
|
||||
this.set('you', you);
|
||||
this.handlePersonaAuth();
|
||||
|
||||
@ -7,13 +7,12 @@ ShareDrop.App.IndexController = Ember.ArrayController.extend({
|
||||
init: function () {
|
||||
// Handle room events
|
||||
$.subscribe('connected.room', this._onRoomConnected.bind(this));
|
||||
$.subscribe('user_list.room', this._onRoomUserList.bind(this));
|
||||
$.subscribe('disconnected.room', this._onRoomDisconnected.bind(this));
|
||||
$.subscribe('user_added.room', this._onRoomUserAdded.bind(this));
|
||||
$.subscribe('user_changed.room', this._onRoomUserChanged.bind(this));
|
||||
$.subscribe('user_removed.room', this._onRoomUserRemoved.bind(this));
|
||||
|
||||
// Handle peer events
|
||||
$.subscribe('connected.server.peer', this._onPeerServerConnected.bind(this));
|
||||
$.subscribe('incoming_connection.p2p.peer', this._onPeerP2PIncomingConnection.bind(this));
|
||||
$.subscribe('outgoing_connection.p2p.peer', this._onPeerP2POutgoingConnection.bind(this));
|
||||
$.subscribe('disconnected.p2p.peer', this._onPeerP2PDisconnected.bind(this));
|
||||
@ -23,15 +22,17 @@ ShareDrop.App.IndexController = Ember.ArrayController.extend({
|
||||
$.subscribe('file_received.p2p.peer', this._onPeerP2PFileReceived.bind(this));
|
||||
$.subscribe('file_sent.p2p.peer', this._onPeerP2PFileSent.bind(this));
|
||||
|
||||
// Connect to PeerJS server first,
|
||||
// so that we already have peer ID when later joining a room.
|
||||
this.set('webrtc', new ShareDrop.WebRTC());
|
||||
// Join the room
|
||||
var room = new ShareDrop.Room(ShareDrop.App.ref);
|
||||
room.join(this.get('you').serialize());
|
||||
this.set('room', room);
|
||||
|
||||
this._super();
|
||||
},
|
||||
|
||||
_onRoomConnected: function (event, data) {
|
||||
var you = this.get('you');
|
||||
var you = this.get('you'),
|
||||
room = this.get('room');
|
||||
|
||||
you.get('peer').setProperties(data.peer);
|
||||
delete data.peer;
|
||||
@ -39,10 +40,17 @@ ShareDrop.App.IndexController = Ember.ArrayController.extend({
|
||||
|
||||
// Find and set your local IP
|
||||
this._setUserLocalIP();
|
||||
|
||||
// Initialize WebRTC
|
||||
this.set('webrtc', new ShareDrop.WebRTC(you.get('uuid'), {
|
||||
room: room.name,
|
||||
firebaseRef: ShareDrop.App.ref
|
||||
}));
|
||||
},
|
||||
|
||||
_onRoomUserList: function (event, data) {
|
||||
data.forEach(this._addPeer.bind(this));
|
||||
_onRoomDisconnected: function () {
|
||||
this.clear();
|
||||
this.set('webrtc', null);
|
||||
},
|
||||
|
||||
_onRoomUserAdded: function (event, data) {
|
||||
@ -80,18 +88,6 @@ ShareDrop.App.IndexController = Ember.ArrayController.extend({
|
||||
this.removeObject(peer);
|
||||
},
|
||||
|
||||
_onPeerServerConnected: function (event, data) {
|
||||
var you = this.get('you');
|
||||
|
||||
// you.set('isConnected', true);
|
||||
you.set('peer.id', data.id);
|
||||
|
||||
// Join room and broadcast your attributes
|
||||
var room = new ShareDrop.Room();
|
||||
room.join(you.serialize());
|
||||
this.set('room', room);
|
||||
},
|
||||
|
||||
_onPeerP2PIncomingConnection: function (event, data) {
|
||||
var connection = data.connection,
|
||||
peer = this.findBy('peer.id', connection.peer);
|
||||
@ -216,7 +212,7 @@ ShareDrop.App.IndexController = Ember.ArrayController.extend({
|
||||
rtc.setLocalDescription(offer);
|
||||
|
||||
var addr = grep(offer.sdp);
|
||||
if (addr) {
|
||||
if (addr && addr !== '0.0.0.0') {
|
||||
console.log('Local IP found: ', addr);
|
||||
you.set('local_ip', addr);
|
||||
}
|
||||
@ -267,7 +263,7 @@ ShareDrop.App.IndexController = Ember.ArrayController.extend({
|
||||
var addr = this.get('you.local_ip'),
|
||||
room = this.get('room');
|
||||
|
||||
if (room && addr !== undefined) {
|
||||
if (room && addr) {
|
||||
console.log('Broadcasting user\'s local IP: ', addr);
|
||||
room.update({local_ip: addr});
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
ShareDrop.Room = function () {
|
||||
var url = window.location.protocol + '//' + window.location.hostname;
|
||||
this._socket = new io.connect(url);
|
||||
ShareDrop.Room = function (firebaseRef) {
|
||||
this._ref = firebaseRef;
|
||||
this.name = null;
|
||||
};
|
||||
|
||||
@ -12,58 +11,57 @@ ShareDrop.Room.prototype.join = function (user) {
|
||||
|
||||
// Join room and listen for changes
|
||||
.then(function (data) {
|
||||
var socket = self._socket;
|
||||
self.name = data.name;
|
||||
user.public_ip = data.public_ip;
|
||||
|
||||
self.name = data.name,
|
||||
// Setup Firebase refs
|
||||
self._connectionRef = self._ref.child('.info/connected');
|
||||
self._roomRef = self._ref.child('rooms/' + self.name);
|
||||
self._usersRef = self._roomRef.child('users');
|
||||
self._userRef = self._usersRef.child(user.uuid);
|
||||
|
||||
$.extend(user, {
|
||||
uuid: data.uuid,
|
||||
public_ip: data.public_ip
|
||||
});
|
||||
|
||||
socket.emit('join', {
|
||||
room: self.name,
|
||||
peer: user
|
||||
});
|
||||
console.log('Room:\t Connecting to: ', self.name);
|
||||
|
||||
socket.on('user_list', function (data) {
|
||||
console.log('Room:\t Connected to: ', self.name);
|
||||
$.publish('connected.room', user);
|
||||
self._connectionRef.on('value', function (snapshot) {
|
||||
// Once connected (or reconnected) to Firebase
|
||||
if (snapshot.val() === true) {
|
||||
console.log('Firebase: (Re)Connected');
|
||||
|
||||
console.log('Room:\t user_list: ', data);
|
||||
$.publish('user_list.room', [data]);
|
||||
});
|
||||
// Remove yourself from the room when disconnected
|
||||
self._userRef.onDisconnect().remove();
|
||||
|
||||
socket.on('user_added', function (user) {
|
||||
console.log('Room:\t user_added: ', user);
|
||||
$.publish('user_added.room', user);
|
||||
});
|
||||
// Join the room
|
||||
self._userRef.set(user, function (error) {
|
||||
console.log('Firebase: User added to the room');
|
||||
$.publish('connected.room', user);
|
||||
});
|
||||
|
||||
socket.on('user_changed', function (user) {
|
||||
console.log('Room:\t user_changed: ', user);
|
||||
$.publish('user_changed.room', user);
|
||||
});
|
||||
self._usersRef.on('child_added', function (snapshot) {
|
||||
var user = snapshot.val();
|
||||
|
||||
socket.on('user_removed', function (user) {
|
||||
console.log('Room:\t user_removed: ', user);
|
||||
$.publish('user_removed.room', user);
|
||||
});
|
||||
console.log('Room:\t user_added: ', user);
|
||||
$.publish('user_added.room', user);
|
||||
});
|
||||
|
||||
socket.on('disconnect', function () {
|
||||
console.log('Room:\t disconnect');
|
||||
});
|
||||
self._usersRef.on('child_removed', function (snapshot) {
|
||||
var user = snapshot.val();
|
||||
|
||||
socket.on('error', function () {
|
||||
console.log('Room:\t error');
|
||||
});
|
||||
console.log('Room:\t user_removed: ', user);
|
||||
$.publish('user_removed.room', user);
|
||||
});
|
||||
|
||||
socket.on('reconnecting', function () {
|
||||
console.log('Room:\t reconnecting');
|
||||
});
|
||||
self._usersRef.on('child_changed', function (snapshot) {
|
||||
var user = snapshot.val();
|
||||
|
||||
socket.on('reconnect', function () {
|
||||
console.log('Room:\t reconnect');
|
||||
console.log('Room:\t user_changed: ', user);
|
||||
$.publish('user_changed.room', user);
|
||||
});
|
||||
} else {
|
||||
console.log('Firebase: Disconnected');
|
||||
|
||||
$.publish('disconnected.room');
|
||||
self._usersRef.off();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -71,8 +69,5 @@ ShareDrop.Room.prototype.join = function (user) {
|
||||
};
|
||||
|
||||
ShareDrop.Room.prototype.update = function (attrs) {
|
||||
this._socket.emit('update', {
|
||||
room: this.name,
|
||||
peer: attrs
|
||||
});
|
||||
this._userRef.update(attrs);
|
||||
};
|
||||
|
||||
@ -1,34 +1,20 @@
|
||||
// TODO: provide TURN server config
|
||||
// once it's possible to create rooms with custom names.
|
||||
ShareDrop.WebRTC = function (options) {
|
||||
this.conn = new Peer({ // PeerJS client library
|
||||
host: 'file-drop-peer-server.herokuapp.com',
|
||||
port: 80,
|
||||
ShareDrop.WebRTC = function (id, options) {
|
||||
var defaults = {
|
||||
config: {'iceServers': [
|
||||
{ url: 'stun:stun.l.google.com:19302' }
|
||||
]},
|
||||
debug: 3
|
||||
});
|
||||
};
|
||||
|
||||
this.conn = new Peer(id, $.extend(defaults, options));
|
||||
|
||||
this.files = {
|
||||
outgoing: {},
|
||||
incoming: {}
|
||||
};
|
||||
|
||||
// When connected to PeerJS server
|
||||
this.conn.on('open', function (id) {
|
||||
var self = this;
|
||||
|
||||
$.publish('connected.server.peer', {id: id});
|
||||
console.log('Peer:\t Connected to server with ID: ', id);
|
||||
|
||||
// TODO: cancel on error/disconnect
|
||||
// Ping WebSocket server to prevent timeout on Heroku
|
||||
window.setInterval(function () {
|
||||
self.socket.send({type: 'ping'});
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
// Listen for incoming connections
|
||||
this.conn.on('connection', function (connection) {
|
||||
$.publish('incoming_connection.p2p.peer', {connection: connection});
|
||||
@ -42,13 +28,6 @@ ShareDrop.WebRTC = function (options) {
|
||||
this.conn.on('error', function (error) {
|
||||
console.log('Peer:\t Error while connecting to server: ', error);
|
||||
});
|
||||
|
||||
// Make sure PeerJS connection is cleaned up
|
||||
window.onunload = window.onbeforeunload = function () {
|
||||
if (!!this.conn && !this.conn.destroyed) {
|
||||
this.conn.destroy();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ShareDrop.WebRTC.CHUNKS_PER_ACK = 64;
|
||||
|
||||
@ -4,10 +4,14 @@ ShareDrop.App.User = ShareDrop.App.Peer.extend({
|
||||
local_ip = this.get('local_ip'),
|
||||
label;
|
||||
|
||||
if (email) {
|
||||
if (email && local_ip) {
|
||||
label = email + ' (' + local_ip + ')';
|
||||
} else {
|
||||
} else if (local_ip) {
|
||||
label = local_ip;
|
||||
} else if (email) {
|
||||
label = email;
|
||||
} else {
|
||||
label = null;
|
||||
}
|
||||
|
||||
return label;
|
||||
|
||||
1178
app/scripts/vendor/peer.js
vendored
1178
app/scripts/vendor/peer.js
vendored
File diff suppressed because it is too large
Load Diff
@ -1,21 +1,22 @@
|
||||
// TODO:
|
||||
// - require process.env.SECRET
|
||||
|
||||
module.exports.server = function (options) {
|
||||
'use strict';
|
||||
|
||||
require('newrelic');
|
||||
|
||||
// Room server
|
||||
var http = require('http'),
|
||||
path = require('path'),
|
||||
express = require('express'),
|
||||
uuid = require('node-uuid'),
|
||||
crypto = require('crypto'),
|
||||
extend = require('deep-extend'),
|
||||
persona = require('express-persona'),
|
||||
socketIo = require('socket.io'),
|
||||
FirebaseTokenGenerator = require("firebase-token-generator"),
|
||||
firebaseTokenGenerator = new FirebaseTokenGenerator(process.env.FIREBASE_SECRET),
|
||||
app = express(),
|
||||
host = process.env.HOST,
|
||||
webPort = process.env.WEB_PORT, // 80 or 443
|
||||
secret = process.env.SECRET,
|
||||
server, io, base;
|
||||
base;
|
||||
|
||||
options = options || {};
|
||||
base = options.base || ['.'];
|
||||
@ -23,7 +24,15 @@ module.exports.server = function (options) {
|
||||
app.use(express.logger());
|
||||
app.use(express.urlencoded());
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: secret }));
|
||||
app.use(express.cookieSession({
|
||||
cookie: {
|
||||
// secure: true,
|
||||
httpOnly: true,
|
||||
maxAge: 30 * 24 * 60 * 60 * 1000 // 30 days
|
||||
},
|
||||
secret: secret,
|
||||
proxy: true
|
||||
}));
|
||||
app.use(express.compress());
|
||||
app.use(express.json());
|
||||
|
||||
@ -49,54 +58,18 @@ module.exports.server = function (options) {
|
||||
var ip = req.headers['x-forwarded-for'] || req.ip,
|
||||
name = crypto.createHmac('md5', secret).update(ip).digest('hex');
|
||||
|
||||
res.json({ name: name, uuid: uuid.v1(), public_ip: ip });
|
||||
res.json({name: name, public_ip: ip});
|
||||
});
|
||||
|
||||
//
|
||||
// Room server
|
||||
//
|
||||
server = http.createServer(app);
|
||||
io = socketIo.listen(server);
|
||||
app.get('/auth', function (req, res) {
|
||||
var id = uuid.v1(),
|
||||
token = firebaseTokenGenerator.createToken(
|
||||
{id: id}, // will be available in Firebase security rules as 'auth'
|
||||
{expires: 32503680000} // 01.01.3000 00:00
|
||||
);
|
||||
|
||||
io.sockets.on('connection', function (client) {
|
||||
|
||||
// When a peer joins a room, send back list of other peers already there
|
||||
client.on('join', function (data) {
|
||||
var room = data.room,
|
||||
peer = data.peer;
|
||||
|
||||
console.log('#join data: ', data);
|
||||
client.peer = peer;
|
||||
|
||||
var clients = io.sockets.clients(room),
|
||||
peers = clients.map(function (client) {return client.peer;});
|
||||
|
||||
// Send back list of other peers in the room
|
||||
client.emit('user_list', peers);
|
||||
|
||||
// Join the room
|
||||
client.join(room);
|
||||
|
||||
// Notify other peers that a new peer has joined the room
|
||||
client.broadcast.to(room).emit('user_added', client.peer);
|
||||
|
||||
// Notify other peers when a peer leaves the room
|
||||
client.on('disconnect', function () {
|
||||
client.broadcast.to(room).emit('user_removed', client.peer);
|
||||
});
|
||||
|
||||
console.log('#join peers already in the room: ', peers);
|
||||
});
|
||||
|
||||
client.on('update', function (data) {
|
||||
var room = data.room,
|
||||
peer = data.peer;
|
||||
|
||||
extend(client.peer, peer);
|
||||
|
||||
client.broadcast.to(room).emit('user_changed', client.peer);
|
||||
});
|
||||
res.json({id: id, token: token});
|
||||
});
|
||||
|
||||
return server;
|
||||
return http.createServer(app);
|
||||
};
|
||||
|
||||
21
newrelic.js
Normal file
21
newrelic.js
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* New Relic agent configuration.
|
||||
*
|
||||
* See lib/config.defaults.js in the agent distribution for a more complete
|
||||
* description of configuration variables and their potential values.
|
||||
*/
|
||||
exports.config = {
|
||||
/**
|
||||
* Array of application names.
|
||||
*/
|
||||
app_name : ['ShareDrop'],
|
||||
|
||||
logging : {
|
||||
/**
|
||||
* Level at which to log. 'trace' is most useful to New Relic when diagnosing
|
||||
* issues with the agent, 'info' and higher will impose the least overhead on
|
||||
* production applications.
|
||||
*/
|
||||
level : 'info'
|
||||
}
|
||||
};
|
||||
13
package.json
13
package.json
@ -1,19 +1,24 @@
|
||||
{
|
||||
"name": "ShareDrop",
|
||||
"version": "0.0.1",
|
||||
"version": "1.0.0",
|
||||
"description": "P2P file sharing",
|
||||
"main": "index.js",
|
||||
"repository" : {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/cowbell/sharedrop.git"
|
||||
},
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Szymon Nowak",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"socket.io": "~0.9.16",
|
||||
"express": "~3.4.7",
|
||||
"express-persona": "~0.1.1",
|
||||
"node-uuid": "~1.4.1",
|
||||
"deep-extend": "~0.2.6",
|
||||
"firebase-token-generator": "^0.1.4",
|
||||
"newrelic": "^1.4.0",
|
||||
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"load-grunt-tasks": "~0.2.1",
|
||||
|
||||
19
todo.txt
19
todo.txt
@ -1,19 +0,0 @@
|
||||
# V1
|
||||
- persona + email update
|
||||
- fix broadcasting emails on page load
|
||||
- ensure that socket.io has the most recent version with emails
|
||||
- tooltips
|
||||
|
||||
# V2 (needs changes to PeerJS library)
|
||||
- serialize files asynchronously
|
||||
- show progress bar for sender and recipient
|
||||
|
||||
# V3:
|
||||
- set email from Persona on server side to avoid faking it
|
||||
- send multiple files (one after another)
|
||||
- allow to get room name from URL (provided or generated) for wan connections; don't get public IP in this case
|
||||
|
||||
# Firebase (?)
|
||||
- use Firebase instead of socket.io for room server
|
||||
- initial vs new peers https://groups.google.com/forum/#!topic/firebase-talk/iZ3eLYAZBkU
|
||||
- less hosting issues, but only max 50 connections on free plan...
|
||||
Loading…
Reference in New Issue
Block a user