sms.js 9.78 KB
/* eslint no-unused-vars: ["error", { "args": "none" }] */
'use strict';
const _ = require('lodash');
const co = Promise.coroutine;
const moment = require('moment');
const cookie = global.yoho.cookie;
const config = global.yoho.config;
const utils = require(global.utils);
const RegServiceModel = require('../models/reg-service');
const PhoneServiceModel = require('../models/phone-service');
const AuthHelperModel = require('../models/auth-helper');
const LoginNewModel = require('../models/login-new');

// constrant
const CODE_REQUIRED = '请输入校验码';
const PASSWORD_REQUIRED = '请输入密码';
const PASSWORD_LENGTH_ERROR = '密码6-20位,请重新输入';
const BAD_PASSWORD = '密码格式不正确';
const LOGIN_SUCCSS = '登录成功';
const VERIFY_ERROR = '校验失败';

class SmsLogin {

    smsLoginPage(req, res, next) {
        _.set(req.session, 'smsLogin.step', 1);

        if (req.session.captchaValidCount == null) { // eslint-disable-line
            req.session.captchaValidCount = 5;
        }

        co(function* () {
            let bannerData = yield req.ctx(LoginNewModel).getTopBanner();
            let banner = _.get(bannerData, 'data[0].data[0].src', '');

            // 是否打开账号登录
            let openPassword = !_.get(req.app.locals.wap, 'close.passwordLogin', false);

            res.render('sms/sms-login-new', {
                module: 'passport',
                page: 'sms-login-new',
                width750: true,
                localCss: true,
                banner: banner,
                countrys: req.ctx(RegServiceModel).getAreaData(), // 地区信息列表
                captchaShow: req.yoho.captchaShow,
                backUrl: 'javascript:history.go(-1)', // eslint-disable-line
                loginUrl: '/passport/login',
                registerUrl: '/passport/reg/index', // 注册的URL链接
                aliLoginUrl: '/passport/login/alipay', // 支付宝快捷登录的URL链接
                weiboLoginUrl: '/passport/login/sina', // 微博登录的URL链接
                qqLoginUrl: '/passport/login/qq', // 腾讯QQ登录的URL链接
                wechatLoginUrl: '/passport/login/wechat', // 微信登录的URL链接
                internationalUrl: '/passport/international', // 国际号登录的URL链接
                phoneRetriveUrl: '/passport/back/mobile', // 通过手机号找回密码的URL链接
                emailRetriveUrl: '/passport/back/email', // 通过邮箱找回密码的URL链接
                isWechat: req.yoho.isWechat,
                openPassword
            });
        })().catch(next);
    }

    /**
     *  step1 的表单提交验证
     */
    indexCheck(req, res, next) {
        let area = req.body.area = (req.body.area || '').trim();
        let mobile = req.body.mobile = (req.body.mobile || '').trim();
        let udid = req.cookies.udid;
        let captcha = req.body.captcha;
        let errorData = {
            code: 400,
            message: ''
        };

        // 简单数据校验
        if ([area, mobile].some(val => val === '')) {
            return res.json(Object.assign(errorData, { message: '请填写手机号' }));
        }

        let params = {
            mobile, area, type: 1, udid, captcha
        };

        if (req.body.geetest_challenge) {
            params.captcha = '';
            params.superCapture = config.superCapture;
        }

        co(function* () {
            let sendSmsResult = yield req.ctx(PhoneServiceModel).sendSMS(params);

            res.json(sendSmsResult);
        })().catch(next);
    }

    tokenBefore(req, res, next) {

        let step = _.get(req.session, 'smsLogin.step');
        let count = _.get(req.session, 'smsLogin.count');
        let interval = _.get(req.session, 'smsLogin.interval');


        if (!req.xhr || step !== 2) {
            return next();
        }

        let now = Date.now();

        // 重发次数用完了, 回冻结5min
        //      1. 过了冻结期, count 重设为 5次
        //      2. 没过冻结期, end
        // 没有用完, 判断是否请求太频繁
        let during = moment.duration(interval - now, 'ms').minutes();
        let message = `请${during}分钟后再试`;

        if (!count) {
            if (interval > now) {
                return res.json({
                    code: 400,
                    message: message,
                    during: Math.ceil((interval - now) / 1000)
                });
            } else {
                _.set(req.session, 'smsLogin.count', 5);
            }
        } else if (interval > now) {
            return res.json({
                code: 429,
                message: message
            });
        }

        next();
    }

    checkBefore(req, res, next) {
        let code = req.query.code = (req.query.code || '').trim();
        let step = _.get(req.session, 'smsLogin.step');

        if (!req.xhr && step !== 2) {
            return next();
        }

        if (!code) {
            return res.json({
                code: 404,
                message: CODE_REQUIRED
            });
        }

        next();
    }

    // AJAX 校验验证码 in step2
    check(req, res, next) {
        let code = req.query.code;
        let mobile = req.query.mobile;
        let area = req.query.area;
        let shopping_key = cookie.getShoppingKey(req); // eslint-disable-line
        let from = req.cookies.from || 'yohobuy';

        if (!mobile || !area) {
            res.json({
                code: 401,
                message: VERIFY_ERROR
            });
            return;
        }

        Promise.all([
            req.ctx(PhoneServiceModel).checkUserPhoneExist(mobile, area),
            req.ctx(PhoneServiceModel).verifySMS(mobile, area, code, 1)
        ]).then(result => {
            let r1 = result[0] || {};
            let r2 = result[1] || {};
            let redirect;

            // 验证码 校验异常
            if (r2.code !== 200) {
                res.json(r2);
                return;
            }

            // 检测 手机号 是否注册 异常
            if (r1.code !== 200) {
                res.json(r1);
                return;
            }

            // 校验失败
            if (r2.data.is_pass !== 'Y') {
                res.json({
                    code: 401,
                    message: '短信验证码错误'
                });

                return;
            }

            // 手机号码 没注册
            if (r1.data.is_register !== 'Y') {
                redirect = '/passport/reg/index';
                _.set(req.session, 'smsLogin.step', 3);

                res.json({
                    code: 200,
                    redirect,
                    newer: true,
                    registerCode: r1.data.code
                });

                return;
            }

            // 手机号码已注册 --> 直接登录
            req.ctx(PhoneServiceModel).autoSignin({
                profile: mobile,
                code: r2.data.code,
                area,
                shopping_key,
                from
            }).then(info => {
                if (info.code !== 200) {
                    return Promise.reject(info);
                }

                res.cookie('_LOGIN_TYPE', 5, {
                    domain: 'm.yohobuy.com'
                });
                return req.ctx(AuthHelperModel).syncUserSession(info.data.uid, req, res, info.data.session_key);
            }).then((authData) => {
                res.json({
                    code: 200,
                    message: LOGIN_SUCCSS,
                    redirect: _.get(authData, 'refer') || utils.refererLimit(req.cookies.refer)
                });

                delete req.session.smsLogin;
            }).catch(error => {
                res.json(error);
            });
        }).catch(next);
    }

    // AJAX 短信登录 设置密码 in step3
    password(req, res, next) {
        let step = _.get(req.session, 'smsLogin.step');

        if (step !== 3) {
            return next();
        }


        let data = {
            code: '400',
            message: BAD_PASSWORD
        };

        let mobile = _.get(req.session, 'smsLogin.mobile');
        let area = _.get(req.session, 'smsLogin.area');
        let password = (req.body.password || '').trim();
        let registerCode = req.body.registerCode || '';
        let clientIp = req.yoho.clientIp || '';
        let from = req.cookies.from || 'yohobuy';

        if (!password) {
            data.message = PASSWORD_REQUIRED;
            return res.json(data);
        }

        if (password.length < 6 || password.length > 20) {
            data.message = PASSWORD_LENGTH_ERROR;
            return res.json(data);
        }

        if (!/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/.test(password)) {
            return res.json(data);
        }

        // 购物车key
        let shoppingKey = cookie.getShoppingKey(req);


        // 验证注册的标识码是否有效
        let resultCopy = null;

        req.ctx(RegServiceModel).regMobileAes(area, mobile, password,
            shoppingKey, registerCode, '0', clientIp, from).then(result => {
            if (!result.code || result.code !== 200) {
                return res.send(result);
            }
            if (!result.data || !result.data.uid) {
                return res.send(result);
            }
            resultCopy = result;

            return req.ctx(AuthHelperModel).syncUserSession(result.data.uid, req, res, result.data.session_key);
        }).then((authData) => {
            if (!resultCopy) {
                return;
            }
            res.json({
                code: 200,
                message: LOGIN_SUCCSS,
                redirect: _.get(authData, 'refer') || utils.refererLimit(req.cookies.refer)
            });
            delete req.session.smsLogin;
        }).catch(next);
    }
}

module.exports = new SmsLogin();