Skip to content

Commit

Permalink
integrated with redis and socket.io
Browse files Browse the repository at this point in the history
composer require predis/predis
npm install --save express socket.io ioredis php-unserialize dotenv lodash
php artisan make:event MessageCreatedEvent
  • Loading branch information
takayukii committed Apr 13, 2016
1 parent 3820fd8 commit a0ba87f
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 9 deletions.
8 changes: 6 additions & 2 deletions .env
@@ -1,6 +1,6 @@
APP_ENV=local
APP_DEBUG=true
APP_KEY=base64:YdijRlQ4RJewAZYjLxk3vVPothPAGBbZi2Gmf5phA9s=
APP_KEY=dd4u1wm2rVi82s6eOI8sTRzaWomob58x
APP_URL=http://localhost

DB_CONNECTION=mysql
Expand All @@ -11,7 +11,7 @@ DB_USERNAME=laravel
DB_PASSWORD=laravel

CACHE_DRIVER=file
SESSION_DRIVER=file
SESSION_DRIVER=redis
QUEUE_DRIVER=sync

REDIS_HOST=127.0.0.1
Expand All @@ -24,3 +24,7 @@ MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

BROADCAST_DRIVER=redis
NODE_SERVER_PORT=3000
SOCKETIO_SRC=//192.168.33.40:3000/socket.io/socket.io.js
36 changes: 36 additions & 0 deletions app/Events/MessageCreatedEvent.php
@@ -0,0 +1,36 @@
<?php

namespace App\Events;

use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class MessageCreatedEvent extends Event implements ShouldBroadcast
{
use SerializesModels;

public $destinations = [];
public $message;

/**
* Create a new event instance.
*
* @return void
*/
public function __construct($destinations, $message)
{
$this->destinations = $destinations;
$this->message = $message;
}

/**
* Get the channels the event should be broadcast on.
*
* @return array
*/
public function broadcastOn()
{
return ['chat'];
}
}
5 changes: 5 additions & 0 deletions app/Http/Controllers/HomeController.php
Expand Up @@ -45,6 +45,11 @@ public function index()
return view('home', compact('users', 'messages'));
}

public function getAuthUser()
{
return \Auth::user()->toJson();
}

public function getMessages($userId)
{
$userId = intval($userId);
Expand Down
1 change: 1 addition & 0 deletions app/Http/routes.php
Expand Up @@ -20,3 +20,4 @@
Route::get('/home', 'HomeController@index');
Route::get('/messages/to/{id}', 'HomeController@getMessages');
Route::post('/messages/to/{id}', 'HomeController@createMessage');
Route::get('/me', 'HomeController@getAuthUser');
21 changes: 21 additions & 0 deletions app/Observers/MessageObserver.php
@@ -0,0 +1,21 @@
<?php

namespace App\Observers;

use App\Message;

class MessageObserver {

public function saving($model)
{
//
}

public function saved(Message $model)
{
$messages = \App\Message::with('fromUser')->with('toUser')
->where('id', $model->id)->get()->toArray();
event(new \App\Events\MessageCreatedEvent([$model->from_user_id, $model->to_user_id], current($messages)));
}

}
3 changes: 1 addition & 2 deletions app/Providers/EventServiceProvider.php
Expand Up @@ -27,7 +27,6 @@ class EventServiceProvider extends ServiceProvider
public function boot(DispatcherContract $events)
{
parent::boot($events);

//
\App\Message::observe(new \App\Observers\MessageObserver);
}
}
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -6,7 +6,8 @@
"type": "project",
"require": {
"php": ">=5.5.9",
"laravel/framework": "5.2.*"
"laravel/framework": "5.2.*",
"predis/predis": "^1.0"
},
"require-dev": {
"fzaninotto/faker": "~1.4",
Expand Down
54 changes: 52 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion package.json
Expand Up @@ -4,7 +4,12 @@
"gulp": "^3.9.1"
},
"dependencies": {
"bootstrap-sass": "^3.0.0",
"dotenv": "^2.0.0",
"express": "^4.13.4",
"ioredis": "^1.15.1",
"laravel-elixir": "^5.0.0",
"bootstrap-sass": "^3.0.0"
"php-unserialize": "0.0.1",
"socket.io": "^1.4.5"
}
}
22 changes: 21 additions & 1 deletion public/js/app.js
Expand Up @@ -22,13 +22,14 @@
data: data,
success: function (message){
console.log('success', message);
$textMessage.val('');
}
})

});

var $selectTo = $('#selectTo');
$selectTo.change(function(){
$selectTo.change(function (){
var userId = $selectTo.val();
$.ajax({
type: 'GET',
Expand All @@ -46,4 +47,23 @@
})
});

var $ulMessages = $('#ulMessages');
var socket = io.connect('//192.168.33.40:3000');
socket.on('chat', function (message, fn){
console.log('on chat', message);

if (+$selectTo.val() === +message.to_user.id || +$selectTo.val() === +message.from_user.id) {
$ulMessages.prepend('<li>' + message.message + ' by ' + message.from_user.name + ' at ' + message.created_at + '</li>');
}
});

$.ajax({
type: 'GET',
url: '/me',
success: function (user){
console.log('user', user);
socket.emit('user', user);
}
});

}(jQuery));
1 change: 1 addition & 0 deletions resources/views/layouts/app.blade.php
Expand Up @@ -77,6 +77,7 @@
<!-- JavaScripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="{{ env('SOCKETIO_SRC') }}"></script>
<script src="/js/app.js"></script>
{{-- <script src="{{ elixir('js/app.js') }}"></script> --}}
</body>
Expand Down
118 changes: 118 additions & 0 deletions socket-server.js
@@ -0,0 +1,118 @@
require('dotenv').config({
path: __dirname +'/.env'
});

const env = process.env;
const port = env.NODE_SERVER_PORT;

const _ = require('lodash');
const redis = require('ioredis');
const redisClient = new redis();
const redisBroadcast = new redis();
const cookie = require('cookie');
const crypto = require('crypto');
const PHPUnserialize = require('php-unserialize');

const server = require('http').createServer();
console.log('Server on Port : ' + port);
server.listen(port);
const io = require('socket.io')(server);

io.use(middlewareAuthCheck);

io.on('connection', (socket) => {
socket.on('user', (user) => {
user = JSON.parse(user);
console.log('user', user);
// mark with userId
socket.userId = user.id;
});

socket.on('disconnect', () => {
console.log('disconnect..');
});
});

redisBroadcast.psubscribe('*', (err, count) => {
});

redisBroadcast.on('pmessage', (subscribed, channel, event) => {
event = JSON.parse(event);
console.log('pmessage', channel, event);

const _sockets = _.toArray(io.sockets.connected);
event.data.destinations.forEach((userId) => {
const socket = _.find(_sockets, (_socket) => {
if (+userId === +_socket.userId) {
return true;
}
});
if (socket) {
console.log('emit', socket.id, userId);
io.to(socket.id).emit('chat', event.data.message);
}
});
});

function middlewareAuthCheck(socket, next) {

try{

var _cookie = socket.request.headers.cookie;
if(!_cookie){
throw new Error('No cookie');
}
_cookie = cookie.parse(_cookie);
if(!_cookie || !_cookie['laravel_session']){
throw new Error('No valid cookie');
}

const sessionId = decryptCookie(_cookie['laravel_session']);
console.log('sessionId', sessionId);

redisClient.get('laravel:' + sessionId, (err, session) => {

if (err) {
next(err);
} else if (session) {
next();
} else {
next(new Error('No session in redis'));
}

});

}catch(err){
console.log('Skip this socket since an error occurred', err);
next(err);
}

};

function decryptCookie(cookie) {
console.log('cookie', cookie);
const parsedCookie = JSON.parse(new Buffer(cookie, 'base64'));

const iv = new Buffer(parsedCookie.iv, 'base64');
const value = new Buffer(parsedCookie.value, 'base64');

var appKey = env.APP_KEY;

if (/base64/.test(env.APP_KEY)) {
const matches = /base64:(.+)/.exec(appKey);
console.log('appKey', matches[1]);
appKey = new Buffer(matches[1], 'base64').toString();
console.log('appKey', appKey);
// TODO: Error in crypto.createDecipheriv if appKey is encoded in base64
// appKey seems to be binary
// https://github.com/laravel/framework/commit/370ae34d41362c3adb61bc5304068fb68e626586
}

const decipher = crypto.createDecipheriv('aes-256-cbc', appKey, iv);
const resultSerialized = Buffer.concat([
decipher.update(value),
decipher.final()
]);

return PHPUnserialize.unserialize(resultSerialized);
};

0 comments on commit a0ba87f

Please sign in to comment.