mirror of
https://github.com/alibaba/anyproxy.git
synced 2025-05-10 14:58:27 +00:00
add ui config server
This commit is contained in:
parent
bc5659b14f
commit
b51236d678
39
README.md
39
README.md
@ -4,6 +4,8 @@ A fully configurable proxy in NodeJS, which can handle HTTPS requests perfectly.
|
||||
|
||||
(Chinese in this doc is nothing but translation of some key points. Be relax if you dont understand.)
|
||||
|
||||
因为一些集团开源/专利限制,我们暂时把anyproxy迁回了gitlab。如果喜欢,欢迎到[https://github.com/alipay-ct-wd/anyproxy](https://github.com/alipay-ct-wd/anyproxy)为我们点赞。
|
||||
|
||||

|
||||
|
||||
Feature
|
||||
@ -12,6 +14,7 @@ Feature
|
||||
* fully configurable, you can modify a request at any stage by your own javascript code
|
||||
* when working as https proxy, it can generate and intercept https requests for any domain without complaint by browser (after you trust its root CA)
|
||||
* a web interface is availabe for you to view request details
|
||||
* (beta)a web UI interface for you to replace some remote response with local data
|
||||
|
||||

|
||||
|
||||
@ -73,19 +76,24 @@ How to write your own rule file
|
||||
```javascript
|
||||
|
||||
module.exports = {
|
||||
/*
|
||||
these functions will overwrite the default ones, write your own when necessary.
|
||||
*/
|
||||
summary:function(){
|
||||
console.log("this is a blank rule for anyproxy");
|
||||
return "this is a blank rule for anyproxy";
|
||||
},
|
||||
|
||||
|
||||
//=======================
|
||||
//when getting a request from user
|
||||
//收到用户请求之后
|
||||
//=======================
|
||||
|
||||
//是否在本地直接发送响应(不再向服务器发出请求)
|
||||
//whether to intercept this request by local logic
|
||||
//if the return value is true, anyproxy will call dealLocalResponse to get response data and will not send request to remote server anymore
|
||||
shouldUseLocalResponse : function(req,reqBody){
|
||||
return false;
|
||||
},
|
||||
|
||||
//如果shouldUseLocalResponse返回true,会调用这个函数来获取本地响应内容
|
||||
//you may deal the response locally instead of sending it to server
|
||||
//this function be called when shouldUseLocalResponse returns true
|
||||
//callback(statusCode,resHeader,responseData)
|
||||
@ -94,6 +102,13 @@ module.exports = {
|
||||
callback(statusCode,resHeader,responseData)
|
||||
},
|
||||
|
||||
|
||||
//=======================
|
||||
//when ready to send a request to server
|
||||
//向服务端发出请求之前
|
||||
//=======================
|
||||
|
||||
//替换向服务器发出的请求协议(http和https的替换)
|
||||
//replace the request protocol when sending to the real server
|
||||
//protocol : "http" or "https"
|
||||
replaceRequestProtocol:function(req,protocol){
|
||||
@ -101,6 +116,7 @@ module.exports = {
|
||||
return newProtocol;
|
||||
},
|
||||
|
||||
//替换向服务器发出的请求参数(option)
|
||||
//req is user's request which will be sent to the proxy server, docs : http://nodejs.org/api/http.html#http_http_request_options_callback
|
||||
//you may return a customized option to replace the original option
|
||||
//you should not write content-length header in options, since anyproxy will handle it for you
|
||||
@ -109,17 +125,25 @@ module.exports = {
|
||||
return newOption;
|
||||
},
|
||||
|
||||
//替换请求的body
|
||||
//replace the request body
|
||||
replaceRequestData: function(req,data){
|
||||
return data;
|
||||
},
|
||||
|
||||
//=======================
|
||||
//when ready to send the response to user after receiving response from server
|
||||
//向用户返回服务端的响应之前
|
||||
//=======================
|
||||
|
||||
//替换服务器响应的http状态码
|
||||
//replace the statusCode before it's sent to the user
|
||||
replaceResponseStatusCode: function(req,res,statusCode){
|
||||
var newStatusCode = statusCode;
|
||||
return newStatusCode;
|
||||
},
|
||||
|
||||
//替换服务器响应的http头
|
||||
//replace the httpHeader before it's sent to the user
|
||||
//Here header == res.headers
|
||||
replaceResponseHeader: function(req,res,header){
|
||||
@ -127,6 +151,7 @@ module.exports = {
|
||||
return newHeader;
|
||||
},
|
||||
|
||||
//替换服务器响应的数据
|
||||
//replace the response from the server before it's sent to the user
|
||||
//you may return either a Buffer or a string
|
||||
//serverResData is a Buffer, you may get its content by calling serverResData.toString()
|
||||
@ -134,12 +159,18 @@ module.exports = {
|
||||
return serverResData;
|
||||
},
|
||||
|
||||
//在请求返回给用户前的延迟时间
|
||||
//add a pause before sending response to user
|
||||
pauseBeforeSendingResponse : function(req,res){
|
||||
var timeInMS = 1; //delay all requests for 1ms
|
||||
return timeInMS;
|
||||
},
|
||||
|
||||
//=======================
|
||||
//https config
|
||||
//=======================
|
||||
|
||||
//是否截获https请求
|
||||
//should intercept https request, or it will be forwarded to real server
|
||||
shouldInterceptHttpsReq :function(req){
|
||||
return false;
|
||||
|
@ -79,7 +79,7 @@ function userRequestHandler(req,userRes){
|
||||
}
|
||||
resourceInfo.resHeader = resHeader || {};
|
||||
resourceInfo.resBody = resBody;
|
||||
resourceInfo.length = resBody.length;
|
||||
resourceInfo.length = resBody ? resBody.length : 0;
|
||||
resourceInfo.statusCode = statusCode;
|
||||
|
||||
GLOBAL.recorder && GLOBAL.recorder.updateRecord(resourceInfoId,resourceInfo);
|
||||
@ -171,12 +171,12 @@ function userRequestHandler(req,userRes){
|
||||
|
||||
//send response
|
||||
},function(callback){
|
||||
if(404 == statusCode){
|
||||
var html404path = pathUtil.join(__dirname, '..', 'web', '404.html');
|
||||
userRes.end(fs.readFileSync(html404path));
|
||||
}else{
|
||||
// if(404 == statusCode){
|
||||
// var html404path = pathUtil.join(__dirname, '..', 'web', '404.html');
|
||||
// userRes.end(fs.readFileSync(html404path));
|
||||
// }else{
|
||||
userRes.end(serverResData);
|
||||
}
|
||||
// }
|
||||
callback();
|
||||
|
||||
//udpate record info
|
||||
@ -304,14 +304,26 @@ function setRules(newRule){
|
||||
if(!newRule){
|
||||
return;
|
||||
}else{
|
||||
|
||||
if(!newRule.summary){
|
||||
newRule.summary = function(){
|
||||
return "this rule file does not have a summary";
|
||||
};
|
||||
}
|
||||
|
||||
userRule = util.merge(defaultRule,newRule);
|
||||
'function' == typeof(userRule.summary) && userRule.summary();
|
||||
'function' == typeof(userRule.summary) && console.log(userRule.summary());
|
||||
}
|
||||
}
|
||||
|
||||
function getRuleSummary(){
|
||||
return userRule.summary();
|
||||
}
|
||||
|
||||
module.exports.userRequestHandler = userRequestHandler;
|
||||
module.exports.connectReqHandler = connectReqHandler;
|
||||
module.exports.setRules = setRules;
|
||||
module.exports.getRuleSummary = getRuleSummary;
|
||||
|
||||
/*
|
||||
note
|
||||
|
@ -1,9 +1,10 @@
|
||||
module.exports = {
|
||||
summary:function(){
|
||||
console.log("this is the default rule for anyproxy, which supports CORS request");
|
||||
return "the default rule for anyproxy, which supports CORS request";
|
||||
},
|
||||
|
||||
shouldUseLocalResponse : function(req,reqBody){
|
||||
|
||||
//intercept all options request
|
||||
if(req.method == "OPTIONS"){
|
||||
return true;
|
||||
|
13
package.json
13
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "anyproxy",
|
||||
"version": "2.2.1",
|
||||
"version": "2.3.0",
|
||||
"description": "a charles/fiddler like proxy written in NodeJs, which can handle HTTPS requests and CROS perfectly.",
|
||||
"main": "proxy.js",
|
||||
"bin": {
|
||||
@ -14,18 +14,15 @@
|
||||
"entities": "^1.1.1",
|
||||
"express": "^4.8.5",
|
||||
"iconv-lite": "^0.4.4",
|
||||
"juicer": "^0.6.6-stable",
|
||||
"nedb": "^0.11.0",
|
||||
"ws": "^0.4.32",
|
||||
"iconv-lite": "^0.4.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
"ws": "^0.4.32"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"test": "nodeunit test.js"
|
||||
},
|
||||
"repository": {
|
||||
},
|
||||
"repository": {},
|
||||
"author": "ottomao@gmail.com",
|
||||
"license": "ISC"
|
||||
}
|
||||
|
117
proxy.js
117
proxy.js
@ -17,10 +17,13 @@ var http = require('http'),
|
||||
getPort = require("./lib/getPort"),
|
||||
requestHandler = require("./lib/requestHandler"),
|
||||
Recorder = require("./lib/Recorder"),
|
||||
inherits = require("util").inherits,
|
||||
util = require("./lib/util"),
|
||||
entities = require("entities"),
|
||||
express = require("express"),
|
||||
path = require("path"),
|
||||
juicer = require('juicer'),
|
||||
events = require("events"),
|
||||
WebSocketServer= require('ws').Server;
|
||||
|
||||
GLOBAL.recorder = new Recorder();
|
||||
@ -30,6 +33,7 @@ var T_TYPE_HTTP = 0,
|
||||
DEFAULT_PORT = 8001,
|
||||
DEFAULT_WEB_PORT = 8002,
|
||||
DEFAULT_WEBSOCKET_PORT = 8003,
|
||||
DEFAULT_CONFIG_PORT = 8080,
|
||||
DEFAULT_HOST = "localhost",
|
||||
DEFAULT_TYPE = T_TYPE_HTTP;
|
||||
|
||||
@ -57,7 +61,7 @@ function proxyServer(option){
|
||||
proxyType = /https/i.test(option.type || DEFAULT_TYPE) ? T_TYPE_HTTPS : T_TYPE_HTTP ,
|
||||
proxyPort = option.port || DEFAULT_PORT,
|
||||
proxyHost = option.hostname || DEFAULT_HOST,
|
||||
proxyRules = option.rule || default_rule;
|
||||
proxyRules = option.rule || default_rule;
|
||||
|
||||
requestHandler.setRules(proxyRules); //TODO : optimize calling for set rule
|
||||
self.httpProxyServer = null;
|
||||
@ -95,7 +99,15 @@ function proxyServer(option){
|
||||
|
||||
//start web interface
|
||||
function(callback){
|
||||
startWebServer();
|
||||
var webServer = new proxyWebServer();
|
||||
var wss = webServer.wss;
|
||||
|
||||
var configServer = new UIConfigServer(DEFAULT_CONFIG_PORT);
|
||||
configServer.on("rule_changed",function() {
|
||||
console.log(arguments);
|
||||
})
|
||||
// var wss = proxyWebServer();
|
||||
|
||||
callback(null);
|
||||
}
|
||||
],
|
||||
@ -119,7 +131,90 @@ function proxyServer(option){
|
||||
}
|
||||
}
|
||||
|
||||
function startWebServer(port){
|
||||
// doing
|
||||
function UIConfigServer(port){
|
||||
var self = this;
|
||||
|
||||
var app = express(),
|
||||
customerRule = {
|
||||
summary: function(){
|
||||
console.log("replace some response with local response");
|
||||
return "replace some response with local response";
|
||||
}
|
||||
},
|
||||
userKey;
|
||||
|
||||
|
||||
customerRule.shouldUseLocalResponse = function(req,reqBody){
|
||||
var url = req.url;
|
||||
if(userKey){
|
||||
var ifMatch = false;
|
||||
userKey.map(function(item){
|
||||
if(ifMatch) return;
|
||||
|
||||
var matchCount = 0;
|
||||
if( !item.urlKey && !item.reqBodyKey){
|
||||
ifMatch = false;
|
||||
return;
|
||||
}else{
|
||||
if(!item.urlKey || (item.urlKey && url.indexOf(item.urlKey) >= 0 ) ){
|
||||
++matchCount;
|
||||
}
|
||||
|
||||
if(!item.reqBodyKey || (item.reqBodyKey && reqBody.toString().indexOf(item.reqBodyKey) >= 0) ){
|
||||
++matchCount;
|
||||
}
|
||||
|
||||
ifMatch = (matchCount==2);
|
||||
if(ifMatch){
|
||||
req.willResponse = item.localResponse;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return ifMatch;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
customerRule.dealLocalResponse = function(req,reqBody,callback){
|
||||
callback(200,{"content-type":"text/html"},req.willResponse)
|
||||
|
||||
return req.willResponse;
|
||||
};
|
||||
|
||||
app.post("/update",function(req,res){
|
||||
var data = "";
|
||||
req.on("data",function(chunk){
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
req.on("end",function(){
|
||||
userKey = JSON.parse(data);
|
||||
|
||||
res.statusCode = 200;
|
||||
res.setHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
res.end(JSON.stringify({success : true}));
|
||||
|
||||
requestHandler.setRules(customerRule);
|
||||
self.emit("rule_changed");
|
||||
});
|
||||
});
|
||||
|
||||
app.use(express.static(__dirname + "/web_uiconfig"));
|
||||
app.listen(port);
|
||||
|
||||
self.app = app;
|
||||
}
|
||||
|
||||
inherits(UIConfigServer, events.EventEmitter);
|
||||
|
||||
|
||||
|
||||
function proxyWebServer(port){
|
||||
var self = this;
|
||||
|
||||
port = port || DEFAULT_WEB_PORT;
|
||||
|
||||
//web interface
|
||||
@ -150,13 +245,22 @@ function startWebServer(port){
|
||||
res.end(entities.encodeHTML(body));
|
||||
});
|
||||
|
||||
app.use(function(req,res,next){
|
||||
var indexHTML = fs.readFileSync("./web/index.html",{encoding:"utf8"});
|
||||
|
||||
if(req.url == "/"){
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
res.end(indexHTML.replace("{{rule}}",requestHandler.getRuleSummary()) );
|
||||
}else{
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
app.use(express.static(__dirname + '/web'));
|
||||
|
||||
app.listen(port);
|
||||
|
||||
var tipText = "web interface started at port " + port;
|
||||
console.log(color.green(tipText));
|
||||
|
||||
|
||||
//web socket interface
|
||||
var wss = new WebSocketServer({port: DEFAULT_WEBSOCKET_PORT});
|
||||
@ -169,6 +273,9 @@ function startWebServer(port){
|
||||
GLOBAL.recorder.on("update",function(data){
|
||||
wss.broadcast( JSON.stringify(data) );
|
||||
});
|
||||
|
||||
self.app = app;
|
||||
self.wss = wss;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,10 +2,10 @@ module.exports = {
|
||||
/*
|
||||
These functions will overwrite the default ones, write your own when necessary.
|
||||
Comments in Chinese are nothing but a translation of key points. Be relax if you dont understand.
|
||||
致中文用户:中文注释都是只摘要,必要时请参阅英文注释。欢迎提出修改建议。
|
||||
致中文用户:中文注释都是只摘要,必要时请参阅英文文档。欢迎提出修改建议。
|
||||
*/
|
||||
summary:function(){
|
||||
console.log("this is a blank rule for anyproxy");
|
||||
return "this is a blank rule for anyproxy";
|
||||
},
|
||||
|
||||
|
||||
|
@ -2,10 +2,13 @@
|
||||
background: #000;
|
||||
height: 42px;
|
||||
position: relative;
|
||||
-webkit-box-shadow: 0px 3px 23px 0px rgba(50, 50, 50, 0.75);
|
||||
-moz-box-shadow: 0px 3px 23px 0px rgba(50, 50, 50, 0.75);
|
||||
box-shadow: 0px 3px 23px 0px rgba(50, 50, 50, 0.75);
|
||||
}
|
||||
|
||||
.topHead h1{
|
||||
color: rgb(204,204,204);
|
||||
color: #CCCCCC;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@ -17,6 +20,17 @@
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.ruleDesc{
|
||||
background: #88C4FE;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.ruleDesc h4{
|
||||
color: #333;
|
||||
line-height: 25px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mainTableWrapper{
|
||||
margin-top: 0;
|
||||
}
|
||||
|
3
web/footer.html
Normal file
3
web/footer.html
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
</body>
|
||||
</html>
|
2
web/header.html
Normal file
2
web/header.html
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
<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">
|
||||
@ -12,8 +14,11 @@
|
||||
<a href="#" class="J_clearBtn"><span class="topBtn">Clear Logs(Ctrl+X)</span></a>
|
||||
<a href="#" class="J_statusBtn"><span class="topBtn">Stop</span></a>
|
||||
<a href="#" class="J_statusBtn btn_disable"><span class="topBtn">Resume</span></a>
|
||||
<a href="http://localhost:8080"><span class="topBtn">Config Local Response(beta)</span></a>
|
||||
</div>
|
||||
<div class="ruleDesc">
|
||||
<h4>rule : <strong>{{rule}}</strong></h4>
|
||||
</div>
|
||||
|
||||
<div class="mainTableWrapper J_mainTable">
|
||||
<table class="uk-table uk-table-condensed uk-table-hover">
|
||||
<thead>
|
||||
@ -90,7 +95,6 @@
|
||||
<% } %>
|
||||
</script>
|
||||
|
||||
<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="/page.js"></script>
|
||||
|
||||
</body>
|
||||
|
14
web/page.js
14
web/page.js
@ -1,11 +1,11 @@
|
||||
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'
|
||||
}
|
||||
});
|
||||
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) {
|
||||
Backbone.$ = $;
|
||||
|
182
web_uiconfig/css/page.css
Normal file
182
web_uiconfig/css/page.css
Normal file
@ -0,0 +1,182 @@
|
||||
.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
Executable file
3
web_uiconfig/css/uikit.gradient.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
208
web_uiconfig/index.html
Normal file
208
web_uiconfig/index.html
Normal file
@ -0,0 +1,208 @@
|
||||
<!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