问题
Using Meteor accounts (and accounts-ui
) is there an easy way to make new user sign-ups invitation only? For example by providing an invitation link or an invitation code.
The only thing related I could find in the Meteor documentation is Meteor.sendEnrollmentEmail but it doesn't solve my problem.
回答1:
You can do this with the built in package, but I found it alot easier and powerful to roll a simple implementation.
You'll need to:
- Create a collection, eg
UserInvitations
to contain the invites to become a user. - Create UI for making
UserInvitations
/ insert some usingmeteor mongo
Using
iron-router
or similar create a route, eg:Router.map -> @route 'register', path: '/register/:invitationId' template: 'userRegistration' data: -> return { invitationId: @params.invitationId } onBeforeAction: -> if Meteor.userId()? Router.go('home') return
When the form in
userRegistration
is submitted - callAccounts.createUser({invitationId: Template.instance().data.invitationId /*,.. other fields */})
On the server, make an
Accounts.onCreateUser
hook to pass through theinvitationId
from options to the userAccounts.onCreateUser(function(options, user){ user.invitationId = options.invitationId return user; });
Also, on the server make an
Accounts.validateNewUser
hook to check theinvitationId
and mark the invitation as usedAccounts.validateNewUser(function(user){ check(user.invitationId, String); // validate invitation invitation = UserInvitations.findOne({_id: user.invitationId, used: false}); if (!invitation){ throw new Meteor.Error(403, "Please provide a valid invitation"); } // prevent the token being re-used. UserInvitations.update({_id: user.invitationId, used: false}, {$set: {used: true}}); return true });
Now, only users that have a valid unused invitationId
can register.
EDIT: Oct 2014 - Updated to use meteor 0.9.x API's
回答2:
To do it with the built in stuff, you can plumb together the existing Accounts.sendEnrollmentEmail
- however it's a little more complicated than the other solution given.
Using the example code below, call the enroll
method as such:
Meteor.call('enroll', 'john.smith', 'js@harvard.edu', {name: 'John Smith'});
Meteor will then email the user a link (You can configure the template with Accounts.emailTemplates
)
When they click the link, meteor calls the function passed to Accounts.onEnrollmentLink
- in this case you can take them to a password setup page; but you have to mess around with their done
callback.
Modify the following code, where it says INSERT XXX HERE
; then in your code call SomeGlobalEnrollmentObjectThing.cancel()
if the user cancels, or SomeGlobalEnrollmentObjectThing.complete(theUsersNewPassword)
if they submit the new password.
if (Meteor.isServer){
Meteor.methods({
"enroll": function(username, email, profile){
var userId;
check(username, String);
check(email, String); // Or email validator
check(profile, {
name: String
}); // your own schema
// check that the current user is privileged (using roles package)
if (!Roles.isInRole(this.userId, 'admin')){
throw new Meteor.Error(403);
}
userId = Accounts.createUser({
username: username,
email: email,
profile: profile
});
Accounts.sendEnrollmentEmail(userId);
}
});
} else {
// uses `underscore`, `reactive-var` and `tracker` packages
function Enrollment(){
this.computation = null;
this.token = new ReactiveVar(null);
this.password = new ReactiveVar(null);
this.cancelled = new ReactiveVar(false);
this.done = null;
this._bind();
}
_.extend(Enrollment.prototype, {
_bind: function(){
Accounts.onEnrollmentLink(_.bind(this.action, this));
},
reset: function(){
this.token.set(null);
this.password.set(null);
this.cancelled.set(false);
this.done = null;
if (this.computation !== null){
this.computation.stop();
this.computation = null;
}
},
cancel: function(){
this.cancelled.set(true);
},
complete: function(password){
this.password.set(password);
},
action: function(token, done){
this.reset();
this.token.set(token);
this.done = done;
this.computation = Tracker.autorun(_.bind(this._computation, this));
// --- INSERT REDIRECT LOGIC HERE [TAKE TO PASSWORD SETUP PAGE]--- //
},
_computation: function(){
var password;
if (this.cancelled.get()){
this.reset();
this.done();
// --- INSERT REDIRECT LOGIC HERE [USER CANCELLED]--- //
} else {
password = this.password.get();
if (password !== null){
Accounts.resetPassword(this.token.get(), password, _.bind(this._complete, this));
}
}
},
_complete: function(err){
// TODO - check if we were reset before callback completed
this.reset();
this.done();
if (err){
// --- INSERT REDIRECT LOGIC HERE [RESET FAILED] --- //
} else {
// --- INSERT REDIRECT LOGIC HERE [SUCCESS] --- //
}
}
});
SomeGlobalEnrollmentObjectThing = new Enrollment();
}
回答3:
I have created a specific solution to this, since all the other solutions only allow you to explicitly create password-based accounts. The t3db0t:accounts-invite
package allows account creation with any service only when you allow them, such as with an 'accept invitation' route. Live demo here.
来源:https://stackoverflow.com/questions/20990550/how-to-make-sign-up-invitation-only