reconstruct map local

This commit is contained in:
OttoMao 2015-07-07 17:31:56 +08:00
parent b595e3aa9c
commit 8a146f7373
36 changed files with 56684 additions and 530 deletions

View File

@ -1,6 +1,13 @@
var utils = require("./util"),
bodyParser = require("body-parser"),
fs = require("fs");
var isRootCAFileExists = require("./certMgr.js").isRootCAFileExists(),
interceptFlag = false;
//e.g. [ { keyword: 'aaa', local: '/Users/Stella/061739.pdf' } ]
var mapConfig = [];
module.exports = {
summary:function(){
var tip = "the default rule for anyproxy which supports CORS. ";
@ -15,13 +22,30 @@ module.exports = {
if(req.method == "OPTIONS"){
return true;
}else{
var simpleUrl = (req.headers.host || "") + (req.url || "");
mapConfig.map(function(item){
var key = item.keyword;
if(simpleUrl.indexOf(key) >= 0){
req.anyproxy_map_local = item.local;
return false;
}
});
return !!req.anyproxy_map_local;
}
},
dealLocalResponse : function(req,reqBody,callback){
if(req.method == "OPTIONS"){
callback(200,mergeCORSHeader(req.headers),"");
}else if(req.anyproxy_map_local){
try{
var fileContent = fs.readFile(req.anyproxy_map_local,function(err,buffer){
callback(200, {}, buffer);
});
}catch(e){
callback(200, {}, "failed to load local file :" + req.anyproxy_map_local);
}
}
},
@ -63,6 +87,31 @@ module.exports = {
setInterceptFlag: function(flag){
interceptFlag = flag && isRootCAFileExists;
},
_plugIntoWebinterface: function(app,cb){
app.get("/filetree",function(req,res){
try{
var root = req.query.root || process.env.HOME || "/";
utils.filewalker(root,function(err, info){
res.json(info);
});
}catch(e){
res.end(e);
}
});
app.use(bodyParser.json());
app.get("/getMapConfig",function(req,res){
res.json(mapConfig);
});
app.post("/setMapConfig",function(req,res){
mapConfig = req.body;
res.json(mapConfig);
});
cb();
}
};

View File

@ -1,3 +1,6 @@
var fs = require("fs"),
path = require("path");
// {"Content-Encoding":"gzip"} --> {"content-encoding":"gzip"}
module.exports.lower_keys = function(obj){
for(var key in obj){
@ -27,5 +30,37 @@ module.exports.simpleRender = function(str, object, regexp){
if (match.charAt(0) == '\\') return match.slice(1);
return (object[name] != null) ? object[name] : '';
});
}
module.exports.filewalker = function(root,cb){
root = root || process.cwd();
var ret = {
directory :[],
file :[]
};
fs.readdir(root,function(err, list){
if(list && list.length){
list.map(function(item){
var fullPath = path.join(root,item),
stat = fs.lstatSync(fullPath);
if(stat.isFile()){
ret.file.push({
name : item,
fullPath : fullPath
});
}else if(stat.isDirectory()){
ret.directory.push({
name : item,
fullPath : fullPath
});
}
});
}
cb && cb.apply(null,[null,ret]);
});
}

View File

@ -14,8 +14,13 @@ var express = require("express"),
function webInterface(config){
var port = config.port,
wsPort = config.wsPort,
ruleSummary = config.ruleSummary,
ipAddress = config.ip;
ipAddress = config.ip,
userRule = config.userRule,
ruleSummary = "";
try{
ruleSummary = userRule.summary();
}catch(e){}
var self = this,
myAbsAddress = "http://" + ipAddress + ":" + port +"/",
@ -81,8 +86,6 @@ function webInterface(config){
res.end(resDom);
});
app.use(function(req,res,next){
var indexTpl = fs.readFileSync(path.join(staticDir,"/index.html"),{encoding:"utf8"}),
indexHTML = util.simpleRender(indexTpl, {
@ -100,7 +103,14 @@ function webInterface(config){
});
app.use(express.static(staticDir));
if(typeof userRule._plugIntoWebinterface == "function"){
userRule._plugIntoWebinterface(app,function(){
app.listen(port);
});
}else{
app.listen(port);
}
self.app = app;
}

View File

@ -9,6 +9,7 @@
"dependencies": {
"async": "~0.9.0",
"async-task-mgr": ">=1.1.0",
"body-parser": "^1.13.1",
"colorful": "^2.1.0",
"commander": "~2.3.0",
"compression": "^1.4.4",

View File

@ -157,7 +157,7 @@ function proxyServer(option){
var config = {
port : proxyWebPort,
wsPort : socketPort,
ruleSummary : requestHandler.getRuleSummary(),
userRule : proxyRules,
ip : ip.address()
};

View File

@ -1,88 +1,25 @@
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;
id = self.props.data.id;
if(!id) return;
ws.reqBody(id,function(content){
if(content.id == self.state.data.id){
if(content.id == self.props.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 = [],
@ -90,9 +27,9 @@ function init(React){
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]))
if(this.props.data.reqHeader){
for(var key in this.props.data.reqHeader){
reqHeaderSection.push(React.createElement("li", {key: "reqHeader_" + key}, React.createElement("strong", null, key), " : ", this.props.data.reqHeader[key]))
}
}
@ -102,7 +39,7 @@ function init(React){
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"),
React.createElement("li", null, this.props.data.method, " ", React.createElement("span", {title: "{this.props.data.path}"}, this.props.data.path), " HTTP/1.1"),
reqHeaderSection
)
)
@ -111,24 +48,24 @@ function init(React){
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)
React.createElement("p", null, this.props.data.reqBody)
)
)
)
);
if(this.state.data.statusCode){
if(this.props.data.statusCode){
if(this.state.body.id == this.state.data.id){
if(this.state.body.id == this.props.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]))
if(this.props.data.resHeader){
for(var key in this.props.data.resHeader){
resHeaderSection.push(React.createElement("li", {key: "resHeader_" + key}, React.createElement("strong", null, key), " : ", this.props.data.resHeader[key]))
}
}
@ -138,7 +75,7 @@ function init(React){
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)),
React.createElement("li", null, "HTTP/1.1 ", React.createElement("span", {className: "http_status http_status_" + this.props.data.statusCode}, this.props.data.statusCode)),
resHeaderSection
)
)
@ -153,17 +90,10 @@ function init(React){
}
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
)
)
)
);
}
});

39
web/build/filter.js Normal file
View File

@ -0,0 +1,39 @@
function init(React){
var Filter = React.createClass({displayName: "Filter",
dealChange:function(){
var self = this,
userInput = React.findDOMNode(self.refs.keywordInput).value;
self.props.onChangeKeyword && self.props.onChangeKeyword.call(null,userInput);
},
setFocus:function(){
var self = this;
React.findDOMNode(self.refs.keywordInput).focus();
},
componentDidUpdate:function(){
this.setFocus();
},
render:function(){
var self = this;
return (
React.createElement("div", {className: "filterSection"},
React.createElement("h4", {className: "subTitle"}, "Log Filter"),
React.createElement("form", {className: "uk-form"},
React.createElement("input", {className: "uk-form-large", ref: "keywordInput", onChange: self.dealChange, type: "text", placeholder: "type keywords or /^regExp$/", width: "300"})
)
)
);
},
componentDidMount:function(){
this.setFocus();
}
});
return Filter;
}
module.exports.init = init;

View File

@ -1,10 +1,15 @@
require("../lib/zepto");
window.$ = window.jQuery = require("../lib/jquery");
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);
RecordPanel = require("./recordPanel").init(React),
MapPanel = require("./mapPanel").init(React),
Detail = require("./detailPanel").init(React),
Popup = require("./popup").init(React),
Filter = require("./filter").init(React);
var ifPause = false,
recordSet = [];
@ -15,6 +20,7 @@ var ifPause = false,
//Event : wsEnd
var eventCenter = new EventManager();
//invoke AnyProxy web socket util
(function(){
try{
@ -63,7 +69,19 @@ var eventCenter = new EventManager();
})();
//record panel
//init popup
var pop;
(function(){
$("body").append('<div id="J_popOuter"></div>');
pop = React.render(
React.createElement(Popup, null),
document.getElementById("J_popOuter")
);
})();
//init record panel
var recorder;
(function(){
//merge : right --> left
function util_merge(left,right){
@ -89,29 +107,40 @@ var eventCenter = new EventManager();
// React.addons.Perf.stop();
}
}
function showDetail(data){
pop.setState({
show: true,
content : React.createElement(Detail, {data: data}),
left:"35%"
});
}
eventCenter.addListener("wsGetUpdate",updateRecordSet);
var Panel = React.render(
React.createElement(RecordPanel, null),
document.getElementById("J_content")
);
eventCenter.addListener('recordSetUpdated',function(){
Panel.setState({
recorder.setState({
list : recordSet
});
});
eventCenter.addListener("filterUpdated",function(newKeyword){
Panel.setState({
recorder.setState({
filter: newKeyword
});
});
//init recorder panel
recorder = React.render(
React.createElement(RecordPanel, {onSelect: showDetail}),
document.getElementById("J_content")
);
})();
//action bar
(function(){
//clear log
function clearLogs(){
recordSet = [];
eventCenter.dispatchEvent("recordSetUpdated");
@ -130,6 +159,7 @@ var eventCenter = new EventManager();
clearLogs();
});
//start , pause
var statusBtn = $(".J_statusBtn");
statusBtn.on("click",function(e){
e.stopPropagation();
@ -145,26 +175,41 @@ var eventCenter = new EventManager();
}
});
function switchFilterWidget(ifToShow){
if(ifToShow){
$(".J_filterSection").show();
$("#J_filterKeyword").focus();
}else{
$(".J_filterSection").hide();
$("#J_filterKeyword").val("");
}
//filter
(function (){
function updateKeyword(key){
eventCenter.dispatchEvent("filterUpdated",key);
}
$(".J_toggleFilterBtn").on("click",function(){
switchFilterWidget( $(".J_filterSection").css("display") == "none" );
var filter = (React.createElement(Filter, {onChangeKeyword: updateKeyword}));
function showFilter(){
pop.setState({
show : true,
content : filter,
left:"70%"
});
}
$(".J_filterCloseBtn").on("click",function(){
switchFilterWidget(false);
});
$("#J_filterKeyword").on("change keyup",function(){
eventCenter.dispatchEvent("filterUpdated",this.value);
$(".J_showFilter").on("click",function(){
showFilter();
});
})();
//map local
(function(){
var mapPanel = (React.createElement(MapPanel, null));
function showMap(){
pop.setState({
show : true,
content : mapPanel,
left:"70%"
})
}
$(".J_showMapPanel").on("click",function(){
showMap();
});
})();
})();

114
web/build/mapForm.js Normal file
View File

@ -0,0 +1,114 @@
require("../lib/jstree");
function init(React){
function fetchTree(root,cb){
if(!root || root == "#"){
root = "";
}
$.getJSON("/filetree?root=" + root,function(resObj){
var ret = [];
try{
$.each(resObj.directory, function(k,item){
if(item.name.indexOf(".") == 0) return;
ret.push({
text : item.name,
id : item.fullPath,
icon : "uk-icon-folder",
children : true
});
});
$.each(resObj.file, function(k,item){
if(item.name.indexOf(".") == 0) return;
ret.push({
text : item.name,
id : item.fullPath,
icon : 'uk-icon-file-o',
children : false
});
});
}catch(e){}
cb && cb.call(null,ret);
});
}
var MapForm = React.createClass({displayName: "MapForm",
submitData:function(){
var self = this,
result = {};
var filePath = React.findDOMNode(self.refs.localFilePath).value,
keyword = React.findDOMNode(self.refs.keywordInput).value;
if(filePath && keyword){
self.props.onSubmit.call(null,{
keyword : keyword,
local : filePath
});
}
},
render:function(){
var self = this;
return (
React.createElement("div", null,
React.createElement("form", {className: "uk-form uk-form-stacked"},
React.createElement("fieldset", null,
React.createElement("legend", null, "add rule"),
React.createElement("div", {className: "uk-form-row"},
React.createElement("label", {className: "uk-form-label", htmlFor: "map_keywordInput"}, "keyword"),
React.createElement("div", {className: "uk-form-controls"},
React.createElement("input", {type: "text", id: "map_keywordInput", ref: "keywordInput", placeholder: "keyword"})
)
),
React.createElement("div", {className: "uk-form-row"},
React.createElement("label", {className: "uk-form-label", htmlFor: "map_localFilePath"}, "local file"),
React.createElement("div", {className: "uk-form-controls"},
React.createElement("input", {type: "text", id: "map_localFilePath", ref: "localFilePath", placeholder: "keyword"})
)
)
)
),
React.createElement("div", {ref: "treeWrapper"}),
React.createElement("button", {className: "uk-button", onClick: self.submitData}, "Add")
)
);
},
componentDidMount :function(){
var self = this;
var wrapperEl = $(React.findDOMNode(self.refs.treeWrapper)),
filePathInput = React.findDOMNode(self.refs.localFilePath);
wrapperEl.jstree({
'core' : {
'data' : function (node, cb) {
fetchTree(node.id,cb);
}
}
});
wrapperEl.on("changed.jstree", function (e, data) {
if(data && data.selected && data.selected.length){
//is folder
if(/folder/.test(data.node.icon)) return;
var item = data.selected[0];
filePathInput.value = item;
}
});
},
componentDidUpdate:function(){}
});
return MapForm;
}
module.exports.init = init;

80
web/build/mapList.js Normal file
View File

@ -0,0 +1,80 @@
function init(React){
var MapList = React.createClass({displayName: "MapList",
getInitialState:function(){
return {
ruleList : []
}
},
appendRecord:function(data){
var self = this,
newState = self.state.ruleList;
if(data && data.keyword && data.local){
newState.push({
keyword : data.keyword,
local : data.local
});
self.setState({
ruleList: newState
});
}
},
removeRecord:function(index){
var self = this,
newList = self.state.ruleList;
newList.splice(index,1);
self.setState({
ruleList : newList
});
},
render:function(){
var self = this,
collection = [];
collection = self.state.ruleList.map(function(item,index){
return (
React.createElement("li", null,
React.createElement("strong", null, item.keyword), React.createElement("a", {className: "removeBtn", href: "#", onClick: self.removeRecord.bind(self,index)}, "remove"), React.createElement("br", null),
React.createElement("span", null, item.local)
)
);
});
return (
React.createElement("ul", {className: "mapRuleList"},
collection
)
);
},
componentDidMount :function(){
var self = this;
$.getJSON("/getMapConfig",function(data){
self.setState({
ruleList : data
});
});
},
componentDidUpdate:function(){
var self = this;
//sync config
var currentList = self.state.ruleList;
$.ajax({
method : "POST",
url : "/setMapConfig",
contentType :"application/json",
data : JSON.stringify(currentList),
dataType : "json",
success :function(res){}
});
}
});
return MapList;
}
module.exports.init = init;

32
web/build/mapPanel.js Normal file
View File

@ -0,0 +1,32 @@
require("../lib/jstree");
function init(React){
var MapForm = require("./mapForm").init(React),
MapList = require("./mapList").init(React);
var MapPanel = React.createClass({displayName: "MapPanel",
appendRecord : function(data){
var self = this,
listComponent = self.refs.list;
listComponent.appendRecord(data);
},
render:function(){
var self = this;
return (
React.createElement("div", {className: "mapWrapper"},
React.createElement("h4", {className: "subTitle"}, "Current Config"),
React.createElement(MapList, {ref: "list"}),
React.createElement("h4", {className: "subTitle"}, "Map Local"),
React.createElement(MapForm, {onSubmit: self.appendRecord})
)
);
}
});
return MapPanel;
}
module.exports.init = init;

94
web/build/popup.js Normal file
View File

@ -0,0 +1,94 @@
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 Popup = React.createClass({displayName: "Popup",
getInitialState : function(){
return {
show : false,
left : "35%",
content : null
};
},
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
});
},
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"
});
}
});
},
componentDidUpdate:function(){
},
render : function(){
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,
this.state.content
)
)
)
);
}
});
return Popup;
}
module.exports.init = init;

View File

@ -9,8 +9,9 @@ function init(React){
};
},
render : function(){
var rowCollection = [],
filterStr = this.state.filter,
var self = this,
rowCollection = [],
filterStr = self.state.filter,
filter = filterStr;
//regexp
@ -20,8 +21,8 @@ function init(React){
}catch(e){}
}
for(var i = this.state.list.length-1 ; i >=0 ; i--){
var item = this.state.list[i];
for(var i = self.state.list.length-1 ; i >=0 ; i--){
var item = self.state.list[i];
if(item){
if(filter && item){
try{
@ -40,7 +41,7 @@ function init(React){
item._needRender = false;
}
rowCollection.push(React.createElement(RecordRow, {key: item.id, data: item}));
rowCollection.push(React.createElement(RecordRow, {key: item.id, data: item, onSelect: self.props.onSelect.bind(self,item)}));
}
}

View File

@ -1,12 +1,4 @@
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, //月份
@ -26,12 +18,6 @@ function init(React){
getInitialState : function(){
return null;
},
handleClick:function(e){
detail.setState({
data : this.props.data,
show : true
});
},
render : function(){
var trClassesArr = [],
trClasses;
@ -45,7 +31,7 @@ function init(React){
var dateStr = dateFormat(new Date(this.props.data.startTime),"hh:mm:ss");
return(
React.createElement("tr", {className: trClasses, onClick: this.handleClick},
React.createElement("tr", {className: trClasses, onClick: this.props.onSelect},
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: "uk-icon-lock"})), " "),
React.createElement("td", {className: "http_status http_status_" + this.props.data.statusCode}, this.props.data.statusCode),
@ -64,7 +50,6 @@ function init(React){
});
return RecordRow;
}
module.exports.init = init;

BIN
web/css/jstree_style/32px.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
web/css/jstree_style/40px.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

1031
web/css/jstree_style/style.css Executable file

File diff suppressed because it is too large Load Diff

1
web/css/jstree_style/style.min.css vendored Executable file

File diff suppressed because one or more lines are too long

BIN
web/css/jstree_style/throbber.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -287,15 +287,7 @@ body, html {
z-index:999
}
.filterSection{
background: #FFF;
padding: 15px 10px;
position: absolute;
left: 0;
top: 0;
border: 1px solid #333;
border-radius: 3px;
}
.filterSection .filterIcon{
font-size: 16px;
@ -310,6 +302,31 @@ body, html {
width: 250px;
}
.filterSection i{
cursor: pointer;
/*map panel*/
.jstree-icon.folder{
background: url(http://img.alicdn.com/bao/uploaded/TB1TqJjIpXXXXcYXFXXSutbFXXX.jpg_q90.jpg) no-repeat !important;
background-size: 100% 100%;
}
.mapWrapper form{
margin-top: 20px;
}
.mapWrapper ul{
list-style: none;
padding-left: 10px;
}
.mapWrapper li{
margin: 10px 0;
}
.mapWrapper .removeBtn{
display: inline-block;
margin-left: 5px;
}
/*map panel end*/

View File

@ -1,22 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>Anyproxy</title>
<title>AnyProxy</title>
<link rel="stylesheet" href="/css/uikit.gradient.min.css" />
<link rel="stylesheet" href="/css/page.css" />
<link rel="stylesheet" type="text/css" href="/css/jstree_style/style.min.css">
<link rel="icon" type="image/png" href="/favico.png" />
</head>
<body>
<div class="topHead">
<div class="logoWrapper">
<h1>Anyproxy</h1>
<h1>AnyProxy</h1>
<div id="J_indicatorEl"></div>
</div>
<div class="ctrlWrapper">
<a href="#"><span class="topBtn J_statusBtn"><i class="uk-icon-stop"></i>Stop</span></a>
<a href="#"><span class="topBtn J_statusBtn btn_disable"><i class="uk-icon-play"></i>Resume</span></a>
<a href="#"><span class="topBtn J_toggleFilterBtn"><i class="uk-icon-filter"></i>Filter</span></a>
<a href="#"><span class="topBtn J_clearBtn"><i class="uk-icon-eraser"></i>Clear(Ctrl+X)</span></a>
<span class="sep">|</span>
@ -24,21 +25,15 @@
<a href="/qr_root" class="J_fetchRootQR" target="_blank"><span class="topBtn"><i class="uk-icon-qrcode"></i>QRCode of rootCA.crt</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>Github</span></a>
<a href="https://github.com/alibaba/anyproxy" target="_blank"><span class="topBtn"><i class="uk-icon-github"></i>Github</span></a>
</div>
<div class="ruleDesc">
<a href="#"><span class="topBtn J_showFilter"><i class="uk-icon-filter"></i>Filter</span></a>
<a href="#"><span class="topBtn J_showMapPanel"><i class="uk-icon-level-down"></i>Map Local</span></a>
<span><i class="uk-icon-chain"></i>Rule : {{rule}}</span>
</div>
<div class="filterSection J_filterSection" style="display:none">
<form class="uk-form">
<input class="uk-form-large" type="text" id="J_filterKeyword" placeholder="type keywords or /^regExp$/" width="200"/>
</form>
<i class="uk-icon-times J_filterCloseBtn"></i>
</div>
<div style="clear:both"></div>
</div>

4
web/lib/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

7420
web/lib/jstree.js Executable file

File diff suppressed because it is too large Load Diff

19596
web/lib/react.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

27795
web/page.js

File diff suppressed because one or more lines are too long

View File

@ -1,88 +1,25 @@
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;
id = self.props.data.id;
if(!id) return;
ws.reqBody(id,function(content){
if(content.id == self.state.data.id){
if(content.id == self.props.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 = [],
@ -90,9 +27,9 @@ function init(React){
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>)
if(this.props.data.reqHeader){
for(var key in this.props.data.reqHeader){
reqHeaderSection.push(<li key={"reqHeader_" + key}><strong>{key}</strong> : {this.props.data.reqHeader[key]}</li>)
}
}
@ -102,7 +39,7 @@ function init(React){
<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>
<li>{this.props.data.method} <span title="{this.props.data.path}">{this.props.data.path}</span> HTTP/1.1</li>
{reqHeaderSection}
</ul>
</div>
@ -111,24 +48,24 @@ function init(React){
<section className="reqBody">
<h4 className="subTitle">request body</h4>
<div className="detail">
<p>{this.state.data.reqBody}</p>
<p>{this.props.data.reqBody}</p>
</div>
</section>
</div>
);
if(this.state.data.statusCode){
if(this.props.data.statusCode){
if(this.state.body.id == this.state.data.id){
if(this.state.body.id == this.props.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>)
if(this.props.data.resHeader){
for(var key in this.props.data.resHeader){
resHeaderSection.push(<li key={"resHeader_" + key}><strong>{key}</strong> : {this.props.data.resHeader[key]}</li>)
}
}
@ -138,7 +75,7 @@ function init(React){
<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>
<li>HTTP/1.1 <span className={"http_status http_status_" + this.props.data.statusCode}>{this.props.data.statusCode}</span></li>
{resHeaderSection}
</ul>
</div>
@ -153,17 +90,10 @@ function init(React){
}
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>
);
}
});

39
web/src/filter.js Normal file
View File

@ -0,0 +1,39 @@
function init(React){
var Filter = React.createClass({
dealChange:function(){
var self = this,
userInput = React.findDOMNode(self.refs.keywordInput).value;
self.props.onChangeKeyword && self.props.onChangeKeyword.call(null,userInput);
},
setFocus:function(){
var self = this;
React.findDOMNode(self.refs.keywordInput).focus();
},
componentDidUpdate:function(){
this.setFocus();
},
render:function(){
var self = this;
return (
<div className="filterSection">
<h4 className="subTitle">Log Filter</h4>
<form className="uk-form">
<input className="uk-form-large" ref="keywordInput" onChange={self.dealChange} type="text" placeholder="type keywords or /^regExp$/" width="300"/>
</form>
</div>
);
},
componentDidMount:function(){
this.setFocus();
}
});
return Filter;
}
module.exports.init = init;

View File

@ -1,10 +1,15 @@
require("../lib/zepto");
window.$ = window.jQuery = require("../lib/jquery");
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);
RecordPanel = require("./recordPanel").init(React),
MapPanel = require("./mapPanel").init(React),
Detail = require("./detailPanel").init(React),
Popup = require("./popup").init(React),
Filter = require("./filter").init(React);
var ifPause = false,
recordSet = [];
@ -15,6 +20,7 @@ var ifPause = false,
//Event : wsEnd
var eventCenter = new EventManager();
//invoke AnyProxy web socket util
(function(){
try{
@ -63,7 +69,19 @@ var eventCenter = new EventManager();
})();
//record panel
//init popup
var pop;
(function(){
$("body").append('<div id="J_popOuter"></div>');
pop = React.render(
<Popup />,
document.getElementById("J_popOuter")
);
})();
//init record panel
var recorder;
(function(){
//merge : right --> left
function util_merge(left,right){
@ -89,29 +107,40 @@ var eventCenter = new EventManager();
// React.addons.Perf.stop();
}
}
function showDetail(data){
pop.setState({
show: true,
content : <Detail data={data}/>,
left:"35%"
});
}
eventCenter.addListener("wsGetUpdate",updateRecordSet);
var Panel = React.render(
<RecordPanel />,
document.getElementById("J_content")
);
eventCenter.addListener('recordSetUpdated',function(){
Panel.setState({
recorder.setState({
list : recordSet
});
});
eventCenter.addListener("filterUpdated",function(newKeyword){
Panel.setState({
recorder.setState({
filter: newKeyword
});
});
//init recorder panel
recorder = React.render(
<RecordPanel onSelect={showDetail}/>,
document.getElementById("J_content")
);
})();
//action bar
(function(){
//clear log
function clearLogs(){
recordSet = [];
eventCenter.dispatchEvent("recordSetUpdated");
@ -130,6 +159,7 @@ var eventCenter = new EventManager();
clearLogs();
});
//start , pause
var statusBtn = $(".J_statusBtn");
statusBtn.on("click",function(e){
e.stopPropagation();
@ -145,26 +175,41 @@ var eventCenter = new EventManager();
}
});
function switchFilterWidget(ifToShow){
if(ifToShow){
$(".J_filterSection").show();
$("#J_filterKeyword").focus();
}else{
$(".J_filterSection").hide();
$("#J_filterKeyword").val("");
}
//filter
(function (){
function updateKeyword(key){
eventCenter.dispatchEvent("filterUpdated",key);
}
$(".J_toggleFilterBtn").on("click",function(){
switchFilterWidget( $(".J_filterSection").css("display") == "none" );
var filter = (<Filter onChangeKeyword={updateKeyword} />);
function showFilter(){
pop.setState({
show : true,
content : filter,
left:"70%"
});
}
$(".J_filterCloseBtn").on("click",function(){
switchFilterWidget(false);
});
$("#J_filterKeyword").on("change keyup",function(){
eventCenter.dispatchEvent("filterUpdated",this.value);
$(".J_showFilter").on("click",function(){
showFilter();
});
})();
//map local
(function(){
var mapPanel = (<MapPanel />);
function showMap(){
pop.setState({
show : true,
content : mapPanel,
left:"70%"
})
}
$(".J_showMapPanel").on("click",function(){
showMap();
});
})();
})();

114
web/src/mapForm.js Normal file
View File

@ -0,0 +1,114 @@
require("../lib/jstree");
function init(React){
function fetchTree(root,cb){
if(!root || root == "#"){
root = "";
}
$.getJSON("/filetree?root=" + root,function(resObj){
var ret = [];
try{
$.each(resObj.directory, function(k,item){
if(item.name.indexOf(".") == 0) return;
ret.push({
text : item.name,
id : item.fullPath,
icon : "uk-icon-folder",
children : true
});
});
$.each(resObj.file, function(k,item){
if(item.name.indexOf(".") == 0) return;
ret.push({
text : item.name,
id : item.fullPath,
icon : 'uk-icon-file-o',
children : false
});
});
}catch(e){}
cb && cb.call(null,ret);
});
}
var MapForm = React.createClass({
submitData:function(){
var self = this,
result = {};
var filePath = React.findDOMNode(self.refs.localFilePath).value,
keyword = React.findDOMNode(self.refs.keywordInput).value;
if(filePath && keyword){
self.props.onSubmit.call(null,{
keyword : keyword,
local : filePath
});
}
},
render:function(){
var self = this;
return (
<div>
<form className="uk-form uk-form-stacked">
<fieldset>
<legend>add rule</legend>
<div className="uk-form-row">
<label className="uk-form-label" htmlFor="map_keywordInput">keyword</label>
<div className="uk-form-controls">
<input type="text" id="map_keywordInput" ref="keywordInput" placeholder="keyword" />
</div>
</div>
<div className="uk-form-row">
<label className="uk-form-label" htmlFor="map_localFilePath">local file</label>
<div className="uk-form-controls">
<input type="text" id="map_localFilePath" ref="localFilePath" placeholder="keyword" />
</div>
</div>
</fieldset>
</form>
<div ref="treeWrapper"></div>
<button className="uk-button" onClick={self.submitData}>Add</button>
</div>
);
},
componentDidMount :function(){
var self = this;
var wrapperEl = $(React.findDOMNode(self.refs.treeWrapper)),
filePathInput = React.findDOMNode(self.refs.localFilePath);
wrapperEl.jstree({
'core' : {
'data' : function (node, cb) {
fetchTree(node.id,cb);
}
}
});
wrapperEl.on("changed.jstree", function (e, data) {
if(data && data.selected && data.selected.length){
//is folder
if(/folder/.test(data.node.icon)) return;
var item = data.selected[0];
filePathInput.value = item;
}
});
},
componentDidUpdate:function(){}
});
return MapForm;
}
module.exports.init = init;

80
web/src/mapList.js Normal file
View File

@ -0,0 +1,80 @@
function init(React){
var MapList = React.createClass({
getInitialState:function(){
return {
ruleList : []
}
},
appendRecord:function(data){
var self = this,
newState = self.state.ruleList;
if(data && data.keyword && data.local){
newState.push({
keyword : data.keyword,
local : data.local
});
self.setState({
ruleList: newState
});
}
},
removeRecord:function(index){
var self = this,
newList = self.state.ruleList;
newList.splice(index,1);
self.setState({
ruleList : newList
});
},
render:function(){
var self = this,
collection = [];
collection = self.state.ruleList.map(function(item,index){
return (
<li>
<strong>{item.keyword}</strong><a className="removeBtn" href="#" onClick={self.removeRecord.bind(self,index)}>remove</a><br />
<span>{item.local}</span>
</li>
);
});
return (
<ul className="mapRuleList">
{collection}
</ul>
);
},
componentDidMount :function(){
var self = this;
$.getJSON("/getMapConfig",function(data){
self.setState({
ruleList : data
});
});
},
componentDidUpdate:function(){
var self = this;
//sync config
var currentList = self.state.ruleList;
$.ajax({
method : "POST",
url : "/setMapConfig",
contentType :"application/json",
data : JSON.stringify(currentList),
dataType : "json",
success :function(res){}
});
}
});
return MapList;
}
module.exports.init = init;

32
web/src/mapPanel.js Normal file
View File

@ -0,0 +1,32 @@
require("../lib/jstree");
function init(React){
var MapForm = require("./mapForm").init(React),
MapList = require("./mapList").init(React);
var MapPanel = React.createClass({
appendRecord : function(data){
var self = this,
listComponent = self.refs.list;
listComponent.appendRecord(data);
},
render:function(){
var self = this;
return (
<div className="mapWrapper">
<h4 className="subTitle">Current Config</h4>
<MapList ref="list"/>
<h4 className="subTitle">Map Local</h4>
<MapForm onSubmit={self.appendRecord}/>
</div>
);
}
});
return MapPanel;
}
module.exports.init = init;

94
web/src/popup.js Normal file
View File

@ -0,0 +1,94 @@
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 Popup = React.createClass({
getInitialState : function(){
return {
show : false,
left : "35%",
content : null
};
},
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
});
},
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"
});
}
});
},
componentDidUpdate:function(){
},
render : function(){
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>
{this.state.content}
</div>
</div>
</div>
);
}
});
return Popup;
}
module.exports.init = init;

View File

@ -9,8 +9,9 @@ function init(React){
};
},
render : function(){
var rowCollection = [],
filterStr = this.state.filter,
var self = this,
rowCollection = [],
filterStr = self.state.filter,
filter = filterStr;
//regexp
@ -20,8 +21,8 @@ function init(React){
}catch(e){}
}
for(var i = this.state.list.length-1 ; i >=0 ; i--){
var item = this.state.list[i];
for(var i = self.state.list.length-1 ; i >=0 ; i--){
var item = self.state.list[i];
if(item){
if(filter && item){
try{
@ -40,7 +41,7 @@ function init(React){
item._needRender = false;
}
rowCollection.push(<RecordRow key={item.id} data={item}></RecordRow>);
rowCollection.push(<RecordRow key={item.id} data={item} onSelect={self.props.onSelect.bind(self,item)}></RecordRow>);
}
}

View File

@ -1,12 +1,4 @@
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, //月份
@ -26,12 +18,6 @@ function init(React){
getInitialState : function(){
return null;
},
handleClick:function(e){
detail.setState({
data : this.props.data,
show : true
});
},
render : function(){
var trClassesArr = [],
trClasses;
@ -45,7 +31,7 @@ function init(React){
var dateStr = dateFormat(new Date(this.props.data.startTime),"hh:mm:ss");
return(
<tr className={trClasses} onClick={this.handleClick}>
<tr className={trClasses} onClick={this.props.onSelect}>
<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="uk-icon-lock"></i></span> </td>
<td className={"http_status http_status_" + this.props.data.statusCode}>{this.props.data.statusCode}</td>
@ -64,7 +50,6 @@ function init(React){
});
return RecordRow;
}
module.exports.init = init;