mirror of
https://github.com/alibaba/anyproxy.git
synced 2025-05-10 14:58:27 +00:00
reconstruct web interface with react.js
This commit is contained in:
parent
157e478abb
commit
d765060fff
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@ cert/**/*.srl
|
||||
cert/**/*.key
|
||||
cert/**/*.crt
|
||||
cert/**/*.csr
|
||||
web/build/.module-cache/
|
||||
tmp.txt
|
||||
.*.swp
|
||||
._*
|
||||
|
@ -3,7 +3,7 @@ var isRootCAFileExists = require("./certMgr.js").isRootCAFileExists(),
|
||||
|
||||
module.exports = {
|
||||
summary:function(){
|
||||
var tip = "the default rule for anyproxy, support : CORS. ";
|
||||
var tip = "the default rule for anyproxy which supports CORS. ";
|
||||
if(!isRootCAFileExists){
|
||||
tip += "\nRoot CA does not exist, will not intercept any https requests.";
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ var express = require("express"),
|
||||
qrCode = require('qrcode-npm'),
|
||||
util = require("./util"),
|
||||
certMgr = require("./certMgr"),
|
||||
logUtil = require("./log");
|
||||
logUtil = require("./log"),
|
||||
compress = require('compression');
|
||||
|
||||
|
||||
function webInterface(config){
|
||||
var port = config.port,
|
||||
@ -21,6 +23,7 @@ function webInterface(config){
|
||||
staticDir = path.join(__dirname,'../web');
|
||||
|
||||
var app = express();
|
||||
app.use(compress()); //invoke gzip
|
||||
app.use(function(req, res, next) {
|
||||
res.setHeader("note", "THIS IS A REQUEST FROM ANYPROXY WEB INTERFACE");
|
||||
return next();
|
||||
|
@ -11,15 +11,16 @@
|
||||
"async-task-mgr": ">=1.1.0",
|
||||
"colorful": "^2.1.0",
|
||||
"commander": "~2.3.0",
|
||||
"compression": "^1.4.4",
|
||||
"express": "^4.8.5",
|
||||
"iconv-lite": "^0.4.6",
|
||||
"ip": "^0.3.2",
|
||||
"juicer": "^0.6.6-stable",
|
||||
"nedb": "^0.11.0",
|
||||
"npm": "^2.7.0",
|
||||
"qrcode-npm": "0.0.3",
|
||||
"stream-throttle": "^0.1.3",
|
||||
"ws": "^0.4.32",
|
||||
"npm": "^2.7.0"
|
||||
"ws": "^0.4.32"
|
||||
},
|
||||
"devDependencies": {
|
||||
"proxy-eval": ">=1.1.1"
|
||||
|
2
proxy.js
2
proxy.js
@ -156,7 +156,7 @@ function proxyServer(option){
|
||||
var config = {
|
||||
port : proxyWebPort,
|
||||
wsPort : socketPort,
|
||||
ruleSummaery : requestHandler.getRuleSummary(),
|
||||
ruleSummary : requestHandler.getRuleSummary(),
|
||||
ip : ip.address()
|
||||
};
|
||||
|
||||
|
1
test/ab_local.sh
Executable file
1
test/ab_local.sh
Executable file
@ -0,0 +1 @@
|
||||
ab -X 127.0.0.1:8001 -n $1 -c $2 -H "HOST:127.0.0.1" http://127.0.0.1:8080/
|
1
test/ab_taobao.sh
Executable file
1
test/ab_taobao.sh
Executable file
@ -0,0 +1 @@
|
||||
ab -X 127.0.0.1:8001 -n $1 -c $2 -H "HOST:www.taobao.com" http://www.taobao.com/
|
15919
web/JSXTransformer.js
15919
web/JSXTransformer.js
File diff suppressed because one or more lines are too long
2
web/build.sh
Executable file
2
web/build.sh
Executable file
@ -0,0 +1,2 @@
|
||||
jsx /src /build
|
||||
webpack --progress --colors
|
@ -30,7 +30,7 @@ function anyproxy_wsUtil(config){
|
||||
|
||||
var dataSocket = new WebSocket("ws://" + baseUrl + ":" + socketPort);
|
||||
|
||||
// self.bodyCbMap = {};
|
||||
self.bodyCbMap = {};
|
||||
dataSocket.onmessage = function(event){
|
||||
config.onGetData && config.onGetData.call(self,event.data);
|
||||
|
||||
@ -42,7 +42,6 @@ function anyproxy_wsUtil(config){
|
||||
}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);
|
||||
@ -89,4 +88,6 @@ anyproxy_wsUtil.prototype.reqBody = function(id,callback){
|
||||
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;
|
||||
}
|
||||
});
|
||||
})();
|
0
web/react.js → web/build/react.js
vendored
0
web/react.js → web/build/react.js
vendored
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;
|
@ -11,6 +11,10 @@ body{
|
||||
min-width: 1090px;
|
||||
}
|
||||
|
||||
body, html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.iconfont{
|
||||
font-family:"iconfont" !important;
|
||||
font-size:16px;font-style:normal;
|
||||
@ -89,7 +93,7 @@ body{
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.topHead .topBtn:hover{
|
||||
.topHead .topBtn:hover:not(.btn_disable){
|
||||
background: #07D;
|
||||
transition:0.1s;
|
||||
color: #FFF;
|
||||
@ -131,7 +135,7 @@ body{
|
||||
}
|
||||
|
||||
.mainTableWrapper thead{
|
||||
background: #DDD;
|
||||
background: #F4F5F9;
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
|
||||
@ -215,14 +219,13 @@ body{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #EEE;
|
||||
opacity: 0.85;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.recordDetailOverlay{
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
left: 35%;
|
||||
right: 0;
|
||||
background: #FFF;
|
||||
border-left: 1px solid #CCC;
|
||||
@ -281,21 +284,21 @@ body{
|
||||
color: #777;
|
||||
}
|
||||
|
||||
#dragbar{
|
||||
.dragbar{
|
||||
position:absolute;
|
||||
left:0px;
|
||||
left:-5px;
|
||||
top:0px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
background-color:#CCC;
|
||||
width: 3px;
|
||||
width: 10px;
|
||||
cursor: col-resize;
|
||||
}
|
||||
#ghostbar{
|
||||
|
||||
.ghostbar{
|
||||
position:fixed;
|
||||
width:3px;
|
||||
height: 100vh;
|
||||
background-color:#000;
|
||||
opacity:0.5;
|
||||
position:absolute;
|
||||
cursor: col-resize;
|
||||
z-index:999
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Anyproxy</title>
|
||||
<link rel="stylesheet" href="/css/uikit.gradient.min.css" />
|
||||
<link rel="stylesheet" href="/css/page.css" />
|
||||
<link rel="icon" type="image/png" href="/favico.png" />
|
||||
<script charset="utf-8" id="seajsnode"src="http://static.alipayobjects.com/seajs/??seajs/2.2.0/sea.js,seajs-combo/1.0.1/seajs-combo.js,seajs-style/1.0.2/seajs-style.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="J_detailPanel" class="detailPanel"></div>
|
||||
<script type="text/javascript">
|
||||
seajs.config({
|
||||
base: 'http://static.alipayobjects.com/',
|
||||
alias: {
|
||||
'$' : 'jquery/jquery/1.7.2/jquery',
|
||||
'Backbone' : 'gallery/backbone/1.1.2/backbone.js',
|
||||
'Underscore': 'gallery/underscore/1.6.0/underscore.js'
|
||||
}
|
||||
});
|
||||
|
||||
seajs.use(['$', 'Underscore', 'Backbone',"./detail"], function($, _, Backbone,Detail) {
|
||||
|
||||
window.render = function(data){
|
||||
Detail.render(data,function(tpl){
|
||||
$("#J_detailPanel").html(tpl);
|
||||
});
|
||||
}
|
||||
|
||||
//init render
|
||||
if(window.renderData){
|
||||
window.render(window.renderData);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
body{
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.detailPanel{
|
||||
padding: 40px 20px;
|
||||
font-size: 22px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.subTitle{
|
||||
border-left: 6px solid #1FA2D6;
|
||||
font-size: 24px;
|
||||
line-height: 36px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,64 +0,0 @@
|
||||
define("./detail",['$', 'gallery/underscore/1.6.0/underscore.js'],function(require,exports,module){
|
||||
var _ = require("gallery/underscore/1.6.0/underscore.js"),
|
||||
$ = require("$");
|
||||
|
||||
var tpl = ""+
|
||||
' <section class="req">'+
|
||||
' <h4 class="subTitle">request</h4>'+
|
||||
' <div class="detail">'+
|
||||
' <ul class="uk-list">'+
|
||||
' <li><%= method %> <span title="<%= path %>"><%= path %></span> HTTP/1.1</li>'+
|
||||
// ' <li></li>'+
|
||||
' <% _.each(reqHeader, function(v,k) { %> <li><strong><%= k %></strong> : <%= v %></li><% }); %>'+
|
||||
' </ul>'+
|
||||
' </div>'+
|
||||
' </section>'+
|
||||
''+
|
||||
' <section class="reqBody">'+
|
||||
' <h4 class="subTitle">request body</h4>'+
|
||||
' <div class="detail">'+
|
||||
' <p><%= reqBody %></p>'+
|
||||
' </div>'+
|
||||
' </section>'+
|
||||
''+
|
||||
' <% if(statusCode) { %>'+
|
||||
' <section class="resHeader">'+
|
||||
' <h4 class="subTitle">response header</h4>'+
|
||||
' <div class="detail">'+
|
||||
' <ul class="uk-list">'+
|
||||
' <li>HTTP/1.1 <span class="http_status http_status_<%= statusCode %>"><%= statusCode %></span></li>'+
|
||||
' <% _.each(resHeader, function(v,k) { %> <li><strong><%= k %></strong> : <%= v %></li><% }); %>'+
|
||||
' </ul>'+
|
||||
' </div>'+
|
||||
' </section>'+
|
||||
''+
|
||||
' <section class="resBody">'+
|
||||
' <h4 class="subTitle">response body</h4>'+
|
||||
' <div class="detail">'+
|
||||
' <pre class="J_responseBody resBodyContent"></pre>'+
|
||||
' </div>'+
|
||||
' </section>'+
|
||||
' <% } %>';
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
exports.render = render;
|
||||
});
|
@ -5,28 +5,24 @@
|
||||
<link rel="stylesheet" href="/css/uikit.gradient.min.css" />
|
||||
<link rel="stylesheet" href="/css/page.css" />
|
||||
<link rel="icon" type="image/png" href="/favico.png" />
|
||||
<script charset="utf-8" id="seajsnode" src="http://static.alipayobjects.com/seajs/??seajs/2.2.0/sea.js,seajs-combo/1.0.1/seajs-combo.js,seajs-style/1.0.2/seajs-style.js"></script>
|
||||
<script src="./react.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="topHead">
|
||||
<div class="logoWrapper">
|
||||
<h1>Anyproxy</h1>
|
||||
<img class="logo_bottom anim_rotation" id="J_indicator" src="./logo_bottom.png" width="50" height="50" style="visibility:hidden" />
|
||||
<div id="J_indicatorEl"></div>
|
||||
</div>
|
||||
|
||||
<div class="ctrlWrapper">
|
||||
<a href="#" class="J_statusBtn"><span class="topBtn"><i class="uk-icon-stop"></i>Stop</span></a>
|
||||
<a href="#" class="J_statusBtn btn_disable"><span class="topBtn"><i class="uk-icon-play"></i>Resume</span></a>
|
||||
<a href="#"><span class="J_statusBtn topBtn"><i class="uk-icon-stop"></i>Stop</span></a>
|
||||
<a href="#"><span class="J_statusBtn btn_disable topBtn"><i class="uk-icon-play"></i>Resume</span></a>
|
||||
<a href="#" class="J_clearBtn"><span class="topBtn"><i class="uk-icon-eraser"></i>Clear(Ctrl+X)</span></a>
|
||||
<span class="sep">|</span>
|
||||
<a href="/fetchCrtFile" target="_blank"><span class="topBtn"><i class="uk-icon-certificate"></i>Download rootCA.crt</span></a>
|
||||
<a href="/qr_root" class="J_fetchRootQR" target="_blank"><span class="topBtn"><i class="uk-icon-certificate"></i>QRCode of rootCA.crt</span></a>
|
||||
<!-- <a href="#"><span class="topBtn"><i class="uk-icon-cog"></i>Others</span></a> -->
|
||||
|
||||
<span class="sep">|</span>
|
||||
<a href="https://github.com/alibaba/anyproxy" target="_blank"><span class="topBtn"><i class="uk-icon-external-link-square"></i>Anyproxy(Github)</span></a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="ruleDesc">
|
||||
@ -38,33 +34,9 @@
|
||||
|
||||
<div class="mainTableWrapper" id="J_content"></div>
|
||||
|
||||
<div class="recordDetailOverlay J_recordDetailOverlay" style="display:none">
|
||||
<div id="dragbar"></div>
|
||||
<span class="escBtn J_escBtn">Close (ESC)</span>
|
||||
<div class="J_recordDetailOverlayContentWrapper">
|
||||
<div class="J_recordDetailOverlayContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="socketPort" value="{{wsPort}}" />
|
||||
<input type="hidden" id="baseUrl" value="{{ipAddress}}" />
|
||||
<input type="hidden" id="customMenu" value="{{menu}}" />
|
||||
|
||||
<script id="main_table_row" type="text/template">
|
||||
<td class="data_id"><%= _id %></td>
|
||||
<td><%= method %> <span class="protocol protocol_<%= protocol %>" title="https"><i class="iconfont">󰃉</i></span> </td>
|
||||
<td class="http_status http_status_<%= statusCode %>"><%= statusCode %></td>
|
||||
<td title="<%= host %>"><%= host %></td>
|
||||
<td title="<%= path %>"><%= path %></td>
|
||||
<td><%= mime %></td>
|
||||
<td><%= startTimeStr %></td>
|
||||
</script>
|
||||
|
||||
<script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script>
|
||||
<script src="/anyproxy_wsUtil.js"></script>
|
||||
<script src="/he.js"></script>
|
||||
<script src="/list.js"></script>
|
||||
|
||||
<script src="/build/index.js"></script>
|
||||
<script src="./page.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
95
web/lib/anyproxy_wsUtil.js
Normal file
95
web/lib/anyproxy_wsUtil.js
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
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);
|
||||
};
|
||||
|
||||
if(typeof module != "undefined"){
|
||||
module.exports = anyproxy_wsUtil;
|
||||
}
|
43
web/lib/event.js
Normal file
43
web/lib/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;
|
16
web/lib/react.js
vendored
Normal file
16
web/lib/react.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
web/lib/zepto.js
Normal file
2
web/lib/zepto.js
Normal file
File diff suppressed because one or more lines are too long
226
web/list.js
226
web/list.js
@ -1,226 +0,0 @@
|
||||
seajs.config({
|
||||
base: 'http://static.alipayobjects.com/',
|
||||
alias: {
|
||||
'$' : 'jquery/jquery/1.7.2/jquery',
|
||||
'Backbone' : 'gallery/backbone/1.1.2/backbone.js',
|
||||
'Underscore': 'gallery/underscore/1.6.0/underscore.js',
|
||||
"Handlebars": 'gallery/handlebars/1.0.2/handlebars.js',
|
||||
"Popup" : 'arale/popup/1.1.6/popup'
|
||||
}
|
||||
});
|
||||
|
||||
seajs.use(['$', 'Underscore', 'Backbone',"Handlebars","Popup","./detail"], function($, _, Backbone,Handlebars,Popup,Detail) {
|
||||
Backbone.$ = $;
|
||||
|
||||
var isInApp = window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.list;
|
||||
|
||||
//record detail
|
||||
var DetailView = function(){
|
||||
var self = this,
|
||||
$detailEl = $(".J_recordDetailOverlay"),
|
||||
$mask;
|
||||
|
||||
//init mask
|
||||
$mask = $("<div></div>").addClass("overlay_mask").hide();
|
||||
$("body").append($mask);
|
||||
|
||||
//bind events
|
||||
$(document).on("keyup",function(e){
|
||||
if(e.keyCode == 27){ //ESC
|
||||
self.hideDetail();
|
||||
}
|
||||
});
|
||||
|
||||
$mask.on("click",function(e){
|
||||
self.hideDetail();
|
||||
});
|
||||
|
||||
$(".J_escBtn",$detailEl).on("click",function(e){
|
||||
self.hideDetail();
|
||||
});
|
||||
|
||||
self.showDetail = function(data){
|
||||
Detail.render(data,function(tpl){
|
||||
$(".J_recordDetailOverlayContent",$detailEl).html(tpl);
|
||||
$detailEl.show();
|
||||
$mask.show();
|
||||
});
|
||||
};
|
||||
|
||||
self.hideDetail = function(){
|
||||
$detailEl.hide();
|
||||
$mask.hide();
|
||||
};
|
||||
};
|
||||
var detailPanel = new DetailView();
|
||||
|
||||
//record list
|
||||
var RecordModel = Backbone.Model.extend({});
|
||||
var RecordList = Backbone.Collection.extend({
|
||||
initialize:function(){
|
||||
var self = this;
|
||||
|
||||
self.on("add",function(data){
|
||||
new RecordRowView({
|
||||
model:data,
|
||||
detailPanel : detailPanel
|
||||
});
|
||||
});
|
||||
|
||||
self.on("reset",function(){
|
||||
$(".J_tableBody").empty();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var RecordRowView = Backbone.View.extend({
|
||||
tagName : "tr",
|
||||
className:function(){
|
||||
return this.model.toJSON().id % 2 ? "row_odd" : "row_even";
|
||||
},
|
||||
tpl : $("#main_table_row").html(),
|
||||
initialize:function(data){
|
||||
var self = this;
|
||||
self.model.on("change",self.render,self);
|
||||
self.addNewRecord();
|
||||
self.detailPanel = data.detailPanel;
|
||||
},
|
||||
events: {
|
||||
click: function(e){
|
||||
e.stopPropagation();
|
||||
var self = this;
|
||||
var detailData = self.model.toJSON();
|
||||
if(!isInApp){
|
||||
self.detailPanel.showDetail(detailData);
|
||||
}else{
|
||||
window.webkit.messageHandlers.list.postMessage(JSON.stringify(detailData));
|
||||
}
|
||||
}
|
||||
},
|
||||
render: function(){
|
||||
var self = this,
|
||||
data = self.model.toJSON();
|
||||
if(!data.statusCode){
|
||||
data.statusCode = "-";
|
||||
}else{
|
||||
self.$el.addClass("record_status_done")
|
||||
}
|
||||
|
||||
if(!data.mime){
|
||||
data.mime = "-";
|
||||
}
|
||||
|
||||
var html = _.template(self.tpl, data);
|
||||
self.$el.attr("recordId",data.id).empty().html(html);
|
||||
|
||||
return self;
|
||||
},
|
||||
addNewRecord:function(){
|
||||
$(".J_tableBody").prepend(this.render().$el);
|
||||
}
|
||||
});
|
||||
|
||||
var recList = new RecordList();
|
||||
|
||||
//other controllers
|
||||
$(".J_clearBtn").on("click",function(e){
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
clearLogs();
|
||||
});
|
||||
|
||||
$(document).on("keyup",function(e){
|
||||
if(e.keyCode == 88 && e.ctrlKey){ // ctrl + x
|
||||
clearLogs();
|
||||
}
|
||||
});
|
||||
|
||||
function clearLogs(){
|
||||
recList.reset();
|
||||
}
|
||||
|
||||
(function(){
|
||||
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;
|
||||
indicatorEl.fadeOut();
|
||||
// indicatorEl.css("visibility","hidden");
|
||||
}else{
|
||||
ifPause = false;
|
||||
indicatorEl.fadeIn();
|
||||
// indicatorEl.css("visibility","visible");
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
|
||||
//draggable panel
|
||||
(function(){
|
||||
var i = 0;
|
||||
var dragging = false,
|
||||
pageX = 0;
|
||||
$('#dragbar').mousedown(function(e){
|
||||
pageX = e.pageX;
|
||||
e.preventDefault();
|
||||
dragging = true;
|
||||
var main = $('.J_recordDetailOverlay');
|
||||
var ghostbar = $('<div>',{
|
||||
id:'ghostbar',
|
||||
css: {
|
||||
height: main.outerHeight(),
|
||||
top: main.offset().top,
|
||||
left: main.offset().left
|
||||
}
|
||||
}).appendTo('body');
|
||||
|
||||
$(document).mousemove(function(e){
|
||||
ghostbar.css("left",e.pageX);
|
||||
});
|
||||
});
|
||||
|
||||
$(document).mouseup(function(e){
|
||||
if(dragging){
|
||||
var deltaPageX = e.pageX - pageX;
|
||||
|
||||
$('.J_recordDetailOverlay').css("left",pageX + deltaPageX);
|
||||
if($('.J_recordDetailOverlay').width()<=100){
|
||||
$('.J_recordDetailOverlay').animate({
|
||||
'right': $('.J_recordDetailOverlay').width()
|
||||
},300,function(){
|
||||
$('.J_escBtn').trigger('click');
|
||||
});
|
||||
}
|
||||
pageX = e.pageX;
|
||||
$('#ghostbar').remove();
|
||||
$(document).unbind('mousemove');
|
||||
dragging = false;
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
});
|
||||
|
||||
Date.prototype.format = function (fmt) {
|
||||
var o = {
|
||||
"M+": this.getMonth() + 1, //月份
|
||||
"d+": this.getDate(), //日
|
||||
"h+": this.getHours(), //小时
|
||||
"m+": this.getMinutes(), //分
|
||||
"s+": this.getSeconds(), //秒
|
||||
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
|
||||
"S": this.getMilliseconds() //毫秒
|
||||
};
|
||||
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.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;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 5.5 KiB |
704
web/page.js
Normal file
704
web/page.js
Normal file
File diff suppressed because one or more lines are too long
174
web/src/detailPanel.js
Normal file
174
web/src/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({
|
||||
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(<li key={"reqHeader_" + key}><strong>{key}</strong> : {this.state.data.reqHeader[key]}</li>)
|
||||
}
|
||||
}
|
||||
|
||||
summarySection = (
|
||||
<div>
|
||||
<section className="req">
|
||||
<h4 className="subTitle">request</h4>
|
||||
<div className="detail">
|
||||
<ul className="uk-list">
|
||||
<li>{this.state.data.method} <span title="{this.state.data.path}">{this.state.data.path}</span> HTTP/1.1</li>
|
||||
{reqHeaderSection}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="reqBody">
|
||||
<h4 className="subTitle">request body</h4>
|
||||
<div className="detail">
|
||||
<p>{this.state.data.reqBody}</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
if(this.state.data.statusCode){
|
||||
|
||||
if(this.state.body.id == this.state.data.id){
|
||||
bodyContent = (<pre className="resBodyContent">{this.state.body.body}</pre>);
|
||||
}else{
|
||||
bodyContent = null;
|
||||
this.loadBody();
|
||||
}
|
||||
|
||||
if(this.state.data.resHeader){
|
||||
for(var key in this.state.data.resHeader){
|
||||
resHeaderSection.push(<li key={"resHeader_" + key}><strong>{key}</strong> : {this.state.data.resHeader[key]}</li>)
|
||||
}
|
||||
}
|
||||
|
||||
detailSection = (
|
||||
<div>
|
||||
<section className="resHeader">
|
||||
<h4 className="subTitle">response header</h4>
|
||||
<div className="detail">
|
||||
<ul className="uk-list">
|
||||
<li>HTTP/1.1 <span className={"http_status http_status_" + this.state.data.statusCode}>{this.state.data.statusCode}</span></li>
|
||||
{resHeaderSection}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="resBody">
|
||||
<h4 className="subTitle">response body</h4>
|
||||
<div className="detail">{bodyContent}</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{display:this.state.show ? "block" :"none"}}>
|
||||
<div className="overlay_mask" onClick={this.setHide}></div>
|
||||
<div className="recordDetailOverlay" ref="mainOverlay" style={{left: this.state.left}}>
|
||||
<div className="dragbar" onMouseDown={this.dealDrag}></div>
|
||||
<span className="escBtn" onClick={this.setHide}>Close (ESC)</span>
|
||||
<div>
|
||||
{summarySection}
|
||||
{detailSection}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return DetailPanel;
|
||||
}
|
||||
|
||||
module.exports.init = init;
|
141
web/src/index.js
Normal file
141
web/src/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(
|
||||
<WsIndicator />,
|
||||
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(
|
||||
<RecordPanel />,
|
||||
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;
|
||||
}
|
||||
});
|
||||
})();
|
51
web/src/recordPanel.js
Normal file
51
web/src/recordPanel.js
Normal file
@ -0,0 +1,51 @@
|
||||
function init(React){
|
||||
var RecordRow = require("./recordRow").init(React);
|
||||
|
||||
var RecordPanel = React.createClass({
|
||||
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(<RecordRow key={item.id} data={item}></RecordRow>);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<table className="uk-table uk-table-condensed uk-table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="col_id">id</th>
|
||||
<th className="col_method">method</th>
|
||||
<th className="col_code">code</th>
|
||||
<th className="col_host">host</th>
|
||||
<th className="col_path">path</th>
|
||||
<th className="col_mime">mime type</th>
|
||||
<th className="col_time">time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rowCollection}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return RecordPanel;
|
||||
}
|
||||
|
||||
module.exports.init = init;
|
70
web/src/recordRow.js
Normal file
70
web/src/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(
|
||||
<DetailPanel />,
|
||||
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({
|
||||
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(
|
||||
<tr className={trClasses} onClick={this.handleClick}>
|
||||
<td className="data_id">{this.props.data._id}</td>
|
||||
<td>{this.props.data.method} <span className={"protocol protocol_" + this.props.data.protocol} title="https"><i className="iconfont">󰃉</i></span> </td>
|
||||
<td className={"http_status http_status_" + this.props.data.statusCode}>{this.props.data.statusCode}</td>
|
||||
<td title={this.props.data.host}>{this.props.data.host}</td>
|
||||
<td title={this.props.data.path}>{this.props.data.path}</td>
|
||||
<td>{this.props.data.mime}</td>
|
||||
<td>{dateStr}</td>
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
shouldComponentUpdate:function(nextPros){
|
||||
return nextPros.data._needRender;
|
||||
},
|
||||
componentDidUpdate:function(){},
|
||||
componentWillUnmount:function(){}
|
||||
});
|
||||
|
||||
return RecordRow;
|
||||
|
||||
}
|
||||
|
||||
module.exports.init = init;
|
18
web/src/wsIndicator.js
Normal file
18
web/src/wsIndicator.js
Normal file
@ -0,0 +1,18 @@
|
||||
function init(React){
|
||||
var WsIndicator = React.createClass({
|
||||
getInitialState:function(){
|
||||
return {
|
||||
isValid: false
|
||||
}
|
||||
},
|
||||
render:function(){
|
||||
return (
|
||||
<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;
|
2
web/watch.sh
Executable file
2
web/watch.sh
Executable file
@ -0,0 +1,2 @@
|
||||
jsx --watch src/ build/ &
|
||||
webpack --progress --colors --watch
|
8
web/webpack.config.js
Normal file
8
web/webpack.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
entry: "./build/index.js",
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: "page.js"
|
||||
},
|
||||
module: {}
|
||||
};
|
@ -20,7 +20,7 @@
|
||||
<button type="button" id="J_sendReq">Send</button>
|
||||
<div class="logPanel" id="J_logPanel"></div>
|
||||
|
||||
<script src="./anyproxy_wsUtil.js"></script>
|
||||
<script src="./lib/anyproxy_wsUtil.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
//print log
|
||||
|
@ -1,182 +0,0 @@
|
||||
.topHead{
|
||||
background: #000;
|
||||
height: 42px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.topHead h1{
|
||||
color: rgb(204,204,204);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.topHead .topBtn{
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.topHead .btn_disable{
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.mainTableWrapper{
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.mainTableWrapper table{
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.mainTableWrapper td,
|
||||
.mainTableWrapper th{
|
||||
padding: 4px 12px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mainTableWrapper tbody tr{
|
||||
color: #AAA;
|
||||
}
|
||||
|
||||
.mainTableWrapper tbody tr.record_status_done{
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.mainTableWrapper .col_id{
|
||||
width: 25px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.mainTableWrapper .col_code{
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.mainTableWrapper .col_method{
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.mainTableWrapper .col_host{
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.mainTableWrapper .col_mime{
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.mainTableWrapper .col_time{
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.mainTableWrapper tr.row_odd{
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.mainTableWrapper tr.row_even{
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
.uk-table-hover tbody tr:hover{
|
||||
cursor: pointer;
|
||||
background: #CCC;
|
||||
}
|
||||
|
||||
.resHeader{
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.resBody textarea{
|
||||
width: 400px;
|
||||
height: 280px;
|
||||
}
|
||||
|
||||
.subTitle{
|
||||
padding-left: 6px;
|
||||
border-left: 3px solid #1FA2D6;
|
||||
font-size: 16px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.detail{
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.overlay_mask{
|
||||
position: fixed;
|
||||
top:0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #EEE;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.recordDetailOverlay{
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
left: 35%;
|
||||
right: 0;
|
||||
background: #FFF;
|
||||
border-left: 1px solid #CCC;
|
||||
top: 0;
|
||||
padding: 10px 10px 20px 10px;
|
||||
overflow-y:scroll;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
}
|
||||
|
||||
.recordDetailOverlay .escBtn{
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 8px;
|
||||
color: #777;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.recordDetailOverlay li{
|
||||
white-space: nowrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.data_id{
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.http_status{
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.http_status_200{
|
||||
color: #408E2F;
|
||||
}
|
||||
|
||||
.http_status_404,
|
||||
.http_status_500,
|
||||
.http_status_501,
|
||||
.http_status_502,
|
||||
.http_status_503,
|
||||
.http_status_504
|
||||
{
|
||||
color: #910A0A;
|
||||
}
|
||||
|
||||
|
||||
#dragbar{
|
||||
position:absolute;
|
||||
left:0px;
|
||||
top:0px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
background-color:#CCC;
|
||||
width: 3px;
|
||||
cursor: col-resize;
|
||||
}
|
||||
#ghostbar{
|
||||
width:3px;
|
||||
background-color:#000;
|
||||
opacity:0.5;
|
||||
position:absolute;
|
||||
cursor: col-resize;
|
||||
z-index:999
|
||||
}
|
3
web_uiconfig/css/uikit.gradient.min.css
vendored
3
web_uiconfig/css/uikit.gradient.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,208 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Anyproxy</title>
|
||||
<link rel="stylesheet" href="/css/uikit.gradient.min.css" />
|
||||
<link rel="stylesheet" href="/css/page.css" />
|
||||
<link rel="icon" type="image/png" href="/favico.png" />
|
||||
<script charset="utf-8" id="seajsnode"src="http://static.alipayobjects.com/seajs/??seajs/2.2.0/sea.js,seajs-combo/1.0.1/seajs-combo.js,seajs-style/1.0.2/seajs-style.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="topHead">
|
||||
<h1>Anyproxy - Settings</h1>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<h3>Current Rules</h3>
|
||||
<hr>
|
||||
<div class="list sectionWrapper">
|
||||
<ul class="uk-list uk-list-line uk-list-space J_listWrapper">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3>Add new rule</h3>
|
||||
<hr>
|
||||
<div class="content sectionWrapper">
|
||||
<form class="uk-form uk-form-stacked J_infoForm">
|
||||
<div class="uk-form-row">
|
||||
<label class="uk-form-label" for="form-s-it">Name</label>
|
||||
<div class="uk-form-controls">
|
||||
<input type="text" name="name" required placeholder="rule name">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uk-form-row">
|
||||
<label class="uk-form-label" for="form-s-it">URL keywords</label>
|
||||
<div class="uk-form-controls">
|
||||
<input type="text" class="uk-form-width-large" name="urlKey" placeholder="api.sample.com/apiA">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uk-form-row">
|
||||
<label class="uk-form-label" for="form-s-ip">Body keywords</label>
|
||||
<div class="uk-form-controls">
|
||||
<input type="text" class="uk-form-width-large" name="reqBodyKey" placeholder="some keywords in request body">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uk-form-row">
|
||||
<label class="uk-form-label" for="form-s-t">using Response Body</label>
|
||||
<div class="uk-form-controls">
|
||||
<textarea cols="70" rows="8" name="localResponse" placeholder="replace response with data"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="uk-form-row">
|
||||
<button class="uk-button J_addBtn" type="button">add</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
.removeBtn{
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.sectionWrapper{
|
||||
padding: 0px 20px 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/template" id="listItemTpl">
|
||||
<li>
|
||||
<strong>{{name}}</strong> <a href="#" class="J_remove removeBtn" ruleId="{{id}}">(remove)</a><br>
|
||||
{{urlKey}} {{reqBodyKey}}
|
||||
<br>{{localResponse}}
|
||||
</li>
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
seajs.config({
|
||||
base: 'http://static.alipayobjects.com/',
|
||||
alias: {
|
||||
'$' : 'jquery/jquery/1.7.2/jquery',
|
||||
'Backbone' : 'gallery/backbone/1.1.2/backbone.js',
|
||||
'Underscore': 'gallery/underscore/1.6.0/underscore.js'
|
||||
}
|
||||
});
|
||||
|
||||
seajs.use(['$','Underscore' ,'Backbone'], function($, _ ,Backbone) {
|
||||
|
||||
|
||||
function dataMgmt(){
|
||||
var self = this,
|
||||
currentID = 0,
|
||||
SAVING_KEY = "anyproxy_local",
|
||||
currentLSString = localStorage.getItem(SAVING_KEY),
|
||||
currentData = currentLSString ? JSON.parse(currentLSString) : [];
|
||||
|
||||
//init currentID
|
||||
currentData.map(function(item){
|
||||
currentID = (item.id >= currentID ? item.id + 1 : currentID);
|
||||
});
|
||||
|
||||
_.extend(self, Backbone.Events);
|
||||
|
||||
self.data = currentData;
|
||||
|
||||
self.add = function(data){
|
||||
data.id = currentID;
|
||||
++currentID;
|
||||
currentData.push(data);
|
||||
updateLS();
|
||||
}
|
||||
|
||||
self.remove = function(targetId){
|
||||
currentData.map(function(item,index){
|
||||
if(parseInt(item.id) == parseInt(targetId)){
|
||||
currentData.splice(index,1);
|
||||
}
|
||||
});
|
||||
updateLS();
|
||||
}
|
||||
|
||||
self.syncToServer = function(cb){
|
||||
$.post("/update",JSON.stringify(currentData),cb);
|
||||
}
|
||||
|
||||
function updateLS(){
|
||||
localStorage.setItem(SAVING_KEY,JSON.stringify(currentData));
|
||||
self.syncToServer(function(){
|
||||
self.trigger("update");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//config.model
|
||||
function ruleViewController(config){
|
||||
var self = this,
|
||||
wrapper = config.wrapper,
|
||||
liTpl = config.liTpl,
|
||||
model = config.model;
|
||||
|
||||
self.render = function(data){
|
||||
return substitute(liTpl,data);
|
||||
}
|
||||
|
||||
model.on("update",function(data){
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
//init
|
||||
model.data.map(function(item){
|
||||
wrapper.append(self.render(item));
|
||||
console.log(item);
|
||||
});
|
||||
}
|
||||
|
||||
var dataMgmtInstance = new dataMgmt();
|
||||
dataMgmtInstance.syncToServer();
|
||||
|
||||
var ruleView = new ruleViewController({
|
||||
model : dataMgmtInstance,
|
||||
wrapper : $(".J_listWrapper"),
|
||||
liTpl : $("#listItemTpl").html()
|
||||
});
|
||||
|
||||
$(".J_addBtn").on("click",function(e){
|
||||
e.preventDefault();
|
||||
var info = $(".J_infoForm").serializeArray();
|
||||
var finalData = {};
|
||||
info.map(function(item){
|
||||
finalData[item.name] = item.value;
|
||||
});
|
||||
|
||||
dataMgmtInstance.add(finalData);
|
||||
});
|
||||
|
||||
$(".J_listWrapper").on("click",function(e){
|
||||
var srcNode = $(e.srcElement);
|
||||
if(srcNode.hasClass("J_remove")){
|
||||
var id = srcNode.attr("ruleId");
|
||||
dataMgmtInstance.remove(parseInt(id));
|
||||
}
|
||||
});
|
||||
|
||||
function substitute(str, object, regexp){
|
||||
return String(str).replace(regexp || (/\{\{([^{}]+)\}\}/g), function(match, name){
|
||||
if (match.charAt(0) == '\\') return match.slice(1);
|
||||
return (object[name] != null) ? object[name] : '';
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
x
Reference in New Issue
Block a user