Authored by 陈峰

Merge branch 'feature/page-cache' of http://git.yoho.cn/OPENTECH/yoho-node-ci in…

…to feature/page-cache
  1 +/**
  2 + * 采集数据
  3 + *
  4 + * @class Collect
  5 + * @author shenzm<zhimin.shen@yoho.cn>
  6 + * @date 2016/10/12
  7 + */
  8 +
  9 +import ssh from 'ssh2';
  10 +import Rp from 'request-promise';
  11 +import Trace from '../logger/trace.js';
  12 +import config from '../../config/config.js';
  13 +
  14 +import {
  15 + Server
  16 +} from '../models';
  17 +
  18 +var tracer = new Trace({
  19 + host: config.influxdb.host,
  20 + port: config.influxdb.port
  21 +});
  22 +
  23 +class Collect {
  24 + constructor(host, projectname) {
  25 + this.host = host;
  26 + this.projectname = projectname;
  27 + this.scriptRunning = false;
  28 + this.retry = {};
  29 + }
  30 +
  31 + async collect() {
  32 + let self = this;
  33 + let server = await Server.findByHost(self.host);
  34 + self.server = {
  35 + host: server.host,
  36 + username: server.username,
  37 + password: server.password,
  38 + port: server.port
  39 + }
  40 +
  41 + let obj = {
  42 + 'total': 0,
  43 + 'status': {}
  44 + };
  45 +
  46 + Rp({
  47 + uri: `http://${self.host}:9615`,
  48 + json: true
  49 + }).then(function(data) {
  50 + var processes = data.processes || [];
  51 + processes.forEach(function(p) {
  52 + if (p.name === 'pm2-server-monit') {
  53 + var cpuUsg = p.pm2_env.axm_monitor['CPU usage'].value;
  54 + var freeMen = p.pm2_env.axm_monitor['Free memory'].value;
  55 +
  56 + obj.cpuUsg = cpuUsg ? parseFloat(cpuUsg.replace('%', '')).toFixed(2) : '';
  57 + obj.freeMen = freeMen ? parseFloat(freeMen.replace('%', '')).toFixed(2) : '';
  58 + }
  59 +
  60 + if (p.name === self.projectname) {
  61 + obj.total++;
  62 +
  63 + if (!obj.status[p.pm2_env.status]) {
  64 + obj.status[p.pm2_env.status] = 1;
  65 + } else {
  66 + obj.status[p.pm2_env.status]++;
  67 + }
  68 + }
  69 + });
  70 +
  71 + if (obj.cpuUsg === undefined) { // install server monit
  72 + const script = 'pm2 install pm2-server-monit';
  73 + self.execScript(script);
  74 + }
  75 +
  76 + // add into influxDB todo
  77 + tracer.report('server_data', {
  78 + host: self.host
  79 + }, obj);
  80 + }).catch(function(err) {
  81 + const script = 'pm2 web';
  82 + self.execScript(script);
  83 + });
  84 + }
  85 +
  86 + execScript(script) {
  87 + let self = this;
  88 + if (self.scriptRunning || self.retry[`${self.host}_${script}`] > 5) {
  89 + // 脚本执行中,或者重试次数大于5次以上时,不执行脚本
  90 + return;
  91 + }
  92 +
  93 + let retryCount = self.retry[`${self.host}_${script}`] || 0;
  94 + self.retry[`${self.host}_${script}`] = ++retryCount;
  95 + self.scriptRunning = true;
  96 +
  97 + let conn = new ssh.Client();
  98 + conn.on('ready', () => {
  99 + self._log(`>>>>host:[${self.host}] script[${script}]`);
  100 + conn.exec(`${script}`, (err, stream) => {
  101 + if (err) {
  102 + conn.end();
  103 + self._log(`host:[${self.host}] script:[${script}] exec fail error: ${err}`);
  104 + self.scriptRunning = false;
  105 + } else {
  106 + stream.stdout.on('data', (data) => {
  107 + //self._log(data.toString());
  108 + });
  109 + stream.stderr.on('data', (data) => {
  110 + //self._log(data.toString());
  111 + });
  112 + stream.on('exit', (code) => {
  113 + conn.end();
  114 + if (code === 0) {
  115 + self._log(`host:[${self.host}] script[${script}] exec success`);
  116 + } else {
  117 + self._log(`host:[${self.host}] script[${script}] exec fail`);
  118 + }
  119 + self.scriptRunning = false;
  120 + });
  121 + }
  122 + });
  123 + }).on('error', (err) => {
  124 + self._log(`connect error ${self.host} ${err}`);
  125 + self.scriptRunning = false;
  126 + }).connect(Object.assign(self.server, {
  127 + readyTimeout: 5000
  128 + }));
  129 + }
  130 +
  131 + _log(msg) {
  132 + console.log(msg);
  133 + }
  134 +}
  135 +
  136 +export default Collect;
@@ -170,7 +170,6 @@ class Deploy { @@ -170,7 +170,6 @@ class Deploy {
170 } 170 }
171 171
172 _log(msg) { 172 _log(msg) {
173 - console.log(msg)  
174 ws.broadcast(`/deploy/${this.project._id}/log`, { 173 ws.broadcast(`/deploy/${this.project._id}/log`, {
175 host: this.info.host, 174 host: this.info.host,
176 msg: msg 175 msg: msg
1 /** 1 /**
2 * 跟踪监控工具, 将监控数据写入influxdb 2 * 跟踪监控工具, 将监控数据写入influxdb
3 * 3 *
4 - * @usage:  
5 - * <code>  
6 - * let trace = new Trace({  
7 - * host: '54.222.219.223',  
8 - * port: 4444  
9 - * });  
10 - *  
11 - * let testTrace = trace.trace('test_key'); // createOrChoose a measurement  
12 - *  
13 - * // testTrace(someTags, someFields);  
14 - * testTrace({ foo: 'bar', foobar: 'baz2'}, {value: 123, value2: 'aaa 123', value3: 1.3, value4: false});  
15 - *  
16 - * </code>  
17 - *  
18 * @author: jiangfeng<jeff.jiang@yoho.cn> 4 * @author: jiangfeng<jeff.jiang@yoho.cn>
19 * @date: 16/8/1 5 * @date: 16/8/1
20 */ 6 */
@@ -36,79 +22,13 @@ class Trace { @@ -36,79 +22,13 @@ class Trace {
36 this.options = options; 22 this.options = options;
37 } 23 }
38 24
39 - /**  
40 - * create or choose a measurement to write point in.  
41 - * @param name {string} the measurement name  
42 - * @param options {object} some options. the protocol of influxdb connection.  
43 - * @returns {function()} a point write function  
44 - */  
45 - trace(name, options) {  
46 - options = _.assign({  
47 - protocol: 'udp'  
48 - }, options);  
49 -  
50 - let self = this;  
51 -  
52 - if (options.protocol === 'udp') {  
53 - return (key, fields) => {  
54 - return self.udpTrace(name, options, key, fields);  
55 - };  
56 - } else if (options.protocol === 'http') {  
57 - return (key, fields) => {  
58 - return self.httpTrace(name, options, key, fields);  
59 - };  
60 - } 25 + report(name, keys, data) {
  26 + if (this.options.appName) {
  27 + keys.appName = this.options.appName;
61 } 28 }
62 29
63 - /**  
64 - * write point into influxdb by UDP  
65 - *  
66 - * @param name {string} the measurement name  
67 - * @param options {object}  
68 - * @param key {object} some keys of data  
69 - * @param fields {object} some fields of data  
70 - * @returns {Promise}  
71 - */  
72 - udpTrace(name, options, key, fields) {  
73 -  
74 - if (_.isArray(key)) {  
75 - key.forEach(p => {  
76 - let line = `${this._escape(name)},${this._makeLine(p)}`;  
77 -  
78 - return this._updPostLine(line);  
79 - });  
80 - } else {  
81 - let line = `${this._escape(name)},${this._makeLine(key, false)} ${this._makeLine(fields, true)}`;  
82 -  
83 - return this._updPostLine(line);  
84 - }  
85 - }  
86 -  
87 - /**  
88 - * upd send.  
89 - * @param line {string} @see infulxdb's line protocol  
90 - * @returns {Promise}  
91 - * @private  
92 - */  
93 - _updPostLine(line) {  
94 - let self = this;  
95 -  
96 - return new Promise((resolve, reject) => {  
97 - let socket = dgram.createSocket("udp4");  
98 - let buff = new Buffer(line);  
99 -  
100 -  
101 - socket.send(buff, 0, buff.length, self.options.port, self.options.host, (err, rp) => {  
102 - socket.close();  
103 -  
104 - console.log(rp);  
105 - if (err) {  
106 - reject(err);  
107 - } else {  
108 - resolve();  
109 - }  
110 - });  
111 - }); 30 + let line = `${this._escape(name)},${this._makeLine(keys, false)} ${this._makeLine(data, true)}`;
  31 + return this._send(line);
112 } 32 }
113 33
114 /** 34 /**
@@ -137,79 +57,51 @@ class Trace { @@ -137,79 +57,51 @@ class Trace {
137 } 57 }
138 58
139 /** 59 /**
140 - * data escape with influxdb's line protocol.  
141 - *  
142 - * @param value {*}  
143 - * @param withQuote {boolean}  
144 - * @returns {*} 60 + * upd send.
  61 + * @param line {string} @see infulxdb's line protocol
  62 + * @returns {Promise}
145 * @private 63 * @private
146 */ 64 */
  65 + _send(line) {
  66 + let self = this;
  67 +
  68 + return new Promise((resolve, reject) => {
  69 + let socket = dgram.createSocket('udp4');
  70 + let buff = new Buffer(line);
  71 +
  72 + socket.send(buff, 0, buff.length, self.options.port, self.options.host, (err) => {
  73 + socket.close();
  74 +
  75 + if (err) {
  76 + reject(err);
  77 + } else {
  78 + resolve();
  79 + }
  80 + });
  81 + });
  82 + }
  83 +
147 _escape(value, withQuote) { 84 _escape(value, withQuote) {
148 if (_.isString(value)) { 85 if (_.isString(value)) {
149 - value = _.replace(value, /,/g, '\\,');  
150 - value = _.replace(value, /=/g, '\\=');  
151 -  
152 if (withQuote) { 86 if (withQuote) {
153 value = '"' + value + '"'; 87 value = '"' + value + '"';
154 } else { 88 } else {
  89 + value = _.replace(value, /,/g, '\\,');
  90 + value = _.replace(value, /=/g, '\\=');
155 value = _.replace(value, /\s/g, '\\ '); 91 value = _.replace(value, /\s/g, '\\ ');
156 } 92 }
157 } else if (_.isInteger(value)) { 93 } else if (_.isInteger(value)) {
158 if (withQuote) { 94 if (withQuote) {
159 value = value + 'i'; 95 value = value + 'i';
160 } 96 }
  97 + } else if (_.isObject(value)) {
  98 + value = '"' + _.replace(JSON.stringify(value), /"/g, '\\"') + '"';
  99 + } else if (_.isNull(value) || _.isNil(value)) {
  100 + value = '""';
161 } 101 }
162 102
163 return value; 103 return value;
164 } 104 }
165 -  
166 - /**  
167 - * write point into influxdb by HTTP. use the open source node-influx module.  
168 - * @see https://github.com/node-influx/node-influx  
169 - *  
170 - * @param name {string} the measurement name  
171 - * @param options {object}  
172 - * @param key {object} some keys of data  
173 - * @param fields {object} some fields of data  
174 - * @returns {Promise}  
175 - */  
176 - httpTrace(name, options, key, fields) {  
177 - let client = this.getHttpClient();  
178 -  
179 - return new Promise((resolve, reject) => {  
180 - if (_.isArray(key)) {  
181 - client.writePoints(name, key, options, (err, rp) => {  
182 - if (err) {  
183 - reject(err);  
184 - } else {  
185 - resolve(rp)  
186 - }  
187 - });  
188 - } else {  
189 - client.writePoint(name, fields, key, options, (err, rp) => {  
190 - if (err) {  
191 - reject(err);  
192 - } else {  
193 - resolve(rp)  
194 - }  
195 - });  
196 - }  
197 - });  
198 - }  
199 -  
200 - /**  
201 - * the singleton http client.  
202 - *  
203 - * @returns {*}  
204 - */  
205 - getHttpClient() {  
206 - if (!this.httpClient) {  
207 - this.httpClient = influx(this.options);  
208 - }  
209 -  
210 - return this.httpClient;  
211 - }  
212 } 105 }
213 106
214 -  
215 module.exports = Trace; 107 module.exports = Trace;
  1 +'use strict';
  2 +
  3 +import Collect from '../../ci/collect_data';
  4 +
  5 +import {
  6 + Project
  7 +} from '../../models';
  8 +
  9 +export default {
  10 + async collect(ctx) {
  11 + let projects,
  12 + servers = {};
  13 +
  14 + setInterval(async() => {
  15 + if (!projects) projects = await Project.findAll();
  16 + if (!projects || !projects.length) {
  17 + return;
  18 + }
  19 +
  20 + projects.forEach(async(p) => {
  21 + const hosts = p.deploy['production'].target;
  22 + hosts.forEach((host) => {
  23 + if (!servers[host]) {
  24 + servers[host] = new Collect(host, p.name);
  25 + }
  26 +
  27 + servers[host].collect();
  28 + });
  29 + });
  30 + }, 5000);
  31 + }
  32 +};
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 import Router from 'koa-router'; 3 import Router from 'koa-router';
4 import moment from 'moment'; 4 import moment from 'moment';
5 import _ from 'lodash'; 5 import _ from 'lodash';
  6 +import process from 'process';
6 7
7 import {Degrade, DegradeServer} from '../../models'; 8 import {Degrade, DegradeServer} from '../../models';
8 9
@@ -13,22 +14,32 @@ import tester from '../../zookeeper/tester'; @@ -13,22 +14,32 @@ import tester from '../../zookeeper/tester';
13 const router = new Router(); 14 const router = new Router();
14 15
15 const ctl = { 16 const ctl = {
16 - async getServer() {  
17 - let server = await DegradeServer.findAll({}); 17 + async index (ctx) {
  18 + let qcloudServer = await DegradeServer.findOne({
  19 + type: 'qcloud'
  20 + });
18 21
19 - server = _.last(server); 22 + if (!qcloudServer) {
  23 + qcloudServer = {};
  24 + }
  25 +
  26 + let awsServer = await DegradeServer.findOne({
  27 + type: 'aws'
  28 + });
20 29
21 - if (server) {  
22 - return `${server.ip}:${server.port}`; 30 + if (!awsServer) {
  31 + awsServer = {};
23 } 32 }
24 - },  
25 - async index (ctx) {  
26 - let serverPath = await ctl.getServer();  
27 - let serverSplit = serverPath ? serverPath.split(':') : ['', ''];  
28 33
29 await ctx.render('action/degrade', { 34 await ctx.render('action/degrade', {
30 - ip: serverSplit[0],  
31 - port: serverSplit[1] 35 + qCloudConfig: {
  36 + ip: qcloudServer.ip,
  37 + port: qcloudServer.port
  38 + },
  39 + awsConfig: {
  40 + ip: awsServer.ip,
  41 + port: awsServer.port
  42 + }
32 }); 43 });
33 }, 44 },
34 async connect(ctx) { 45 async connect(ctx) {
@@ -50,6 +61,7 @@ const ctl = { @@ -50,6 +61,7 @@ const ctl = {
50 let degrades = await Degrade.findAll(); 61 let degrades = await Degrade.findAll();
51 62
52 for(let i of degrades) { 63 for(let i of degrades) {
  64 + // 从zookeeper读取配置信息,memcached只做PHP读取使用
53 i.checked = await getter(server, i.path); 65 i.checked = await getter(server, i.path);
54 } 66 }
55 67
@@ -63,15 +75,18 @@ const ctl = { @@ -63,15 +75,18 @@ const ctl = {
63 }); 75 });
64 }, 76 },
65 async server(ctx) { 77 async server(ctx) {
66 - let ip = ctx.request.body.ip;  
67 - let port = ctx.request.body.port; 78 + let {ip, port, type} = ctx.request.body;
68 79
69 - let serverCount = await DegradeServer.count({}); 80 + let serverCount = await DegradeServer.count({
  81 + type: type
  82 + });
70 83
71 // keep one server 84 // keep one server
72 if (serverCount) { 85 if (serverCount) {
73 - let serverConfig = await DegradeServer.findAll({});  
74 - let id = _.last(serverConfig)._id; // get the latest item 86 + let serverConfig = await DegradeServer.findOne({
  87 + type: type
  88 + });
  89 + let id = serverConfig._id; // get the latest item
75 90
76 await DegradeServer.update({ 91 await DegradeServer.update({
77 _id: id 92 _id: id
@@ -84,30 +99,41 @@ const ctl = { @@ -84,30 +99,41 @@ const ctl = {
84 } else { 99 } else {
85 await DegradeServer.insert({ 100 await DegradeServer.insert({
86 ip: ip, 101 ip: ip,
87 - port: port 102 + port: port,
  103 + type: type
88 }); 104 });
89 } 105 }
90 106
91 ctx.body = { 107 ctx.body = {
92 code: 200, 108 code: 200,
93 - message: `${serverCount ? 'update' : 'new'} server success` 109 + message: `${serverCount ? 'update' : 'new'} ${type} server success`
94 }; 110 };
95 }, 111 },
96 async setter(ctx) { 112 async setter(ctx) {
97 - let {checked, id} = ctx.query; 113 + let {checked, id, type} = ctx.query;
98 114
99 let theDegrade = await Degrade.findById(id); 115 let theDegrade = await Degrade.findById(id);
100 116
101 let path = theDegrade.path; 117 let path = theDegrade.path;
102 118
103 - let serverPath = await ctl.getServer(); 119 + let server = await await DegradeServer.findOne({
  120 + type: type
  121 + });
104 122
105 - await setter(serverPath, path, checked.toString()); 123 + let result = await setter(`${server.ip}:${server.port}`, path, checked.toString());
106 124
  125 + // result结果以zookeeper写为准
  126 + if (result) {
107 ctx.body = { 127 ctx.body = {
108 code: 200, 128 code: 200,
109 message: 'update success' 129 message: 'update success'
110 }; 130 };
  131 + } else {
  132 + ctx.body = {
  133 + code: 500,
  134 + message: 'update fail,Please retry'
  135 + }
  136 + }
111 } 137 }
112 }; 138 };
113 139
@@ -61,6 +61,9 @@ const p = { @@ -61,6 +61,9 @@ const p = {
61 deploy.env = env; 61 deploy.env = env;
62 deploy.name = envs[env]; 62 deploy.name = envs[env];
63 63
  64 + if (typeof deploy.target === 'string') {
  65 + deploy.target = [deploy.target];
  66 + }
64 67
65 let promises = deploy.target.map((host) => { 68 let promises = deploy.target.map((host) => {
66 console.log('read host :' + host); 69 console.log('read host :' + host);
@@ -220,6 +223,10 @@ const p = { @@ -220,6 +223,10 @@ const p = {
220 let project = await Project.findById(building.projectId); 223 let project = await Project.findById(building.projectId);
221 let targets = project.deploy[building.env].target; 224 let targets = project.deploy[building.env].target;
222 225
  226 + if (typeof targets === 'string') {
  227 + targets = [targets];
  228 + }
  229 +
223 targets.forEach(async(host) => { 230 targets.forEach(async(host) => {
224 231
225 let doc = await DeployInfo.findOne({ 232 let doc = await DeployInfo.findOne({
@@ -394,7 +401,11 @@ const p = { @@ -394,7 +401,11 @@ const p = {
394 return; 401 return;
395 } 402 }
396 403
397 - const hosts = project.deploy[env].target; 404 + let hosts = project.deploy[env].target;
  405 +
  406 + if (typeof hosts === 'string') {
  407 + hosts = [hosts];
  408 + }
398 hosts.forEach((host) => { 409 hosts.forEach((host) => {
399 var obj = { 410 var obj = {
400 'total': 0, 411 'total': 0,
@@ -10,6 +10,7 @@ import Koa from 'koa'; @@ -10,6 +10,7 @@ import Koa from 'koa';
10 import hbs from '../../middleware/yoho-koa-hbs'; 10 import hbs from '../../middleware/yoho-koa-hbs';
11 import helpers from '../../lib/helpers'; 11 import helpers from '../../lib/helpers';
12 import routers from './routers' 12 import routers from './routers'
  13 +import collectData from './actions/collect_data';
13 14
14 const app = new Koa(); 15 const app = new Koa();
15 16
@@ -29,6 +30,9 @@ const mastersUrl = [ @@ -29,6 +30,9 @@ const mastersUrl = [
29 '/users' 30 '/users'
30 ]; 31 ];
31 32
  33 +// 服务器监控数据采集
  34 +collectData.collect();
  35 +
32 app.use(async(ctx, next) => { 36 app.use(async(ctx, next) => {
33 ctx.locals = { 37 ctx.locals = {
34 title: 'Yoho Node.js 持续集成平台' 38 title: 'Yoho Node.js 持续集成平台'
@@ -38,7 +42,7 @@ app.use(async(ctx, next) => { @@ -38,7 +42,7 @@ app.use(async(ctx, next) => {
38 ctx.locals.layout = null; 42 ctx.locals.layout = null;
39 } 43 }
40 44
41 - if (ctx.session && ctx.session.user ) { 45 + if (ctx.session && ctx.session.user) {
42 ctx.locals.is_master = ctx.session.user.role === '1000'; 46 ctx.locals.is_master = ctx.session.user.role === '1000';
43 ctx.locals.current_user = ctx.session.user; 47 ctx.locals.current_user = ctx.session.user;
44 } 48 }
@@ -35,136 +35,161 @@ @@ -35,136 +35,161 @@
35 float: right; 35 float: right;
36 cursor: pointer; 36 cursor: pointer;
37 } 37 }
  38 +
  39 + .config-panel .server-port {
  40 + width: 100px;
  41 + }
  42 +
  43 + .config-panel .form-inline {
  44 + padding: 20px 0;
  45 + border-bottom: 1px solid #d9edf7;
  46 + }
38 </style> 47 </style>
39 <div class="degrade-page"> 48 <div class="degrade-page">
40 <div class="panel panel-info"> 49 <div class="panel panel-info">
41 - <div class="panel-heading">zookeeper server</div> 50 + <div class="panel-heading">zookeeper configure</div>
42 <div class="panel-body"> 51 <div class="panel-body">
  52 + <div class="row">
  53 + <div class="col-md-6 config-panel" data-type="qcloud">
  54 + <div class="panel panel-success">
  55 + <div class="panel-heading">
  56 + 腾讯云
  57 + <span class="refresh">
  58 + <i class="glyphicon glyphicon-refresh"></i>
  59 + </span>
  60 + </div>
  61 + <div class="panel-body">
  62 + {{# qCloudConfig}}
43 <div class="form-inline"> 63 <div class="form-inline">
44 <div class="current-server{{#unless ip}} hide{{/unless}}"> 64 <div class="current-server{{#unless ip}} hide{{/unless}}">
45 当前server地址: 65 当前server地址:
46 - <span id="server-in-use" class="server-in-use" data-ip="{{ip}}" data-port="{{port}}">{{ip}}:{{port}}</span>  
47 - <span id="server-modify-btn" class="ip-port-modify-btn">修改</span> 66 + <span class="server-in-use" data-ip="{{ip}}" data-port="{{port}}">{{ip}}:{{port}}</span>
  67 + <span class="ip-port-modify-btn">修改</span>
48 </div> 68 </div>
49 <div class="server-editor{{#if ip}} hide{{/if}}"> 69 <div class="server-editor{{#if ip}} hide{{/if}}">
50 <div class="form-group"> 70 <div class="form-group">
51 - <label for="server-ip">Server IP</label>  
52 - <input id="server-ip" class="form-control" type="text" placeholder="input server ip" value="{{ip}}"> 71 + <label>Server IP</label>
  72 + <input class="form-control server-ip" type="text" value="{{ip}}">
53 </div> 73 </div>
54 <div class="form-group"> 74 <div class="form-group">
55 <label for="server-port">Server Port</label> 75 <label for="server-port">Server Port</label>
56 - <input id="server-port" class="form-control" type="text" placeholder="input server port" value="{{port}}"> 76 + <input class="form-control server-port" type="text" value="{{port}}">
57 </div> 77 </div>
58 - <span id="server-sure" class="btn btn-default">保存</span>  
59 - <span id="err-tip" class="err-tip hide">  
60 - <i class="glyphicon glyphicon-remove-sign"></i>  
61 - 小哥,填错了吧  
62 - </span> 78 + <span class="btn btn-default server-sure">保存</span>
63 </div> 79 </div>
64 </div> 80 </div>
  81 + {{/ qCloudConfig}}
  82 +
  83 + <div class="panel-body list-container">
  84 + Waitting for connecting to server...
  85 + </div>
65 </div> 86 </div>
66 </div> 87 </div>
67 - <div class="panel panel-danger"> 88 + </div>
  89 + <div class="col-md-6 config-panel" data-type="aws">
  90 + <div class="panel panel-warning">
68 <div class="panel-heading"> 91 <div class="panel-heading">
69 - degrade point  
70 - <span id="refresh-connection" class="refresh"> 92 + AWS
  93 + <span class="refresh">
71 <i class="glyphicon glyphicon-refresh"></i> 94 <i class="glyphicon glyphicon-refresh"></i>
72 </span> 95 </span>
73 </div> 96 </div>
74 - <div id="list-container" class="panel-body"> 97 + <div class="panel-body">
  98 + {{# awsConfig}}
  99 + <div class="form-inline">
  100 + <div class="current-server{{#unless ip}} hide{{/unless}}">
  101 + 当前server地址:
  102 + <span class="server-in-use" data-ip="{{ip}}" data-port="{{port}}">{{ip}}:{{port}}</span>
  103 + <span class="ip-port-modify-btn">修改</span>
  104 + </div>
  105 + <div class="server-editor{{#if ip}} hide{{/if}}">
  106 + <div class="form-group">
  107 + <label>Server IP</label>
  108 + <input class="form-control server-ip" type="text" value="{{ip}}">
  109 + </div>
  110 + <div class="form-group">
  111 + <label>Server Port</label>
  112 + <input class="form-control server-port" type="text" value="{{port}}">
  113 + </div>
  114 + <span class="btn btn-default server-sure">保存</span>
  115 + </div>
  116 + </div>
  117 + {{/ awsConfig}}
  118 +
  119 + <div class="panel-body list-container">
75 Waitting for connecting to server... 120 Waitting for connecting to server...
76 </div> 121 </div>
77 </div> 122 </div>
  123 + </div>
  124 + </div>
  125 + </div>
  126 + </div>
  127 + </div>
78 </div> 128 </div>
79 129
80 <script> 130 <script>
81 $(function() { 131 $(function() {
82 - var $ip = $('#server-ip');  
83 - var $port = $('#server-port');  
84 - var $tip = $('#err-tip');  
85 -  
86 - var $listContainer = $('#list-container');  
87 -  
88 - function validateIP(ip) {  
89 - if (ip === 'localhost' ||  
90 - /^(\d)+\.(\d)+\.(\d)+\.(\d)+$/.test(ip))  
91 - {  
92 - $ip.closest('.form-group').removeClass('has-error');  
93 - return true;  
94 - }  
95 -  
96 - $ip.closest('.form-group').addClass('has-error');  
97 - return false;  
98 - }  
99 132
100 - function validatePort(port) {  
101 - if (/^[1-9]\d*$/.test(port) && +port >= 1 && +port <= 65535) {  
102 - $port.closest('.form-group').removeClass('has-error');  
103 - return true;  
104 - }  
105 -  
106 - $port.closest('.form-group').addClass('has-error');  
107 - return false;  
108 - } 133 + $('.ip-port-modify-btn').click(function() {
  134 + var $form = $(this).closest('.form-inline');
109 135
110 - $('#server-modify-btn').click(function() {  
111 - $('.server-editor').removeClass('hide');  
112 - $('.current-server').addClass('hide'); 136 + $('.server-editor', $form).removeClass('hide');
  137 + $('.current-server', $form).addClass('hide');
113 }); 138 });
114 139
115 // server 140 // server
116 - $('#server-sure').click(function() {  
117 - var ip = $.trim($ip.val());  
118 - var port = $.trim($port.val()); 141 + $('.server-sure').click(function() {
  142 + var $panel = $(this).closest('.config-panel');
119 143
120 - if (!validateIP(ip) || !validatePort(port)) {  
121 - $tip.removeClass('hide');  
122 - return;  
123 - } 144 + var $ip = $('.server-ip', $panel);
  145 + var $port = $('.server-port', $panel);
124 146
125 - $tip.addClass('hide'); 147 + var ip = $.trim($ip.val());
  148 + var port = $.trim($port.val());
126 149
127 $.ajax({ 150 $.ajax({
128 type: 'POST', 151 type: 'POST',
129 url: '/degrade/server', 152 url: '/degrade/server',
130 data: { 153 data: {
131 ip: ip, 154 ip: ip,
132 - port: port 155 + port: port,
  156 + type: $panel.data('type')
133 } 157 }
134 }).then(function(data) { 158 }).then(function(data) {
135 if (data.code === 200) { 159 if (data.code === 200) {
136 //$.pjax.reload('#pjax-container'); 160 //$.pjax.reload('#pjax-container');
137 - $('.server-editor').addClass('hide');  
138 - $('.current-server').removeClass('hide'); 161 + $('.server-editor', $panel).addClass('hide');
  162 + $('.current-server', $panel).removeClass('hide');
139 163
140 - $('#server-in-use').text(ip + ':' + port).data('ip', ip).data('port', port); 164 + $('.server-in-use', $panel).text(ip + ':' + port).data('ip', ip).data('port', port);
141 165
142 - $('#list-container').html('waiting for connecting server...'); 166 + $('.list-container', $panel).html('waiting for connecting server...');
143 } 167 }
144 }); 168 });
145 }); 169 });
146 170
147 - $listContainer.on('click', '#degrade-tab li', function() { 171 + $('.list-container').on('click', '.degrade-tab li', function() {
148 var $this = $(this); 172 var $this = $(this);
  173 + var $panel = $this.closest('.config-panel');
149 174
150 if ($this.hasClass('active')) { 175 if ($this.hasClass('active')) {
151 return; 176 return;
152 } 177 }
153 178
154 - $('li', $('#degrade-tab')).toggleClass('active'); 179 + $this.parent().children('li').toggleClass('active');
155 180
156 var index = $this.index(); 181 var index = $this.index();
157 182
158 if (index === 0) { 183 if (index === 0) {
159 184
160 //PC active 185 //PC active
161 - $('.pc-degrade').removeClass('hide');  
162 - $('.wap-degrade').addClass('hide'); 186 + $('.pc-degrade', $panel).removeClass('hide');
  187 + $('.wap-degrade', $panel).addClass('hide');
163 } else { 188 } else {
164 189
165 // wap active 190 // wap active
166 - $('.wap-degrade').removeClass('hide');  
167 - $('.pc-degrade').addClass('hide'); 191 + $('.wap-degrade', $panel).removeClass('hide');
  192 + $('.pc-degrade', $panel).addClass('hide');
168 } 193 }
169 }).on('change', '.degrade-content input[type="checkbox"]', function() { 194 }).on('change', '.degrade-content input[type="checkbox"]', function() {
170 var $checkbox = $(this), 195 var $checkbox = $(this),
@@ -180,19 +205,26 @@ @@ -180,19 +205,26 @@
180 checked: checked, 205 checked: checked,
181 id: id 206 id: id
182 } 207 }
  208 + }).then(function(data) {
  209 + if (data.code !== 200) {
  210 + $checkbox.prop('checked', !checked);
  211 + alert(data.message);
  212 + }
183 }); 213 });
184 }); 214 });
185 215
186 - $('#refresh-connection').click(function() { 216 + $('.refresh').click(function() {
  217 + var $panel = $(this).closest('.config-panel');
  218 +
187 $.ajax({ 219 $.ajax({
188 type: 'GET', 220 type: 'GET',
189 url: '/degrade/connect', 221 url: '/degrade/connect',
190 data: { 222 data: {
191 - ip: $('#server-in-use').data('ip'),  
192 - port: $('#server-in-use').data('port') 223 + ip: $('.server-in-use', $panel).data('ip'),
  224 + port: $('.server-in-use', $panel).data('port')
193 } 225 }
194 }).then(function(data) { 226 }).then(function(data) {
195 - $('#list-container').html(data); 227 + $('.list-container', $panel).html(data);
196 }); 228 });
197 }) 229 })
198 }) 230 })
@@ -8,8 +8,13 @@ module.exports = (server, path, val) => new Promise((resolve, reject) => { @@ -8,8 +8,13 @@ module.exports = (server, path, val) => new Promise((resolve, reject) => {
8 8
9 client.once('connected', function () { 9 client.once('connected', function () {
10 client.setData(path, new Buffer(val.toString()), function(err, data, stat) { 10 client.setData(path, new Buffer(val.toString()), function(err, data, stat) {
11 - console.log('path %s data change to', path, val); 11 + if (err) {
  12 + console.log('update path %s data error');
12 resolve(); 13 resolve();
  14 + } else {
  15 + console.log('path %s data change to', path, val);
  16 + resolve(true);
  17 + }
13 client.close(); 18 client.close();
14 }); 19 });
15 }); 20 });
@@ -7,13 +7,17 @@ const env = process.env.NODE_ENV || 'development'; @@ -7,13 +7,17 @@ const env = process.env.NODE_ENV || 'development';
7 const defaults = { 7 const defaults = {
8 port: 9000, 8 port: 9000,
9 buildDir: path.normalize(__dirname + '/../packages/'), 9 buildDir: path.normalize(__dirname + '/../packages/'),
10 - dbDir: path.normalize(__dirname + '/../db') 10 + dbDir: path.normalize(__dirname + '/../db'),
  11 + influxdb: {
  12 + host: '54.222.219.223',
  13 + port: 4444
  14 + }
11 }; 15 };
12 16
13 const specific = { 17 const specific = {
14 development: {}, 18 development: {},
15 test: {}, 19 test: {},
16 - production: {} 20 + production: {},
17 }; 21 };
18 22
19 export default Object.assign(defaults, specific[env]); 23 export default Object.assign(defaults, specific[env]);