Authored by 陈轩

Merge branch 'master' of http://git.yoho.cn/fe/yohobuywap-node into feature/reds_shop

... ... @@ -24,7 +24,10 @@ const favicon = require('serve-favicon');
const uuid = require('uuid');
const pkg = require('./package.json');
const session = require('client-sessions');
const cookieSession = require('client-sessions');
const memcachedSession = require('yoho-express-session');
const memcached = require('connect-memcached');
const MemcachedStore = memcached(memcachedSession);
const _ = require('lodash');
const app = express();
... ... @@ -73,25 +76,63 @@ app.use(bodyParser.urlencoded({
app.use(cookieParser());
app.use(compression());
app.use(session({
requestKey: 'session',
app.use(memcachedSession({
proxy: true,
resave: false,
saveUninitialized: true,
unset: 'destroy',
secret: '82dd7e724f2c6870472c89dfa43cf48d',
name: 'yohobuy_session',
cookie: {
domain: 'yohobuy.com',
httpOnly: false
},
store: new MemcachedStore({
hosts: config.memcache.session,
prefix: 'yohobuy_session:',
reconnect: 5000,
timeout: 1000,
retries: 0
})
}));
app.use(cookieSession({
requestKey: 'session2',
cookieName: 'yohobuy_session_cookie',
secret: '82dd7e724f2c6870472c89dfa43cf48d',
domain: config.cookieDomain
}));
app.use((req, res, next) => {
req.user = {}; // 全局的用户数据
req.yoho = {}; // req和res绑定yoho对象,用于传递全局数据, 如req.yoho.channel等
req.app.locals.wap = app.locals.wap; // zookeper对象赋值
if (!req.session) {
req.session = {};
if (req.session) {
let sessionKeys = Object.keys(req.session || {});
let backSessionKeys = Object.keys(req.session2.sessionBack || {});
if (backSessionKeys.length > sessionKeys.length) {
let differences = _.difference(backSessionKeys, sessionKeys);
_.forEach(differences, d => {
req.session[d] = req.session2.sessionBack[d];
});
}
req.session2.sessionBack = req.session;
} else {
req.session = new memcachedSession.Session(req);
req.session = _.assign(req.session, req.session2.sessionBack);
}
if (!req.session.id) {
req.session.id = uuid.v4();
if (!req.sessionID) {
req.sessionID = uuid.v4();
}
req.sessionID = req.session.id;
next();
});
app.use((req, res, next) => {
req.user = {}; // 全局的用户数据
req.yoho = {}; // req和res绑定yoho对象,用于传递全局数据, 如req.yoho.channel等
req.app.locals.wap = app.locals.wap; // zookeper对象赋值
next();
});
... ...
const qs = require('querystring');
exports.index = (req, res) => {
res.redirect('http://ad.yoho.cn/html5/2016/12/redbag/?' + qs.stringify(req.query));
};
... ...
... ... @@ -29,6 +29,8 @@ const share = require(`${cRoot}/share`);
const aliCloud = require(`${cRoot}/ali-cloud`);
const studentMarket = require(`${cRoot}/student-market`);
const redbag = require(`${cRoot}/redbag`);
// routers
router.get('/demo', demo.index);
... ... @@ -128,4 +130,6 @@ router.get('/wechat/1111', wechat.feature);
router.get('/student-market', student.getUser, studentMarket.index);
router.get('/redbag/2017', redbag.index);
module.exports = router;
... ...
<div class='yoho-page plusstar-page'>
{{#if result.channel}}
<div class="tab-nav {{#if isApp}}isApp{{/if}}">
<ul>
{{#each result.channel}}
<li class='{{#if focus}} focus {{/if}}' data-code='{{code}}'>
<span>{{mame}}</span>
</li>
{{/each}}
</ul>
</div>
<div class="tab-nav {{#if isApp}}isapp{{/if}}">
<ul>
{{#each result.channel}}
<li class='{{#if focus}} focus {{/if}}' data-code='{{code}}'>
<span>{{mame}}</span>
</li>
{{/each}}
</ul>
</div>
{{#if isApp}}<div class='isapp-margin-bottom'></div>{{/if}}
{{/if}}
<!--/tab-nav-->
... ...
... ... @@ -8,6 +8,7 @@
const _ = require('lodash');
const helpers = global.yoho.helpers;
const config = global.yoho.config;
const service = require('../models/back-service');
const captchaService = require('../models/captcha-service');
... ... @@ -139,9 +140,16 @@ const setNewPasswordByEmailAPI = (req, res) => {
* 找回密码页面-通过手机号
*/
const indexMobilePage = (req, res, next) => {
_.set(req.session, 'backupCaptch.verifyResult', false);
if (req.session.captchaValidCount == null) { // eslint-disable-line
req.session.captchaValidCount = 5;
}
service.getAreaDataAsync()
.then(result => {
res.render('back/mobile', Object.assign({
width750: true,
module: 'passport',
page: 'back-mobile',
title: '找回密码-通过手机号'
... ... @@ -189,6 +197,7 @@ const sendCodeToMobileAPI = (req, res, next) => {
let verifyCode = req.body.verifyCode || '';
let phoneNum = req.body.phoneNum || '';
let areaCode = req.body.areaCode || '86';
let testCode = req.body.yohobuy;
/* 如果设置了冻结时间,验证 */
if (_.has(req.session, 'backupCaptch.timeout')) {
... ... @@ -203,32 +212,35 @@ const sendCodeToMobileAPI = (req, res, next) => {
}
}
/* 如果设置了验证次数,验证是否合法,验证次数减 1;没有,设置验证次数 */
if (_.has(req.session, 'backupCaptch.useTime')) {
if (parseInt(req.session.backupCaptch.useTime, 10) <= 0) {
let errorCount = _.get(req.session, 'captchaValidCount', 4); // 初始1次 + 后续4次, 同一个验证码 共5次
/* 如果超过验证次数,冻结 5 分钟,更新验证次数 */
req.session.backupCaptch.timeout = Date.now() + 5 * 60 * 1000;
req.session.backupCaptch.useTime = 5;
return res.json({
code: 401,
message: '请5分钟后尝试!'
});
}
req.session.backupCaptch.useTime = req.session.backupCaptch.useTime - 1;
if (!errorCount) {
_.set(req.session, 'captchaValidCount', 4);
} else {
req.session.backupCaptch.useTime = 5;
--req.session.captchaValidCount;
}
if (verifyCode) {
if (verifyCode.toString() === _.get(req, 'session.backupCaptch.code', '').toString()) {
let captcha = _.get(req, 'session.captcha');
if ((captcha && verifyCode.toString() === captcha) || (testCode === config.testCode)) {
req.session.backupCaptch.verifyResult = true;
} else {
return res.json({
let jsonData = {
code: 400,
message: '验证码输入错误'
});
message: '请将图片旋转到正确位置'
};
if (req.session.captchaValidCount === 0) {
req.session.captcha = null; // 验证码 用过就扔
jsonData.changeCaptcha = true;
}
return res.json(jsonData);
}
_.set(req.session, 'captcha', null);
} else if (!req.session.backupCaptch.verifyResult) {
return res.json({
... ... @@ -247,6 +259,8 @@ const sendCodeToMobileAPI = (req, res, next) => {
return res.json(ERR);
}
service.sendCodeToMobileAsync(phoneNum, areaCode)
.then(result => {
if (_.isEmpty(result) || result.code !== 200) {
... ...
'use strict';
let imgCheckAPI = require('../../serverAPI/imgcheck');
let imgCheckAPI = require('../models/imgcheck');
exports.get = (req, res, next) => {
let data = {
... ...
... ... @@ -5,7 +5,7 @@
* @date: 2016/05/09
*/
'use strict';
const _ = require('lodash');
const passport = require('passport');
// const md5 = require('md5');
... ... @@ -78,8 +78,13 @@ const common = {
}
};
const local = {
loginPage: (req, res) => {
if (req.session.captchaValidCount == null) { // eslint-disable-line
req.session.captchaValidCount = 5;
}
// 先清除cookie
// res.clearCookie('LE' + md5('_LOGIN_EXPIRE'), {
// domain: 'yohobuy.com'
... ... @@ -97,7 +102,9 @@ const local = {
});
res.render('login', {
width750: true,
loginIndex: true, // 模板中使用JS的标识
captchaShow: _.get(req.session, 'login.errorCount') <= 0,
// 返回的URL链接
backUrl: 'javascript:history.go(-1)', // eslint-disable-line
... ... @@ -135,9 +142,12 @@ const local = {
});
res.render('international', {
width750: true,
// 返回的URL链接
backUrl: 'javascript:history.go(-1)', // eslint-disable-line
loginInternational: true, // 模板中使用JS的标识
captchaShow: _.get(req.session, 'login.errorCount') <= 0,
isPassportPage: true, // 模板中模块标识
headerText: '登录',
areaCode: '+86', // 默认区号
... ... @@ -148,13 +158,64 @@ const local = {
});
},
login: (req, res, next) => {
let count = _.get(req.session, 'login.errorCount');
if (count == null) { // eslint-disable-line
_.set(req.session, 'login.errorCount', 3);
}
if (count <= 0) {
let captchaInput = req.body.captcha;
let captchaCode = _.get(req.session, 'captcha');
let testCode = req.body.yohobuy;
let errorCount = _.get(req.session, 'captchaValidCount'); // 初始1次 + 后续4次, 同一个验证码 共5次
let jsonData = {
code: 400,
message: '请将图片旋转到正确方向',
captchaShow: true
};
--req.session.captchaValidCount;
if (!errorCount) {
_.set(req.session, 'captchaValidCount', 5);
// delete req.session.captcha; // 验证码 用过就扔
req.session.captcha = null;
jsonData.changeCaptcha = true;
}
if (
!(
(captchaInput && captchaCode && captchaInput === captchaCode) ||
(testCode === config.testCode)
)
) {
res.json(jsonData);
return;
}
}
passport.authenticate('local', (err, user) => {
let loginSession = req.session.login;
if (err) {
res.json({
let obj = {
code: 400,
message: err,
data: ''
});
};
--loginSession.errorCount;
if (loginSession.errorCount <= 0) {
obj.captchaShow = true;
}
res.json(obj);
} else {
let refer = req.cookies.refer;
... ... @@ -182,7 +243,12 @@ const local = {
})(req, res, next);
},
logout: (req, res) => {
req.session.reset();
if (req.session && req.session.destroy) {
req.session.destroy();
}
if (req.session2 && req.session2.reset) {
req.session2.reset();
}
res.clearCookie('_UID', {
domain: 'yohobuy.com'
... ...
... ... @@ -8,6 +8,7 @@
'use strict';
const _ = require('lodash');
const url = require('url');
const utils = require(global.utils);
const helpers = global.yoho.helpers;
const sign = global.yoho.sign;
... ... @@ -26,6 +27,7 @@ const captchaService = require('../models/captcha-service');
}
*/
/**
* 步骤校验
* step: 预期步骤
... ... @@ -58,6 +60,17 @@ let index = (req, res) => {
return res.redirect(req.get('refer') || '/');
}
// 判断是否 来自 个人中心
if (!_.get(req.session, 'phoneReg.isFromMy')) {
let referer = req.get('Referer') || '';
let urlObj = url.parse(referer, true, true);
referer = _.get(urlObj, 'query.refer', '');
urlObj = url.parse(referer, true, true);
urlObj.path === '/home' && _.set(req.session, 'phoneReg.isFromMy', '1');
}
// 设置注册有效时间30分钟, 防机器刷
// req.session.REG_EXPIRE = Date.now() + 1800000;
let refer = req.query.refer;
... ... @@ -76,6 +89,7 @@ let index = (req, res) => {
}
res.render('reg/index', {
width750: true,
module: 'passport',
page: 'reg',
title: '注册',
... ... @@ -99,7 +113,7 @@ let verifyMobile = (req, res, next) => {
let mobile = +req.body.phoneNum;
let area = +(req.body.areaCode || 86);
let captcha = (req.body.captcha || '').trim();
let diffCaptcha = _.get(req.session, 'phoneReg.captcha');
let diffCaptcha = _.get(req.session, 'captcha');
// error case: 没有验证码
if (!diffCaptcha) {
... ... @@ -110,7 +124,7 @@ let verifyMobile = (req, res, next) => {
});
}
delete req.session.phoneReg.captcha; // captcha 一次性
delete req.session.captcha; // captcha 一次性
// error case: 验证码不匹配
if (captcha !== diffCaptcha) {
... ... @@ -189,7 +203,6 @@ let codeAction = (req, res, next) => {
res.render('reg/code', {
page: 'code',
title: '注册-验证码',
backUrl: '/?go=1', // eslint-disable-line
headerText: '注册', // 头部信息
isPassportPage: true, // 模板中模块标识
areaCode: area, // 默认的区号
... ... @@ -365,6 +378,7 @@ let setPassword = (req, res, next) => {
let password = req.body.password;
let token = req.body.token;
let smsCode = +req.body.smsCode;
let isFromMy = _.get(req.session, 'phoneReg.isFromMy', '0');
// 判断参数是否合法
if (!smsCode || !_.isString(token) || !_.isNumber(mobile) || !_.isNumber(area) || !password) {
... ... @@ -387,29 +401,40 @@ let setPassword = (req, res, next) => {
let shoppingKey = cookie.getShoppingKey(req);
// 验证注册的标识码是否有效
RegService.regMobileAes(area, mobile, password, shoppingKey, smsCode).then((result) => {
let resultCopy = null;
RegService.regMobileAes(area, mobile, password, shoppingKey, smsCode, isFromMy).then((result) => {
if (!result.code || result.code !== 200) {
return Promise.reject(result);
}
if (!result.data || !result.data.uid) {
return Promise.reject(result);
}
resultCopy = result;
return AuthHelper.syncUserSession(result.data.uid, req, res);
}).then(() => {
// 返回跳转到来源页面
let refer = req.cookies.refer;
if (refer) {
refer = decodeURI(req.cookies.refer);
// isFromMy to 新人会场
if (resultCopy.data.newUserPage) {
refer = resultCopy.data.msgDelivery; // 来自个人中心,跳新人会场
} else {
refer = '/home';
}
if (refer) {
refer = decodeURI(req.cookies.refer);
} else {
refer = '/home';
}
if (/sign|login/.test(refer)) {
refer = '/home';
}
if (/sign|login/.test(refer)) {
refer = '/home';
refer = utils.refererLimit(refer);
}
refer = utils.refererLimit(refer);
delete req.session.phoneNum;
... ...
/* eslint no-unused-vars: ["error", { "args": "none" }] */
'use strict';
const _ = require('lodash');
const moment = require('moment');
const helpers = global.yoho.helpers;
const cookie = global.yoho.cookie;
const config = global.yoho.config;
const EventEmitter = require('events');
const utils = require(global.utils);
const RegService = require('../models/reg-service');
const PhoneService = require('../models/phone-service');
const AuthHelper = require('../models/auth-helper');
const captchaService = require('../models/captcha-service');
// constrant
const CODE_REQUIRED = '请输入校验码';
const PASSWORD_REQUIRED = '请输入密码';
const PASSWORD_LENGTH_ERROR = '密码6-20位,请重新输入';
const BAD_PASSWORD = '密码格式不正确';
const TOO_MANY = '请求太频繁';
const LOGIN_SUCCSS = '登录成功';
const VERIFY_ERROR = '校验失败';
... ... @@ -38,12 +39,13 @@ exports.beforeIn = (req, res, next) => {
const _step1 = (req, res, next) => {
_.set(req.session, 'smsLogin.step', 1);
if (req.session.smsLogin.count == null) { // eslint-disable-line
req.session.smsLogin.count = 5;
if (req.session.captchaValidCount == null) { // eslint-disable-line
req.session.captchaValidCount = 5;
}
let template = 'sms/login';
let viewData = {
width750: true,
module: 'passport',
page: 'sms-login',
title: '手机短信登录',
... ... @@ -55,6 +57,8 @@ const _step1 = (req, res, next) => {
};
res.render(template, viewData);
};
// 短信登录 第二步: 输入 校验码
... ... @@ -118,44 +122,90 @@ exports.loginPage = (req, res, next) => {
}
};
exports.tokenBefore = (req, res, next) => {
let area = req.query.area = (req.query.area || '').trim();
let mobile = req.query.mobile = (req.query.mobile || '').trim();
let step = _.get(req.session, 'smsLogin.step');
let count = _.get(req.session, 'smsLogin.count');
let interval = _.get(req.session, 'smsLogin.interval');
let captcha1 = _.get(req.session, 'smsLogin.captcha');
let captcha2 = (req.query.captcha || '').trim();
/**
* step1 的表单提交验证
*/
exports.indexCheck = (req, res, next) => {
_.set(req.session, 'smsLogin.step', 1);
if (!req.xhr) {
return next(404);
let area = req.body.area = (req.body.area || '').trim();
let mobile = req.body.mobile = (req.body.mobile || '').trim();
let captcode = (req.body.captcode || '').trim();
let captcodeValid = _.get(req.session, 'captcha');
let testCode = req.body.yohobuy;
let em = new EventEmitter();
let errorCount = _.get(req.session, 'captchaValidCount', 4 - 1); // 初始1次 + 后续4次, 同一个验证码 共5次
let errorData = {
code: 400,
message: ''
};
if (!errorCount) {
_.set(req.session, 'captchaValidCount', 3);
} else {
--req.session.captchaValidCount;
}
if ([area, mobile].some(val => val === '')) {
return res.json({
code: 401,
message: '手机号 必填'
// 校验 成功
em.on('resolve', () => {
// 1. 将信息放入 session
_.set(req.session, 'smsLogin.area', area);
_.set(req.session, 'smsLogin.mobile', mobile);
_.set(req.session, 'smsLogin.step', 2);
req.session.captcha = null;
PhoneService.sendSMS(mobile, area, 1);
res.json({
code: 200,
redirect: '/passport/sms_login?step=2'
});
});
// 校验 失败
em.on('reject', error => {
_.set(req.session, 'smsLogin.step', 1);
res.json(error);
});
if (req.session.captchaValidCount === 0) {
req.session.captcha = null; // 验证码 用过就扔
errorData.changeCaptcha = true;
}
delete req.session.smsLogin.captcha; // 图形验证码 一次性
// step1 要 校验图形验证码
if (step === 1) {
if (!captcha2) {
return res.json({
code: 400,
message: '请填写验证码'
});
}
// 验证
if ([area, mobile].some(val => val === '')) {
return em.emit('reject', Object.assign(errorData, { message: '请填写手机号'}));
} else if (!captcode) {
return em.emit('reject', Object.assign(errorData, {message: '请填写验证码'}));
} else if (
!(
(captcodeValid && captcode === captcodeValid) || (testCode === config.testCode)
)
) {
return em.emit('reject', Object.assign(errorData, {message: '请将图片旋转到正确位置'}));
}
if (captcha1 !== captcha2) {
return res.json({
code: 400,
message: VERIFY_ERROR
});
}
// congratulation~~
em.emit('resolve');
};
exports.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(404);
}
let now = Date.now();
... ... @@ -164,11 +214,14 @@ exports.tokenBefore = (req, res, next) => {
// 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: TOO_MANY,
message: message,
during: Math.ceil((interval - now) / 1000)
});
} else {
... ... @@ -177,17 +230,19 @@ exports.tokenBefore = (req, res, next) => {
} else if (interval > now) {
return res.json({
code: 429,
message: TOO_MANY
message: message
});
}
next();
};
// AJAX 获取验证码
exports.token = (req, res, next) => {
let area = req.query.area;
let mobile = req.query.mobile;
let area = _.get(req.session, 'smsLogin.area');
let mobile = _.get(req.session, 'smsLogin.mobile');
PhoneService.sendSMS(mobile, area, 1).then(result => {
if (result.code === 200) {
... ... @@ -376,6 +431,7 @@ exports.password = (req, res, next) => {
/**
* 生成 校验码
*/
/*
exports.genCaptcha = (req, res) => {
let captcha = captchaService.generateCaptcha(90, 52, 4);
... ... @@ -386,3 +442,4 @@ exports.genCaptcha = (req, res) => {
.status(200)
.send(captcha.image);
};
*/
... ...
This diff could not be displayed because it is too large.
'use strict';
const _ = require('lodash');
const aes = require('./aes-pwd');
const sign = global.yoho.sign;
const api = global.yoho.API;
... ... @@ -87,10 +88,17 @@ class Auth {
domain: 'yohobuy.com',
expires: new Date(Date.now() + 2592000000) // 有效期一年
});
req.session.AVATAR = data.head_ico;
_.set(req.session, 'USER.AVATAR', data.head_ico);
_.set(req.session, 'USER.NAME', data.profile_name);
}
req.session.TOKEN = publicToken;
req.session.LOGIN_UID = uid;
_.set(req.session, 'USER.ENCRYPTION_UID', encryptionUid);
res.cookie('_TOKEN', publicToken, {
httpOnly: true,
domain: 'yohobuy.com',
... ...
'use strict';
const _ = require('lodash');
let captchaData = require('../data/captcha.json');
let api = global.yoho.API;
// let api = global.yoho.API;
/**
* 获取图形旋转验证码
* @return Promise
... ... @@ -24,9 +25,16 @@ let api = global.yoho.API;
*}
*/
exports.gen = () => {
let params = {
method: 'web.register.getVerifiedGraphicCode'
};
// let params = {
// method: 'web.register.getVerifiedGraphicCode'
// };
// return api.get('', params);
let random = _.random(0, captchaData.length);
return api.get('', params);
return Promise.resolve({
code: 200,
data: captchaData[random]
});
};
... ...
... ... @@ -96,13 +96,16 @@ const RegService = {
return api.post('', params);
},
regMobileAes(area, mobile, password, shoppingKey, smsCode) {
regMobileAes(area, mobile, password, shoppingKey, smsCode, isFromMy) {
isFromMy = isFromMy || '0';
let params = {
method: 'app.passport.registerAES',
area: area,
profile: mobile,
password: aes.aesPwd(password),
verifyCode: smsCode
verifyCode: smsCode,
isFromMy
};
if (shoppingKey) {
... ...
... ... @@ -41,6 +41,7 @@ router.post('/passport/login/auth', login.local.login);
// SMS 短信
router.use('/passport/sms_login', login.common.beforeLogin, smsLogin.beforeIn);
router.get('/passport/sms_login', smsLogin.loginPage);
router.post('/passport/sms_login/step1_check', smsLogin.indexCheck);
router.get('/passport/sms_login/token.json',
smsLogin.tokenBefore,
smsLogin.token); // only ajax;
... ... @@ -48,7 +49,6 @@ router.get('/passport/sms_login/check.json',
smsLogin.checkBefore,
smsLogin.check); // only ajax
router.post('/passport/sms_login/password.json', smsLogin.password);
router.get('/passport/sms_login/captcha.png', smsLogin.genCaptcha);
// 微信登录
router.get('/passport/login/wechat', login.common.beforeLogin, login.wechat.login);
... ... @@ -126,4 +126,9 @@ router.get('/passport/newpower', agreement.newpower);// 新力传媒
router.get('/passport/yohobuy', agreement.aboutYoho);// 关于有货
router.get('/passport/agreement', agreement.agreement);// 服务条款
// 验证码
let captcha = require(`${cRoot}/captcha`);
router.get('/passport/captcha/get', captcha.get);
module.exports = router;
... ...
... ... @@ -6,12 +6,11 @@
<span id="area-code" class="area-code">{{areaCode}}</span>
<input id="phone-num" class="input phone-num" type="text" placeholder="手机号">
</div>
<div class="passport-captcha row">
<div class="passport-captcha-img"><img id="verify-code-img" src="{{verifySrc}}" alt="verify code"></div>
<div class="passport-captcha-input">
<input id="verify-code" type="text" placeholder="验证码">
</div>
{{!--图片验证 start--}}
<div id="js-img-check">
<input type="hidden" name="captsrc" value="{{captsrc}}">
</div>
{{!--图片验证 end--}}
<span id="btn-next" class="btn btn-next disable row">下一步</span>
</div>
</div>
... ...
... ... @@ -9,6 +9,8 @@
<div class="input-container row has-eye">
<input id="pwd" class="pwd input" type="password" placeholder="密码">
</div>
<div id="js-img-check" {{#captchaShow }}data-init{{/captchaShow}}></div>
<span id="btn-login" class="btn btn-login disble row">登录</span>
</div>
</div>
\ No newline at end of file
... ...
... ... @@ -8,9 +8,10 @@
<div class="input-container row has-eye">
<input id="pwd" class="pwd input" type="password" placeholder="密码">
</div>
<div id="js-img-check" {{#captchaShow }}data-init{{/captchaShow}}></div>
<span id="btn-login" class="btn btn-login disable">登录</span>
<p class="op-container">
{{!--<a class="sms-login" href={{smsLoginUrl}}>手机号码快捷登录</a>--}}
<a class="sms-login" href={{smsLoginUrl}}>手机号码快捷登录</a>
<span id="forget-pwd" class="forget-pwd">忘记密码</span>
</p>
<div class="third-party-login">
... ... @@ -43,4 +44,4 @@
</li>
</ul>
</div>
</div>
\ No newline at end of file
</div>
... ...
... ... @@ -6,16 +6,13 @@
<span id="area-code" class="area-code">{{areaCode}}</span>
<input id="phone-num" class="input phone-num" type="text" placeholder="手机号">
</div>
<!-- 验证码: start-->
<div class="passport-captcha row">
<div class="passport-captcha-img">
<img class="passport-captcha-png" src="{{captchaUrl}}">
</div>
<div class="passport-captcha-input">
<input id="js-captcha" type="text" placeholder="验证码">
</div>
{{!--图片验证 start--}}
<div id="js-img-check">
<input type="hidden" name="captsrc" value="{{captsrc}}">
</div>
<!-- 验证码: end-->
{{!--图片验证 end--}}
<span id="btn-next" class="btn btn-next disable row">下一步</span>
<p class="register-tip">Yoho!Family账号可登录Yoho!Buy有货、Yoho!Now、mars及SHOW</p>
</div>
... ...
... ... @@ -7,12 +7,11 @@
<input id="phone-num" class="input phone-num" type="text" placeholder="手机号">
<button class="clear-input" type="button"></button>
</div>
<div class="passport-captcha row">
<div class="passport-captcha-img"><img src="{{captchaUrl}}" alt=""></div>
<div class="passport-captcha-input">
<input id="js-captcha" type="text" placeholder="验证码">
</div>
{{!--图片验证 start--}}
<div id="js-img-check">
<input type="hidden" name="captsrc" value="{{captsrc}}">
</div>
{{!--图片验证 end--}}
<button id="btn-next" class="btn btn-next disable row" disabled>获取短信验证码</button>
</div>
</div>
\ No newline at end of file
... ...
... ... @@ -15,10 +15,12 @@ exports.handleOrderList = (data, w, h) => {
}
function replaceWH(img) {
return img.replace(/(\{width\}|\{height\})/g, function($0) {
return img.replace(/(^https?:|\{width\}|\{height\})/g, function($0) {
const dict = {
'{width}': w,
'{height}': h,
'http:': '',
'https:': ''
};
return dict[$0];
... ...
... ... @@ -15,6 +15,7 @@ module.exports = {
port: 6001,
siteUrl: '//m.yohobuy.com',
assetUrl: '//127.0.0.1:5001',
testCode: 'yoho4946abcdef#$%&!@',
domains: {
// api: 'http://api-test3.yohops.com:9999/',
// service: 'http://service-test3.yohops.com:9999/',
... ...
... ... @@ -6,7 +6,7 @@ const headerModel = require('../models/header');
const logger = global.yoho.logger;
const forceNoCache = (res) => {
if (res) {
if (res && !res.finished) {
res.set({
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
... ... @@ -47,6 +47,8 @@ exports.notFound = () => {
*/
exports.serverError = () => {
return (err, req, res, next) => {
console.log(err);
forceNoCache(res);
logger.error(`error at path: ${req.url}`);
... ...
{
"name": "m-yohobuy-node",
"version": "5.3.5",
"version": "5.3.6",
"private": true,
"description": "A New Yohobuy Project With Express",
"repository": {
... ... @@ -24,9 +24,9 @@
"cheerio": "^0.22.0",
"client-sessions": "^0.7.0",
"compression": "^1.6.2",
"connect-memcached": "^0.2.0",
"connect-multiparty": "^2.0.0",
"cookie-parser": "^1.4.3",
"cookie-session": "^1.2.0",
"express": "^4.14.0",
"feed": "^0.3.0",
"lodash": "^4.17.2",
... ... @@ -43,6 +43,7 @@
"request-promise": "^3.0.0",
"serve-favicon": "^2.3.2",
"uuid": "^2.0.3",
"yoho-express-session": "^2.0.0",
"yoho-node-lib": "0.2.2",
"yoho-zookeeper": "^1.0.4"
},
... ...
... ... @@ -12,4 +12,6 @@
<li class="img-check-pic" data-val="0" style="background-image:url('{{imgSrc}}');"></li>
</ul>
</div>
<input id="yohobuy" type="text" style="display:none;">
</div>
\ No newline at end of file
... ...
... ... @@ -19,6 +19,18 @@ var tip = require('../../plugin/tip');
var trim = $.trim;
var showErrTip = tip.show;
// 图片验证码
let ImgCheck = require('plugin/img-check');
let imgCheck = new ImgCheck('#js-img-check', {
useREM: {
rootFontSize: 40,
picWidth: 150
}
});
imgCheck.init();
api.selectCssHack($('#country-select'));
api.bindClearEvt();
... ... @@ -45,12 +57,13 @@ $verifyCodeImg.on('touchstart', function() {
$btnNext.on('touchstart', function() {
var pn = trim($phoneNum.val()),
area = trim($countrySelect.val()),
verify = trim($verifyCode.val());
verify = trim(imgCheck.getResults());
if ($btnNext.hasClass('disable')) {
return;
}
if (verify && area && pn && api.phoneRegx[area].test(pn)) {
$.ajax({
url: '/passport/back/sendcode',
... ... @@ -58,23 +71,25 @@ $btnNext.on('touchstart', function() {
data: {
areaCode: area.replace('+', ''),
phoneNum: pn,
verifyCode: verify
verifyCode: verify,
yohobuy: $('#yohobuy').val()
},
success: function(data) {
if (data.code === 200) {
location.href = data.data;
return;
} else if (data.code === 409) {
showErrTip(data.message);
location.href = data.refer;
} else {
showErrTip(data.message);
}
data.changeCaptcha && imgCheck.refresh();
}
});
} else if (!area) {
showErrTip('出错了,请重新刷新页面');
} else if (!verify) {
showErrTip('请输入验证码');
} else {
showErrTip('手机号格式不正确,请重新输入');
}
... ...
... ... @@ -4,6 +4,7 @@
* @date: 2015/10/8
*/
var $ = require('yoho-jquery');
var ImgCheck = require('plugin/img-check');
var $phoneNum = $('#phone-num'),
$countrySelect = $('#country-select'),
... ... @@ -11,6 +12,8 @@ var $phoneNum = $('#phone-num'),
$pwd = $('#pwd'),
$loginBtn = $('#btn-login'),
$captcha = $('#js-img-check'),
pnPass = false,
pwdPass = false;
... ... @@ -20,13 +23,28 @@ var tip = require('../../plugin/tip');
var trim = $.trim;
var showErrTip = tip.show;
var imgCheck = new ImgCheck($captcha, {
useREM: {
rootFontSize: 40,
picWidth: 150
}
});
if ($captcha.data('init') != null) { //eslint-disable-line
imgCheck.init();
}
// 登录按钮状态切换
function switchLoginBtnStatus() {
if (pnPass && pwdPass) {
$loginBtn.removeClass('disable');
} else {
$loginBtn.addClass('disable');
}
var bool = !(pnPass && pwdPass);
$loginBtn.toggleClass('disable', bool);
}
function resetForm() {
$pwd.val('').focus();
$loginBtn.text('登录').addClass('disable');
}
// Android-UC下显示select的direction:rtl无效的临时解决办法
... ... @@ -67,23 +85,39 @@ $countrySelect.change(function() {
$loginBtn.on('touchstart', function() {
var pn = trim($phoneNum.val()),
areaCode = $countrySelect.val(),
pwd = trim($pwd.val());
pwd = trim($pwd.val()),
captcha = null;
if ($loginBtn.hasClass('disable')) {
return;
}
if (imgCheck.atWorking) {
captcha = imgCheck.getResults();
if (captcha === '0000') {
return tip.show(' 请将图片旋转到正确方向');
}
}
$loginBtn.text('正在登录...').addClass('disable');
if ((api.phoneRegx[areaCode].test(pn) || areaCode !== '+86') && api.pwdValidate(pwd)) {
let data = {
areaCode: areaCode.replace('+', ''),
account: pn,
password: pwd
};
if (imgCheck.atWorking) {
$.extend(data, {captcha});
}
$.ajax({
type: 'POST',
url: '/passport/login/auth',
data: {
areaCode: areaCode.replace('+', ''),
account: pn,
password: pwd
},
data,
success: function(data) {
var res,
time;
... ... @@ -110,21 +144,27 @@ $loginBtn.on('touchstart', function() {
location.href = res.href;
}, 3000);
$loginBtn.text('登录成功').off();
showErrTip('登录成功');
} else {
if (data.captchaShow) {
imgCheck.atWorking ? imgCheck.refresh() : imgCheck.init();
}
showErrTip(data.message);
resetForm();
}
},
error: function() {
showErrTip('网络断开连接啦~');
},
complete: function() {
$loginBtn.text('登录').removeClass('disable');
$loginBtn.text('登录');
imgCheck.atWorking && imgCheck.refresh();
}
});
} else {
showErrTip('账号或密码有错误,请重新输入');
$loginBtn.text('登录').removeClass('disable');
$loginBtn.text('登录').addClass('disable');
}
});
... ...
... ... @@ -4,6 +4,7 @@
* @date: 2015/9/30
*/
var $ = require('yoho-jquery');
var ImgCheck = require('plugin/img-check');
var $account = $('#account'),
$pwd = $('#pwd'),
... ... @@ -12,6 +13,8 @@ var $account = $('#account'),
$mask = $('#retrive-pwd-mask'),
$ways = $('#retrive-pwd-ways'),
$captcha = $('#js-img-check'),
accPass = false,
pwdPass = false;
... ... @@ -21,13 +24,30 @@ var tip = require('../../plugin/tip');
var trim = $.trim;
var showErrTip = tip.show;
var imgCheck = new ImgCheck($captcha, {
useREM: {
rootFontSize: 40,
picWidth: 150
}
});
if ($captcha.data('init') != null) { //eslint-disable-line
imgCheck.init();
}
// 登录按钮状态切换
function switchLoginBtnStatus() {
if (accPass && pwdPass) {
$loginBtn.removeClass('disable');
} else {
$loginBtn.addClass('disable');
}
var bool = true;
bool = !(accPass && pwdPass);
$loginBtn.toggleClass('disable', bool);
}
function resetForm() {
$pwd.val('').focus();
$loginBtn.text('登录').addClass('disable');
}
// 显示找回密码面板
... ... @@ -70,23 +90,39 @@ $pwd.bind('input', function() {
// Login
$loginBtn.on('touchstart', function() {
var acc = trim($account.val()),
pwd = trim($pwd.val());
pwd = trim($pwd.val()),
captcha = null;
if ($loginBtn.hasClass('disable')) {
return;
}
// if (imgCheck.atWorking) {
captcha = imgCheck.getResults();
// if (captcha === '0000') {
// return tip.show(' 请将图片旋转到正确方向');
// }
// }
$loginBtn.text('正在登录...').addClass('disable');
// 验证账号(数字或者邮箱)和密码合理性
if ((/^[0-9]+$/.test(acc) || api.emailRegx.test(acc)) && api.pwdValidate(pwd)) {
let data = {
account: acc,
password: pwd,
yohobuy: $('#yohobuy').val()
};
if (imgCheck.atWorking) {
$.extend(data, {captcha});
}
$.ajax({
type: 'POST',
url: '/passport/login/auth',
data: {
account: acc,
password: pwd
},
data,
success: function(data) {
var res;
... ... @@ -95,12 +131,22 @@ $loginBtn.on('touchstart', function() {
showErrTip('登录成功');
location.href = res.href;
$loginBtn.text('登录成功').off();
} else {
if (data.captchaShow) {
imgCheck.atWorking ? (data.changeCaptcha && imgCheck.refresh()) : imgCheck.init();
}
showErrTip(data.message);
resetForm();
}
return data;
},
error: function() {
showErrTip('网络断开连接啦~');
imgCheck.atWorking && imgCheck.refresh();
},
complete: function() {
$loginBtn.text('登录').removeClass('disable');
... ...
... ... @@ -35,7 +35,7 @@ $pwd.bind('input', function() {
});
$btnSure.toggleClass('disable', !bool);
})
});
qs = window.queryString;
... ... @@ -137,4 +137,4 @@ $('.agreement-detail').on('click', function() {
// 如果有值, 立刻校验
if ($pwd.val()) {
$pwd.triggerHandler('input');
}
\ No newline at end of file
}
... ...
... ... @@ -24,17 +24,29 @@ api.selectCssHack($('#country-select'));
api.bindClearEvt();
// 图片验证码
let ImgCheck = require('plugin/img-check');
let imgCheck = new ImgCheck('#js-img-check', {
useREM: {
rootFontSize: 40,
picWidth: 150
}
});
imgCheck.init();
/**
* 必填校验
*/
function checkEnableNext() {
var phone = trim($phoneNum.val());
var area = trim($countrySelect.val());
var captcha = trim($captcha.val());
var ret = true;
$.each([phone, area, captcha], function(i, val) {
$.each([phone, area], function(i, val) {
if (!val) {
ret = false;
return ret;
... ... @@ -44,24 +56,13 @@ function checkEnableNext() {
return ret;
}
/**
* 刷新 校验码
*/
function refreshCaptcha() {
$captcha.val('').focus();
$captchaPNG.attr('src', ['//m.yohobuy.com/passport/reg/captcha.png', '?t=', Date.now()].join(''));
}
/*
Event bind
*/
$('.reg-page')
.on('input', '.phone-num, #js-captcha', function() {
.on('input', '.phone-num', function() {
$btnNext.toggleClass('disable', !checkEnableNext());
})
.on('click', '.passport-captcha-png', refreshCaptcha);
});
$countrySelect.change(function() {
$areaCode.text($countrySelect.val());
... ... @@ -70,10 +71,10 @@ $countrySelect.change(function() {
$btnNext.on('touchstart', function() {
var pn = trim($phoneNum.val()),
areaCode = $countrySelect.val(),
captcha = $captcha.val().trim();
captcha = imgCheck.getResults();
if (!captcha) {
tip.show('请输入验证码');
if (captcha === '0000') {
tip.show('请将图片旋转到正确位置');
return false;
}
... ... @@ -102,7 +103,7 @@ $btnNext.on('touchstart', function() {
if (data.code === 200) {
location.href = data.data;
} else {
refreshCaptcha();
imgCheck.refresh();
showErrTip(data.message);
requested = false;
... ... @@ -110,7 +111,7 @@ $btnNext.on('touchstart', function() {
},
error: function() {
showErrTip('出错了,请重试');
refreshCaptcha();
imgCheck.refresh();
requested = false;
}
});
... ...
... ... @@ -17,10 +17,26 @@ tip = require('plugin/tip');
api = require('./api');
checkPoint = require('./smslogin/check-point');
// 图片验证码
let ImgCheck = require('plugin/img-check');
let imgCheck = new ImgCheck('#js-img-check', {
useREM: {
rootFontSize: 40,
picWidth: 150
}
});
imgCheck.init();
page = {
init: function() {
this.domInit();
this.bindEvent();
this.toggleNextBtn();
},
domInit: function() {
$countrySelect = $('#country-select');
... ... @@ -28,8 +44,6 @@ page = {
$nextBtn = $('#btn-next');
$phoneNum = $('#phone-num');
$resetBtn = $('.clear-input');
$captcha = $('.passport-captcha input');
$captchaPNG = $('.passport-captcha-img img');
},
bindEvent: function() {
var self = this;
... ... @@ -41,12 +55,6 @@ page = {
self.toggleNextBtn();
});
$captcha.on('input', function() {
self.toggleNextBtn();
});
$captchaPNG.on('click', $.proxy(this.refreshCapatch, this));
$nextBtn.on('click', function() {
self.goNext();
});
... ... @@ -62,7 +70,7 @@ page = {
// 切换$nextBtn disable状态
toggleNextBtn: function() {
var bool = Boolean($.trim($phoneNum.val())) && Boolean($.trim($captcha.val()));
var bool = Boolean($.trim($phoneNum.val()));
$nextBtn
.toggleClass('disable', !bool)
... ... @@ -71,17 +79,11 @@ page = {
$resetBtn.toggle(bool);
},
refreshCapatch: function() {
$captchaPNG.attr('src', '/passport/sms_login/captcha.png?t=' + Date.now());
$captcha.val('');
},
// 提交按钮
goNext: function() {
var self = this;
var areaCode = $countrySelect.val();
var phone = $.trim($phoneNum.val());
var captcha = $.trim($captcha.val());
var captcha = $.trim(imgCheck.getResults());
if ($nextBtn.prop('disabled')) {
return;
... ... @@ -92,11 +94,17 @@ page = {
return;
}
// if (captcha === '0000') {
// tip.show('请将图片旋转到正确位置');
// return;
// }
$nextBtn.prop('disabled', true);
$.get('/passport/sms_login/token.json', {
$.post('/passport/sms_login/step1_check', {
area: areaCode.replace('+', ''),
mobile: phone,
captcha: captcha
captcode: captcha,
yohobuy: $('#yohobuy').val()
})
.done(function(data) {
if (data.code === 200) {
... ... @@ -104,11 +112,12 @@ page = {
$nextBtn.off();
location.href = data.redirect;
} else {
self.refreshCapatch();
data.changeCaptcha && imgCheck.refresh();
tip.show(data.message);
}
})
.fail(function() {
imgCheck.refresh();
tip.show('出错了, 请重试');
})
.always(function() {
... ...
... ... @@ -12,10 +12,12 @@ var sendInfo = function(eventName) {
channel = channelMap[channel] || 1;
param = JSON.stringify({C_ID: channel});
yas && yas.sendCustomInfo({
op: eventName,
param: param
}, true);
if (yas && yas.sendCustomInfo) {
yas.sendCustomInfo({
op: eventName,
param: param
}, true);
}
};
module.exports = sendInfo;
... ...
... ... @@ -20,7 +20,7 @@
background-color: #fff;
top: 0;
&.isApp {
&.isapp {
position: fixed;
}
... ... @@ -59,6 +59,10 @@
}
}
.isapp-margin-bottom {
margin-bottom: 80px;
}
.empty-height {
height: 72px;
}
... ...