I have an interceptor:
Router.onBeforeAction(function() {
if (!Meteor.userId()) {
console.log("lets login");
Router.go("login");
} else {
this.next();
}
}, {
except: ['login', 'signup']
});
It works very well, until I replace the !Meteor.userId() for Meteor.user(). It seems that .user when refreshing the page goes undefined and redirect it to my login page. My login router also verifies .user and here it is right.
Why this difference?
That's true, Meteor.userId() returns the id while Meteor.user() returns the object.
However, returning an object (Meteor.user()) takes more time than just returning an ID, due to the asynchronous issue, by the time the script is checking if (!Meteor.user()) {...}, Meteor.user() has not been processed and returned yet!
As a result, there are several ways to due with that asynchronous issue (for example, in Meteor/React application, we may have something like subscribe, and wait until the handle is ready)
Related
Here's the rundown:
I'm trying to run Stripe API on my Meteor app asynchronously
Long story short, everything works (i.e. subscription and charge is/are created normally and shows up in my Stripe dashboard)
When errors occur, the errors throw normally and show on client via user friendly alerts
I have a problem when there is a success and customer subscription is created, the result is not present in client and instead always returns as an error, despite it being a successful process
Here's what my method looks like on the server:
createCustomer: function(token, email, plan){
try{
let createCustomer = Meteor.wrapAsync(stripe.customers.create);
let result = createCustomer({
source: token,
email: email,
plan: plan
});
let subscription = {
customer: result.id,
sub: result.subscriptions.data[0].id,
plan: result.subscriptions.data[0].plan.name
};
Meteor.users.update({_id: Meteor.userId()}, {$set: subscription});
} catch(error){
if(error.code === "incorrect_cvc"){
throw new Meteor.Error("incorrect_cvc", error.message);
}
// More of such errors follows
}
}
Here's what it looks like on the client:
Stripe.card.createToken({
number: number,
cvc: cvc,
exp_month: exp,
exp_year: exp_year,
address_zip: zip,
address_country: country
}, function(status, response){
if(response.error){
console.log("Make sure all fields are filled before submitting order.");
} else{
let token = response.id;
Meteor.call("createCustomer", token, email, plan, function(error, result){
if(result){
console.log("Congratulations, everything worked!");
} else{
if(error.error === "incorrect_cvc"){
console.log("oops, the CSV is incorrect");
}
// More of such errors follow..
}
})
}
});
So, everything works in terms of when there is a real error, it throws fine on server + client. When user uses card, the charges are created and subscription is always created. HOWEVER, when there is a success and everything clicking fine, I still receive an error on client via callback and the result is never true or triggered. No idea why.
Not 100% up on Meteor, but it looks to me like your createCustomer method doesn't actually return anything, so the result from your (err, result) might never have anything in it?
As was mentioned in the comments, you might want to separate out the steps and wrap each in its own try-catch set so you can better isolate the issue.
Also, I feel like you could probably generalize your server-side error code to something like:
throw new Meteor.Error(error.error, error.message);
And I might even be tempted to do something like this, at least during testing/development - that way you can actually console.log() the original error in the browser:
throw new Meteor.Error(error.error, error.message, JSON.stringify(error));
i store email using session .set('email','email name') but when i reload page that time this session email is become undefined. i use Session.get('email') to get user email.
Router.route('profile', {
path: '/profile',
data: function() {
$("body").removeClass('home');
this.render('profile');
setTimeout(function(){
$('#username').html(Session.get('first_name'));
$('#profile_username').html(Session.get('first_name'));
$('#setting_name').val(Session.get('first_name'));
$('#setting_username').val(Session.get('first_name'));
$('#setting_email').val(Session.get('email'));
$('#user_id').val(Session.get('id'));
$('.setting_day').val(Session.get('day'));
$('.setting_month').val(Session.get('month'));
$('.setting_year').val(Session.get('year'));
if(Session.get('image')!= ''){
$('.user_profile_image').attr("src",Session.get('image'));
}
if(Session.get('gender') == 0){
$('#user_gender').html('Male');
}else{
$('#user_gender').html('Female');
}
$('#day').html(Session.get('day'));
$('#month').html(Session.get('month'));
$('#year').html(Session.get('year'));
},100);
},
onBeforeAction: function () {
alert(Session.get('email'));
if(Session.get('email')){
this.next();
}else {
this.redirect('/');
}
}
});
install persistent package Session. Your session variables will persist across routes also. You need to configure it via Meteor settings. so don't forget include the settings when you run project.
u2622:persistent-session
When you reload the page page in meteor all the client side reactive things reinitialize So if you want to keep the email when page refresh then you have to send it on the server and then you can fetch this when according to your need. You can save it into a collection and then fetch from a meteor call or publish-subscribe according to your need.
When you refresh the page you are no longer in the same session so what you describe is the expected and correct default behavior. There is a package (I don't know it's name right now, but should be easy to find on atmospherejs) which gives you Session.setPersistent(...). I think this is what you are looking for.
i have an issue with callback method.
i have created on methods.js in server folder
and one callback.js file in client/test/mytest folder.
my callback.js contains following code
Template.testHello.events({
"click #testHello": function(e) {
Meteor.call("testmethod",function(error, id) {
if (error) {
Errors.throwError(error.reason);
}
return false;
});
return false;
}
});
and methods.js file code is
Meteor.methods({
testmethod: function(att) {
alert("hello testmethod..");
}
});
but when i clicked on button "testHello" then it gives me error like "internal server error 500".
can anyone have idea about this?
Thanks,
It makes no sense to have client-only method calls because Meteor methods are intended to perform RMI (remote method invokation) on the server.
Move your methods.js to either server/methods.js or lib/methods.js if you want your method to have a simulation counterpart on the client.
EDIT :
As hinted by #user728291, the alert method is defined on the window object which is a browser related object thus only available on client environment, you can use console.log instead to print something on the server.
I might have this pretty close but I'm lacking the knowledge to fix this last issue.
I wanted to use a custom authentication system instead of using accounts-ui so I could track some additional details about each user.
Everything worked great until I get to the resetPassword part. If a user submits their email address in the forgotPassword form, the email is received. But when you click the reset password link in the email it does not display the resetPassword template.
This is on SO here:
Meteor account email verify fails two ways
And the iron-router github issue tracker here (which has the most fixes though is more focused on the enrollmentemail than resetPassword which I'm assuming should be very similar):
Iron-router swallows Accounts.sendEnrollmentEmail
If I understand correctly from the iron-router issue tracker above, iron-router doesn't (or didn't and maybe still doesn't) support hashbang urls like that being sent in the reset password email. A URL like:
http://localhost:3000/#/reset-password/T4rPxcVNWKwBONHSRajSk7dNZvM_YRxTLyzxZVv5SuU
Meteor was then updated so that meteor accounts-base strips out everything after the # and stores them in variables in the Accounts namespace.
While I think I understand all of that, now the question is why I can't get the suggestions in the issue tracker to work for my reset password code. I'm using everything that is in the custom auth system by Julien Le Coupanec and then I've done the following from the issue tracker:
router.js
Router.map(function() {
this.route('invList', {path: '/'});
this.route('resetPassword', {
controller: 'AccountController',
path: '/reset-password/:token',
action: 'resetPassword'
});
});
AccountController = RouteController.extend({
resetPassword: function () {
Accounts.resetPassword(this.params.token, function () {
Router.go('/reset-password');
});
}
});
overrideaccounts.js in /server
(function () {
"use strict";
Accounts.urls.resetPassword = function (token) {
return Meteor.absoluteUrl('reset-password/' + token);
};
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl('verify-email/' + token);
};
Accounts.urls.enrollAccount = function (token) {
return Meteor.absoluteUrl('enroll-account/' + token);
};
})();
I'm wondering if the issues isn't related to either bad routing on my part (likely since I don't have my head wrapped around it well yet), if I put "server code" as is listed in the issue track in the right place, or if the session related code below is what is causing the resetPassword template to not display. Or something else that I'm missing of course.
main.js
//forgotPassword helper and event handler
Template.main.helpers({
showForgotPassword: function() {
return Session.get('showForgotPassword');
},
resetPassword: function(){
return Session.get('resetPassword');
}
});
After spending many hours on what I thought would be a really simple authentication system, I'm still at a loss. Appreciate any advice!
Don't struggle with hacking the hash and iron router, just back to Meteor original design flow.
When user click the verify link in email, it lead back to "/" (home), so just did this:
Template.home.created = function() {
if (Accounts._verifyEmailToken) {
Accounts.verifyEmail(Accounts._verifyEmailToken, function(err){
if (err != null) {
// handle the error
} else {
// do what you want, maybe redirec to some route show verify successful message
}
});
}
};
I did this and verify email right, same way worked for enroll, reset password...
I've just spent a few hours reading SO with answers such as Meteor: Calling an asynchronous function inside a Meteor.method and returning the result
Unfortunately, I still didn't manage to user fibers, or futures for that matter.
I'm trying to do something fairly simple (I think!).
When creating a user, add a variable to the user object, based on the result of an asynchronous method. So imagine if you will my async method is called on a 3rd party db server called BANK, which could take several seconds to return.
Accounts.onCreateUser(function(options,user){
var Fiber = Npm.require("fibers");
Fiber(function() {
BANK.getBalance(function(err, theBalance) {
if (err) return console.log(err);
_.extend(user,{
balance: theBalance;
});
});
}).run();
return user;
});
So what happens in the above is that the BANK method is called, but by the time it returns the code has already moved on and _.extend is never invoked.
I tried placing the return call inside the Fiber, that only made things worse: it never return user. Well it did, but 3 seconds too late so by then everything downstream was bailing out.
Thank you for any help!
Answering my own question which hopefully will help some people in the future. This is based on the excellent advice of Avital Oliver and David Glasser to have a look at Mike Bannister's meteor-async.md. You can read it here: https://gist.github.com/possibilities/3443021
Accounts.onCreateUser(function(options,user){
_.extend(user,{
balance: getBalance(),
});
return user;
});
function getBalance() {
var Future = Npm.require("fibers/future");
var fut = new Future();
BANK.getBalance(function(err, bal) {
if (err) return console.log(err);
fut.return(bal);
});
return fut.wait();
}
I believe there's an even better way to handle this, which is directly by wrapping the BANK API in Futures within the npm package itself, as per this example (from Avital Oliver): https://github.com/avital/meteor-xml2js-npm-demo/blob/master/xml2js-demo.js
I hope it helps!
Use this.unblock() on server side code.
From Meteor 1.0 documentation: "Allow subsequent method from this client to begin running in a new fiber.On the server, methods from a given client run one at a time. The N+1th invocation from a client won't start until the Nth invocation returns. However, you can change this by calling this.unblock. This will allow the N+1th invocation to start running in a new fiber."
Meteor.methods({checkTwitter: function (userId) {
check(userId, String);
this.unblock();
try {
var result = HTTP.call("GET", "http://api.twitter.com/xyz",
{params: {user: userId}});
return true;
} catch (e) {
// Got a network error, time-out or HTTP error in the 400 or 500 range.
return false;
}
}});
method calls use the sync style (see 'sync call' here http://docs.meteor.com/#meteor_call) on the server side, which is where this create user method runs - you should be able to do something like
Accounts.onCreateUser(function(options, user) {
user.balance = Meteor.call('getBankBalance', params);
return user;
});
Thanks yo so much that's work, This solution its better for Meteor projects, because Fibers module installed by default. mrt add npm has a method for this too -> Meteor.sync . For any nodeJS projects there is a other module based on Fibers, its name is Fibrous
Reference:https://github.com/goodeggs/fibrous