Merge branch 'master' of git.yoho.cn:fe/yohobuywap-node into feature/featureTemplate
Showing
34 changed files
with
524 additions
and
41 deletions
@@ -51,6 +51,11 @@ app.set('etag', false); | @@ -51,6 +51,11 @@ app.set('etag', false); | ||
51 | 51 | ||
52 | app.enable('trust proxy'); | 52 | app.enable('trust proxy'); |
53 | 53 | ||
54 | +// 请求限制中间件 | ||
55 | +if (!app.locals.devEnv) { | ||
56 | + app.use(require('./doraemon/middleware/limiter')); | ||
57 | +} | ||
58 | + | ||
54 | // 指定libray目录 | 59 | // 指定libray目录 |
55 | global.utils = path.resolve('./utils'); | 60 | global.utils = path.resolve('./utils'); |
56 | 61 |
apps/3party/controllers/check.js
0 → 100644
1 | +'use strict'; | ||
2 | +const _ = require('lodash'); | ||
3 | +const cache = global.yoho.cache.master; | ||
4 | + | ||
5 | +exports.index = (req, res) => { | ||
6 | + res.render('check', { | ||
7 | + width750: true, | ||
8 | + localCss: true | ||
9 | + }); | ||
10 | +}; | ||
11 | + | ||
12 | +exports.submit = (req, res) => { | ||
13 | + let captchaCode = _.get(req.session, 'captcha'); | ||
14 | + let remoteIp = req.get('X-Forwarded-For') || req.ip; | ||
15 | + | ||
16 | + if (remoteIp.indexOf(',') > 0) { | ||
17 | + let arr = remoteIp.split(','); | ||
18 | + | ||
19 | + remoteIp = arr[0]; | ||
20 | + } | ||
21 | + | ||
22 | + if (req.body.captcha === captchaCode) { | ||
23 | + let key = `pc:limiter:${remoteIp}`; | ||
24 | + cache.delAsync(key).then(() => { | ||
25 | + return res.json({ | ||
26 | + code: 200 | ||
27 | + }); | ||
28 | + }).catch(() => { | ||
29 | + return res.json({ | ||
30 | + code: 400 | ||
31 | + }); | ||
32 | + }); | ||
33 | + } else { | ||
34 | + return res.json({ | ||
35 | + code: 400 | ||
36 | + }); | ||
37 | + } | ||
38 | + | ||
39 | +}; |
@@ -9,9 +9,12 @@ | @@ -9,9 +9,12 @@ | ||
9 | const router = require('express').Router(); // eslint-disable-line | 9 | const router = require('express').Router(); // eslint-disable-line |
10 | const cRoot = './controllers'; | 10 | const cRoot = './controllers'; |
11 | const ads = require(`${cRoot}/ads`); | 11 | const ads = require(`${cRoot}/ads`); |
12 | +const check = require(`${cRoot}/check`); | ||
12 | 13 | ||
13 | // routers | 14 | // routers |
14 | 15 | ||
15 | router.get('/ads', ads.index); | 16 | router.get('/ads', ads.index); |
17 | +router.get('/check', check.index); | ||
18 | +router.post('/check/submit', check.submit); | ||
16 | 19 | ||
17 | module.exports = router; | 20 | module.exports = router; |
apps/3party/views/action/check.hbs
0 → 100644
apps/3party/views/partial/.gitkeep
0 → 100644
@@ -181,6 +181,7 @@ exports.orderSub = (req, res, next) => { | @@ -181,6 +181,7 @@ exports.orderSub = (req, res, next) => { | ||
181 | let yohoCoin = req.body.yohoCoin || 0; | 181 | let yohoCoin = req.body.yohoCoin || 0; |
182 | let skuList = req.body.skuList || ''; | 182 | let skuList = req.body.skuList || ''; |
183 | let orderInfo; | 183 | let orderInfo; |
184 | + let isWechat = req.yoho.isWechat; | ||
184 | 185 | ||
185 | try { | 186 | try { |
186 | orderInfo = JSON.parse(req.cookies['order-info']); | 187 | orderInfo = JSON.parse(req.cookies['order-info']); |
@@ -260,11 +261,11 @@ exports.orderSub = (req, res, next) => { | @@ -260,11 +261,11 @@ exports.orderSub = (req, res, next) => { | ||
260 | 261 | ||
261 | result = yield cartModel.orderSub(uid, addressId, 'bundle', deliveryTimeId, | 262 | result = yield cartModel.orderSub(uid, addressId, 'bundle', deliveryTimeId, |
262 | deliveryId, invoices, paymentTypeId, paymentType, msg, couponCode, | 263 | deliveryId, invoices, paymentTypeId, paymentType, msg, couponCode, |
263 | - yohoCoin, null, unionKey, userAgent, times, activityInfo, ip); | 264 | + yohoCoin, null, unionKey, userAgent, times, activityInfo, ip, isWechat); |
264 | } else { | 265 | } else { |
265 | result = yield cartModel.orderSub(uid, addressId, cartType, deliveryTimeId, | 266 | result = yield cartModel.orderSub(uid, addressId, cartType, deliveryTimeId, |
266 | deliveryId, invoices, paymentTypeId, paymentType, msg, couponCode, | 267 | deliveryId, invoices, paymentTypeId, paymentType, msg, couponCode, |
267 | - yohoCoin, skuList, unionKey, userAgent, null, null, ip); | 268 | + yohoCoin, skuList, unionKey, userAgent, null, null, ip, isWechat); |
268 | } | 269 | } |
269 | 270 | ||
270 | // 提交成功清除Cookie | 271 | // 提交成功清除Cookie |
@@ -178,12 +178,13 @@ exports.ticketsOrderCompute = (uid, productSku, buyNumber, yohoCoin) => { | @@ -178,12 +178,13 @@ exports.ticketsOrderCompute = (uid, productSku, buyNumber, yohoCoin) => { | ||
178 | * @param string|null $userAgent 联盟过来用户下单时需要的User-Agent信息 | 178 | * @param string|null $userAgent 联盟过来用户下单时需要的User-Agent信息 |
179 | * @param int $times | 179 | * @param int $times |
180 | * @param null $activityInfo 套餐数据 | 180 | * @param null $activityInfo 套餐数据 |
181 | + * @param isWechat 是否是微信商城 | ||
181 | * @return array 接口返回的数据 | 182 | * @return array 接口返回的数据 |
182 | */ | 183 | */ |
183 | exports.orderSub = (uid, addressId, cartType, deliveryTime, | 184 | exports.orderSub = (uid, addressId, cartType, deliveryTime, |
184 | deliveryWay, invoices, paymentId, paymentType, remark, | 185 | deliveryWay, invoices, paymentId, paymentType, remark, |
185 | couponCode, yohoCoin, skuList, qhyUnio, | 186 | couponCode, yohoCoin, skuList, qhyUnio, |
186 | - userAgent, times, activityInfo, ip) => { | 187 | + userAgent, times, activityInfo, ip, isWechat) => { |
187 | if (!qhyUnio) { | 188 | if (!qhyUnio) { |
188 | qhyUnio = ''; | 189 | qhyUnio = ''; |
189 | } | 190 | } |
@@ -215,7 +216,7 @@ exports.orderSub = (uid, addressId, cartType, deliveryTime, | @@ -215,7 +216,7 @@ exports.orderSub = (uid, addressId, cartType, deliveryTime, | ||
215 | return shoppingAPI.orderSub(uid, addressId, cartType, deliveryTime, | 216 | return shoppingAPI.orderSub(uid, addressId, cartType, deliveryTime, |
216 | deliveryWay, invoices, paymentId, paymentType, | 217 | deliveryWay, invoices, paymentId, paymentType, |
217 | remark, couponCode, yohoCoin, skuList, qhyUnio, | 218 | remark, couponCode, yohoCoin, skuList, qhyUnio, |
218 | - userAgent, times, activityInfo, ip).then(orderSubRes => { | 219 | + userAgent, times, activityInfo, ip, isWechat).then(orderSubRes => { |
219 | let finalResult = {}; | 220 | let finalResult = {}; |
220 | 221 | ||
221 | if (orderSubRes && orderSubRes.data && orderSubRes.data.is_hint === 'Y') { | 222 | if (orderSubRes && orderSubRes.data && orderSubRes.data.is_hint === 'Y') { |
@@ -7,6 +7,7 @@ | @@ -7,6 +7,7 @@ | ||
7 | const _ = require('lodash'); | 7 | const _ = require('lodash'); |
8 | const api = global.yoho.API; | 8 | const api = global.yoho.API; |
9 | const helpers = global.yoho.helpers; | 9 | const helpers = global.yoho.helpers; |
10 | +const utils = require('../../../utils'); | ||
10 | 11 | ||
11 | const genderMap = { | 12 | const genderMap = { |
12 | boys: '1,3', | 13 | boys: '1,3', |
@@ -33,27 +34,27 @@ let _processCateData = (list, channel) => { | @@ -33,27 +34,27 @@ let _processCateData = (list, channel) => { | ||
33 | // 如果有二级菜单,二级菜单跳转,否则一级菜单跳转 | 34 | // 如果有二级菜单,二级菜单跳转,否则一级菜单跳转 |
34 | if (firstItem.sub && firstItem.sub.length) { | 35 | if (firstItem.sub && firstItem.sub.length) { |
35 | _.map(firstItem.sub, function(secondItem) { | 36 | _.map(firstItem.sub, function(secondItem) { |
36 | - secondItem.url = helpers.urlFormat('/', { | 37 | + secondItem.url = helpers.urlFormat('/', utils.mapSort({ |
37 | sort: _.get(secondItem, 'relation_parameter.sort'), | 38 | sort: _.get(secondItem, 'relation_parameter.sort'), |
38 | sort_name: secondItem.category_name, | 39 | sort_name: secondItem.category_name, |
39 | gender: genderMap[key] || '' | 40 | gender: genderMap[key] || '' |
40 | - }, 'list'); | 41 | + }), 'list'); |
41 | }); | 42 | }); |
42 | 43 | ||
43 | firstItem.sub.unshift({ | 44 | firstItem.sub.unshift({ |
44 | category_name: `全部${firstItem.category_name}`, | 45 | category_name: `全部${firstItem.category_name}`, |
45 | - url: helpers.urlFormat('/', { | 46 | + url: helpers.urlFormat('/', utils.mapSort({ |
46 | sort: _.get(firstItem, 'relation_parameter.sort'), | 47 | sort: _.get(firstItem, 'relation_parameter.sort'), |
47 | sort_name: firstItem.category_name, | 48 | sort_name: firstItem.category_name, |
48 | gender: genderMap[key] || '' | 49 | gender: genderMap[key] || '' |
49 | - }, 'list') | 50 | + }), 'list') |
50 | }); | 51 | }); |
51 | } else { | 52 | } else { |
52 | - firstItem.url = helpers.urlFormat('/', { | 53 | + firstItem.url = helpers.urlFormat('/', utils.mapSort({ |
53 | sort: _.get(firstItem, 'relation_parameter.sort'), | 54 | sort: _.get(firstItem, 'relation_parameter.sort'), |
54 | sort_name: firstItem.category_name, | 55 | sort_name: firstItem.category_name, |
55 | gender: genderMap[key] || '' | 56 | gender: genderMap[key] || '' |
56 | - }, 'list'); | 57 | + }), 'list'); |
57 | } | 58 | } |
58 | }); | 59 | }); |
59 | }); | 60 | }); |
@@ -103,7 +103,7 @@ const editorRedirect = (req, res, next) => { | @@ -103,7 +103,7 @@ const editorRedirect = (req, res, next) => { | ||
103 | param = '?' + param; | 103 | param = '?' + param; |
104 | } | 104 | } |
105 | redirectUrl += `-${id}/${param}`; | 105 | redirectUrl += `-${id}/${param}`; |
106 | - res.redirect(redirectUrl); | 106 | + res.redirect(301, redirectUrl); |
107 | } else { | 107 | } else { |
108 | return next(); | 108 | return next(); |
109 | } | 109 | } |
@@ -469,7 +469,7 @@ const indexRedirect = (req, res, next) => { | @@ -469,7 +469,7 @@ const indexRedirect = (req, res, next) => { | ||
469 | param = '?' + param; | 469 | param = '?' + param; |
470 | } | 470 | } |
471 | redirectUrl += `${id}.html${param}`; | 471 | redirectUrl += `${id}.html${param}`; |
472 | - res.redirect(redirectUrl); | 472 | + res.redirect(301, redirectUrl); |
473 | } else { | 473 | } else { |
474 | return next(); | 474 | return next(); |
475 | } | 475 | } |
@@ -111,7 +111,9 @@ const local = { | @@ -111,7 +111,9 @@ const local = { | ||
111 | res.render('login', { | 111 | res.render('login', { |
112 | width750: true, | 112 | width750: true, |
113 | loginIndex: true, // 模板中使用JS的标识 | 113 | loginIndex: true, // 模板中使用JS的标识 |
114 | - captchaShow: _.get(req.session, 'login.errorCount') <= 0, | 114 | + |
115 | + // captchaShow: _.get(req.session, 'login.errorCount') <= 0, | ||
116 | + captchaShow: true, // 170306 因为暴力破解密码问题,要求每次都展示验证码 | ||
115 | 117 | ||
116 | // 返回的URL链接 | 118 | // 返回的URL链接 |
117 | backUrl: 'javascript:history.go(-1)', // eslint-disable-line | 119 | backUrl: 'javascript:history.go(-1)', // eslint-disable-line |
@@ -162,7 +164,9 @@ const local = { | @@ -162,7 +164,9 @@ const local = { | ||
162 | // 返回的URL链接 | 164 | // 返回的URL链接 |
163 | backUrl: 'javascript:history.go(-1)', // eslint-disable-line | 165 | backUrl: 'javascript:history.go(-1)', // eslint-disable-line |
164 | loginInternational: true, // 模板中使用JS的标识 | 166 | loginInternational: true, // 模板中使用JS的标识 |
165 | - captchaShow: _.get(req.session, 'login.errorCount') <= 0, | 167 | + |
168 | + // captchaShow: _.get(req.session, 'login.errorCount') <= 0, | ||
169 | + captchaShow: true, // 170306 因为暴力破解密码问题,要求每次都展示验证码 | ||
166 | isPassportPage: true, // 模板中模块标识 | 170 | isPassportPage: true, // 模板中模块标识 |
167 | headerText: '登录', | 171 | headerText: '登录', |
168 | areaCode: '+86', // 默认区号 | 172 | areaCode: '+86', // 默认区号 |
@@ -173,6 +177,10 @@ const local = { | @@ -173,6 +177,10 @@ const local = { | ||
173 | }); | 177 | }); |
174 | }, | 178 | }, |
175 | login: (req, res, next) => { | 179 | login: (req, res, next) => { |
180 | + | ||
181 | + // 170306 因为暴力破解密码问题,要求每次都校验验证码 | ||
182 | + _.set(req.session, 'login.errorCount', 0); | ||
183 | + | ||
176 | let count = _.get(req.session, 'login.errorCount'); | 184 | let count = _.get(req.session, 'login.errorCount'); |
177 | 185 | ||
178 | if (count == null) { // eslint-disable-line | 186 | if (count == null) { // eslint-disable-line |
@@ -419,16 +419,19 @@ let setPassword = (req, res, next) => { | @@ -419,16 +419,19 @@ let setPassword = (req, res, next) => { | ||
419 | 419 | ||
420 | RegService.regMobileAes(area, mobile, password, shoppingKey, smsCode, isFromMy).then((result) => { | 420 | RegService.regMobileAes(area, mobile, password, shoppingKey, smsCode, isFromMy).then((result) => { |
421 | if (!result.code || result.code !== 200) { | 421 | if (!result.code || result.code !== 200) { |
422 | - return Promise.reject(result); | 422 | + return res.send(result); |
423 | } | 423 | } |
424 | if (!result.data || !result.data.uid) { | 424 | if (!result.data || !result.data.uid) { |
425 | - return Promise.reject(result); | 425 | + return res.send(result); |
426 | } | 426 | } |
427 | 427 | ||
428 | resultCopy = result; | 428 | resultCopy = result; |
429 | 429 | ||
430 | return AuthHelper.syncUserSession(result.data.uid, req, res); | 430 | return AuthHelper.syncUserSession(result.data.uid, req, res); |
431 | }).then(() => { | 431 | }).then(() => { |
432 | + if (!resultCopy) { | ||
433 | + return; | ||
434 | + } | ||
432 | // 返回跳转到来源页面 | 435 | // 返回跳转到来源页面 |
433 | let refer = req.cookies.refer; | 436 | let refer = req.cookies.refer; |
434 | 437 |
@@ -86,7 +86,7 @@ const newDetail = { | @@ -86,7 +86,7 @@ const newDetail = { | ||
86 | reject(); | 86 | reject(); |
87 | } | 87 | } |
88 | }).then(skn => { | 88 | }).then(skn => { |
89 | - return res.redirect(`/product/${skn}.html${param}`); | 89 | + return res.redirect(301, `/product/${skn}.html${param}`); |
90 | }, () => { | 90 | }, () => { |
91 | return next(); | 91 | return next(); |
92 | }); | 92 | }); |
@@ -142,8 +142,8 @@ router.get('/search/fuzzyDatas', search.fuzzyDatas); | @@ -142,8 +142,8 @@ router.get('/search/fuzzyDatas', search.fuzzyDatas); | ||
142 | router.get('/search/search', search.search); | 142 | router.get('/search/search', search.search); |
143 | 143 | ||
144 | // 品类 | 144 | // 品类 |
145 | -router.get('/index/index', list.category); | ||
146 | -router.get('/list/index', list.category); // 兼容 PC 的链接 | 145 | +router.get('/index/index', rewrite.sortParams, list.category); |
146 | +router.get('/list/index', rewrite.sortParams, list.category); // 兼容 PC 的链接 | ||
147 | 147 | ||
148 | // 品牌 | 店铺 | 148 | // 品牌 | 店铺 |
149 | router.get('/index/shopAppCookie', list.shopAppCookie); | 149 | router.get('/index/shopAppCookie', list.shopAppCookie); |
@@ -153,11 +153,12 @@ exports.checkTickets = (uid, productSku, buyNumber, useYohoCoin, yohoCoinMode) = | @@ -153,11 +153,12 @@ exports.checkTickets = (uid, productSku, buyNumber, useYohoCoin, yohoCoinMode) = | ||
153 | * @param string|null $userAgent 联盟过来用户下单时需要的User-Agent信息 | 153 | * @param string|null $userAgent 联盟过来用户下单时需要的User-Agent信息 |
154 | * @param $times | 154 | * @param $times |
155 | * @param null $activityInfo 套餐信息 | 155 | * @param null $activityInfo 套餐信息 |
156 | + * @param isWechat 是否是微信商城 | ||
156 | * @return array 接口返回的数据 | 157 | * @return array 接口返回的数据 |
157 | */ | 158 | */ |
158 | exports.orderSub = (uid, addressId, cartType, deliveryTime, | 159 | exports.orderSub = (uid, addressId, cartType, deliveryTime, |
159 | deliveryWay, invoices, paymentId, paymentType, remark, couponCode, | 160 | deliveryWay, invoices, paymentId, paymentType, remark, couponCode, |
160 | - yohoCoin, skuList, qhyUnion, userAgent, times, activityInfo, ip) => { | 161 | + yohoCoin, skuList, qhyUnion, userAgent, times, activityInfo, ip, isWechat) => { |
161 | if (!activityInfo) { | 162 | if (!activityInfo) { |
162 | activityInfo = null; | 163 | activityInfo = null; |
163 | } | 164 | } |
@@ -226,6 +227,11 @@ exports.orderSub = (uid, addressId, cartType, deliveryTime, | @@ -226,6 +227,11 @@ exports.orderSub = (uid, addressId, cartType, deliveryTime, | ||
226 | params.qhy_union = qhyUnion; | 227 | params.qhy_union = qhyUnion; |
227 | } | 228 | } |
228 | 229 | ||
230 | + // 是否是微信商城 | ||
231 | + if (isWechat) { | ||
232 | + params.client_type = 'wechat'; | ||
233 | + } | ||
234 | + | ||
229 | return api.post('', params, { | 235 | return api.post('', params, { |
230 | headers: { | 236 | headers: { |
231 | 'X-Forwarded-For': ip || '', | 237 | 'X-Forwarded-For': ip || '', |
@@ -72,7 +72,6 @@ module.exports = { | @@ -72,7 +72,6 @@ module.exports = { | ||
72 | udp: { // send by udp | 72 | udp: { // send by udp |
73 | measurement: 'yohobuy_wap_node_log', | 73 | measurement: 'yohobuy_wap_node_log', |
74 | level: 'error', // logger level | 74 | level: 'error', // logger level |
75 | - host: 'influxdblog.web.yohoops.org', // influxdb host | ||
76 | port: '4444' // influxdb port | 75 | port: '4444' // influxdb port |
77 | }, | 76 | }, |
78 | console: { | 77 | console: { |
@@ -107,7 +106,8 @@ module.exports = { | @@ -107,7 +106,8 @@ module.exports = { | ||
107 | key: '7e6f3307b64cc87c79c472814b88f7fb', | 106 | key: '7e6f3307b64cc87c79c472814b88f7fb', |
108 | appSecret: 'ce21ae4a3f93852279175a167e54509b', | 107 | appSecret: 'ce21ae4a3f93852279175a167e54509b', |
109 | notifyUrl: domains.service + 'payment/weixin_notify', | 108 | notifyUrl: domains.service + 'payment/weixin_notify', |
110 | - } | 109 | + }, |
110 | + maxQps: 1200 | ||
111 | }; | 111 | }; |
112 | 112 | ||
113 | if (isProduction) { | 113 | if (isProduction) { |
doraemon/middleware/limiter.js
0 → 100644
doraemon/middleware/limiter/index.js
0 → 100644
1 | +'use strict'; | ||
2 | + | ||
3 | +const _ = require('lodash'); | ||
4 | +const logger = global.yoho.logger; | ||
5 | +const ip = require('./rules/ip-list'); | ||
6 | +const userAgent = require('./rules/useragent'); | ||
7 | +const qpsLimiter = require('./rules/qps-limit'); | ||
8 | +const fakerLimiter = require('./rules/faker-limit'); | ||
9 | +const captchaPolicy = require('./policies/captcha'); | ||
10 | +const reporterPolicy = require('./policies/reporter'); | ||
11 | + | ||
12 | +const IP_WHITE_LIST = [ | ||
13 | + // '106.38.38.146', | ||
14 | + // '218.94.75.58' | ||
15 | +]; | ||
16 | + | ||
17 | +const limiter = (rule, policy, context) => { | ||
18 | + return rule(context, policy); | ||
19 | +}; | ||
20 | + | ||
21 | +module.exports = (req, res, next) => { | ||
22 | + let remoteIp = req.get('X-Forwarded-For') || req.connection.remoteAddress; | ||
23 | + logger.debug('request remote ip: ', remoteIp); | ||
24 | + | ||
25 | + if (remoteIp.indexOf(',') > 0) { | ||
26 | + let arr = remoteIp.split(','); | ||
27 | + | ||
28 | + remoteIp = arr[0]; | ||
29 | + } | ||
30 | + | ||
31 | + const excluded = _.includes(IP_WHITE_LIST, remoteIp); | ||
32 | + const enabled = !_.get(req.app.locals, 'wap.sys.noLimiter'); | ||
33 | + | ||
34 | + // 判断获取remoteIp成功,并且开关未关闭 | ||
35 | + if (enabled && remoteIp && !excluded) { | ||
36 | + const context = { | ||
37 | + req: req, | ||
38 | + res: res, | ||
39 | + next: next, | ||
40 | + remoteIp: remoteIp | ||
41 | + }; | ||
42 | + | ||
43 | + Promise.all([ | ||
44 | + limiter(userAgent, captchaPolicy, context), | ||
45 | + limiter(ip, captchaPolicy, context), | ||
46 | + limiter(qpsLimiter, captchaPolicy, context), | ||
47 | + //limiter(fakerLimiter, reporterPolicy, context) | ||
48 | + ]).then((results) => { | ||
49 | + let allPass = true, exclusion = false, policy = null; | ||
50 | + | ||
51 | + logger.debug('limiter result: ' + JSON.stringify(results)); | ||
52 | + | ||
53 | + _.forEach(results, (result) => { | ||
54 | + if (typeof result === 'object' && !exclusion) { | ||
55 | + exclusion = result.exclusion; | ||
56 | + } | ||
57 | + | ||
58 | + if (!excluded && typeof result === 'function') { | ||
59 | + allPass = false; | ||
60 | + } | ||
61 | + | ||
62 | + if (typeof result === 'function') { | ||
63 | + policy = result; | ||
64 | + } | ||
65 | + }); | ||
66 | + | ||
67 | + if (exclusion) { | ||
68 | + return next(); | ||
69 | + } else if (!allPass && policy) { | ||
70 | + policy(req, res, next); | ||
71 | + } else { | ||
72 | + return next(); | ||
73 | + } | ||
74 | + | ||
75 | + }).catch((err) => { | ||
76 | + logger.error(err); | ||
77 | + return next(); | ||
78 | + }); | ||
79 | + } else { | ||
80 | + return next(); | ||
81 | + } | ||
82 | +}; |
1 | +'use strict'; | ||
2 | + | ||
3 | +const helpers = global.yoho.helpers; | ||
4 | +const _ = require('lodash'); | ||
5 | + | ||
6 | +const WHITE_LIST = [ | ||
7 | + '/3party/check', | ||
8 | + '/passport/imagesNode', | ||
9 | + '/passport/cert/headerTip', | ||
10 | + '/passport/captcha/get', | ||
11 | + '/3party/check/submit' | ||
12 | +]; | ||
13 | + | ||
14 | +module.exports = (req, res, next) => { | ||
15 | + let refer = req.method === 'GET' ? req.get('Referer') : ''; | ||
16 | + let limitAPI = helpers.urlFormat('/3party/check', {refer: refer}); | ||
17 | + let limitPage = helpers.urlFormat('/3party/check', {refer: req.protocol + '://' + req.get('host') + req.originalUrl}); | ||
18 | + | ||
19 | + if (_.indexOf(WHITE_LIST, req.path) >= 0) { | ||
20 | + return next(); | ||
21 | + } | ||
22 | + | ||
23 | + if (req.xhr) { | ||
24 | + return res.json({ | ||
25 | + code: 400, | ||
26 | + data: {refer: limitAPI} | ||
27 | + }); | ||
28 | + } | ||
29 | + | ||
30 | + return res.redirect(limitPage); | ||
31 | +}; |
1 | +'use strict'; | ||
2 | + | ||
3 | +const logger = global.yoho.logger; | ||
4 | +const cache = global.yoho.cache.master; | ||
5 | +const ONE_DAY = 60 * 60 * 24; | ||
6 | + | ||
7 | +module.exports = (limiter, policy) => { | ||
8 | + const req = limiter.req, | ||
9 | + res = limiter.res, | ||
10 | + next = limiter.next; | ||
11 | + | ||
12 | + const key = `pc:limiter:faker:${limiter.remoteIp}`; | ||
13 | + | ||
14 | + if (req.header('X-Requested-With') === 'XMLHttpRequest') { | ||
15 | + cache.decrAsync(key, 1); | ||
16 | + } | ||
17 | + | ||
18 | + res.on('render', function() { | ||
19 | + cache.incrAsync(key, 1); | ||
20 | + }); | ||
21 | + | ||
22 | + return cache.getAsync(key).then((result) => { | ||
23 | + if (result) { | ||
24 | + if (result > 100) { | ||
25 | + return Promise.resolve(policy);//policy(req, res, next); | ||
26 | + } else { | ||
27 | + return Promise.resolve(true); | ||
28 | + } | ||
29 | + } else { | ||
30 | + cache.setAsync(key, 1, ONE_DAY); // 设置key,1m失效 | ||
31 | + return Promise.resolve(true); | ||
32 | + } | ||
33 | + }); | ||
34 | +}; |
doraemon/middleware/limiter/rules/ip-list.js
0 → 100644
1 | +'use strict'; | ||
2 | + | ||
3 | +const cache = global.yoho.cache.master; | ||
4 | +const _ = require('lodash'); | ||
5 | + | ||
6 | +module.exports = (limiter) => { | ||
7 | + const key = `pc:limiter:${limiter.remoteIp}`; | ||
8 | + | ||
9 | + return cache.getAsync(key).then((result) => { | ||
10 | + if (result && _.isNumber(result)) { | ||
11 | + return Promise.resolve({ | ||
12 | + exclusion: result === -1 | ||
13 | + }); | ||
14 | + } else { | ||
15 | + return Promise.resolve(true); | ||
16 | + } | ||
17 | + }); | ||
18 | +}; |
1 | +'use strict'; | ||
2 | + | ||
3 | +const logger = global.yoho.logger; | ||
4 | +const cache = global.yoho.cache.master; | ||
5 | +const config = global.yoho.config; | ||
6 | +const ONE_DAY = 60 * 60 * 24; | ||
7 | +const MAX_QPS = config.maxQps; | ||
8 | +const _ = require('lodash'); | ||
9 | + | ||
10 | +const PAGES = { | ||
11 | + '/product/\\/pro_([\\d]+)_([\\d]+)\\/(.*)/': 5, | ||
12 | + '/product/list/index': 5 | ||
13 | +}; | ||
14 | + | ||
15 | +function urlJoin(a, b) { | ||
16 | + if (_.endsWith(a, '/') && _.startsWith(b, '/')) { | ||
17 | + return a + b.substring(1, b.length); | ||
18 | + } else if (!_.endsWith(a, '/') && !_.startsWith(b, '/')) { | ||
19 | + return a + '/' + b; | ||
20 | + } else { | ||
21 | + return a + b; | ||
22 | + } | ||
23 | +} | ||
24 | + | ||
25 | +module.exports = (limiter, policy) => { | ||
26 | + const req = limiter.req, | ||
27 | + res = limiter.res, | ||
28 | + next = limiter.next; | ||
29 | + | ||
30 | + const key = `pc:limiter:${limiter.remoteIp}`; | ||
31 | + | ||
32 | + res.on('render', function() { | ||
33 | + let route = req.route ? req.route.path : ''; | ||
34 | + let appPath = req.app.mountpath; | ||
35 | + | ||
36 | + if (_.isArray(route) && route.length > 0) { | ||
37 | + route = route[0]; | ||
38 | + } | ||
39 | + | ||
40 | + let pageKey = urlJoin(appPath, route.toString()); // route may be a regexp | ||
41 | + let pageIncr = PAGES[pageKey] || 0; | ||
42 | + | ||
43 | + if (/^\/p([\d]+)/.test(req.path)) { | ||
44 | + pageIncr = 5; | ||
45 | + } | ||
46 | + | ||
47 | + if (pageIncr > 0) { | ||
48 | + cache.incrAsync(key, pageIncr, (err) => {}); | ||
49 | + } | ||
50 | + }); | ||
51 | + | ||
52 | + return cache.getAsync(key).then((result) => { | ||
53 | + logger.debug('qps limiter: ' + key + '@' + result + ' max: ' + MAX_QPS); | ||
54 | + | ||
55 | + if (result && _.isNumber(result)) { | ||
56 | + | ||
57 | + if (result === -1) { | ||
58 | + return Promise.resolve(true); | ||
59 | + } | ||
60 | + | ||
61 | + if (result > MAX_QPS) { // 判断 qps | ||
62 | + cache.touch(key, ONE_DAY); | ||
63 | + logger.debug('req limit', key); | ||
64 | + | ||
65 | + return Promise.resolve(policy); | ||
66 | + } else { | ||
67 | + cache.incrAsync(key, 1); // qps + 1 | ||
68 | + return Promise.resolve(true); | ||
69 | + | ||
70 | + } | ||
71 | + } else { | ||
72 | + cache.setAsync(key, 1, 60); // 设置key,1m失效 | ||
73 | + return Promise.resolve(true); | ||
74 | + } | ||
75 | + }); | ||
76 | +}; |
1 | +'use strict'; | ||
2 | + | ||
3 | +const cache = global.yoho.cache.master; | ||
4 | +const _ = require('lodash'); | ||
5 | +const logger = global.yoho.logger; | ||
6 | + | ||
7 | + | ||
8 | +module.exports = (limiter, policy) => { | ||
9 | + const req = limiter.req, | ||
10 | + res = limiter.res, | ||
11 | + next = limiter.next; | ||
12 | + const blackKey = 'pc:limiter:ua:black', | ||
13 | + whiteKey = 'pc:limiter:ua:white'; | ||
14 | + | ||
15 | + const ua = limiter.req.header('User-Agent'); | ||
16 | + | ||
17 | + return Promise.all([ | ||
18 | + cache.getAsync(blackKey), | ||
19 | + cache.getAsync(whiteKey) | ||
20 | + ]).then((args) => { | ||
21 | + const blacklist = args[0] || [], whitelist = args[1] || []; | ||
22 | + | ||
23 | + if (blacklist.length === 0 && whitelist.length === 0) { | ||
24 | + return Promise.resolve(true); | ||
25 | + } | ||
26 | + | ||
27 | + const test = (list) => { | ||
28 | + let result = false; | ||
29 | + | ||
30 | + _.each(list, (item) => { | ||
31 | + let regexp; | ||
32 | + | ||
33 | + try { | ||
34 | + regexp = new RegExp(item); | ||
35 | + } catch (e) { | ||
36 | + logger.error(e); | ||
37 | + } | ||
38 | + | ||
39 | + if (regexp.test(ua)) { | ||
40 | + result = true; | ||
41 | + } | ||
42 | + }); | ||
43 | + | ||
44 | + return result; | ||
45 | + }; | ||
46 | + | ||
47 | + if (test(blacklist)) { | ||
48 | + return Promise.resolve(policy); | ||
49 | + } else if (test(whitelist)) { | ||
50 | + return Promise.resolve({ | ||
51 | + exclusion: true | ||
52 | + }); | ||
53 | + } else { | ||
54 | + return Promise.resolve(true); | ||
55 | + } | ||
56 | + }); | ||
57 | + | ||
58 | +}; |
@@ -7,7 +7,13 @@ | @@ -7,7 +7,13 @@ | ||
7 | 7 | ||
8 | const typeLib = require('../../config/type-lib'); | 8 | const typeLib = require('../../config/type-lib'); |
9 | const _ = require('lodash'); | 9 | const _ = require('lodash'); |
10 | +const utils = require('../../utils'); | ||
11 | +const helpers = global.yoho.helpers; | ||
10 | 12 | ||
13 | + | ||
14 | +/** | ||
15 | + * 解析url规则中的参数 | ||
16 | + */ | ||
11 | const resolve = (req, res, next) => { | 17 | const resolve = (req, res, next) => { |
12 | let path, | 18 | let path, |
13 | params = { | 19 | params = { |
@@ -44,6 +50,9 @@ const resolve = (req, res, next) => { | @@ -44,6 +50,9 @@ const resolve = (req, res, next) => { | ||
44 | next(); | 50 | next(); |
45 | }; | 51 | }; |
46 | 52 | ||
53 | +/** | ||
54 | + * 简介channel参数 | ||
55 | + */ | ||
47 | const channel = (req, res, next) => { | 56 | const channel = (req, res, next) => { |
48 | let channelName; | 57 | let channelName; |
49 | 58 | ||
@@ -79,7 +88,26 @@ const channel = (req, res, next) => { | @@ -79,7 +88,26 @@ const channel = (req, res, next) => { | ||
79 | next(); | 88 | next(); |
80 | }; | 89 | }; |
81 | 90 | ||
91 | +/** | ||
92 | + * 参数排序 | ||
93 | + */ | ||
94 | +const sortParams = (req, res, next) => { | ||
95 | + let sorts = utils.mapSort(req.query); | ||
96 | + let queryKeys = _.keys(req.query); | ||
97 | + let index = 0; | ||
98 | + let matched = _.map(sorts, (val, key) => { | ||
99 | + return key === queryKeys[index++]; | ||
100 | + }); | ||
101 | + | ||
102 | + if (_.every(matched, match => match)) { | ||
103 | + return next(); | ||
104 | + } else { | ||
105 | + return res.redirect(helpers.urlFormat('/', sorts, 'list')); | ||
106 | + } | ||
107 | +}; | ||
108 | + | ||
82 | module.exports = { | 109 | module.exports = { |
83 | resolve, | 110 | resolve, |
84 | - channel | 111 | + channel, |
112 | + sortParams | ||
85 | }; | 113 | }; |
1 | { | 1 | { |
2 | "name": "m-yohobuy-node", | 2 | "name": "m-yohobuy-node", |
3 | - "version": "5.4.25", | 3 | + "version": "5.4.30", |
4 | "private": true, | 4 | "private": true, |
5 | "description": "A New Yohobuy Project With Express", | 5 | "description": "A New Yohobuy Project With Express", |
6 | "repository": { | 6 | "repository": { |
@@ -48,7 +48,7 @@ | @@ -48,7 +48,7 @@ | ||
48 | "xml2js": "^0.4.17", | 48 | "xml2js": "^0.4.17", |
49 | "yoho-express-session": "^2.0.0", | 49 | "yoho-express-session": "^2.0.0", |
50 | "yoho-node-lib": "^0.2.8", | 50 | "yoho-node-lib": "^0.2.8", |
51 | - "yoho-zookeeper": "^1.0.6" | 51 | + "yoho-zookeeper": "^1.0.8" |
52 | }, | 52 | }, |
53 | "devDependencies": { | 53 | "devDependencies": { |
54 | "autoprefixer": "^6.7.4", | 54 | "autoprefixer": "^6.7.4", |
public/js/3party/check.page.js
0 → 100644
1 | +require('3party/check.page.css'); | ||
2 | +require('../common'); | ||
3 | +// 图片验证码 | ||
4 | +let ImgCheck = require('plugin/img-check'); | ||
5 | + | ||
6 | +let imgCheck = new ImgCheck('#js-img-check', { | ||
7 | + useREM: { | ||
8 | + rootFontSize: 40, | ||
9 | + picWidth: 150 | ||
10 | + } | ||
11 | +}); | ||
12 | + | ||
13 | +imgCheck.init(); | ||
14 | + | ||
15 | +$(function() { | ||
16 | + $('.submit').on('click', function() { | ||
17 | + $.ajax({ | ||
18 | + method: 'POST', | ||
19 | + url: '/3party/check/submit', | ||
20 | + data: { | ||
21 | + captcha: $.trim(imgCheck.getResults()) | ||
22 | + }, | ||
23 | + success: function(ret) { | ||
24 | + if (ret.code === 200) { | ||
25 | + window.location.href = decodeURIComponent(window.queryString.refer) || '//m.yohobuy.com'; | ||
26 | + } else { | ||
27 | + imgCheck.refresh(); | ||
28 | + } | ||
29 | + } | ||
30 | + }); | ||
31 | + }); | ||
32 | +}); |
@@ -18,9 +18,9 @@ require('activity/promotion/promotion.page.css'); | @@ -18,9 +18,9 @@ require('activity/promotion/promotion.page.css'); | ||
18 | 18 | ||
19 | var share = require('../common/share'); | 19 | var share = require('../common/share'); |
20 | share({ | 20 | share({ |
21 | - title: 'YOHO!BUY有货邀请同学认证赠好礼!立享【学生专属】4大特权!', | 21 | + title: '【有货】学生认证看过来!专享特价,任性分期,立即走起→', |
22 | link: location.href, | 22 | link: location.href, |
23 | - desc: '每邀请1名同学完成认证立赠300有货币!上不封顶!', | 23 | + desc: '邀请同学认证更有惊喜好礼相送~', |
24 | imgUrl: 'http://img11.static.yhbimg.com/taobaocms/2017/01/17/16/0108d724a0ef1f001d3ee6010aa79d960e.png' | 24 | imgUrl: 'http://img11.static.yhbimg.com/taobaocms/2017/01/17/16/0108d724a0ef1f001d3ee6010aa79d960e.png' |
25 | }); | 25 | }); |
26 | 26 | ||
@@ -303,9 +303,9 @@ $( | @@ -303,9 +303,9 @@ $( | ||
303 | var shareUrl = location.href.replace('userUid=', 'oldUid='); | 303 | var shareUrl = location.href.replace('userUid=', 'oldUid='); |
304 | 304 | ||
305 | share({ | 305 | share({ |
306 | - title: 'YOHO!BUY有货邀请同学认证赠好礼!立享【学生专属】4大特权!', | 306 | + title: '【有货】学生认证看过来!专享特价,任性分期,立即走起→', |
307 | link: shareUrl, | 307 | link: shareUrl, |
308 | - desc: '每邀请1名同学完成认证立赠300有货币!上不封顶!', | 308 | + desc: '邀请同学认证更有惊喜好礼相送~', |
309 | imgUrl: 'http://img11.static.yhbimg.com/taobaocms/2017/01/17/16/0108d724a0ef1f001d3ee6010aa79d960e.png' | 309 | imgUrl: 'http://img11.static.yhbimg.com/taobaocms/2017/01/17/16/0108d724a0ef1f001d3ee6010aa79d960e.png' |
310 | }); | 310 | }); |
311 | } | 311 | } |
@@ -87,7 +87,9 @@ if (canOpenApp()) { | @@ -87,7 +87,9 @@ if (canOpenApp()) { | ||
87 | }, 2000); | 87 | }, 2000); |
88 | 88 | ||
89 | if (isiOS) { | 89 | if (isiOS) { |
90 | - window.location.href = appPath; | 90 | + setTimeout(function() { |
91 | + window.location.href = appPath; | ||
92 | + }, 2000); | ||
91 | } else { | 93 | } else { |
92 | ifr = document.createElement('iframe'); | 94 | ifr = document.createElement('iframe'); |
93 | ifr.src = appPath; | 95 | ifr.src = appPath; |
@@ -46,7 +46,7 @@ function switchLoginBtnStatus() { | @@ -46,7 +46,7 @@ function switchLoginBtnStatus() { | ||
46 | } | 46 | } |
47 | 47 | ||
48 | function resetForm() { | 48 | function resetForm() { |
49 | - $pwd.val('').focus(); | 49 | + // $pwd.val('').focus(); |
50 | $loginBtn.text('登录').addClass('disable'); | 50 | $loginBtn.text('登录').addClass('disable'); |
51 | } | 51 | } |
52 | 52 | ||
@@ -123,7 +123,7 @@ $loginBtn.on('touchstart', function() { | @@ -123,7 +123,7 @@ $loginBtn.on('touchstart', function() { | ||
123 | type: 'POST', | 123 | type: 'POST', |
124 | url: '/passport/login/auth', | 124 | url: '/passport/login/auth', |
125 | data, | 125 | data, |
126 | - success: function(data) { | 126 | + success: function(data) { //eslint-disable-line |
127 | var res, | 127 | var res, |
128 | LOGI_TYPE; | 128 | LOGI_TYPE; |
129 | 129 |
@@ -101,8 +101,12 @@ function setPassword() { | @@ -101,8 +101,12 @@ function setPassword() { | ||
101 | showErrTip(data.message); | 101 | showErrTip(data.message); |
102 | } | 102 | } |
103 | }, | 103 | }, |
104 | - error: function() { | 104 | + error: function(data) { |
105 | $btnSure.removeClass('disable'); | 105 | $btnSure.removeClass('disable'); |
106 | + | ||
107 | + if (data && data.responseJSON && data.responseJSON.message) { | ||
108 | + showErrTip(data.message); | ||
109 | + } | ||
106 | } | 110 | } |
107 | }); | 111 | }); |
108 | } | 112 | } |
public/scss/3party/check.page.css
0 → 100644
1 | +@import "layout/img-check"; | ||
2 | + | ||
3 | +.check-page { | ||
4 | + margin: 20px auto; | ||
5 | + width: 700px; | ||
6 | + | ||
7 | + .submit { | ||
8 | + width: 100%; | ||
9 | + height: 100px; | ||
10 | + line-height: 100px; | ||
11 | + text-align: center; | ||
12 | + font-size: 32px; | ||
13 | + color: #fff; | ||
14 | + background: #5cb85c; | ||
15 | + border-radius: 10px; | ||
16 | + } | ||
17 | +} |
@@ -312,14 +312,14 @@ | @@ -312,14 +312,14 @@ | ||
312 | id: id | 312 | id: id |
313 | }, resultData => { | 313 | }, resultData => { |
314 | 314 | ||
315 | - // /* 结果返回 */ | ||
316 | - // if (resultData.length < 1) { | ||
317 | - // areaCode.val(id); | ||
318 | - // let returnTitle = this.returnTitle(); | ||
319 | - | ||
320 | - // area.val(returnTitle); | ||
321 | - // this.show = false; | ||
322 | - // } | 315 | + /* 结果返回 */ |
316 | + if (resultData.length < 1) { | ||
317 | + areaCode.val(id); | ||
318 | + let returnTitle = this.returnTitle(); | ||
319 | + | ||
320 | + area.val(returnTitle); | ||
321 | + this.show = false; | ||
322 | + } | ||
323 | 323 | ||
324 | /* 数据绑定 */ | 324 | /* 数据绑定 */ |
325 | switch ((id + '').length) { | 325 | switch ((id + '').length) { |
@@ -30,3 +30,19 @@ exports.refererLimit = (referer, blacklist) => { // eslint-disable-line | @@ -30,3 +30,19 @@ exports.refererLimit = (referer, blacklist) => { // eslint-disable-line | ||
30 | 30 | ||
31 | return result; | 31 | return result; |
32 | }; | 32 | }; |
33 | + | ||
34 | +/** | ||
35 | + * 对象字段排序 | ||
36 | + */ | ||
37 | +exports.mapSort = obj => { | ||
38 | + if (!obj) { | ||
39 | + return {}; | ||
40 | + } | ||
41 | + let data = {}; | ||
42 | + | ||
43 | + Object.keys(obj).sort().forEach(k => { | ||
44 | + data[k] = obj[k]; | ||
45 | + }); | ||
46 | + | ||
47 | + return data; | ||
48 | +}; |
-
Please register or login to post a comment