diff --git a/config/env/torrents.js b/config/env/torrents.js index 503e206c..9e4c4fdc 100644 --- a/config/env/torrents.js +++ b/config/env/torrents.js @@ -156,6 +156,7 @@ module.exports = { */ sign: { openSignup: true, + signUpActiveTokenExpires: 60 * 60 * 1000 * 2, allowSocialSignin: false, showDemoSignMessage: true }, diff --git a/modules/core/client/app/trans-string-en.js b/modules/core/client/app/trans-string-en.js index dda18ecc..d1805de9 100644 --- a/modules/core/client/app/trans-string-en.js +++ b/modules/core/client/app/trans-string-en.js @@ -144,6 +144,7 @@ //page title PAGETITLE: { + ACCOUNT_ACTIVE: 'Account Active', UPLOAD: 'Upload', MOVIE_LIST: 'Movie List', TV_LIST: 'TV List', @@ -229,7 +230,12 @@ ENTER_USERNAME: 'Enter your account username or email.', RESET_PASS_OK: 'Password successfully reset', RESET_PASS_INVALID: 'Password reset is invalid', - RE_RESET_PASSWORD: 'Ask for a new password reset?' + RE_RESET_PASSWORD: 'Ask for a new password reset?', + + ACTIVE_INVALID: 'Can not active your account, maybe this token already used or this token is expired.', + ACTIVE_ERROR: 'Account active ERROR!', + ACTIVE_FAILED: 'Account active and login failed', + ACTIVE_SUCCESSFULLY: 'Account active successfully, will redirect to home after 3 seconds automate.' }, //TorrentsController & views @@ -1143,6 +1149,11 @@ ANDROID: 'Android', CAR: 'Car' } + }, + + //server returned string + SERVER: { + SENDING_ACTIVE_MAIL_SUCCESSFULLY: 'Welcome join {{site}}, We`ve sent you an email to {{mail}}, please check you mail box and click the active url to verify you mail address and active you account in {{hours}} hours, thanks!' } }; diff --git a/modules/core/client/app/trans-string-zh.js b/modules/core/client/app/trans-string-zh.js index 6c18b778..6c8d29c5 100644 --- a/modules/core/client/app/trans-string-zh.js +++ b/modules/core/client/app/trans-string-zh.js @@ -144,6 +144,7 @@ //page title PAGETITLE: { + ACCOUNT_ACTIVE: '帐号激活', UPLOAD: '上传', MOVIE_LIST: '电影列表', TV_LIST: '电视剧列表', @@ -229,7 +230,12 @@ ENTER_USERNAME: '请输入你的帐号用户名或注册邮箱', RESET_PASS_OK: '密码重置成功', RESET_PASS_INVALID: '密码重置失败', - RE_RESET_PASSWORD: '再次请求重置密码?' + RE_RESET_PASSWORD: '再次请求重置密码?', + + ACTIVE_INVALID: '帐户激活失败, 可能该链接已被使用或者已失效.', + ACTIVE_ERROR: '帐户激活错误!', + ACTIVE_FAILED: '帐户激活与登录失败', + ACTIVE_SUCCESSFULLY: '帐户激活成功, 3秒钟后将自动跳转到首页.' }, //TorrentsController & views @@ -1143,6 +1149,11 @@ ANDROID: 'Android', CAR: 'Car' } + }, + + //server returned string + SERVER: { + SENDING_ACTIVE_MAIL_SUCCESSFULLY: '欢迎加入 {{site}}, 我们已向你的邮箱 {{mail}} 发送了一封电子邮件, 请在 {{hours}} 小时内检查您的邮箱并点击邮件中的链接地址来验证您的邮箱地址并激您的帐号,谢谢!' } }; diff --git a/modules/core/client/less/margin.less b/modules/core/client/less/margin.less index d49f1a77..16989263 100644 --- a/modules/core/client/less/margin.less +++ b/modules/core/client/less/margin.less @@ -30,6 +30,10 @@ margin-top: 50px; } +.margin-top-100 { + margin-top: 100px; +} + .margin-bottom-5 { margin-bottom: 5px; } @@ -114,6 +118,10 @@ padding-top: 50px; } +.padding-top-100 { + padding-top: 100px; +} + .padding-bottom-10 { padding-bottom: 10px; } diff --git a/modules/core/client/less/mt.less b/modules/core/client/less/mt.less index 7f801a04..76815a19 100644 --- a/modules/core/client/less/mt.less +++ b/modules/core/client/less/mt.less @@ -40,6 +40,10 @@ body { } } +.active-notice { + font-size: 18px; +} + .backdrop { background-image: url("http://image.tmdb.org/t/p/w1280/5pAGnkFYSsFJ99ZxDIYnhQbQFXs.jpg"); background-position: center; diff --git a/modules/users/client/config/users.client.routes.js b/modules/users/client/config/users.client.routes.js index d699cb7a..b0ac6a0f 100644 --- a/modules/users/client/config/users.client.routes.js +++ b/modules/users/client/config/users.client.routes.js @@ -149,6 +149,15 @@ pageTitle: 'SIGNIN' } }) + .state('authentication.active', { + url: '/active?method', + templateUrl: '/modules/users/client/views/authentication/active.client.view.html', + controller: 'AuthenticationController', + controllerAs: 'vm', + data: { + pageTitle: 'PAGETITLE.ACCOUNT_ACTIVE' + } + }) .state('authentication.invite', { abstract: true, url: '/invite', diff --git a/modules/users/client/controllers/authentication.client.controller.js b/modules/users/client/controllers/authentication.client.controller.js index 71de3ddc..d31bf2d5 100644 --- a/modules/users/client/controllers/authentication.client.controller.js +++ b/modules/users/client/controllers/authentication.client.controller.js @@ -5,15 +5,16 @@ .module('users') .controller('AuthenticationController', AuthenticationController); - AuthenticationController.$inject = ['$scope', '$state', 'UsersService', '$location', '$window', 'Authentication', 'PasswordValidator', 'Notification', + AuthenticationController.$inject = ['$scope', '$state', 'UsersService', '$location', '$window', '$timeout', 'Authentication', 'PasswordValidator', 'Notification', 'MeanTorrentConfig', 'getStorageLangService', '$rootScope', '$stateParams', 'InvitationsService']; - function AuthenticationController($scope, $state, UsersService, $location, $window, Authentication, PasswordValidator, Notification, MeanTorrentConfig, + function AuthenticationController($scope, $state, UsersService, $location, $window, $timeout, Authentication, PasswordValidator, Notification, MeanTorrentConfig, getStorageLangService, $rootScope, $stateParams, InvitationsService) { var vm = this; vm.lang = getStorageLangService.getLang(); vm.signConfig = MeanTorrentConfig.meanTorrentConfig.sign; + vm.appConfig = MeanTorrentConfig.meanTorrentConfig.app; vm.authentication = Authentication; vm.getPopoverMsg = PasswordValidator.getPopoverMsg; vm.signup = signup; @@ -22,14 +23,19 @@ vm.usernameRegex = /^(?=[\w.-]+$)(?!.*[._-]{2})(?!\.)(?!.*\.$).{3,34}$/; vm.credentials = {}; + vm.activeMethod = $state.params.method; // Get an eventual error defined in the URL query string: if ($location.search().err) { Notification.error({message: $location.search().err}); } - // If user is signed in then redirect back home - if (vm.authentication.user) { - $location.path('/'); + /** + * account active successfully, redirect to home after 2 seconds + */ + if (vm.activeMethod === 'successfully') { + $timeout(function () { + $state.go('home'); + }, 3000); } /** @@ -69,6 +75,16 @@ UsersService.userSignup(vm.credentials) .then(onUserSignupSuccess) .catch(onUserSignupError); + + function onUserSignupSuccess(response) { + vm.waitToActive = true; + vm.waitToActiveTranslate = response.message; + } + + function onUserSignupError(response) { + Notification.error({message: response.data.message, title: ' Signup Error!', delay: 6000}); + } + } /** @@ -87,6 +103,20 @@ UsersService.userSignin(vm.credentials) .then(onUserSigninSuccess) .catch(onUserSigninError); + + function onUserSigninSuccess(response) { + // If successful we assign the response to the global user model + vm.authentication.user = response; + $rootScope.$broadcast('auth-user-changed'); + $rootScope.$broadcast('user-invitations-changed'); + Notification.info({message: 'Welcome ' + response.firstName}); + // And redirect to the previous or home page + $state.go($state.previous.state.name || 'home', $state.previous.params); + } + + function onUserSigninError(response) { + Notification.error({message: response.data.message, title: ' Signin Error!', delay: 6000}); + } } // OAuth provider request @@ -103,51 +133,6 @@ $window.location.href = url; } - // Authentication Callbacks - /** - * onUserSignupSuccess - * @param response - */ - function onUserSignupSuccess(response) { - // If successful we assign the response to the global user model - vm.authentication.user = response; - $rootScope.$broadcast('auth-user-changed'); - $rootScope.$broadcast('user-invitations-changed'); - Notification.success({message: ' Signup successful!'}); - // And redirect to the previous or home page - $state.go($state.previous.state.name || 'home', $state.previous.params); - } - - /** - * onUserSignupError - * @param response - */ - function onUserSignupError(response) { - Notification.error({message: response.data.message, title: ' Signup Error!', delay: 6000}); - } - - /** - * onUserSigninSuccess - * @param response - */ - function onUserSigninSuccess(response) { - // If successful we assign the response to the global user model - vm.authentication.user = response; - $rootScope.$broadcast('auth-user-changed'); - $rootScope.$broadcast('user-invitations-changed'); - Notification.info({message: 'Welcome ' + response.firstName}); - // And redirect to the previous or home page - $state.go($state.previous.state.name || 'home', $state.previous.params); - } - - /** - * onUserSigninError - * @param response - */ - function onUserSigninError(response) { - Notification.error({message: response.data.message, title: ' Signin Error!', delay: 6000}); - } - /** * markLinkClick * @param evt diff --git a/modules/users/client/views/authentication/active.client.view.html b/modules/users/client/views/authentication/active.client.view.html new file mode 100644 index 00000000..92b81c25 --- /dev/null +++ b/modules/users/client/views/authentication/active.client.view.html @@ -0,0 +1,16 @@ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
diff --git a/modules/users/client/views/authentication/signup.client.view.html b/modules/users/client/views/authentication/signup.client.view.html index 74f39fb3..4ae51767 100644 --- a/modules/users/client/views/authentication/signup.client.view.html +++ b/modules/users/client/views/authentication/signup.client.view.html @@ -4,74 +4,82 @@
- {{ 'SIGN.SIGN_UP' | translate}} +
+ {{ 'SIGN.SIGN_UP' | translate}} -
- + +
+
+
+
+ +
diff --git a/modules/users/server/controllers/users/users.authentication.server.controller.js b/modules/users/server/controllers/users/users.authentication.server.controller.js index c4810eda..ae9a51c6 100644 --- a/modules/users/server/controllers/users/users.authentication.server.controller.js +++ b/modules/users/server/controllers/users/users.authentication.server.controller.js @@ -10,8 +10,12 @@ var path = require('path'), passport = require('passport'), User = mongoose.model('User'), Invitation = mongoose.model('Invitation'), + nodemailer = require('nodemailer'), traceLogCreate = require(path.resolve('./config/lib/tracelog')).create; +var smtpTransport = nodemailer.createTransport(config.mailer.options); +var mtConfig = config.meanTorrentConfig; + // URLs for which user can't be redirected on signin var noReturnUrls = [ '/authentication/signin', @@ -33,6 +37,9 @@ exports.signup = function (req, res) { user.displayName = user.firstName + ' ' + user.lastName; user.passkey = user.randomAsciiString(32); + user.signUpActiveToken = user.randomAsciiString(32); + user.signUpActiveExpires = Date.now() + mtConfig.sign.signUpActiveTokenExpires; + // Then save the user user.save(function (err) { if (err) { @@ -59,14 +66,45 @@ exports.signup = function (req, res) { user.password = undefined; user.salt = undefined; - req.login(user, function (err) { + /* send an account active mail */ + var httpTransport = 'http://'; + if (config.secure && config.secure.ssl === true) { + httpTransport = 'https://'; + } + var baseUrl = req.app.get('domain') || httpTransport + req.headers.host; + res.render(path.resolve('modules/users/server/templates/sign-up-active-email'), { + name: user.displayName, + appName: config.app.title, + hours: mtConfig.sign.signUpActiveTokenExpires / (60 * 60 * 1000), + url: baseUrl + '/api/auth/active/' + user.signUpActiveToken + }, function (err, emailHTML) { if (err) { - res.status(400).send(err); + return res.status(400).send({ + message: 'ACTIVE_MAIL_RENDER_ERROR' + }); } else { - res.json(user); + var mailOptions = { + to: user.email, + from: config.mailer.from, + subject: 'Sign up account active of ' + config.app.title, + html: emailHTML + }; + smtpTransport.sendMail(mailOptions, function (err) { + if (!err) { + res.send({ + message: 'SENDING_ACTIVE_MAIL_SUCCESSFULLY' + }); + } else { + return res.status(400).send({ + message: 'SENDING_ACTIVE_MAIL_FAILED' + }); + } + + }); } }); + //create trace log traceLogCreate(req, traceConfig.action.userSignUp, { user: user._id, @@ -77,6 +115,43 @@ exports.signup = function (req, res) { }); }; +/** + * active sign up from email token + */ +exports.active = function (req, res, next) { + User.findOne({ + signUpActiveToken: req.params.token, + status: 'inactive', + signUpActiveExpires: { + $gt: Date.now() + } + }, function (err, u) { + if (err || !u) { + return res.redirect('/authentication/active?method=invalid'); + } else { + u.update({ + $set: { + status: 'normal', + signUpActiveToken: undefined, + signUpActiveExpires: undefined + } + }).exec(function (err) { + if (err) { + return res.redirect('/authentication/active?method=error'); + } else { + req.login(u, function (err) { + if (err) { + return res.redirect('/authentication/active?method=failed'); + } else { + return res.redirect('/authentication/active?method=successfully'); + } + }); + } + }); + } + }); +}; + /** * Signin after passport authentication */ diff --git a/modules/users/server/controllers/users/users.password.server.controller.js b/modules/users/server/controllers/users/users.password.server.controller.js index 7ce6d768..6d8abc27 100644 --- a/modules/users/server/controllers/users/users.password.server.controller.js +++ b/modules/users/server/controllers/users/users.password.server.controller.js @@ -126,7 +126,6 @@ exports.validateResetToken = function (req, res) { }); }; - /** * invite sign up from email token */ diff --git a/modules/users/server/models/user.server.model.js b/modules/users/server/models/user.server.model.js index 949be6d7..baf48121 100644 --- a/modules/users/server/models/user.server.model.js +++ b/modules/users/server/models/user.server.model.js @@ -129,7 +129,7 @@ var UserSchema = new Schema({ }, status: { type: String, - default: 'normal' + default: 'inactive' }, vip_start_at: { type: Date, @@ -213,6 +213,13 @@ var UserSchema = new Schema({ type: Date, default: Date.now }, + /* for sing up active */ + signUpActiveToken: { + type: String + }, + signUpActiveExpires: { + type: Date + }, /* For reset password */ resetPasswordToken: { type: String diff --git a/modules/users/server/routes/auth.server.routes.js b/modules/users/server/routes/auth.server.routes.js index 803791a7..65ff578a 100644 --- a/modules/users/server/routes/auth.server.routes.js +++ b/modules/users/server/routes/auth.server.routes.js @@ -15,6 +15,7 @@ module.exports = function (app) { app.route('/api/auth/reset/:token').post(users.reset); app.route('/api/auth/invite/:token').get(users.invite); + app.route('/api/auth/active/:token').get(users.active); // Setting up the users authentication api app.route('/api/auth/signup').post(users.signup); diff --git a/modules/users/server/templates/sign-up-active-email.server.view.html b/modules/users/server/templates/sign-up-active-email.server.view.html new file mode 100644 index 00000000..0dfe238e --- /dev/null +++ b/modules/users/server/templates/sign-up-active-email.server.view.html @@ -0,0 +1,22 @@ + + + + + + + + +

Dear {{name}},

+
+

+ You have requested to sign up and register for your account at {{appName}} +

+

We should verify your email address, Please visit this url to active your account in {{hours}} hours:

+

{{url}}

+ If you didn't make this request, you can ignore this email. +
+
+

The {{appName}} Support Team

+ + +