I am trying to make email verification that with the accounts-password package work however I have come across a weird problem.
It seems the # in the email verification URL is causing an issue. The verification email URL usually looks like : http://localhost:3000/#/verify-email/cnaTqQSCgYAksIsFo5FgmV94NHwrfaM2g5GvdZDUMlN
When I click on this, nothing seems to happen; it just re-directs to localhost:3000/#
However when I remove the # (http://localhost:3000/verify-email/cnaTqQSCgYAksIsFo5FgmV94NHwrfaM2g5GvdZDUMlN) this seems to work perfectly.
The URL (http://localhost:3000/#/verify-email/cnaTqQSCgYAksIsFo5FgmV94NHwrfaM2g5GvdZDUMlN) comes from Meteor so it's not something I create.
Here are my routes and controllers (using iron-router)
Router.route('/verify-email/:_token', {
controller : 'AccountController',
action : 'verifyEmail'
});
AccountController = RouteController.extend({
fastRender: true,
data: function () {},
onBeforeAction: function () {
this.render('Loading');
this.next();
},
verifyEmail: function() {
var verificationToken = this.params._token;
console.log(verificationToken);
Accounts.verifyEmail(verificationToken, function(error) {
if (error) {
console.log(error);
} else {
Router.go('/');
}
});
}
});
Any help is appreciated.
The conflict might be connected to the accounts-password package together with iron:router as outlined here:
...add a server file that overrides the urls with # paths that Meteor creates, so that the Iron-Router can work:
(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);
};
})();
Hope it will guide you in the right direction.
Related
I created a collection for adminuser's enter the system.I mean I dont want to use account packet for admin side but I dont know How to make Route setting after admin to be login.I made something but it doesnt work correct,
login.html
Template.login.events({
'click #entre': function (e, template) {
var Username = template.$('#username').val();
var Password = template.$('#password').val();
var getinfo= admin.findOne({});
if (Username == " " || Password == "") {
swal("Error", "All fields must be Completed.", "error");
} else if (getinfo.username== Username && getinfo.password== Password) {
swal("Success", "Welcome admin.", "success");
Session.set("hi", true);
} else {
swal("error", "Login Informations wrong.", "error");
}
}
});
router.js
Router.onBeforeAction(function () {
if (!Session.get("hi")) {
this.render('login');
} else {
this.render('dashboard');
}
});
Router.route('userList', function () {
this.render('userList');
});
Router.route('addnewuser', function () {
this.render('addnewuser');
});
Note:I want to make that when admin to be login,it can reach to all pages userlist,addnewuser etc.
If I understood it properly you want to give admin rights to certain routes of yours. There are several ways to achieve this using iron:router, either by building a Controller or using Filters. I would create an Iron Router Controller to tackle that and attach it to any route that needs that kind of checks. Both Controllers and Filters are actually reusable bits of code which is what we are looking for.
Make sure you add alanning:roles to your packages list (if you haven't already) and add some admin roles to at least one of your Meteor.users() like it's shown here
Building the actual Controllers is easy.
lib/router.js
AdminController = RouteController.extend({
onBeforeAction: function () {
var loggedInUser = Meteor.userId();
if (!!loggedInUser) {
if (!Roles.userIsInRole(loggedInUser, 'admin')) {
// Basic redirect to the homepage
Router.go('homepage');
this.stop();
}
} else {
// Log them in when they are not
Router.go('login');
this.stop();
}
this.next();
}
});
Router.route('/admin', {
name: 'admin',
controller: AdminController,
waitOn: function () {
// return subscriptions here
}
});
Continue to add that to any other routes you like.
I am trying to use Twitter REST API GET followers/ids and save it to Mongo using Mongo collection insert method
Code inside /server/server.js:
Meteor.startup(function () {
// code to run on server at startup
TwitterFollowersIDsCollecions = new Mongo.Collection("twitterFollowersIDs");
var Twit = Meteor.npmRequire('twit');
var T = new Twit({
consumer_key: '###',
consumer_secret: '###',
access_token: '###',
access_token_secret: '###'
});
var getTwitterFollowersIDsAsync = function (screenname, cb) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
console.log(data);
var vids = data.ids;
for(var i in vids) {
TwitterFollowersIDsCollecions.insert({
twitterFollowerID:vids[i]
});
}
}
);
};
Meteor.methods({
getTwitterFollowersIDsCollectionsClient : function (screenname){
var getTwitterFollowersIDsNow = Meteor.wrapAsync(getTwitterFollowersIDsAsync);
var result = getTwitterFollowersIDsNow('meteorjs');
console.log(result);
return result;
}
});
});
Error in server console:
Error: Meteor code must always run within a Fiber.
Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
Objective is to save twitter followers to a Mongo collection.
Meteor v. 1.1.0.2
Meteor packages:
meteor-platform
autopublish
insecure
differential:vulcanize
accounts-twitter
accounts-ui
meteorhacks:npm
npm-container
npm modules being used inside Meteor through meteorhacks:npm: "twit": "1.1.20" (added inside packages.json)
**UPDATE Second attempt **
Meteor.startup(function () {
// code to run on server at startup
TwitterFollowersIDsCollecions = new Mongo.Collection("twitterFollowersIDs");
var Twit = Meteor.npmRequire('twit');
var T = new Twit({
consumer_key: '###',
consumer_secret: '###',
access_token: '###',
access_token_secret: '###'
});
Meteor.methods({
// this is the server method called from the client
getTwitterFollowersIDsCollectionsClient : function (){
setTimeout(function(){
Meteor.call('getTwitterFollowersIDsNow', 'meteorjs');
},10);
return;
},
getTwitterFollowersIDsNow : function (screenname) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
console.log(data);
});
}
});
});
I'm then calling the below code from browser console:
Meteor.call('getTwitterFollowersIDsCollectionsClient');
The server crashes with the same error:
Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
UDPATE:
getTwitterFollowersIDsCollectionsClient : function (screenname){
Meteor.setTimeout(function(screenname){
T.get('followers/ids', { screen_name: screenname }, Meteor.bindEnvironment(function (err, data, response) {
console.log("from getTwitterFollowersIDsCollectionsClient : "+data.ids);
var vids = data.ids;
for(var i in vids)
{
TwitterFollowersIDsCollecions.insert({
twitterFollowerID:vids[i]
});
}
return data;
}));
},10);
return;
}
Added Meteor.bindEnvironment to T.get callback method. This code worked and I was able to save the follower IDs to a mongo collection
Glad you got it working, but I played around with this and Meteor provides another, super easy way: wrapAsync. At least, it was easy once I figured it out! Here's the server code I wound up with -
var T = new TwitMaker({
consumer_key: '...'
, consumer_secret: '...'
, access_token: '...'
, access_token_secret: '...'
})
var wrapGet = Meteor.wrapAsync(T.get, T);
Meteor.methods({
getTwitImg: function(target) {
data = wrapGet('users/show', {screen_name: target});
if (data) {
img_url = data['profile_image_url'];
US.update({twitter: target}, {$set: {'targetImg': img_url}});
return img_url;
}
}
});
For the client and template code see this gist: https://gist.github.com/DanAncona/a09ce375e48bfa8efeca
Your code is a bit confusing. It seems like you're trying to execute a web service call async, but still return the result immediately (which won't work).
First of all, you probably wouldn't need to wrap the function to fetch the followers in an async block.
If you want your server method to return something immediately to the client after it has been called, I'd use a Meteor.setTimeout (see What's the point of Meteor.setTimeout() vs just setTimeout()?) block and call another method to do the fetching:
Meteor.methods({
// this is the server method called from the client
getTwitterFollowersIDsCollectionsClient : function (screenname){
Meteor.setTimeout(function() {
Meteor.call('getTwitterFollowersIDsNow', 'meteorjs');
}, 10);
return;
},
getTwitterFollowersIDsNow : function (screenname) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
console.log(data);
var vids = data.ids;
for(var i in vids) {
TwitterFollowersIDsCollecions.insert({
twitterFollowerID:vids[i]
});
}
}
}
});
Ideally you would use a template helper to retrieve your followers from your collection. Due to these kind of helpers being reactive, you could just call the server method from the client and let the reactivity of Meteor solve your problem of returning the followers via the helper (which is re-executed/re-rendering the template on data change).
try calling:
var wrappedInsert = Meteor.bindEnvironment(function(tweet) {
TweetsCollection.insert(tweet);},
"Failed to insert tweet into Posts collection.");
from inside of api callback
getTwitterFollowersIDsNow : function (screenname) {
T.get('followers/ids', { screen_name: screenname }, function (err, data, response) {
for(var i in data)
{
wrappedInsert(data[i]);
}
});
}
I tried to write my own custom auth form in meteor. for the email validation part, the system send out an email with the route and token appended on it. However, I want to get the token in the validation page, so I tired the following
Accounts.onEmailVerificationLink(function(token, done) {
console.log("hello");
Session.set(verifyEmailToken, token);
doneCallback = done;
});
Template.emailVerified.onCreated(function(){
console.log(Session.get(verifyEmailToken));
Accounts.verifyEmail(Session.get(verifyEmailToken),function(err){
if(err){
Session.set(ERROR_KEY,err.reason);
}else{
Session.set(SUCESS_KEY,"Your email has been verified, thank you!");
if (doneCallback) {
doneCallback();
}
}
});
});
But the Accounts.onEmailVerificationLink method doesn't seem like have been invoked. Did I miss something there? Any help is appreciated.
Try this:
Server code:
Accounts.urls.verifyEmail = function(token) {
return Meteor.absoluteUrl('verify/' + token);
};
Common/Both Router:
AccountController = RouteController.extend({
verifyEmail: function() {
Accounts.verifyEmail(this.params.token, function(err) {
if (err) {
// error
} else {
//
}
});
}
});
Router.map(function() {
return this.route('verifyEmail', {
controller: 'AccountController',
path: '/verify/:token',
action: 'verifyEmail'
});
});
In my application I want to seed the database with users and send them an enrollment link to activate their account (and choose a password). I also want them to verify/change some profile data.
On the server I seed the database like this:
Meteor.startup(function () {
if(Meteor.users.find().count() === 0) {
var user_id = Accounts.createUser({ email: 'some#email.com', profile: { some: 'profile' } });
Accounts.sendEnrollmentEmail(user_id);
}
})
The enrollment link is sent as expected, but I want to create a custom template for when the url in the email is clicked. Preferably handled by iron-router. (Not using the accounts-ui package).
I tried things like redirecting the user to a custom route like this:
var doneCallback, token;
Accounts.onEnrollmentLink(function (token, done) {
doneCallback = done;
token = token;
Router.go('MemberEnroll')
});
which is not working (it changes the url but not rendering my template)
I also tried to change the enroll URL on the server like this:
Accounts.urls.enrollAccount = function (token) {
return Meteor.absoluteUrl('members/enroll/' + token);
};
But when I do this, the Accounts.onEnrollmentLink callback does not fire.
Also, changing the URL is not documented so I'm not sure its a good practice at all.
Any help is appreciated.
In my application I'm doing like this
this.route('enroll', {
path: '/enroll-account/:token',
template: 'enroll_page',
onBeforeAction: function() {
Meteor.logout();
Session.set('_resetPasswordToken', this.params.token);
this.subscribe('enrolledUser', this.params.token).wait();
},
data: function() {
if(this.ready()){
return {
enrolledUser: Meteor.users.findOne()
}
}
}
})
As enrollment url is like this
http://www.yoursite.com/enroll-account/hkhk32434kh42hjkhk43
when users click on the link they will redirect to this template and you can render your template
In my publication
Meteor.publish('enrolledUser', function(token) {
return Meteor.users.find({"services.password.reset.token": token});
});
After taking the password from the user
Accounts.resetPassword(token, creds.password,function(e,r){
if(e){
alert("Sorry we could not reset your password. Please try again.");
}else{
alert("Logged In");
Router.go('/');
}
})
enroll link
Accounts.urls.enrollAccount = function (token) {
return Meteor.absoluteUrl('enroll-account/' + token);
};
Im afraid now isnt possible, what i did is changing the html and css using "rendered" function but it has some probs with delay
Meteor.startup(function(){
Template["_enrollAccountDialog"].rendered = function(){
document.getElementById('enroll-account-password-label').innerHTML = 'Escolha sua senha';
$('.accounts-dialog').css('background-color','#f4f5f5');
$('.accounts-dialog').css('text-align','center');
$('.accounts-dialog').removeAttr('width');
document.getElementById('login-buttons-enroll-account-button').className = ' create-account-button';
document.getElementById('login-buttons-enroll-account-button').innerHTML = 'Criar conta';
}
});
I'm using balanced-payments and their version 1.1 of balanced.js within Meteor.
I'm trying to create a new customer using
balanced.marketplace.customers.create(formData);
Here is my CheckFormSubmitEvents.js file
Template.CheckFormSubmit.events({
'submit form': function (e, tmpl) {
e.preventDefault();
var recurringStatus = $(e.target).find('[name=is_recurring]').is(':checked');
var checkForm = {
name: $(e.target).find('[name=name]').val(),
account_number: $(e.target).find('[name=account_number]').val(),
routing_number: $(e.target).find('[name=routing_number]').val(),
recurring: { is_recurring: recurringStatus },
created_at: new Date
}
checkForm._id = Donations.insert(checkForm);
Meteor.call("balancedCardCreate", checkForm, function(error, result) {
console.log(result);
// Successful tokenization
if(result.status_code === 201 && result.href) {
// Send to your backend
jQuery.post(responseTarget, {
uri: result.href
}, function(r) {
// Check your backend result
if(r.status === 201) {
// Your successful logic here from backend
} else {
// Your failure logic here from backend
}
});
} else {
// Failed to tokenize, your error logic here
}
// Debuging, just displays the tokenization result in a pretty div
$('#response .panel-body pre').html(JSON.stringify(result, false, 4));
$('#response').slideDown(300);
});
}
});
Here is my Methods.js file
var wrappedDelayedFunction = Async.wrap(balanced.marketplace.customers.create);
Meteor.methods({
balancedCardCreate: function (formData) {
console.log(formData);
var response = wrappedDelayedFunction(formData);
console.log(response);
return response;
}
});
I get nothing back when I submit the form, except that on the server console I do see the log of the form data.
I'm sure I'm not calling some of these async functions correctly. The hard part for me here is that the balanced function are async, but I don't know if they fit into the same mold as some of the examples I've seen.
I've tried to follow this example code.
http://meteorhacks.com/improved-async-utilities-in-meteor-npm.html
Is there a specific change that needs to be done in regard to working with balanced here? Does anyone have any tips for working with Async functions or see something specific about my code that I've done wrong?
Thanks
The NPM utilities Async.wrap does the same thing as the undocumented Meteor function Meteor._wrapAsync, in that it takes an asynchronous function with the last argument function(err, result) {} and turns it into a synchronous function which takes the same arguments, but either returns a result or throws an error instead of using the callback. The function yields in a Fiber until the asynchronous callback returns, so that other code in the event loop can run.
One pitfall with this is that you need to make sure that the function you wrap is called with the correct context. So if balanced.marketplace.customers.create is a prototype method that expects this to be set to something, it will not be set properly unless you bind it yourself, using function.bind or any of the other various library polyfills.
For more information, see https://stackoverflow.com/a/21542356/586086.
What I ended up doing was using a future. This works great, I just need to do better at catching errors. Which will be a question for a pro I think ; - )
Credit should go to user3374348 for answering another similar question of mine, which solved both of these.
https://stackoverflow.com/a/23777507/582309
var Future = Npm.require("fibers/future");
function extractFromPromise(promise) {
var fut = new Future();
promise.then(function (result) {
fut["return"](result);
}, function (error) {
fut["throw"](error);
});
return fut.wait();
}
Meteor.methods({
createCustomer: function (data) {
balanced.configure(Meteor.settings.balancedPaymentsAPI);
var customerData = extractFromPromise(balanced.marketplace.customers.create({
'name': data.fname + " " + data.lname,
"address": {
"city": data.city,
"state": data.region,
"line1": data.address_line1,
"line2": data.address_line2,
"postal_code": data.postal_code,
},
'email': data.email_address,
'phone': data.phone_number
}));
var card = extractFromPromise(balanced.marketplace.cards.create({
'number': data.card_number,
'expiration_year': data.expiry_year,
'expiration_month': data.expiry_month,
'cvv': data.cvv
}));
var associate = extractFromPromise(card.associate_to_customer(customerData.href).debit({
"amount": data.total_amount*100,
"appears_on_statement_as": "Trash Mountain" }));
});
As Andrew mentioned, you need to set the context for the method.
Here's the way you can do that with Async.wrap
Async.wrap(balanced.marketplace.customers, "create");