I made a prototype for a real-time voting system by using deepstream.io
and rethinkdb
as persistence store. So far, it works, and multiple browsers can stay in sync in getting the latest number of vote counts (see screenshot below), however, one thing I don't like it is the vote count
is incremented in the browser, which is sent to the deepstream.io
remote server.
the JS code embedded in the client's browser:
/**
* Login to deepstream as Frank
*/
var ds = deepstream( 'localhost:6020' ).login({ username: 'Frank'});
var name = 'upvote';
var upVotes;
var voteText = $('.streaming-prices .vote');
var record = ds.record.getRecord(name);
$('#upvote_btn').click(function() {
// on button click, increment the vote counts
// set the record stored in the rethinkdb storage
record.set({
count: ++upVotes
});
});
record.subscribe('count', function(newVal) {
console.info('count updated, newVal: ', newVal);
upVotes = newVal;
voteText.text(newVal);
});
the server.js code:
var PermissionHandler = require( './permission-handler' );
var DeepstreamServer = require( 'deepstream.io' ),
RdbC = require( 'deepstream.io-storage-rethinkdb' ),
server = new DeepstreamServer();
server.set('host', '0.0.0.0');
server.set('port', 6020);
server.set( 'tcpHost', '0.0.0.0' );
server.set( 'tcpPort', '6022' );
server.set( 'permissionHandler', new PermissionHandler() );
server.set( 'storage', new RdbC({
port: 28015,
host: '127.0.0.1',
splitChar: '/',
database: 'votings',
defaultTable: 'question'
}));
server.start();
So you can see that the js code in the client increments the vote counts directly and updates the record, which is to be sent to the deepstream.io server for updating the database. I don't like this part, because I don't want the user to possibly mess up the total vote counts. Instead, I would like the client just to send something like +1
to the server, and let the server update the total counts for persistence. I am not sure if this is possible, can someone shed some light? I appreciate it
Good point, allowing clients to manipulate votes might not work so well!
I would do it using an rpc, that way in the browser you can let a trusted provider increment the record on the users behalf.
The three things you'll need to do is:
Code would look something as follows:
The JS code in browser
/**
* Login to deepstream as Frank
*/
var ds = deepstream( 'localhost:6020' ).login( { username: 'Frank'} );
var voteText = $('.streaming-prices .vote');
var name = 'vote';
var record = ds.record.getRecord( name );
$('#upvote_btn').click(function() {
// on button click, increment the vote counts
// set the record stored in the rethinkdb storage
ds.rpc.make( 'upvote', {}, function( error, response ){
//notify user when upvote succesfull
});
});
record.subscribe('count', function( newVal ) {
console.info( 'count updated, newVal: ', newVal);
voteText.text(newVal);
});
The rpc provider that handles upvotes
/**
* Login to deepstream as Frank
*/
var ds = deepstream( 'localhost:6021' ).login( { username: 'upvote-provider' } );
var name = 'vote';
var record = ds.record.getRecord( name );
ds.rpc.provide( 'upvote', function( data, response ){
record.set( 'count', record.get( 'count' ) + 1 );
response.send();
});
The server should also have permissions to only allow the upvote-provider to change the votes.
canPerformAction: function( username, message, callback ) {
/**
* If the user is a provider, it has all permissions
*/
if( username === 'upvote-provider' ) {
callback( null, true );
}
/**
* Otherwise the client request is valid unless it's trying to change the score
*/
else {
var messageObj = Deepstream.readMessage( message );
var isAllowed = messageObj.name !== 'vote' || messageObj.isRead;
var errorMessage = isAllowed ? null : 'Can\'t change votes via client, use \'upvote\' rpc instead';
callback( errorMessage, isAllowed );
}
}