I am using github repo by #mleanos for MeanJS using Socket for article create and update, it uses socket to create and update articles in realtime, while updating article, the data reloads, as in, the list of articles blinks off and comes again. this happens only while updating the article. While creating, it creates new article seamlessly. how do i update the article without the data blinking on and off.
Follow this link for the github repo.
Socket Article server controller
socket.on('orderCreate', function (order) {
var user = socket.request.user;
order = new Order(order);
order.user = user;
order.save(function (err) {
if (err) {
// Emit an error response event
io.emit('orderCreateError', { data: order, message: errorHandler.getErrorMessage(err) });
} else {
// Emit a success response event
io.emit('orderCreateSuccess', { data: order, message: 'Order created' });
}
});
});
// Update an Order, and then emit the response back to all connected clients.
socket.on('orderUpdate', function (data) {
var user = socket.request.user;
// Find the Order to update
Order.findById(data._id).populate('user', 'displayName').exec(function (err, order) {
if (err) {
// Emit an error response event
io.emit('orderUpdateError', { data: data, message: errorHandler.getErrorMessage(err) });
} else if (!order) {
// Emit an error response event
io.emit('orderUpdateError', { data: data, message: 'No order with that identifier has been found' });
} else {
order.name = data.name;
order.save(function (err) {
if (err) {
// Emit an error response event
io.emit('orderUpdateError', { data: data, message: errorHandler.getErrorMessage(err) });
} else {
// Emit a success response event
io.emit('orderUpdateSuccess', { data: order, updatedBy: user.displayName, updatedAt: new Date(Date.now()).toLocaleString(), message: 'Updated' });
}
});
}
});
});
Socket Client Controller
function saveUsingSocketEventsUpdate(isValid) {
vm.error = null;
if (!isValid) {
$scope.$broadcast('show-errors-check-validity', 'orderForm');
return false;
}
var order = vm.order;
// we can send the user back to the orders list already
// TODO: move create/update logic to service
if (vm.order._id) {
vm.order.$update(successCallback, errorCallback);
} else {
vm.order.$save(successCallback, errorCallback);
}
function successCallback(res) {
$state.go('orders.view', {
orderId: res._id
});
}
function errorCallback(res) {
vm.error = res.data.message;
}
// wait to send create request so we can create a smooth transition
$timeout(function () {
// TODO: move create/update logic to service
if (vm.order._id) {
Socket.emit('orderUpdate', vm.order);
} else {
Socket.emit('orderCreate', vm.order);
}
}, 2000);
}
I provided an answer in the MEANJS issue that was created for this. Since I'm the contributor that provided the branch that was used as a basis for this implementation.
The cause of the issue was that the entire list was being reloaded when any Order was updated. The solution was to update the Order that already existed in the list.
Side note: I suggested that if the Order wasn't found to get it from the server & add it to the list. This may or may not be the desired behavior. This would depend on the needs of the application.
Related
I read some tutorials on MEAN Stack and I'm currently working on one which is almost complete, until I tried my update function. I keep on receiving a 400 message and when i look at the chrome dev tools and under the network tab I read the response which is "_id" is not allowed.
here is the update function
$scope.update = () => {
console.log($scope.contact._id);
$http.put('/api/contacts/updatecontact/' + $scope.contact._id, $scope.contact)
.then(function(data) {
alert('Data was updated Successfully');
refresh();
});
};
here is the api.
app.put('/api/contacts/updatecontact/:id', (req, res) => {
const id = req.params.id;
console.log(req.body.name);
// validation
const { error } = validateInput(req.body);
if (error){
return res.status(400).send(error.details[0].message);
}
const updatedContact = Contact.where({ _id: id });
updatedContact.update({
$set: { name: req.body.name, email: req.body.email
}
}, (err, contact) => {
if(err){
console.log('error occured');
}
res.json(contact);
console.log('contact successfully updated');
});
console.log(updatedContact);
});
hope i can get some help with this.
It seems the node.api is validating the json object that you are passing. Make sure the properties you sent form the client.
Make sure you are not validating or remove the validation on the node.
I apologise if this is a stupid question, please allow me to explain a little. I am running a MEAN application.
In the server routes for my CRUD generated module I have included two separate post requests to the same api end point.
app.route('/api/tasks/:taskId').all(tasksPolicy.isAllowed)
.get(tasks.read)
.put(tasks.update)
.delete(tasks.delete)
.post(tasks.newCo)
.post(tasks.newOffer);
Each of these perform a separate push request into my mongo document for task, based on the taskId.
When I run one function at a time, each individual function works successfully and pushes into the correct array.
However, when I run include both functions on the same page at the same time the newOffer function pushes a null value into the newCo array. And the newCo function continues to work successfully.
I have no idea why..
again, I apologise if this is a stupid question.
server.controller.js
/**
* Add a new comment
*/
exports.newCo = function(req, res) {
Task.findOneAndUpdate({_id: req.params.taskId},
{
"$push": {
comments: req.body.comment
}
}, {
new: true //to return updated document
})
.exec(function(error, task) {
if (error) {
return res.status(400).send({message: 'Failed to add comment due to invalid params!'});
}
return res.status(200).send(task);
});
};
/**
* Add a new offer
*/
exports.newOffer = function(req, res) {
Task.findOneAndUpdate({_id: req.params.taskId},
{
"$push": {
offers: req.body.offer
}
}, {
new: true //to return updated document
})
.exec(function(error, task) {
if (error) {
return res.status(400).send({message: 'Failed to add offer due to invalid params!'});
}
return res.status(200).send(task);
});
};
client.controller.js
vm.newCo = function() {
$http.post('/api/tasks/' + task._id , {comment: { comment: vm.task.newComment, user: vm.authentication.user, profileImageURL: vm.authentication.user.profileImageURL, displayName: vm.authentication.user.displayName } })
.then(successCallback, errorCallback);
function successCallback(res) {
$state.transitionTo($state.current, $state.params, {
reload: true,
inherit: false,
notify: true
});
}
function errorCallback(res) {
vm.error = res.data.message;
}
};
//close new comment function
//new offer
vm.newOffer = function() {
$http.post('/api/tasks/' + task._id , {offer: { offerDesc: vm.task.offerDesc, offerPrice: vm.task.offerPrice, user: vm.authentication.user, profileImageURL: vm.authentication.user.profileImageURL, displayName: vm.authentication.user.displayName } })
.then(successCallback, errorCallback);
alert('it worked!');
function successCallback(res) {
$state.transitionTo($state.current, $state.params, {
reload: true,
inherit: false,
notify: true
});
}
function errorCallback(res) {
vm.error = res.data.message;
}
};
//close new offer function
you couldn't use same api for two post method. you can use different methods for same api like (post, get, put, delete ...) but not same method multiple time.
you should use different api for two post method .
like for task api
app.route('/api/tasks/:taskId').all(tasksPolicy.isAllowed)
.get(tasks.read)
.put(tasks.update)
.delete(tasks.delete)
.post(tasks.newCo);
for offer api
app.route('/api/offer/:taskId').all(tasksPolicy.isAllowed)
.post(tasks.newOffer);
if you use same api and two post method then always call first post method so second one always unreachable. When you call this api to add offer for that task then call tasks.newCo function and when you want receive req.body.comment get null or undefined so add empty or null comment but never will add offer.
For put request:
router.put('/:id', controller.update);
My update method look like this:
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Thing.findById(req.params.id, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) { return res.status(404).send('Not Found'); }
var updated = _.merge(thing, req.body);
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.status(200).json(thing);
});
});
};
Making request:
$http.put('/api/things/'+ thing._id, updatedThingObject)
.success(function(update){
console.log("update", update)
})
.error(function(err){
console.log("err", err)
})
It gives connection error on passing the object while making the request in angular.
The error looks like this:
PUT http://localhost:9000/api/things/56c8325b9a0ee7d00d266495
net::ERR_CONNECTION_REFUSED(anonymous function) # angular.js:11442sendReq #
If I take off the updated object, it makes the request just fine but ofcourse nothing gets updated in
that case. What might be wrong here,please?
I figured.
The reason for the functions not being called is that I have a function that is being called repetitively in Node .
var autoCreate = function(){
console.log("THING CREATED AUTOMATICALLY")
var randomNumb=0;
clearTimeout(randomNumb);
randomNumb = (Math.random()* (10-5) + 5).toFixed(0);
console.log("random number", randomNumb)
var randomThing =randomstring({
length: randomNumb,
numeric: false,
letters: true,
special: false
});
console.log("ranfom thing", randomThing)
Thing.create({
name: randomThing,
readByUser: false
}, function(err, thing) {
console.log("THING IS", thing)
//setTimeout(autoCreate, randomNumb * 1000);
});
}
setTimeout(autoCreate, 10*1000);
Since this is running when post/put request is made, I get connection error. How do I handle this to be able to have this function running and be able to make put/post requests as well?
Refer to this question: Braintree Dropin UI does not work with Ionic Framework unless force refresh
My current Ionic / Angular / Firebase + a very simple Node server app has security issue when using Braintree to charge user credit card. The problem, according to #RaymondBerg is because client can post any customerId and create a braintree token and charge that customer. Since all my user authorization happened in Firebase / Angular - client side. So when user do a $HTTP.post from my AngularJS/Ionic to my Node server, I don't want to authorize them again (as I don't even know how to do that so I use Firebase).
So what is the strategy here to setup Firebase and my Node server to work with payment system like braintree?
One thing I can think off, is to first create a node in my firebase before http request and then pass in the client $id for request in client side (Ionic app):
$scope.getToken = function () {
var ref = new Firebase('[FirebaseURL]/braintreePaymentToken');
var tokenObj = $firebaseObject(ref.child(posterId));
tokenObj.tokenGenerated = true;
tokenObj.$save().then(function(){
$http({
method: 'POST',
url: 'http://localhost:3000/api/v1/token',
data: {
//user $id from Firebase
userId: snapshot.key(),
}
})
}
In Firebase, I set up a security rule as:
"braintreePayment": {
".read": false,
".write": false,
},
"braintreePaymentToken": {
"$uid": {
".read": "auth != null",
".write": "auth != null && auth.uid == $uid",
}
},
This way, the temp node braintreePaymentToken can ONLY be written by current login user in the app. Other login user (nefarious user) can not write on this node b/c their auth.uid will not equal to posterId, which posterId is the user who need to pay.
On server end, I use once to see if I can find the value:
var ref = new Firebase('[FirebaseURL]');
app.post('/api/v1/token', jsonParser, function (request, response) {
var userId = request.body.userId;
console.log (userId);
//customerId from braintree is stored here so no one except the server can read it
ref.child('braintreePayment').child(userId).once("value", function(snapshot){
var exists = (snapshot.val() !== null);
console.log (exists);
if (exists) {
console.log ("using exsiting customer!");
//If braintreePaymentToken with userId child exsited, it mean this request is come from my Ionic client, not from anywhere else.
ref.child('braintreePaymentToken').child(userId).once("value", function(snap) {
if (snap.val()) {
gateway.clientToken.generate({
customerId: snapshot.val().customerId
}, function (err, res) {
if (err) throw err;
response.json({
"client_token": res.clientToken
});
//After I return the clientToken, I delete the braintreePaymentToken node. It is like using Firebase to send email with Zaiper. More secue I guess?
ref.child('braintreePaymentToken').child(userId).remove();
});
else {
response.json({
"client_token": "Unauthorized Access!"
});
}
} else {
console.log ("using no customer!");
gateway.clientToken.generate({}, function (err, res) {
if (err) throw err;
response.json({
"client_token": res.clientToken
});
});
}
});
});
And when user hit pay button on my client(ionic app), I do the Firebase Once request again to see if the customerId already in my firebase/braintreePayment. If not, we save one with the return transaction customerId created by braintree.
app.post('/api/v1/process', jsonParser, function (request, response) {
var transaction = request.body;
ref.child('braintreePayment').child(transaction.userId).once("value", function(snapshot){
var exists = (snapshot.val() !== null);
console.log (exists);
if (exists) {
console.log ("Return customer!");
gateway.transaction.sale({
amount: transaction.amount,
paymentMethodNonce: transaction.payment_method_nonce,
options: {
submitForSettlement: true
},
}, function (err, result) {
if (err) throw err;
response.json(result);
});
} else {
console.log ("First time customer!");
gateway.transaction.sale({
amount: transaction.amount,
paymentMethodNonce: transaction.payment_method_nonce,
options: {
store_in_vault_on_success: true,
submitForSettlement: true
},
}, function (err, result) {
if (err) throw err;
console.log ("Customer Id: " + result.transaction.customer.id);
var customerId = result.transaction.customer.id;
ref.child('braintreePayment').child(transaction.userId).update({customerId: customerId});
response.json(result);
});
}
});
});
As you see, this is REALLY COMPLICATED. But I do not know a better, secue way to do this...
Is this the best way to structure between Firebase, Node, and Braintree? Is this address the OWASP security concern? Is there way to improve this code to be better or there is a better way to do it?
Thanks!
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);
}
});
};