Authored by 周少峰

Merge branch 'feature/block-spider' into hotfix/searchUid

'use strict';
const robotCheckService = require('../models/robot-check-service');
const captchaService = require('../../passport/controllers/captcha');
const index = (req, res, next) => {
let channel = req.yoho.channel || 'boys';
robotCheckService.index(channel).then((result) => {
return res.render('robot-check', Object.assign({
module: '3party',
page: 'robot-check'
}, result));
}).catch(next);
};
const check = captchaService.requiredAPI;
const isHuman = (req, res) => {
let remoteIp = req.get('X-Forwarded-For') || req.ip;
if (remoteIp.indexOf(',') > 0) {
let arr = remoteIp.split(',');
remoteIp = arr[0];
}
robotCheckService.removeBlack(remoteIp).then(() => {
return res.json({
code: 200
});
}).catch(() => {
return res.json({
code: 400
});
});
};
module.exports = {
index,
check,
isHuman
};
... ...
'use strict';
const cache = global.yoho.cache.master;
const Promise = require('bluebird');
const co = Promise.coroutine;
const HeaderModel = require('../../../doraemon/models/header');
const index = co(function* (channel) {
const header = yield HeaderModel.requestHeaderData(channel);
return {
headerData: header.headerData
};
});
const removeBlack = (remoteIp) => {
let key = `pc:limiter:${remoteIp}`;
return cache.delAsync(key);
};
module.exports = {
index,
removeBlack
};
... ...
/**
* router of sub app 3party-ad
* router of sub app 3party
* @author: htoooth<ht.anglenx@gmail.com>
* @date: 2016/11/08
*/
... ... @@ -10,9 +10,10 @@ const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
const ads = require(`${cRoot}/ads`); // 第三方广告平台对接
// Your controller here
const robot = require(`${cRoot}/robot-check`); // 机器人检查
router.get('/ads', ads.jump);
router.get('/check', robot.index);
router.post('/check', robot.check, robot.isHuman);
module.exports = router;
... ...
<div class="robot-check-page yoho-page">
<div class="captcha">
<div class="title">请输入正确的验证码,继续访问</div>
<div class="captcha-wrap"> </div>
<a class="btn confirm">确定</a>
</div>
</div>
... ...
... ... @@ -281,6 +281,7 @@ const _getSkuDataByProductBaseInfo = (data) => {
let skuGoods = null;// sku商品
let defaultImage = '';// 默认图
let defaultSkuFlag = false; // 选中状态
let marketPrice = _.get(data, 'market_price', 0.0);
if (_.isEmpty(_.get(data, 'goods_list', []))) {
return {
... ... @@ -335,6 +336,9 @@ const _getSkuDataByProductBaseInfo = (data) => {
if (data.attribute === 3) {
// 虚拟商品,门票默认最大为4,
size.storage_number = size.storage_number > 4 ? 4 : size.storage_number;
} else {
// 将(价格 > 500)并且(库存 > 5) 将库存变成 5 ,这里是为了防爬虫
size.storage_number = marketPrice > 500.0 && size.storage_number > 5 ? 5 : size.storage_number;
}
// 如果status为0,即skc下架时就跳过该商品
... ... @@ -1470,24 +1474,10 @@ const getDetailHeader = (pid, uid, isStudent, vipLevel, dataMd5, cookie) => {
return productAPI.getProductAsync(pid, uid, isStudent, vipLevel)
.then(currentUserProductInfo)
.then((result) => {
if (_.isEmpty(result) || !_.get(result, 'goodsInfo.md5')) {
return {
code: 204, // 没有改变数据
data: {}
};
}
if (_.get(result, 'goodsInfo.md5') !== dataMd5 || uid) {
return {
code: 200, // 改变数据
data: result
};
} else {
return {
code: 204, // 没有改变数据
data: {}
};
}
return {
code: 200, // 改变数据
data: result
};
});
};
... ... @@ -1500,6 +1490,17 @@ const saleReturn = (skn) => {
};
/**
* 第一次把售价隐藏,防爬虫的需要
*/
const _removeSalePrice = (productInfo) => {
delete productInfo.goodsInfo.salePrice;
delete productInfo.goodsInfo.hasOtherPrice;
delete productInfo.goodsInfo.promotion;
return productInfo;
};
/**
* 获取某一个商品详情主页面
*/
const showMainAsync = (data) => {
... ... @@ -1537,7 +1538,7 @@ const showMainAsync = (data) => {
// 商品价格
result.productDetailPage = true;
result.detail = productInfo;
result.detail = _removeSalePrice(productInfo);
// 商品介绍
let intro = _getIntroInfo(productSkn, maxSortId, productDescription);
... ...
... ... @@ -22,14 +22,14 @@ module.exports = {
// service: 'http://service-test3.yohops.com:9999/',
// prod
// singleApi: 'http://single.yoho.cn/',
// api: 'http://api.yoho.cn/',
// service: 'http://service.yoho.cn/',
singleApi: 'http://single.yoho.cn/',
api: 'http://api.yoho.cn/',
service: 'http://service.yoho.cn/',
// gray
singleApi: 'http://single.gray.yohops.com/',
api: 'http://api.gray.yohops.com/',
service: 'http://service.gray.yohops.com/',
//singleApi: 'http://single.gray.yohops.com/',
//api: 'http://api.gray.yohops.com/',
//service: 'http://service.gray.yohops.com/',
// dev
// api: 'http://dev-api.yohops.com:9999/',
... ... @@ -117,7 +117,8 @@ module.exports = {
apiCache: {
cache: false
},
zookeeperServer: '192.168.102.168:2188'
zookeeperServer: '192.168.102.168:2188',
maxQps: 1800
};
if (isProduction) {
... ...
... ... @@ -4,37 +4,94 @@
const cache = global.yoho.cache.master;
const _ = require('lodash');
const logger = global.yoho.logger;
const config = global.yoho.config;
const helpers = global.yoho.helpers;
let ONE_DAY = 60 * 60 * 24;
const MAX_QPS = config.maxQps;
let pages = {
'/product/\\/pro_([\\d]+)_([\\d]+)\\/(.*)/': 5,
'/product/list/index': 5
};
function urlJoin(a, b) {
if (_.endsWith(a, '/') && _.startsWith(b, '/')) {
return a + b.substring(1, b.length);
} else if (!_.endsWith(a, '/') && !_.startsWith(b, '/')) {
return a + '/' + b;
} else {
return a + b;
}
}
module.exports = (req, res, next) => {
let remoteIp = req.get('X-Forwarded-For') || '';
if (remoteIp.indexOf(',') > 0) {
let arr = remoteIp.split(',');
remoteIp = arr[0];
}
if (remoteIp && !_.get(req.app.locals, 'pc.sys.noLimiter')) { // 判断获取remoteIp成功,并且开关未关闭
if (remoteIp && !_.get(req.app.locals, 'pc.sys.noLimiter')) { // 判断获取remoteIp成功,并且开关未关闭
let key = `pc:limiter:${remoteIp}`;
logger.debug(`request limiter key=${key}`);
res.on('render', function() {
let route = req.route ? req.route.path : '';
let appPath = req.app.mountpath;
if (_.isArray(route) && route.length > 0) {
route = route[0];
}
cache.getAsync(key).then(result => {
let pageKey = urlJoin(appPath, route.toString()); // route may be a regexp
let pageIncr = pages[pageKey] || 0;
if (pageIncr > 0) {
cache.incrAsync(key, pageIncr);
}
});
const limiterPage = () => {
let refer = req.method === 'GET' ? req.get('Referer') : '';
let limitAPI = helpers.urlFormat('/3party/check', {refer: refer});
let limitPage = helpers.urlFormat('/3party/check', {refer: req.protocol + '://' + req.get('host') + req.originalUrl});
if (_.indexOf(['/3party/check', '/passport/imagesNode', '/passport/cert/headerTip'], req.path) >= 0) {
return next();
}
if (req.xhr) {
return res.json({
code: 400,
data: {refer: limitAPI}
});
}
return res.redirect(limitPage);
};
cache.getAsync(key).then((result) => {
if (result && _.isNumber(result)) {
if (result > 30) { // 判断 qps
res.status(403).end();
if (result > MAX_QPS) { // 判断 qps
cache.touch(key, ONE_DAY);
return limiterPage();
} else {
cache.incrAsync(key, 1); // qps + 1
next();
return next();
}
} else {
cache.setAsync(key, 1, 1); // 设置key,1s失效
next();
cache.setAsync(key, 1, 60); // 设置key,1m失效
return next();
}
}).catch(e => {
logger.error(`request limiter get key[${key}] from cache error.`, e);
next();
}).catch((err) => {
logger.error(`request limiter get key[${key}] from cache error.`, err);
return next();
});
} else {
next();
return next();
}
};
... ...
var $ = require('yoho-jquery'),
Captcha = require('../plugins/captcha');
var captcha = new Captcha('.captcha-wrap', {
checkURI: '/3party/check'
}).init();
require('../common');
require('../common/promise');
$.sleep(500).then(function() {
captcha.refresh();
});
$('.confirm').on('click', function() {
captcha.check().then(function() {
window.jumpUrl(window.queryString().refer || '//www.yohobuy.com');
});
});
... ...
@import "robot-check";
... ...
.robot-check-page {
height: 400px;
.captcha {
width: 1150px;
margin-left: auto;
margin-right: auto;
margin-top: 150px;
}
.title {
font-size: 20px;
text-align: center;
}
.captcha-wrap {
width: 270px;
margin-left: auto;
margin-right: auto;
position: relative;
}
.confirm {
display: block;
color: white;
width: 270px;
height: 50px;
background: #ff1901;
margin-left: auto;
margin-right: auto;
text-align: center;
font-size: 20px;
line-height: 50px;
cursor: pointer;
letter-spacing: 10px;
}
}
... ...
... ... @@ -17,3 +17,4 @@
@import 'guang/index';
@import 'cart/index';
@import 'service/index';
@import '3party/index';
... ...