Authored by yyq

客户端预加载

  1 +
  2 +const Router = require('koa-router');
  3 +const _ = require('lodash');
  4 +const md5 = require('md5');
  5 +const stream = require('stream');
  6 +
  7 +const CDNCONFIG = require('../../../config/cdn_config');
  8 +
  9 +
  10 +let r = new Router();
  11 +
  12 +const manifestPath = 'client/precache/statics/manifest.json';
  13 +
  14 +const client = {
  15 + index: async(ctx, next) => {
  16 + await ctx.render('action/client-precache', {
  17 + manifestPath
  18 + });
  19 + },
  20 + save: async(ctx, next) => {
  21 + let files = ctx.request.body.files;
  22 + let lastRevision = ctx.request.body.lastRevision;
  23 + let trimFiles = [];
  24 +
  25 + _.forEach(files, val => {
  26 + trimFiles.push(_.trim(val));
  27 + });
  28 +
  29 + trimFiles = _.compact(trimFiles);
  30 +
  31 + let revision = md5(trimFiles);
  32 +
  33 + if (revision === lastRevision) {
  34 + return ctx.body = {
  35 + code: 400,
  36 + message: '请修改后再保存'
  37 + };
  38 + }
  39 +
  40 + let ste = new Buffer.from(JSON.stringify({
  41 + files: trimFiles,
  42 + revision
  43 + }));
  44 +
  45 + const bufferStream = new stream.PassThrough();
  46 + let readable = bufferStream.end(new Buffer.from(JSON.stringify({
  47 + files: trimFiles,
  48 + revision
  49 + })));
  50 + let config = _.cloneDeep(CDNCONFIG.main);
  51 +
  52 + _.assign(config, {
  53 + key: manifestPath
  54 + });
  55 +
  56 + let qnMain = require('../../../lib/qiniu')(config);
  57 + let upResult = await qnMain.uploadAsync(readable, {key: manifestPath});
  58 +
  59 + if (upResult && upResult.url) {
  60 + return ctx.body = {
  61 + code: 200,
  62 + message: '修改成功',
  63 + data: {
  64 + url: upResult.url
  65 + }
  66 + };
  67 + } else {
  68 + return ctx.body = {
  69 + code: 400,
  70 + message: '修改失败'
  71 + };
  72 + }
  73 + }
  74 +}
  75 +
  76 +r.get('/client', client.index);
  77 +r.post('/client/save', client.save);
  78 +
  79 +module.exports = r;
@@ -11,6 +11,7 @@ const monitor = require('./actions/monitor'); @@ -11,6 +11,7 @@ const monitor = require('./actions/monitor');
11 const users = require('./actions/users'); 11 const users = require('./actions/users');
12 const hotfix = require('./actions/hotfix'); 12 const hotfix = require('./actions/hotfix');
13 const operationLog = require('./actions/operation_log'); 13 const operationLog = require('./actions/operation_log');
  14 +const clientPrecache = require('./actions/client-precache');
14 const pageCache = require('./actions/page_cache'); 15 const pageCache = require('./actions/page_cache');
15 const cdnCache = require('./actions/cdn_cache'); 16 const cdnCache = require('./actions/cdn_cache');
16 const productCache = require('./actions/product_cache'); 17 const productCache = require('./actions/product_cache');
@@ -57,6 +58,7 @@ module.exports = function(app) { @@ -57,6 +58,7 @@ module.exports = function(app) {
57 base.use('/monitor', monitor.routes(), monitor.allowedMethods()); 58 base.use('/monitor', monitor.routes(), monitor.allowedMethods());
58 base.use('/users', users.routes(), users.allowedMethods()); 59 base.use('/users', users.routes(), users.allowedMethods());
59 // base.use('/hotfix', hotfix.routes(), hotfix.allowedMethods()); 60 // base.use('/hotfix', hotfix.routes(), hotfix.allowedMethods());
  61 + base.use('/precache', clientPrecache.routes(), clientPrecache.allowedMethods());
60 base.use('/operation', operationLog.routes(), operationLog.allowedMethods()); 62 base.use('/operation', operationLog.routes(), operationLog.allowedMethods());
61 base.use('/check', checkcode.routes(), checkcode.allowedMethods()); 63 base.use('/check', checkcode.routes(), checkcode.allowedMethods());
62 base.use('/page_cache', pageCache.routes(), pageCache.allowedMethods()); 64 base.use('/page_cache', pageCache.routes(), pageCache.allowedMethods());
  1 +<style type="text/css">
  2 +.panel-body-container {
  3 + position: relative;
  4 + min-height: 100px;
  5 +}
  6 +
  7 +.edit-area {
  8 + width: 100%;
  9 + position: absolute;
  10 + left: 0;
  11 + top: 0;
  12 + bottom: 0;
  13 +}
  14 +
  15 +.edit-area > textarea {
  16 + width: 100%;
  17 + height: 100%;
  18 +}
  19 +
  20 +.pannel-option > * {
  21 + margin-left: 10px;
  22 +}
  23 +
  24 +.pannel-option .save,
  25 +.pannel-option .cancle {
  26 + display: none;
  27 +}
  28 +
  29 +.pannel-option.pannel-edit .edit {
  30 + display: none;
  31 +}
  32 +
  33 +.pannel-option.pannel-edit .cancle,
  34 +.pannel-option.pannel-edit .save {
  35 + display: inline;
  36 +}
  37 +
  38 +.edit-area {
  39 + display: none;
  40 +}
  41 +</style>
  42 +
  43 +<div class="pageheader">
  44 + <div class="media">
  45 + <div class="pageicon pull-left">
  46 + <i class="fa fa-th-list"></i>
  47 + </div>
  48 + <div class="media-body">
  49 + <ul class="breadcrumb">
  50 + <li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
  51 + <li><a href="/servers">Projects</a></li>
  52 + </ul>
  53 + <h4>客户端预加载清单</h4>
  54 + </div>
  55 + </div>
  56 + <!-- media -->
  57 +</div>
  58 +
  59 +<div class="contentpanel task-panel" style="padding-bottom:0;">
  60 + <!-- pageheader -->
  61 + <div class="panel panel-default">
  62 + <div class="panel-heading">
  63 + <div class="pull-right pannel-option">
  64 + <a href="javascript:;" class="tooltips edit">编辑</a>
  65 + <a href="javascript:;" class="tooltips save">保存</a>
  66 + <a href="javascript:;" class="tooltips cancle">取消</a>
  67 + </div>
  68 + <h4 class="panel-title">预加载清单</h4>
  69 + </div>
  70 + <div class="panel-body">
  71 + <div class="panel-body-container">
  72 + <div id="edit-area" class="edit-area">
  73 + <textarea id="textarea" class="form-control" pleaceholder="支持多条换行输入"></textarea>
  74 + </div>
  75 + <div id="static-list" class="static-list"></div>
  76 + </div>
  77 + </div>
  78 + </div>
  79 +</div>
  80 +
  81 +<script>
  82 + var files = [];
  83 + var revision = ''
  84 +
  85 + function resetManifest(list) {
  86 + files = list || [];
  87 +
  88 + var h = '';
  89 +
  90 + for (var i = 0; i < files.length; i++) {
  91 + h += '<p>' + files[i] + '</p>';
  92 + }
  93 +
  94 + $('#static-list').html(h);
  95 + }
  96 +
  97 + function getManifest(file) {
  98 + $.get(file + '?t=' + new Date().getTime(), function(res) {
  99 + if (res && res.files) {
  100 + resetManifest(res.files);
  101 + revision = res.revision;
  102 + };
  103 + });
  104 + }
  105 +
  106 + $('.pannel-option').on('click', '.tooltips', function() {
  107 + var $this = $(this),
  108 + $par = $this.parent(),
  109 + $edit = $('#edit-area');
  110 +
  111 + $par.toggleClass('pannel-edit');
  112 +
  113 + if ($this.hasClass('save')) {
  114 + var _files = $('#textarea').val().split('\n');
  115 + $.post('/precache/client/save', {
  116 + files: _files,
  117 + lastRevision: revision
  118 + }, function(res) {
  119 + if (res.code === 200) {
  120 + getManifest(res.data.url);
  121 + $edit.hide();
  122 + } else {
  123 + $par.addClass('pannel-edit');
  124 + alert(res.message || '保存失败');
  125 + }
  126 + });
  127 + } else if ($this.hasClass('edit')) {
  128 + $('#textarea').val(files.join('\n'));
  129 + $edit.show();
  130 + } else {
  131 + $edit.hide();
  132 + }
  133 + });
  134 +
  135 + getManifest('//cdn.yoho.cn/{{manifestPath}}');
  136 +</script>
@@ -26,6 +26,10 @@ const normalMenus = [ @@ -26,6 +26,10 @@ const normalMenus = [
26 title: 'Memcached', 26 title: 'Memcached',
27 link: '/api_cache', 27 link: '/api_cache',
28 isClassic: true 28 isClassic: true
  29 + }, {
  30 + title: '客户端预加载',
  31 + link: '/precache/client',
  32 + isClassic: true
29 }] 33 }]
30 }, 34 },
31 { 35 {