mirror of
https://github.com/alibaba/anyproxy.git
synced 2025-08-04 21:39:04 +00:00
reconstruct web interface with react.js
This commit is contained in:
93
web/build/anyproxy_wsUtil.js
Normal file
93
web/build/anyproxy_wsUtil.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
web socket util for AnyProxy
|
||||
https://github.com/alibaba/anyproxy
|
||||
*/
|
||||
|
||||
/*
|
||||
{
|
||||
baseUrl : ""
|
||||
}
|
||||
config
|
||||
config.baseUrl
|
||||
config.port
|
||||
config.onOpen
|
||||
config.onClose
|
||||
config.onError
|
||||
config.onGetData
|
||||
config.onGetUpdate
|
||||
config.onGetBody
|
||||
config.onError
|
||||
*/
|
||||
function anyproxy_wsUtil(config){
|
||||
config = config || {};
|
||||
if(!WebSocket){
|
||||
throw (new Error("webSocket is not available on this browser"));
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var baseUrl = config.baseUrl || "127.0.0.1",
|
||||
socketPort = config.port || 8003;
|
||||
|
||||
var dataSocket = new WebSocket("ws://" + baseUrl + ":" + socketPort);
|
||||
|
||||
self.bodyCbMap = {};
|
||||
dataSocket.onmessage = function(event){
|
||||
config.onGetData && config.onGetData.call(self,event.data);
|
||||
|
||||
try{
|
||||
var data = JSON.parse(event.data),
|
||||
type = data.type,
|
||||
content = data.content,
|
||||
reqRef = data.reqRef;
|
||||
}catch(e){
|
||||
config.onError && config.onError.call(self, new Error("failed to parse socket data - " + e.toString()) );
|
||||
}
|
||||
|
||||
if(type == "update"){
|
||||
config.onGetUpdate && config.onGetUpdate.call(self, content);
|
||||
|
||||
}else if(type == "body"){
|
||||
config.onGetBody && config.onGetBody.call(self, content, reqRef);
|
||||
|
||||
if(data.reqRef && self.bodyCbMap[reqRef]){
|
||||
self.bodyCbMap[reqRef].call(self,content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dataSocket.onopen = function(e){
|
||||
config.onOpen && config.onOpen.call(self,e);
|
||||
}
|
||||
dataSocket.onclose = function(e){
|
||||
config.onClose && config.onClose.call(self,e);
|
||||
}
|
||||
dataSocket.onerror = function(e){
|
||||
config.onError && config.onError.call(self,e);
|
||||
}
|
||||
|
||||
self.dataSocket = dataSocket;
|
||||
};
|
||||
|
||||
anyproxy_wsUtil.prototype.send = function(data){
|
||||
if(typeof data == "object"){
|
||||
data = JSON.stringify(data);
|
||||
}
|
||||
this.dataSocket.send(data);
|
||||
};
|
||||
|
||||
anyproxy_wsUtil.prototype.reqBody = function(id,callback){
|
||||
if(!id) return;
|
||||
|
||||
var payload = {
|
||||
type : "reqBody",
|
||||
id : id
|
||||
};
|
||||
if(callback){
|
||||
var reqRef = "r_" + Math.random()*100 + "_" + (new Date().getTime());
|
||||
payload.reqRef = reqRef;
|
||||
this.bodyCbMap[reqRef] = callback;
|
||||
}
|
||||
this.send(payload);
|
||||
};
|
||||
|
||||
module.exports = anyproxy_wsUtil;
|
||||
27
web/build/detail.js
Normal file
27
web/build/detail.js
Normal file
@@ -0,0 +1,27 @@
|
||||
define(function(require,exports,module){
|
||||
var $ = require("$");
|
||||
|
||||
|
||||
|
||||
|
||||
// var cbMap = {};
|
||||
// function render(data,cb){
|
||||
// var resultEl = $(_.template(tpl, data)),
|
||||
// id = data._id;
|
||||
// try{
|
||||
// //if finished
|
||||
// var reqRef = "r" + Math.random() + "_" + new Date().getTime();
|
||||
// if(data.statusCode){
|
||||
// //fetch body
|
||||
// ws.reqBody(id,function(content){
|
||||
// $(".J_responseBody", resultEl).html(he.encode(content.body));
|
||||
// cb(resultEl);
|
||||
// });
|
||||
// }
|
||||
// }catch(e){
|
||||
// cb(resultEl);
|
||||
// };
|
||||
// }
|
||||
|
||||
module.exports = DetailPanel;
|
||||
});
|
||||
174
web/build/detailPanel.js
Normal file
174
web/build/detailPanel.js
Normal file
@@ -0,0 +1,174 @@
|
||||
function init(React){
|
||||
|
||||
function dragableBar(initX,cb){
|
||||
var self = this,
|
||||
dragging = true;
|
||||
|
||||
var ghostbar = $('<div class="ghostbar"></div>').css("left",initX).prependTo('body');
|
||||
|
||||
$(document).mousemove(function(e){
|
||||
e.preventDefault();
|
||||
ghostbar.css("left",e.pageX + "px");
|
||||
});
|
||||
|
||||
$(document).mouseup(function(e){
|
||||
if(!dragging) return;
|
||||
|
||||
dragging = false;
|
||||
|
||||
var deltaPageX = e.pageX - initX;
|
||||
cb && cb.call(null,{
|
||||
delta : deltaPageX,
|
||||
finalX : e.pageX
|
||||
});
|
||||
|
||||
ghostbar.remove();
|
||||
$(document).unbind('mousemove');
|
||||
});
|
||||
}
|
||||
|
||||
var DetailPanel = React.createClass({displayName: "DetailPanel",
|
||||
getInitialState : function(){
|
||||
return {
|
||||
show : false,
|
||||
data : {},
|
||||
body : {id : -1, content : null},
|
||||
left : "35%"
|
||||
};
|
||||
},
|
||||
componentDidMount:function(){
|
||||
var self = this;
|
||||
$(document).on("keyup",function(e){
|
||||
if(e.keyCode == 27){ //ESC
|
||||
self.setState({
|
||||
show : false
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
setHide:function(){
|
||||
this.setState({
|
||||
show : false
|
||||
});
|
||||
},
|
||||
setShow:function(ifShow){
|
||||
this.setState({
|
||||
show : true
|
||||
});
|
||||
},
|
||||
loadBody:function(){
|
||||
var self = this,
|
||||
id = self.state.data.id;
|
||||
if(!id) return;
|
||||
|
||||
ws.reqBody(id,function(content){
|
||||
if(content.id == self.state.data.id){
|
||||
self.setState({
|
||||
body : content
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
dealDrag:function(){
|
||||
var self = this,
|
||||
leftVal = $(React.findDOMNode(this.refs.mainOverlay)).css("left");
|
||||
dragableBar(leftVal, function(data){
|
||||
if(data && data.finalX){
|
||||
if(window.innerWidth - data.finalX < 200){
|
||||
data.finalX = window.innerWidth - 200;
|
||||
}
|
||||
self.setState({
|
||||
left : data.finalX + "px"
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
render : function(){
|
||||
var reqHeaderSection = [],
|
||||
resHeaderSection = [],
|
||||
summarySection,
|
||||
detailSection,
|
||||
bodyContent;
|
||||
|
||||
if(this.state.data.reqHeader){
|
||||
for(var key in this.state.data.reqHeader){
|
||||
reqHeaderSection.push(React.createElement("li", {key: "reqHeader_" + key}, React.createElement("strong", null, key), " : ", this.state.data.reqHeader[key]))
|
||||
}
|
||||
}
|
||||
|
||||
summarySection = (
|
||||
React.createElement("div", null,
|
||||
React.createElement("section", {className: "req"},
|
||||
React.createElement("h4", {className: "subTitle"}, "request"),
|
||||
React.createElement("div", {className: "detail"},
|
||||
React.createElement("ul", {className: "uk-list"},
|
||||
React.createElement("li", null, this.state.data.method, " ", React.createElement("span", {title: "{this.state.data.path}"}, this.state.data.path), " HTTP/1.1"),
|
||||
reqHeaderSection
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
React.createElement("section", {className: "reqBody"},
|
||||
React.createElement("h4", {className: "subTitle"}, "request body"),
|
||||
React.createElement("div", {className: "detail"},
|
||||
React.createElement("p", null, this.state.data.reqBody)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if(this.state.data.statusCode){
|
||||
|
||||
if(this.state.body.id == this.state.data.id){
|
||||
bodyContent = (React.createElement("pre", {className: "resBodyContent"}, this.state.body.body));
|
||||
}else{
|
||||
bodyContent = null;
|
||||
this.loadBody();
|
||||
}
|
||||
|
||||
if(this.state.data.resHeader){
|
||||
for(var key in this.state.data.resHeader){
|
||||
resHeaderSection.push(React.createElement("li", {key: "resHeader_" + key}, React.createElement("strong", null, key), " : ", this.state.data.resHeader[key]))
|
||||
}
|
||||
}
|
||||
|
||||
detailSection = (
|
||||
React.createElement("div", null,
|
||||
React.createElement("section", {className: "resHeader"},
|
||||
React.createElement("h4", {className: "subTitle"}, "response header"),
|
||||
React.createElement("div", {className: "detail"},
|
||||
React.createElement("ul", {className: "uk-list"},
|
||||
React.createElement("li", null, "HTTP/1.1 ", React.createElement("span", {className: "http_status http_status_" + this.state.data.statusCode}, this.state.data.statusCode)),
|
||||
resHeaderSection
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
React.createElement("section", {className: "resBody"},
|
||||
React.createElement("h4", {className: "subTitle"}, "response body"),
|
||||
React.createElement("div", {className: "detail"}, bodyContent)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
React.createElement("div", {style: {display:this.state.show ? "block" :"none"}},
|
||||
React.createElement("div", {className: "overlay_mask", onClick: this.setHide}),
|
||||
React.createElement("div", {className: "recordDetailOverlay", ref: "mainOverlay", style: {left: this.state.left}},
|
||||
React.createElement("div", {className: "dragbar", onMouseDown: this.dealDrag}),
|
||||
React.createElement("span", {className: "escBtn", onClick: this.setHide}, "Close (ESC)"),
|
||||
React.createElement("div", null,
|
||||
summarySection,
|
||||
detailSection
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return DetailPanel;
|
||||
}
|
||||
|
||||
module.exports.init = init;
|
||||
43
web/build/event.js
Normal file
43
web/build/event.js
Normal file
@@ -0,0 +1,43 @@
|
||||
//Ref : http://jsfiddle.net/JxYca/3/
|
||||
var EventManager = function() {
|
||||
this.initialize();
|
||||
};
|
||||
EventManager.prototype = {
|
||||
initialize: function() {
|
||||
//declare listeners as an object
|
||||
this.listeners = {};
|
||||
},
|
||||
// public methods
|
||||
addListener: function(event, fn) {
|
||||
if (!this.listeners[event]) {
|
||||
this.listeners[event] = [];
|
||||
}
|
||||
if (fn instanceof Function) {
|
||||
this.listeners[event].push(fn);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
dispatchEvent: function(event, params) {
|
||||
// loop through listeners array
|
||||
for (var index = 0, l = this.listeners[event].length; index < l; index++) {
|
||||
// execute matching 'event' - loop through all indices and
|
||||
// when ev is found, execute
|
||||
this.listeners[event][index].call(window, params);
|
||||
}
|
||||
},
|
||||
removeListener: function(event, fn) {
|
||||
// split array 1 item after our listener
|
||||
// shorten to remove it
|
||||
// join the two arrays back together
|
||||
if (this.listeners[event]) {
|
||||
for (var i = 0, l = this.listners[event].length; i < l; i++) {
|
||||
if (this.listners[event][i] === fn) {
|
||||
this.listners[event].slice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = EventManager;
|
||||
141
web/build/index.js
Normal file
141
web/build/index.js
Normal file
@@ -0,0 +1,141 @@
|
||||
require("../lib/zepto");
|
||||
var EventManager = require('../lib/event'),
|
||||
Anyproxy_wsUtil = require("../lib/anyproxy_wsUtil"),
|
||||
React = require("../lib/react");
|
||||
|
||||
var WsIndicator = require("./wsIndicator").init(React),
|
||||
RecordPanel = require("./recordPanel").init(React);
|
||||
|
||||
var ifPause = false,
|
||||
recordSet = [];
|
||||
|
||||
//Event : wsGetUpdate
|
||||
//Event : recordSetUpdated
|
||||
//Event : wsOpen
|
||||
//Event : wsEnd
|
||||
var eventCenter = new EventManager();
|
||||
|
||||
//invoke AnyProxy web socket util
|
||||
(function(){
|
||||
try{
|
||||
var ws = window.ws = new Anyproxy_wsUtil({
|
||||
baseUrl : document.getElementById("baseUrl").value,
|
||||
port : document.getElementById("socketPort").value,
|
||||
onOpen : function(){
|
||||
eventCenter.dispatchEvent("wsOpen");
|
||||
},
|
||||
onGetUpdate : function(content){
|
||||
eventCenter.dispatchEvent("wsGetUpdate",content);
|
||||
},
|
||||
onError : function(e){
|
||||
eventCenter.dispatchEvent("wsEnd");
|
||||
},
|
||||
onClose : function(e){
|
||||
eventCenter.dispatchEvent("wsEnd");
|
||||
}
|
||||
});
|
||||
window.ws = ws;
|
||||
|
||||
}catch(e){
|
||||
alert("failed to invoking web socket on this browser");
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
//websocket status indicator
|
||||
(function(){
|
||||
var wsIndicator = React.render(
|
||||
React.createElement(WsIndicator, null),
|
||||
document.getElementById("J_indicatorEl")
|
||||
);
|
||||
|
||||
eventCenter.addListener("wsOpen",function(){
|
||||
wsIndicator.setState({
|
||||
isValid : true
|
||||
});
|
||||
});
|
||||
|
||||
eventCenter.addListener("wsEnd",function(){
|
||||
wsIndicator.setState({
|
||||
isValid : false
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
//record panel
|
||||
(function(){
|
||||
//merge : right --> left
|
||||
function util_merge(left,right){
|
||||
for(var key in right){
|
||||
left[key] = right[key];
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
function updateRecordSet(newRecord){
|
||||
if(ifPause) return;
|
||||
|
||||
if(newRecord && newRecord.id){
|
||||
if(!recordSet[newRecord.id]){
|
||||
recordSet[newRecord.id] = newRecord;
|
||||
}else{
|
||||
util_merge(recordSet[newRecord.id],newRecord);
|
||||
}
|
||||
|
||||
recordSet[newRecord.id]._justUpdated = true;
|
||||
// React.addons.Perf.start();
|
||||
eventCenter.dispatchEvent("recordSetUpdated");
|
||||
// React.addons.Perf.stop();
|
||||
}
|
||||
}
|
||||
eventCenter.addListener("wsGetUpdate",updateRecordSet);
|
||||
|
||||
var Panel = React.render(
|
||||
React.createElement(RecordPanel, null),
|
||||
document.getElementById("J_content")
|
||||
);
|
||||
|
||||
eventCenter.addListener('recordSetUpdated',function(){
|
||||
Panel.setState({
|
||||
list : recordSet
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
//action bar
|
||||
(function(){
|
||||
function clearLogs(){
|
||||
recordSet = [];
|
||||
eventCenter.dispatchEvent("recordSetUpdated");
|
||||
}
|
||||
|
||||
$(document).on("keyup",function(e){
|
||||
if(e.keyCode == 88 && e.ctrlKey){ // ctrl + x
|
||||
clearLogs();
|
||||
}
|
||||
});
|
||||
|
||||
var clearLogBtn = $(".J_clearBtn");
|
||||
clearLogBtn.on("click",function(e){
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
clearLogs();
|
||||
});
|
||||
|
||||
var statusBtn = $(".J_statusBtn");
|
||||
statusBtn.on("click",function(e){
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
$(".J_statusBtn").removeClass("btn_disable");
|
||||
$(this).addClass("btn_disable");
|
||||
|
||||
if(/stop/i.test($(this).html()) ){
|
||||
ifPause = true;
|
||||
}else{
|
||||
ifPause = false;
|
||||
}
|
||||
});
|
||||
})();
|
||||
21598
web/build/react.js
vendored
Normal file
21598
web/build/react.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
51
web/build/recordPanel.js
Normal file
51
web/build/recordPanel.js
Normal file
@@ -0,0 +1,51 @@
|
||||
function init(React){
|
||||
var RecordRow = require("./recordRow").init(React);
|
||||
|
||||
var RecordPanel = React.createClass({displayName: "RecordPanel",
|
||||
getInitialState : function(){
|
||||
return {
|
||||
list : []
|
||||
};
|
||||
},
|
||||
render : function(){
|
||||
var rowCollection = [];
|
||||
for(var i = this.state.list.length-1 ; i >=0 ; i--){
|
||||
var item = this.state.list[i];
|
||||
if(item){
|
||||
|
||||
if(item._justUpdated){
|
||||
item._justUpdated = false;
|
||||
item._needRender = true;
|
||||
}else{
|
||||
item._needRender = false;
|
||||
}
|
||||
|
||||
rowCollection.push(React.createElement(RecordRow, {key: item.id, data: item}));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
React.createElement("table", {className: "uk-table uk-table-condensed uk-table-hover"},
|
||||
React.createElement("thead", null,
|
||||
React.createElement("tr", null,
|
||||
React.createElement("th", {className: "col_id"}, "id"),
|
||||
React.createElement("th", {className: "col_method"}, "method"),
|
||||
React.createElement("th", {className: "col_code"}, "code"),
|
||||
React.createElement("th", {className: "col_host"}, "host"),
|
||||
React.createElement("th", {className: "col_path"}, "path"),
|
||||
React.createElement("th", {className: "col_mime"}, "mime type"),
|
||||
React.createElement("th", {className: "col_time"}, "time")
|
||||
)
|
||||
),
|
||||
React.createElement("tbody", null,
|
||||
rowCollection
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return RecordPanel;
|
||||
}
|
||||
|
||||
module.exports.init = init;
|
||||
70
web/build/recordRow.js
Normal file
70
web/build/recordRow.js
Normal file
@@ -0,0 +1,70 @@
|
||||
function init(React){
|
||||
var DetailPanel = require("./detailPanel").init(React);
|
||||
|
||||
$("body").append('<div id="J_detailPanel"></div>');
|
||||
var detail = React.render(
|
||||
React.createElement(DetailPanel, null),
|
||||
document.getElementById("J_detailPanel")
|
||||
);
|
||||
|
||||
function dateFormat(date,fmt) {
|
||||
var o = {
|
||||
"M+": date.getMonth() + 1, //月份
|
||||
"d+": date.getDate(), //日
|
||||
"h+": date.getHours(), //小时
|
||||
"m+": date.getMinutes(), //分
|
||||
"s+": date.getSeconds(), //秒
|
||||
"q+": Math.floor((date.getMonth() + 3) / 3), //季度
|
||||
"S" : date.getMilliseconds() //毫秒
|
||||
};
|
||||
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
|
||||
for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
|
||||
return fmt;
|
||||
}
|
||||
|
||||
var RecordRow = React.createClass({displayName: "RecordRow",
|
||||
getInitialState : function(){
|
||||
return null;
|
||||
},
|
||||
handleClick:function(e){
|
||||
detail.setState({
|
||||
data : this.props.data,
|
||||
show : true
|
||||
});
|
||||
},
|
||||
render : function(){
|
||||
var trClassesArr = [],
|
||||
trClasses;
|
||||
if(this.props.data.statusCode){
|
||||
trClassesArr.push("record_status_done");
|
||||
}
|
||||
|
||||
trClassesArr.push( ((Math.floor(this.props.data._id /2) - this.props.data._id /2) == 0)? "row_even" : "row_odd" );
|
||||
trClasses = trClassesArr.join(" ");
|
||||
|
||||
var dateStr = dateFormat(new Date(this.props.data.startTime),"hh:mm:ss");
|
||||
|
||||
return(
|
||||
React.createElement("tr", {className: trClasses, onClick: this.handleClick},
|
||||
React.createElement("td", {className: "data_id"}, this.props.data._id),
|
||||
React.createElement("td", null, this.props.data.method, " ", React.createElement("span", {className: "protocol protocol_" + this.props.data.protocol, title: "https"}, React.createElement("i", {className: "iconfont"}, "É")), " "),
|
||||
React.createElement("td", {className: "http_status http_status_" + this.props.data.statusCode}, this.props.data.statusCode),
|
||||
React.createElement("td", {title: this.props.data.host}, this.props.data.host),
|
||||
React.createElement("td", {title: this.props.data.path}, this.props.data.path),
|
||||
React.createElement("td", null, this.props.data.mime),
|
||||
React.createElement("td", null, dateStr)
|
||||
)
|
||||
);
|
||||
},
|
||||
shouldComponentUpdate:function(nextPros){
|
||||
return nextPros.data._needRender;
|
||||
},
|
||||
componentDidUpdate:function(){},
|
||||
componentWillUnmount:function(){}
|
||||
});
|
||||
|
||||
return RecordRow;
|
||||
|
||||
}
|
||||
|
||||
module.exports.init = init;
|
||||
18
web/build/wsIndicator.js
Normal file
18
web/build/wsIndicator.js
Normal file
@@ -0,0 +1,18 @@
|
||||
function init(React){
|
||||
var WsIndicator = React.createClass({displayName: "WsIndicator",
|
||||
getInitialState:function(){
|
||||
return {
|
||||
isValid: false
|
||||
}
|
||||
},
|
||||
render:function(){
|
||||
return (
|
||||
React.createElement("img", {className: "logo_bottom anim_rotation", src: "https://t.alipayobjects.com/images/rmsweb/T1P_dfXa8oXXXXXXXX.png", width: "50", height: "50", style: {display: this.state.isValid ?"block" : "none"}})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return WsIndicator;
|
||||
}
|
||||
|
||||
module.exports.init = init;
|
||||
Reference in New Issue
Block a user