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.
Related
I am working on a application and I am using MEAN stack as technology. In AngularJS, I am using ngResource to CRUD operations. Can any one suggest how to send username and password to server and get response back to check if the credentials are valid. I need help in ngResource and mongoose code. Thanks.
Check out the mean.js boilerplate:
https://github.com/meanjs/mean
You'll see how they do it pretty quickly:
moduleName.client.controller.js will make an http call, using the injected http. Here is an example of the call being made from /modules/users/client/controllers/authentication.client.controller.js (with some edits to the code to make it easier to see what you're looking for):
AuthenticationController.$inject = ['$scope', '$state', '$http', 'Authentication'];
function AuthenticationController($scope, $state, $http, Authentication, ) {
...
vm.authentication = Authentication;
$http.post('/api/auth/signup', vm.credentials).success(function (response) {
// If successful we assign the response to the global user model
vm.authentication.user = response;
}).error(function (response) {
vm.error = response.message;
});
}
Now, this call is posted to '/api/auth/signup'. The file that handles this route is located in /modules/users/server/routes/auth.server.routes.js:
modules.exports = function(app) {
var users = require('../controllers/users.server.controller');
...
app.route('/api/auth/signup').post(users.signup);
}
As you can see, the route (the url) matches the one you called from the client controller. As that $http call from the controller was a $http.post(), the route entry must match. You can see that it does above.
The parameter users.signup passed above refers to a function in yet another file: /modules/users/server/controllers/users/users.authentication.server.controller.js. This is your main controller for the authentication part of the users module. Now, within this file we can see the signup function is exported:
/* note: there are global variables here, see below */
exports.signup = function (req, res) {
// For security measurement we remove the roles from the req.body object
delete req.body.roles;
// Init user and add missing fields
var user = new User(req.body);
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function (err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
// Remove sensitive data before login
user.password = undefined;
user.salt = undefined;
req.login(user, function (err) {
if (err) {
res.status(400).send(err);
} else {
res.json(user);
}
});
}
});
};
Now, a lot is going on here, but we can break it down.
The req variable is the post request as passed by $http. The res variable is the response the client expects to receive back.
Notice how vm.credentials was passed in $http.post('/api/auth/signup/', vm.credentials)? This is bound to req.body and may be accessed by your server controller from that.
So in this example, the req.body is the required data to create a new user on the server. This is done using mongoose, which has a schema called User. Access this by declaring globals at the top of your controller:
var mongoose = require('mongoose'),
User = mongoose.model('User');
You can see that a new user is instantiated above. It is saved via the mongoose call .save().
Lastly, your server functions should response to the client's request using the res variable passed to the function. See how once the user is created successfully, the function calls
res.jsonp(user);
This is success() to the client, which accepts the response and binds it to a local variable vm.authentication.user
Hope this helps!
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...
});
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.
$http.post('/#/college', $scope.userb)
.success(function(data, status) {
console.log("Sent ok");
})
.error(function(data, status) {
console.log("Error");
})
[1]: http://i.stack.imgur.com/NlHyy.jpg
Is this the correct format/way to post my form data using http.post.?
The above code always returns "error" in the console.
please guide me to use http.post to post my form datathrough my controller.
var nodemailer = require("nodemailer");
var bodyparser = require("body-parser");
var app = express();
var smtpTransport = nodemailer.createTransport("SMTP",{
service: "Gmail",
auth: {
user: "abc#gmail.com",
pass: "abc202"
}
});
var rand, mailOptions, host, link;
app.get('/#/college',function(req,res){
rand=Math.floor((Math.random() * 100) + 54);
host=req.get('host');
link="http://"+req.get('host')+"/verify?id="+rand;
mailOptions={
to : req.userb.counselloremail,
subject : "Please confirm your Email account",
html : "Hello,<br> Please Click on the link to verify your email.<br>Click here to verify"
}
console.log(mailOptions);
smtpTransport.sendMail(mailOptions, function(error, response){
if(error){
console.log(error);
res.end("error");
}else{
console.log("Message sent: " + response.message);
res.end("sent");
}
});
app.use(morgan('dev'));
app.use(gzippo.staticGzip("" + __dirname + "/dist"));
app.listen(process.env.PORT || 5000);
I am trying to access the email address from the scope variable and post it so that i may send a confirmation mail that he is successfully registered now. Also I am sending the code of my web.js file that receives the posted data and sends the mail.
That URL you are posting to is not going to be valid.
If you are posting to another route in your app, the URL would just be #/college.
I'm also a little worried that you don't have anything setup to receive a post request, like on a server or anything. Could you give some more detail about what you are trying to do and your setup?
var dataObject = {
userid : LoginUserID
};
var responsePromise = $http.post(ApiAccessUrl+"/store/userstoreslist/", dataObject, {});
responsePromise.success(function(dataFromServer, status, headers, config)
{
var outputDate=angular.fromJson(dataFromServer);
});
responsePromise.error(function(data, status, headers, config) {
console.log("Error in fetching user store call!");
});
This is the correct way of sending data using http.post
Should be a fairly easy one here for anyone who knows Angular. I am trying to update the data that is displayed after I make a PUT request to update the object. Here is some code:
Post service (services/post.js)
'use strict';
angular.module('hackaboxApp')
.factory('Post', function($resource) {
return $resource('/api/posts/:id', {id : '#id'}, {
'update': { method: 'PUT' }
})
});
Server side controller function that gets executed when trying to update data (lib/controllers/api.js)
exports.editsave = function(req, res, next) {
var posty = req.body;
console.log(posty._id.toString() + " this is posty");
function callback (err, numAffected) {
console.log(err + " " + numAffected);
if(!err) {
res.send(200);
//res.redirect('/forum');
}
}
Post.update(posty, { id: posty._id.toString() }, callback);
};
This is the console output for the above code:
53c54a0d4960ddc11495d7d7 this is posty
null 0
So as you can see, it isn't affecting any of the MongoDB documents, but it also isn't producing errors.
This is what happens on the client (Angular) side when a post is updated:
$scope.saveedit = function() {
console.log($scope.post._id + " post id");
// Now call update passing in the ID first then the object you are updating
Post.update({ id:$scope.post._id }, $scope.post, function() {$location.path('/forum')});
};
After the redirect, $location.path('/forum'), none of the data is displayed as being updated...when I look in the database...nothing has changed either...it is like I am missing the step to save the changes...but I thought that update (a PUT request) would do that for me.
I use ng-init="loadposts()" when the /forum route is loaded:
$scope.loadposts = function() {
$http.get('/api/posts').success(function (data) {$scope.posts = data});
};
Shouldn't all the new data be loaded after this? Any help would be appreciated. Thanks!
Your server side output indicate that the update query doesn't match any document in the database.
I'm guessing that you are using Mongoose in NodeJS server side code to connect to mongodb.
If that the case, your update statement seems incorrect.
Instead of { id: .. } it should be { _id: .. }
Also the conditions object and updated object are swapped.
The statement should be like this:
Post.update({ _id: posty._id.toString() }, posty, callback);
If you are not using Mongoose, please eloborate more on which library you are using or better than that, show the code where the Post variable is defined in your server side code.
Ok I got it.
the problem is that you are not using the Angular resource api correct.
This code need to be changed:
$scope.saveedit = function() {
console.log($scope.post._id + " post id");
Post.update({ id:$scope.post._id }, $scope.post, function() {$location.path('/forum')});
};
Into:
// Update existing Post
$scope.saveedit = function() {
var editedpost = new Post($scope.post); //New post object
editedpost.$update(function() {
$location.path('/forum');
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
And as for the server code (taken from my own working module):
exports.update = function (req, res) {
var post == req.post;
post = _.extend(post, req.body);
post.save(function (err) {
if (err) {
return res.send(400, {
message: getErrorMessage(err)
});
} else {
res.jsonp(post);
}
});
};