Authored by 陈峰

pagecache支持多云

  1 +/**
  2 + *
  3 + * @author: chenfeng<feng.chen@yoho.cn>
  4 + * @date: 16/10/19
  5 + */
  6 +
  7 +'use strict';
  8 +import Model from './model';
  9 +import utils from 'utility';
  10 +import rp from 'request-promise';
  11 +import ws from '../../lib/ws';
  12 +
  13 +class CdnCache extends Model {
  14 + constructor() {
  15 + super('cdn_page');
  16 + }
  17 + async removeCache(queryUris, objectType) {
  18 + let self = this;
  19 + self._requestApi({
  20 + Action: 'RefreshObjectCaches',
  21 + ObjectPath: queryUris,
  22 + ObjectType: objectType
  23 + })
  24 + }
  25 + async _requestApi(params) {
  26 + let self = this;
  27 + let cdns = await self.findAll();
  28 +
  29 + if (!cdns.length) {
  30 + return;
  31 + }
  32 + params = Object.assign(params, {
  33 + Format: 'JSON',
  34 + Version: '2014-11-11',
  35 + AccessKeyId: cdns[0].keyId,
  36 + SignatureMethod: 'HMAC-SHA1',
  37 + TimeStamp: self._getUtcTime(new Date()),
  38 + SignatureVersion: '1.0',
  39 + SignatureNonce: parseInt(Math.random() * 100000, 10) + ''
  40 + });
  41 + let sign = self._sign(params, cdns[0].keySecret);
  42 + params.Signature = sign;
  43 + rp({
  44 + uri: `${cdns[0].scheme}://${cdns[0].cdnApi}`,
  45 + qs: params,
  46 + json: true
  47 + })
  48 + .then(function (res) {
  49 + self._broadcast(`清理提交成功,RefreshTaskId:${res.RefreshTaskId}`)
  50 + })
  51 + .catch(function (err) {
  52 + self._broadcast(`清理失败:${err.response.body}`)
  53 + });
  54 +
  55 + }
  56 + _getUtcTime(date) {
  57 + let year = date.getUTCFullYear();
  58 + let month = date.getUTCMonth() + 1;
  59 + let day = date.getUTCDate() < 10 ? '0' + date.getUTCDate() : date.getUTCDate();
  60 + let hour = date.getUTCHours() < 10 ? '0' + date.getUTCHours() : date.getUTCHours();
  61 + let minute = date.getUTCMinutes() < 10 ? '0' + date.getUTCMinutes() : date.getUTCMinutes();
  62 + let second = date.getUTCSeconds() < 10 ? '0' + date.getUTCSeconds() : date.getUTCSeconds();
  63 + month = month < 10 ? '0' + month : month;
  64 +
  65 + return `${year}-${month}-${day}T${hour}:${minute}:${second}Z`
  66 + }
  67 + _sign(params, keySecret) {
  68 + let data = {};
  69 + Object.keys(params).sort().forEach(k => data[k]=params[k]);
  70 + let signString = '';
  71 + Object.keys(data).forEach(k => {
  72 + data[k] = encodeURIComponent(data[k])
  73 + signString += `&${k}=${data[k]}`;
  74 + });
  75 + if (signString) {
  76 + signString = `GET&%2F&${encodeURIComponent(signString.substring(1, signString.length))}`;
  77 + let sign = utils.hmac('sha1', keySecret + '&', signString);
  78 + return sign;
  79 + }
  80 + return '';
  81 + }
  82 + _broadcast(message) {
  83 + console.log(message)
  84 + ws.broadcast(`/cdn_cache/log`, {
  85 + message: message
  86 + });
  87 + }
  88 + async init() {
  89 + let count = await this.count({});
  90 + if (count === 0) {
  91 + await this.insert({
  92 + scheme: 'https',
  93 + cdnApi: 'cdn.aliyuncs.com',
  94 + keyId: 'Mt6m3xVdWddj8bDe',
  95 + keySecret: 'ExHpmJJ7ayJEEMz2LUffKRe3ehMGDs'
  96 + })
  97 + }
  98 + }
  99 +}
  100 +
  101 +export default CdnCache;
@@ -9,6 +9,8 @@ import UserModel from './user'; @@ -9,6 +9,8 @@ import UserModel from './user';
9 import HotfixModel from './hotfix'; 9 import HotfixModel from './hotfix';
10 import OperationLoggerModel from './operation_logger'; 10 import OperationLoggerModel from './operation_logger';
11 import PageCacheModel from './page_cache'; 11 import PageCacheModel from './page_cache';
  12 +import CdnCacheModel from './cdn_cache';
  13 +import ProductCacheModel from './product_cache';
12 14
13 shelljs.mkdir('-p', config.dbDir); 15 shelljs.mkdir('-p', config.dbDir);
14 16
@@ -20,9 +22,13 @@ const User = new UserModel(); @@ -20,9 +22,13 @@ const User = new UserModel();
20 const Hotfix = new HotfixModel(); 22 const Hotfix = new HotfixModel();
21 const OperationLogger = new OperationLoggerModel(); 23 const OperationLogger = new OperationLoggerModel();
22 const PageCache = new PageCacheModel(); 24 const PageCache = new PageCacheModel();
  25 +const CdnCache = new CdnCacheModel();
  26 +const ProductCache = new ProductCacheModel();
23 27
24 User.init(); 28 User.init();
25 PageCache.init(); 29 PageCache.init();
  30 +CdnCache.init();
  31 +ProductCache.init();
26 32
27 export { 33 export {
28 Server, 34 Server,
@@ -32,5 +38,7 @@ export { @@ -32,5 +38,7 @@ export {
32 User, 38 User,
33 Hotfix, 39 Hotfix,
34 OperationLogger, 40 OperationLogger,
35 - PageCache 41 + PageCache,
  42 + CdnCache,
  43 + ProductCache
36 }; 44 };
@@ -28,7 +28,7 @@ class PageCahe extends Model { @@ -28,7 +28,7 @@ class PageCahe extends Model {
28 * @param {[array]} keys [key列表] 28 * @param {[array]} keys [key列表]
29 * @param {[string]} storeTableName [pc和wap的标识] 29 * @param {[string]} storeTableName [pc和wap的标识]
30 */ 30 */
31 - async removeCache(queryUris, storeTableName) { 31 + async removeCache(queryUris, storeTableName, serverType) {
32 let self = this; 32 let self = this;
33 let queryUriList = queryUris.split('\n'); 33 let queryUriList = queryUris.split('\n');
34 34
@@ -49,8 +49,7 @@ class PageCahe extends Model { @@ -49,8 +49,7 @@ class PageCahe extends Model {
49 // } 49 // }
50 50
51 } 51 }
52 - let servers = await self.findAll();  
53 - 52 + let servers = await self.find({server: serverType});
54 self._broadcast(`共${servers.length}nginx准备清理..`) 53 self._broadcast(`共${servers.length}nginx准备清理..`)
55 for (let i = 0; i < servers.length; i++) { 54 for (let i = 0; i < servers.length; i++) {
56 try { 55 try {
@@ -67,9 +66,9 @@ class PageCahe extends Model { @@ -67,9 +66,9 @@ class PageCahe extends Model {
67 * [全量清理缓存] 66 * [全量清理缓存]
68 * @param {[string]} storeTableName [pc和wap的标识] 67 * @param {[string]} storeTableName [pc和wap的标识]
69 */ 68 */
70 - async removeAllCache(storeTableName) { 69 + async removeAllCache(storeTableName, serverType) {
71 let self = this; 70 let self = this;
72 - let servers = await self.findAll(); 71 + let servers = await self.find({server: serverType});
73 72
74 self._broadcast(`共${servers.length}nginx准备清理`) 73 self._broadcast(`共${servers.length}nginx准备清理`)
75 for (let i = 0; i < servers.length; i++) { 74 for (let i = 0; i < servers.length; i++) {
@@ -216,14 +215,24 @@ class PageCahe extends Model { @@ -216,14 +215,24 @@ class PageCahe extends Model {
216 async init() { 215 async init() {
217 let count = await this.count({}); 216 let count = await this.count({});
218 if (count === 0) { 217 if (count === 0) {
219 - // await this.insert({  
220 - // host: '127.0.0.1',  
221 - // username: 'chenfeng',  
222 - // password: '1',  
223 - // port: 22,  
224 - // tag: 'nginx2',  
225 - // cachepath: '/usr/local/nginx'  
226 - // }); 218 + await this.insert({
  219 + host: '127.0.0.1',
  220 + username: 'chenfeng',
  221 + password: '1',
  222 + port: 22,
  223 + tag: 'nginx2',
  224 + cachepath: '/usr/local/nginx',
  225 + server: 'qCloud'
  226 + });
  227 + await this.insert({
  228 + host: '127.0.0.1',
  229 + username: 'chenfeng',
  230 + password: '1',
  231 + port: 22,
  232 + tag: 'nginx2',
  233 + cachepath: '/usr/local/nginx',
  234 + server: 'AWS'
  235 + });
227 // await this.insert({ 236 // await this.insert({
228 // host: '10.66.1.2', 237 // host: '10.66.1.2',
229 // username: 'node', 238 // username: 'node',
@@ -232,14 +241,15 @@ class PageCahe extends Model { @@ -232,14 +241,15 @@ class PageCahe extends Model {
232 // tag: 'nginx2', 241 // tag: 'nginx2',
233 // cachepath: '/usr/local/openresty/nginx' 242 // cachepath: '/usr/local/openresty/nginx'
234 // }); 243 // });
235 - await this.insert({  
236 - host: '10.66.1.3',  
237 - username: 'www',  
238 - password: 'yoho9646',  
239 - port: 22,  
240 - tag: 'nginx3',  
241 - cachepath: '/usr/local/openresty/nginx'  
242 - }); 244 + // await this.insert({
  245 + // host: '10.66.1.3',
  246 + // username: 'www',
  247 + // password: 'yoho9646',
  248 + // port: 22,
  249 + // tag: 'nginx3',
  250 + // cachepath: '/usr/local/openresty/nginx',
  251 + // server: 'qCloud'
  252 + // });
243 // await this.insert({ 253 // await this.insert({
244 // host: '10.66.1.15', 254 // host: '10.66.1.15',
245 // username: 'node', 255 // username: 'node',
  1 +/**
  2 + *
  3 + * @author: chenfeng<feng.chen@yoho.cn>
  4 + * @date: 16/10/20
  5 + */
  6 +
  7 +import Model from './model';
  8 +import rp from 'request-promise';
  9 +import ws from '../../lib/ws';
  10 +
  11 +class ProductCache extends Model{
  12 + constructor() {
  13 + super('product_cache');
  14 + }
  15 + async removePriceCache(list) {
  16 + let self = this;
  17 + let apis = await self.findAll();
  18 +
  19 + if (!apis.length) {
  20 + return;
  21 + }
  22 + let url = apis[0].priceCacheApi;
  23 + await self._batchPost(list, url);
  24 + }
  25 + async removeProductCache(list) {
  26 + let self = this;
  27 + let apis = await self.findAll();
  28 +
  29 + if (!apis.length) {
  30 + return;
  31 + }
  32 + let url = apis[0].productCacheApi;
  33 + await self._batchPost(list, url);
  34 + }
  35 + async _batchPost(list, url) {
  36 + let self = this;
  37 + let interval = 2;
  38 + return new Promise(async (resolve, reject) => {
  39 + let tick = parseInt(list.length / interval, 10) + (list.length % interval > 0 ? 1 : 0);
  40 + let post = async (i, len) => {
  41 + if (i < len) {
  42 + let limit = list.length > (i + 1) * interval ? (i + 1) * interval : list.length;
  43 + let datas = list.slice(i * interval, limit);
  44 + try {
  45 + await self._postApi(datas, url);
  46 + self._broadcast(`进度:${limit}/${list.length}`)
  47 + } catch(err) {
  48 + self._broadcast(`错误:${err.message}`)
  49 + }
  50 + setTimeout(async () => {
  51 + await post(++i, tick);
  52 + }, 500);
  53 + } else {
  54 + resolve();
  55 + }
  56 + }
  57 + await post(0, tick);
  58 + });
  59 + }
  60 + async _postApi(data, url) {
  61 + return new Promise((resolve, reject) => {
  62 + rp({
  63 + method: 'POST',
  64 + uri: url,
  65 + body: data,
  66 + json: true
  67 + })
  68 + .then(function (res) {
  69 + res.code === 200 ? resolve(res) : reject(res);
  70 + })
  71 + .catch(function (err) {
  72 + console.log(err.response.body);
  73 + reject(err.response.body)
  74 + });
  75 + })
  76 +
  77 + }
  78 + _broadcast(message) {
  79 + console.log(message)
  80 + ws.broadcast(`/product_cache/log`, {
  81 + message: message
  82 + });
  83 + }
  84 + async init() {
  85 + let count = await this.count({});
  86 +
  87 + if (count === 0) {
  88 + await this.insert({
  89 + priceCacheApi: 'http://service-test3.yohops.com:9999/erp/clear/batch/productPriceCache',
  90 + productCacheApi: 'http://service-test3.yohops.com:9999/erp/clear/batch/productCache'
  91 + });
  92 + }
  93 + }
  94 +}
  95 +export default ProductCache;
  1 +/**
  2 + *
  3 + * @author: chenfeng<feng.chen@yoho.cn>
  4 + * @date: 16/10/19
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +import Router from 'koa-router';
  10 +import {
  11 + CdnCache
  12 +} from '../../models'
  13 +
  14 +const r = new Router();
  15 +
  16 +const cdnCache = {
  17 + async query(ctx) {
  18 + let date = {
  19 + typeList: [{
  20 + name: '文件',
  21 + typeName: 'File',
  22 + }, {
  23 + name: '目录',
  24 + typeName: 'Directory',
  25 + }]
  26 + }
  27 + await ctx.render('action/cdn_cache', date);
  28 + },
  29 + async clear(ctx) {
  30 + let queryUris = ctx.request.body.query_uri;
  31 + let objectType = ctx.request.body.object_type;
  32 + if (queryUris.trim() && objectType) {
  33 + CdnCache.removeCache(queryUris, objectType);
  34 + }
  35 + return ctx.body = {
  36 + code: 200
  37 + };
  38 + }
  39 +}
  40 +r.get('/query', cdnCache.query);
  41 +r.post('/clear', cdnCache.clear);
  42 +
  43 +export default r;
@@ -24,6 +24,11 @@ const pageCahe = { @@ -24,6 +24,11 @@ const pageCahe = {
24 tableName: 'ngx_cache_wap', 24 tableName: 'ngx_cache_wap',
25 name: 'Wap' 25 name: 'Wap'
26 }], 26 }],
  27 + serverList: [{
  28 + name: 'AWS'
  29 + }, {
  30 + name: 'qCloud'
  31 + }],
27 count: 0 32 count: 0
28 }; 33 };
29 await ctx.render('action/page_cache', data); 34 await ctx.render('action/page_cache', data);
@@ -31,16 +36,23 @@ const pageCahe = { @@ -31,16 +36,23 @@ const pageCahe = {
31 async clear(ctx) { 36 async clear(ctx) {
32 let queryUris = ctx.request.body.query_uri; 37 let queryUris = ctx.request.body.query_uri;
33 let storeTableName = ctx.request.body.table_name; 38 let storeTableName = ctx.request.body.table_name;
  39 + let serverType = ctx.request.body.server;
34 if (queryUris && storeTableName) { 40 if (queryUris && storeTableName) {
35 - await PageCache.removeCache(queryUris, storeTableName); 41 + PageCache.removeCache(queryUris, storeTableName, serverType);
36 } 42 }
37 - 43 + return ctx.body = {
  44 + code: 200
  45 + };
38 }, 46 },
39 async clearAll(ctx) { 47 async clearAll(ctx) {
40 let storeTableName = ctx.request.body.table_name; 48 let storeTableName = ctx.request.body.table_name;
  49 + let serverType = ctx.request.body.server;
41 if (storeTableName) { 50 if (storeTableName) {
42 - await PageCache.removeAllCache(storeTableName); 51 + PageCache.removeAllCache(storeTableName, serverType);
43 } 52 }
  53 + return ctx.body = {
  54 + code: 200
  55 + };
44 } 56 }
45 } 57 }
46 r.get('/query', pageCahe.query); 58 r.get('/query', pageCahe.query);
  1 +/**
  2 + *
  3 + * @author: chenfeng<feng.chen@yoho.cn>
  4 + * @date: 16/10/19
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +import Router from 'koa-router';
  10 +import {
  11 + ProductCache
  12 +} from '../../models'
  13 +
  14 +const r = new Router();
  15 +
  16 +const productCache = {
  17 + async query(ctx) {
  18 + await ctx.render('action/product_cache');
  19 + },
  20 + async clear(ctx) {
  21 + let type = ctx.request.body.type;
  22 + if (type) {
  23 + ProductCache.removePriceCache([51068877, 51068893, 51117073])
  24 + }
  25 + return ctx.body = {
  26 + code: 200
  27 + };
  28 + }
  29 +}
  30 +
  31 +r.get('/query', productCache.query);
  32 +r.post('/clear', productCache.clear);
  33 +
  34 +export default r;
@@ -9,7 +9,9 @@ import monitor from './actions/monitor'; @@ -9,7 +9,9 @@ import monitor from './actions/monitor';
9 import users from './actions/users'; 9 import users from './actions/users';
10 import hotfix from './actions/hotfix'; 10 import hotfix from './actions/hotfix';
11 import operationLog from './actions/operation_log'; 11 import operationLog from './actions/operation_log';
12 -import pageCahe from './actions/page_cache'; 12 +import pageCache from './actions/page_cache';
  13 +import cdnCache from './actions/cdn_cache';
  14 +import productCache from './actions/product_cache';
13 15
14 const noAuth = new Router(); 16 const noAuth = new Router();
15 const base = new Router(); 17 const base = new Router();
@@ -35,7 +37,9 @@ export default function (app) { @@ -35,7 +37,9 @@ export default function (app) {
35 base.use('/users', users.routes(), users.allowedMethods()); 37 base.use('/users', users.routes(), users.allowedMethods());
36 base.use('/hotfix', hotfix.routes(), hotfix.allowedMethods()); 38 base.use('/hotfix', hotfix.routes(), hotfix.allowedMethods());
37 base.use('/operation', operationLog.routes(), operationLog.allowedMethods()); 39 base.use('/operation', operationLog.routes(), operationLog.allowedMethods());
38 - base.use('/page_cache', pageCahe.routes(), pageCahe.allowedMethods()); 40 + base.use('/page_cache', pageCache.routes(), pageCache.allowedMethods());
  41 + base.use('/cdn_cache', cdnCache.routes(), cdnCache.allowedMethods());
  42 + base.use('/product_cache', productCache.routes(), productCache.allowedMethods());
39 43
40 base.use('', index.routes(), index.allowedMethods()); 44 base.use('', index.routes(), index.allowedMethods());
41 45
  1 +<div class="pageheader">
  2 + <div class="media">
  3 + <div class="pageicon pull-left">
  4 + <i class="fa fa-th-list"></i>
  5 + </div>
  6 + <div class="media-body">
  7 + <ul class="breadcrumb">
  8 + <li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
  9 + <li><a href="/servers">缓存管理</a></li>
  10 + </ul>
  11 + <h4>清理CdnCahe缓存</h4>
  12 + </div>
  13 + </div>
  14 + <!-- media -->
  15 +</div>
  16 +
  17 +<div class="contentpanel">
  18 + <div class="row cache-panel">
  19 + <div class="panel panel-default">
  20 + <div class="panel-body">
  21 + <div class="col-sm-6">
  22 + <div class="operations mb20">
  23 +
  24 + </div>
  25 + <div class="query">
  26 + <textarea name="" id="uri" cols="30" rows="20" class="form-control" placeholder="支持多条换行输入"></textarea>
  27 + </div>
  28 + </div>
  29 + <div class="col-sm-6">
  30 + <div class="panel">
  31 + <div class="panel-heading">
  32 + <h4 class="panel-title">日志</h4>
  33 + </div><!-- panel-heading -->
  34 + <div class="panel-body yoho-log-dark">
  35 + <div class="results-list ">
  36 +
  37 + </div><!-- results-list -->
  38 + </div><!-- panel-body -->
  39 + </div><!-- panel -->
  40 + </div>
  41 + </div>
  42 + <div class="panel-footer">
  43 + <select id="objectType" class="form-control input-sm selcet-auto pull-left mr20">
  44 + {{#each typeList}}
  45 + <option value="{{typeName}}">{{name}}</option>
  46 + {{/each}}
  47 + </select>
  48 + <button class="btn btn-warning btn-clear pull-left">清除</button>
  49 + <div class="result pull-left ml20"></div>
  50 + </div>
  51 + </div>
  52 + </div>
  53 +</div>
  54 +
  55 +<script>
  56 + var posing = false;
  57 + $(document).on('ready pjax:success', function() {
  58 + $('.btn-clear').click(function() {
  59 + var typeName = $('#objectType').val();
  60 + var uri = $('#uri').val();
  61 + if (uri && typeName) {
  62 + $logs.empty();
  63 + $.post('/cdn_cache/clear', {
  64 + query_uri: uri,
  65 + object_type: typeName
  66 + }, function(res) {
  67 + });
  68 + }
  69 + })
  70 +
  71 + function layoutResize() {
  72 + $('.yoho-log-dark').height($('body').height() - 450);
  73 + }
  74 +
  75 + $(window).resize(function() {
  76 + layoutResize();
  77 + });
  78 +
  79 + layoutResize();
  80 + var $logs = $('.yoho-log-dark .results-list');
  81 + var $dark = $('.yoho-log-dark');
  82 + function appendLog(message) {
  83 + var html = '<p><span class="message">- ' + message + '</span></p>';
  84 + $logs.append(html);
  85 + $dark.scrollTop($logs[0].scrollHeight);
  86 + }
  87 +
  88 + var ws = io();
  89 + ws.on('connect', function() {
  90 + ws.on('/cdn_cache/log', function(data) {
  91 + appendLog(data.message)
  92 + });
  93 + });
  94 +
  95 + });
  96 +</script>
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 <li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li> 8 <li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
9 <li><a href="/servers">缓存管理</a></li> 9 <li><a href="/servers">缓存管理</a></li>
10 </ul> 10 </ul>
11 - <h4>清理缓存</h4> 11 + <h4>清理PageCahe缓存</h4>
12 </div> 12 </div>
13 </div> 13 </div>
14 <!-- media --> 14 <!-- media -->
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 23
24 </div> 24 </div>
25 <div class="query"> 25 <div class="query">
26 - <textarea name="" id="uri" cols="30" rows="20" class="form-control" placeholder="支持多条换行输入">http://m.yohobuy.com</textarea> 26 + <textarea name="" id="uri" cols="30" rows="20" class="form-control" placeholder="支持多条换行输入"></textarea>
27 </div> 27 </div>
28 </div> 28 </div>
29 <div class="col-sm-6"> 29 <div class="col-sm-6">
@@ -40,6 +40,11 @@ @@ -40,6 +40,11 @@
40 </div> 40 </div>
41 </div> 41 </div>
42 <div class="panel-footer"> 42 <div class="panel-footer">
  43 + <select id="servers" class="form-control input-sm selcet-auto pull-left mr20">
  44 + {{#each serverList}}
  45 + <option value="{{name}}">{{name}}</option>
  46 + {{/each}}
  47 + </select>
43 <select id="stores" class="form-control input-sm selcet-auto pull-left mr20"> 48 <select id="stores" class="form-control input-sm selcet-auto pull-left mr20">
44 {{#each storeList}} 49 {{#each storeList}}
45 <option value="{{tableName}}">{{name}}</option> 50 <option value="{{tableName}}">{{name}}</option>
@@ -56,39 +61,16 @@ @@ -56,39 +61,16 @@
56 <script> 61 <script>
57 var posing = false; 62 var posing = false;
58 $(document).on('ready pjax:success', function() { 63 $(document).on('ready pjax:success', function() {
59 - $('.btn-success').click(function() {  
60 - if (posing) {  
61 - return;  
62 - }  
63 - posing = true;  
64 - $('.page-count>span').text('查询中..');  
65 - $('.btn-search').addClass('disabled');  
66 - var tableName = $('#stores').val();  
67 - var uri = $('#uri').val();  
68 - if (uri && tableName) {  
69 - $.post('/page_cache/search', {  
70 - query_uri: uri,  
71 - table_name: tableName  
72 - }, function (res) {  
73 - posing = false;  
74 - $('.btn-search').removeClass('disabled');  
75 - if (res.code === 200) {  
76 - $('.page-count>span').text(res.data);  
77 - } else {  
78 - $('.page-count>span').text('查询失败');  
79 - }  
80 -  
81 - })  
82 - }  
83 - });  
84 $('.btn-clear').click(function() { 64 $('.btn-clear').click(function() {
85 var tableName = $('#stores').val(); 65 var tableName = $('#stores').val();
  66 + var server = $('#servers').val();
86 var uri = $('#uri').val(); 67 var uri = $('#uri').val();
87 if (uri && tableName) { 68 if (uri && tableName) {
88 $logs.empty(); 69 $logs.empty();
89 $.post('/page_cache/clear', { 70 $.post('/page_cache/clear', {
90 query_uri: uri, 71 query_uri: uri,
91 - table_name: tableName 72 + table_name: tableName,
  73 + server: server
92 }, function(res) { 74 }, function(res) {
93 }); 75 });
94 } 76 }
  1 +<div class="pageheader">
  2 + <div class="media">
  3 + <div class="pageicon pull-left">
  4 + <i class="fa fa-th-list"></i>
  5 + </div>
  6 + <div class="media-body">
  7 + <ul class="breadcrumb">
  8 + <li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
  9 + <li><a href="/servers">缓存管理</a></li>
  10 + </ul>
  11 + <h4>清理商品缓存</h4>
  12 + </div>
  13 + </div>
  14 + <!-- media -->
  15 +</div>
  16 +
  17 +<div class="contentpanel">
  18 + <div class="row cache-panel">
  19 + <div class="panel panel-default">
  20 + <div class="panel-body">
  21 + <div class="col-sm-6">
  22 + <div class="operations mb20">
  23 +
  24 + </div>
  25 + <div class="query">
  26 + <textarea name="" id="uri" cols="30" rows="20" class="form-control" placeholder="支持多条换行输入"></textarea>
  27 + </div>
  28 + </div>
  29 + <div class="col-sm-6">
  30 + <div class="panel">
  31 + <div class="panel-heading">
  32 + <h4 class="panel-title">日志</h4>
  33 + </div><!-- panel-heading -->
  34 + <div class="panel-body yoho-log-dark">
  35 + <div class="results-list ">
  36 +
  37 + </div><!-- results-list -->
  38 + </div><!-- panel-body -->
  39 + </div><!-- panel -->
  40 + </div>
  41 + </div>
  42 + <div class="panel-footer">
  43 + <select id="selectType" class="form-control input-sm selcet-auto pull-left mr20">
  44 + <option value="1">批量变价</option>
  45 + <option value="2">其它批量</option>
  46 + </select>
  47 + <button class="btn btn-warning btn-clear pull-left">清除</button>
  48 + <div class="result pull-left ml20"></div>
  49 + </div>
  50 + </div>
  51 + </div>
  52 +</div>
  53 +
  54 +<script>
  55 + var posing = false;
  56 + $(document).on('ready pjax:success', function() {
  57 + $('.btn-clear').click(function() {
  58 + var selectType = $('#selectType').val();
  59 + if (selectType) {
  60 + $logs.empty();
  61 + $.post('/product_cache/clear', {
  62 + type: selectType
  63 + }, function(res) {
  64 + });
  65 + }
  66 + })
  67 +
  68 + function layoutResize() {
  69 + $('.yoho-log-dark').height($('body').height() - 450);
  70 + }
  71 +
  72 + $(window).resize(function() {
  73 + layoutResize();
  74 + });
  75 +
  76 + layoutResize();
  77 + var $logs = $('.yoho-log-dark .results-list');
  78 + var $dark = $('.yoho-log-dark');
  79 + function appendLog(message) {
  80 + var html = '<p><span class="message">- ' + message + '</span></p>';
  81 + $logs.append(html);
  82 + $dark.scrollTop($logs[0].scrollHeight);
  83 + }
  84 +
  85 + var ws = io();
  86 + ws.on('connect', function() {
  87 + ws.on('/product_cache/log', function(data) {
  88 + appendLog(data.message)
  89 + });
  90 + });
  91 +
  92 + });
  93 +</script>
@@ -28,7 +28,8 @@ @@ -28,7 +28,8 @@
28 <li class="parent"><a href=""><i class="fa fa-history"></i> <span>缓存管理</span></a> 28 <li class="parent"><a href=""><i class="fa fa-history"></i> <span>缓存管理</span></a>
29 <ul class="children"> 29 <ul class="children">
30 <li><a href="/page_cache/query">PageCahe清理</a></li> 30 <li><a href="/page_cache/query">PageCahe清理</a></li>
31 - {{!-- <li><a href="/cdn_cache/query">CDN清理</a></li> --}} 31 + <li><a href="/cdn_cache/query">CDN清理</a></li>
  32 + <li><a href="/product_cache/query">商品清理</a></li>
32 </ul> 33 </ul>
33 </li> 34 </li>
34 {{#if is_master}} 35 {{#if is_master}}
  1 +0 info it worked if it ends with ok
  2 +1 verbose cli [ '/usr/local/bin/node',
  3 +1 verbose cli '/usr/local/bin/npm',
  4 +1 verbose cli 'run',
  5 +1 verbose cli 'babel',
  6 +1 verbose cli 'app.js' ]
  7 +2 info using npm@3.8.9
  8 +3 info using node@v6.2.0
  9 +4 verbose run-script [ 'prebabel', 'babel', 'postbabel' ]
  10 +5 info lifecycle yoho-node-ci@0.0.1~prebabel: yoho-node-ci@0.0.1
  11 +6 silly lifecycle yoho-node-ci@0.0.1~prebabel: no script for prebabel, continuing
  12 +7 info lifecycle yoho-node-ci@0.0.1~babel: yoho-node-ci@0.0.1
  13 +8 verbose lifecycle yoho-node-ci@0.0.1~babel: unsafe-perm in lifecycle true
  14 +9 verbose lifecycle yoho-node-ci@0.0.1~babel: PATH: /usr/local/lib/node_modules/npm/bin/node-gyp-bin:/Users/chenfeng/Documents/source/yoho/yoho-node-ci/node_modules/.bin:/usr/local/bin:/usr/local/lib/node_modules/npm/bin/node-gyp-bin:/Users/chenfeng/Documents/source/yoho/yoho-node-ci/node_modules/.bin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/share/dotnet:/Users/chenfeng/Documents/tools/android-sdk-macosx/platform-tools:/Users/chenfeng/Documents/tools/android-sdk-macosx/tools:/usr/local/nginx/sbin
  15 +10 verbose lifecycle yoho-node-ci@0.0.1~babel: CWD: /Users/chenfeng/Documents/source/yoho/yoho-node-ci
  16 +11 silly lifecycle yoho-node-ci@0.0.1~babel: Args: [ '-c', 'babel-node "app.js"' ]
  17 +12 silly lifecycle yoho-node-ci@0.0.1~babel: Returned: code: 1 signal: null
  18 +13 info lifecycle yoho-node-ci@0.0.1~babel: Failed to exec babel script
  19 +14 verbose stack Error: yoho-node-ci@0.0.1 babel: `babel-node "app.js"`
  20 +14 verbose stack Exit status 1
  21 +14 verbose stack at EventEmitter.<anonymous> (/usr/local/lib/node_modules/npm/lib/utils/lifecycle.js:245:16)
  22 +14 verbose stack at emitTwo (events.js:106:13)
  23 +14 verbose stack at EventEmitter.emit (events.js:191:7)
  24 +14 verbose stack at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/lib/utils/spawn.js:24:14)
  25 +14 verbose stack at emitTwo (events.js:106:13)
  26 +14 verbose stack at ChildProcess.emit (events.js:191:7)
  27 +14 verbose stack at maybeClose (internal/child_process.js:850:16)
  28 +14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:215:5)
  29 +15 verbose pkgid yoho-node-ci@0.0.1
  30 +16 verbose cwd /Users/chenfeng/Documents/source/yoho/yoho-node-ci
  31 +17 error Darwin 16.0.0
  32 +18 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "babel" "app.js"
  33 +19 error node v6.2.0
  34 +20 error npm v3.8.9
  35 +21 error code ELIFECYCLE
  36 +22 error yoho-node-ci@0.0.1 babel: `babel-node "app.js"`
  37 +22 error Exit status 1
  38 +23 error Failed at the yoho-node-ci@0.0.1 babel script 'babel-node "app.js"'.
  39 +23 error Make sure you have the latest version of node.js and npm installed.
  40 +23 error If you do, this is most likely a problem with the yoho-node-ci package,
  41 +23 error not with npm itself.
  42 +23 error Tell the author that this fails on your system:
  43 +23 error babel-node "app.js"
  44 +23 error You can get information on how to open an issue for this project with:
  45 +23 error npm bugs yoho-node-ci
  46 +23 error Or if that isn't available, you can get their info via:
  47 +23 error npm owner ls yoho-node-ci
  48 +23 error There is likely additional logging output above.
  49 +24 verbose exit [ 1, true ]
@@ -56,6 +56,7 @@ @@ -56,6 +56,7 @@
56 "nedb-promise": "^2.0.0", 56 "nedb-promise": "^2.0.0",
57 "qn": "^1.3.0", 57 "qn": "^1.3.0",
58 "qs": "^6.2.0", 58 "qs": "^6.2.0",
  59 + "request-promise": "^4.1.1",
59 "shelljs": "^0.7.0", 60 "shelljs": "^0.7.0",
60 "socket.io": "^1.4.6", 61 "socket.io": "^1.4.6",
61 "ssh2": "^0.5.0", 62 "ssh2": "^0.5.0",