private chat using socket.id on socket.io - angularjs

i am fresher at nodejs and socket.io. i am trying to made a chat application using nodejs, socket.io and angularjs in express framework. i am lacking basic idea how chat is performed privately.up to this stage my code works chatting in a group of connected users. here is my server code
var server = require('http').Server(app);
var io = require('socket.io')(server);
var socket = require('./routes/socket.js');
server.listen(8000);
console.log('server listening on port:8000');
io.on('connection',socket);
and my main socket file consit code like:
module.exports = function(socket){
console.log('connected'+' '+'socketId :'+socket.id);
//console.log(req.session.id);
var users =[];
socket.emit(socket.id);
socket.on('username',function(data){
users.push({id:socket.id,message:data.username});
socket.emit('username', users)
})
socket.on('typing',function(data){
//socket.emit('typing',{message:"helo angular"});
socket.broadcast.emit('typing',{message:data.message});
});
socket.on('typing-stop',function(data){
//socket.emit('typing',{message:"helo angular"});
debugger;
socket.broadcast.emit('typing-stop',{message:data.message});
});
socket.on('new-user',function(data){
socket.emit('new-user',data);
socket.broadcast.emit('new-user',data);
})
socket.on('message',function(data){
users.push({message:data.message});
socket.emit('message',{id:socket.id,message:data.message});
socket.broadcast.emit('message',{id:socket.id,message:data.message});// emit the message to every one connected in server
})
socket.on('disconnect',function(){
console.log('user disconnected');
socket.broadcast.emit('disconnected',{'message':'user left the chat room'});
});
}
i am abe to load all the users who get logged in my app.
all i want is to click to the available and start private messaging, till now chat is public everyone connected in server can see message.
my angularjs controller code goes like:
function orgController(notifyService, chatSocket, $state,$http) {
chatSocket.connect();
var vm = this;
vm.sendMessage = sendMessage;
vm.messages = [];
vm.users = [];
var id = $state.params.id;
$http.get('/users/' + id).then(function(result) {
console.log(result.data);
vm.userData = result.data;
chatSocket.emit('new-user', { 'username': result.data.details.firstName + ' ' + result.data.details.lastName });
});
chatSocket.on('new-user',function(data){
vm.users.push(data);
})
function sendMessage(msg) {
//console.log(msg);
if (msg != null && msg != '') {
chatSocket.emit('message', { message: msg });
vm.msg = '';
} else {
vm.msg = '';
}
}
chatSocket.on('message', function(data) {
//debugger;
console.log(data);
vm.messages.push(data);
});
}
NOTE: i have included angular-socket.io modules and inject its dependency in a service called chatSocket which only return socketFactory.
now i want to click in a user from logged in userlist and start communication. how can i do it from (socket.id). which socket generates or from session id? anyone has better way of doing such. any suggestion and response are highly appreciated.

Basically what you need to do is emit an event to a specific socket like this.
io.to(socket.id).emit('privateMessage', {message: <message goes here>});
then on the client side
socket.on('privateMessage', function(data){
var message = data.message;
//do stuff, display message etc...
});

Related

Integrating Stripe JS with MEAN Stack Application

I'm trying to integrate stripe in my MEAN stack application. It's not communicating with Stripe to receive the token. I'm not sure why.
I have stripe source code sourced in my index.html page for my angular application.
It is not communicating with Stripe to receive the token, that console.log never fills, So I know it's not communicating with Stripe for some reason, but the same source code communicates with Stripe in a standalone application. I also believe it is failing when sending server side. I'm trying to send this request from port 3000 to port 8082.
Then I have the below script in another HTML page in my app for stripe:
Stripe.setPublishableKey('pk_test_******************');
var $btn = $('#submit');
$btn.on('click', function() {
$btn.prop('disabled', true);
$btn.button('progress');
var cardNum = $('#card-num').val();
var cardExp = $('#card-exp').val().split('/');
var cardCVC = $('#card-cvc').val();
// First submit the card information to Stripe to get back a token
console.log("starting stripe token");
Stripe.card.createToken({
number: cardNum,
exp_month: cardExp[0],
exp_year: cardExp[1],
cvc: cardCVC
}, function(status, response) {
var $form = $('#form');
var token = response.id;
console.log(response.id);
// Save the token into a hidden input field
$form.append($('<input type="hidden" name="stripeToken" />').val(token));
// Now submit the form to our server so it can make the charge against the token
$.post("http://localhost:8082/charge", $form.get(0), function(res) {
console.log("response from charge: " + res);
});
// All done!
$btn.addClass('btn-success').removeClass('btn-primary');
$btn.button('success');
setTimeout(function() {
$('#checkout').modal('hide');
}, 250);
});
return false;
});
I see the console.log response of starting stripe token but it doesn't actually communicate with stripe.
Then here is my server side code:
app.post('/charge', function(req, res) {
// Connect to the db
MongoClient.connect("mongodb://localhost:27017/meanAuth", function(err, db) {
if(!err) {
console.log("We are connected");
}
});
var stripeToken = req.body.stripeToken;
var amount = 1000;
stripe.charges.create({
card: stripeToken,
currency: 'usd',
amount: amount
},
function(err, charge) {
if (err) {
res.send(500, err);
} else {
res.send(204);
}
});
});
My reason for doing this is I'm trying to send user information in my request to the server side code so the server can update a value in my Mongo database.
I need help! Thanks in advance

POST returns 404 Not Found

I've recently started using MEAN Stack to create a basic application, so im a total beginner, I set up my controllers and everything and I want to post, however it returns api/user not found, can someone pinpoint the problem and help me out? Thanks.
Server.js:
var app = require ('./app/app');
var signupController = require ('./server/signup-controller');
app.post('api/users', signupController.create);
app.listen('8080', function(){
console.log('[OK] => HTTP Server listening on http://localhost:8080');
require('./app/db').init('mongodb://localhost:27017/shopialmedia' );
});
Server side Controller (signup-controller.js):
module.exports.create = function (req, res) {
console.log(req.body);
}
Client Side Controller (signup-controller.js):
app.controller('signupController', ['$scope', '$resource', function ($scope, $resource) {
var User = $resource('/api/users');
$scope.createUser = function () {
var user = new User();
user.email = $scope.userEmail;
user.password = $scope.userPass;
user.firstName = $scope.userFName;
user.lastName = $scope.userLName;
user.age = $scope.userAge;
user.$save(function (result){
$scope.user.push(result);
$scope.userEmail = '';
$scope.userPass = '';
$scope.userFName = '';
$scope.userLName = '';
$scope.userAge = '';
});
}
}]);
My module :
var app = angular.module('signupApp', ['ngResource']);
My app.js :
var express = require ('express');
var app = express();
app.use(express.static('public'));
require('./routes')(app);
module.exports = app;
When I go to run the application on my web page and submit the information, it returns api/user 404 Not found any suggestions on what to do, I'd greatly appreciate it. As I said im a beginner so please take that into consideration.
Server.js Add:
app.get('api/users', signupController.list);
signup-controller.js Add:
mongoose = require('mongoose'),
User = mongoose.model('User');
module.exports.list = function(req, res){
var searcher = req.query || {};
User.find(searcher)
.sort('-created')
.exec(function (err, users) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
}
res.json(users);
});
};
Obviously, you'll also have to connect to mongoose with a file probably called main.js then you'll need a UserModel.js (or equivalent) where you can define the attributes of your user etc using the mongoose Schema...
I mean you're missing a lot if you want something a little simpler to start you can just do:
module.exports.list = function(req, res){
var users = [];
res.json(users);
};
Also think you need this and this is also nicer formatting:
var router = express.Router();
router.route('/')
.get(users.list)
.get(users.create)
app.use('/api/users', router);
Not sure what this is supposed to do or what ./routes is: require('./routes')(app);

Using Socket.io, Angular, JWT Authenitication data not available till after user authenticated refresh

I am using Angular and Socket.io, socket-jwt for authentication the sockets, and angular-socket-io on the front end.
When the page loads, the client attempts to connect to the socket.io server. It is denied because their is no token established for the client.
When a socket connection is established I am decoding the token on the server and then I want to emit data to the client including the socket.id where I will update the model and view.
After a user authenticates, it is not until the page is refreshed that the socket.io data is available and then emits to the client.
How do I go about establishing the socket.io connection after the user has logged in and making the data available?
I have tried calling my socket factory, using socket.connect() and io.connect() inside the login controller after a user logs in.
socket factory
app.factory('socket', function ($window, socketFactory) {
var token = $window.localStorage.yourTokenKey;
var myIoSocket = 'http://localhost:8080';
var socket = io.connect(myIoSocket, {'query' : 'token=' + token});
return socket;
});
login function in login controller
$scope.login = function() {
AuthService.login($scope.user)
.then(function(msg) {
socket.connect();
$state.go('home');
}, function() {
console.log('Login Failed');
});
};
server code
var socketIo = require('socket.io');
var io = socketIo.listen(server);
io.use(socketioJwt.authorize({
secret: config.secret,
handshake: true
}));
io.on("connection", function(socket) {
var token = socket.handshake.query.token;
var decoded = jwt.decode(token);
var user = {
name: decoded._doc.name,
userId: decoded._doc._id,
socketId: socket.id
};
socket.emit(‘other event’, user);
});

MobileFirst: Adapter Authentication calls Submit success repeatedly in Angular App

Environment:
MFPF v7.0
Eclipse: Luna SR.2 (4.4.2)
Windows 7
I face an strange issue. I am using adapter based authentication in one of my Angular based project.
The app authenticates well, but it repeatedly calls the submitSuccess.
I guess, it has something with the way Angular works, either I should use Challenge Handler as a Service or Controller. Because the way MobileFirst detects & handle instances of a/any handler objects. And that cause reference mis-match to dispose off or execute the relevant functions at appropriate time.
Currently I use it as a service.
Below is the challenge handler that I use.
define(['angular'], function(angular){
var loginChallengeHandler = angular.module('webApp.loginChallengeHandler', [])
.service('loginChallengeHandler', function(){
var _this = this;
_this.AuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
_this.AuthRealmChallengeHandler.isCustomResponse = function(response) {
console.error("AuthRealmChallengeHandler.isCustomResponse:: " , response);
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
if (typeof(response.responseJSON.authRequired) !== 'undefined' || response.responseJSON.authRequired == true){
return true;
} else {
return false;
}
};
_this.AuthRealmChallengeHandler.handleChallenge = function(response){
console.error("AuthRealmChallengeHandler.handleChallenge:: " , response);
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
console.error("------Auth Required----- ");
_authenticationFailed(response);
} else if (authRequired == false){
console.error("------Auth PASSED ----- ");
//Now tell WL Authentication that user has been verified successfully so that it finishes the authentication process
_this.AuthRealmChallengeHandler.submitSuccess();
console.error("------ submitSuccess ----- ");
}
};
_this.AuthRealmChallengeHandler.userLogin = function(dataObjRef) {
var loginStatePromise = $q.defer();
_this.AuthRealmChallengeHandler.submitAdapterAuthentication(options,{
onFailure: function (error) {
loginStatePromise.resolve({ state:false , val: "" });
console.log("submitAdapterAuthentication Failed called ", error);
},
onSuccess: function(response) {
console.log("-> submitAdapterAuthentication onSuccess called " , response);
loginStatePromise.resolve({ state: _state , val: _msg });
},
timeout: 30000
});
return loginStatePromise.promise;
};
_this.AuthRealmChallengeHandler.logout = function (){
var userLogoutPromise = $q.defer();
WL.Client.logout("AdapterAuthRealm",{
onSuccess: function(){
console.log("onSuccess");
userLogoutPromise.resolve(true);
},
onFailure: function(){
console.log("onFailure");
userLogoutPromise.resolve(false);
},
timeout: 30000
});
return userLogoutPromise.promise;
};
var _authenticationFailed = function(response){
console.error("_authenticationFailed:: " , response);
//register failure request
_this.AuthRealmChallengeHandler.submitFailure();
};
});
return loginChallengeHandler;
});
I have also tried to bind the handler object with window object, so that it can access the handler's instance methods correctly.
Like:
window.AuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
window.AuthRealmChallengeHandler.isCustomResponse = function(response) {
.
.
But still same issue.
I solved this issue and here is my solution for anyone facing similar issue in future.
Solution Description (few words)
As per my understanding, the IBM MobileFirst is expecting only one challenge-handler instance (the object that is create via createChallengeHandler function) to exists in the app. So most probably it assumes that the instance would be hooked into the window object.
Now based on this knowledge, we can see that above code is not working even we made instance through service ( i.e. singleton per angular app). Why ? Because, now the handler object becomes accessible via another reference, and this caused issues in resolving the handler references within the WL APIs.
So I just changed a bit of code (hooked it into window) so that WL APIs could reach the correct handler instance and clean-up the requests poll before marking the call successful and dispose off all the cached requests.
One more thing I would suggest.
Create only one handler instance in your client code
Create it as a service or factory - both are singletons in angularjs
Avoid using controllers, because there can be many controller instances within the angular app and it would lead to multiple handler references
And importantly trust IBM MobileFirst :)
Working Challenge Handler as Service
define(['angular'], function(angular){
'use strict';
var loginChallengeHandler = angular.module('webApp.loginChallengeHandler', [])
.service('loginChallengeHandler', function(){
//NOTE:- Below Must be bind with Window Object, otherwise will NOT work as per challenge handlers default behavior
window.AuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
AuthRealmChallengeHandler.isCustomResponse = function(response) {
if (response && response.responseJSON && typeof (response.responseJSON.authStatus) === "string"){
return true;
} else {
return false;
}
};
AuthRealmChallengeHandler.handleChallenge = function(response){
var authStatus = response.responseJSON.authStatus;
if (authStatus === "failed"){
console.error("------Auth Required----- ");
_authenticationFailed(response);
} else if (authStatus === "passed"){
console.error("------Auth PASSED ----- ");
//do something here like change page etc.
//Now tell WL Authentication that user has been verified successfully so that it finishes the authentication process
AuthRealmChallengeHandler.submitSuccess();
}
};
AuthRealmChallengeHandler.userLogin = function(dataObjRef) {
var loginStatePromise = $q.defer();
AuthRealmChallengeHandler.submitAdapterAuthentication(options,{
onFailure: function (error) {
loginStatePromise.resolve({ state:false , val: "" });
},
onSuccess: function(response) {
loginStatePromise.resolve({ state: _state , val: _msg });
},
timeout: 30000
});
return loginStatePromise.promise;
};
AuthRealmChallengeHandler.logout = function (){
var userLogoutPromise = $q.defer();
WL.Client.logout("AdapterAuthRealm",{
onSuccess: function(){
//$state.go("home.login");
userLogoutPromise.resolve(true);
},
onFailure: function(){
userLogoutPromise.resolve(false);
},
timeout: 30000
});
return userLogoutPromise.promise;
};
var _authenticationFailed = function(response){
//register failure request
AuthRealmChallengeHandler.submitFailure();
};
});//end of service
return loginChallengeHandler;
});
Adapter
function onAuthRequired(headers, errorMessage){
errorMessage = errorMessage ? errorMessage : null;
return {
authStatus: "failed",
errorMessage: errorMessage
};
}
function Login(request){
if(request){
/* IF user credentials are Verified Correctly
* and user is authenticated then create User Identity
* and return success message if it is required by client app.
*/
userIdentity = {
userId: "abc",
displayName: "ABc",
attributes: {}
};
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
WL.Logger.error("Auth Successful:");
return {
authStatus: "passed",
submitResponse: "send a Success message in case is required on client-side"
};
}else{
return onAuthRequired(null, "send an error message if required on client side");
}
}
I faced the same issue with adapter based authentication but I was using pure javascript, so no angular. From that I can tell you it's a MobileFirst issue and nothing related to angular.
This might sound contradictory to the documentations but don't call the submitSuccess function, just call your code on successful authentication. It will work fine and authenticate properly.
Also, make sure that you only have the security test set on the specific functions that you use after auth and not on the auth function itself.
Your code seems fine to me but I'm not that good in angular.

Second socket.io emit on server side not being received by angular

I'm building an application using socket.io to 'match' users in realtime. The idea is that user a presses a button to be matched and then when user b makes a match request the two are matched by sending io messages to both clients. I'm using AngularJS and ui-router and bt-fords socket.io.
The client makes a request when a controller is entered as shown below:
lunchrControllers.controller('UserMatchingController', ['$state', 'socket', 'authService',
function ($state, socket, authService) {
socket.emit('match', {userEmail: authService.currentUser()});
var currEmail = authService.currentUser();
// only want to catch emits suffixed with the users email
socket.on('matched' + currEmail, function (data) {
$state.go('users.matched', {name: data.name})
});
}]);
where authService is used to persist the email of the currently logged in user. On the server side I have code that does a bunch of db work with mongoose (it can definitely be cleaned up) as such:
var mongoose = require('mongoose');
var User = mongoose.model('User');
module.exports = function(socket) {
socket.on('match', function(data){
User.findOne({email: data.userEmail}, function(error, user) {
if(error){
return
}
user.wantsToBeMatched = true;
user.save(function(err){
// bleh
});
});
User.find({email: {'$ne': data.userEmail }, wantsToBeMatched: true}, function(error, users){
if(error || users.length == 0){
//bleh
return
}
console.log(users);
User.findOne({email: data.userEmail}, function(error, currentUser) {
if(error) {
return;
}
// there is another user who wants to be matched
users[0].wantsToBeMatched = false;
users[0].matchedWith = currentUser.firstname;
currentUser.wantsToBeMatched = false;
currentUser.matchedWith = users[0].firstname;
users[0].save(function() {});
currentUser.save(function() {});
console.log("I'm gonna emit " + ('matched' + currentUser.email) + " with name = " + currentUser.matchedWith);
console.log("I'm gonna emit " + ('matched' + users[0].email) + ' with name = ' + users[0].matchedWith);
socket.emit('matched' + users[0].email, {name: users[0].matchedWith});
socket.emit('matched' + currentUser.email, {name: currentUser.matchedWith});
});
});
})
};
The problem is that only the second user (in this example user b) to request a match receives the socket.emit from the server. I've tried changing the order of emit messages in the server which didn't help. The console logging logs exactly what I'd expect:
I'm gonna emit matcheda#a.com with name = b
I'm gonna emit matchedb#b.com with name = a
I've tried using different browsers, using incognito, putting the code on an EC2 instance and having a friend try it out on a different IP and get the same result everytime.
Basically, the problem boils down to the following code not being executed in the browser of user a:
socket.on('matched' + currEmail, function (data) {
$state.go('users.matched', {name: data.name})
});
Figured it out. Here's the change that I needed to make:
socket.broadcast.emit('matched' + users[0].email, {name: users[0].matchedWith});
I was missing the word broadcast. The reason my previous code wasn't working is because (according to my new understanding of socket.io) socket.emit only sends messages to the client which triggered the socket.on. To send to other clients the message needs to be broadcasted.

Resources