1
0
mirror of https://github.com/ehang-io/nps synced 2025-10-30 15:26:58 +08:00

dashboard 备注 客户端管理优化 多客户端支持 流量显示支持 热更新支持 404错误页支持

This commit is contained in:
刘河
2019-01-25 12:10:12 +08:00
parent c533436c78
commit c34e5e1a7d
37 changed files with 5415 additions and 732 deletions

View File

@@ -27,14 +27,18 @@ func (s *BaseController) Prepare() {
//加载模板
func (s *BaseController) display(tpl ...string) {
var tplname string
if s.Data["menu"] == nil {
s.Data["menu"] = s.actionName
}
if len(tpl) > 0 {
tplname = strings.Join([]string{tpl[0], "html"}, ".")
} else {
tplname = s.controllerName + "/" + s.actionName + ".html"
}
s.Data["menu"] = s.actionName
ip := s.Ctx.Request.Host
s.Data["ip"] = utils.GetHostByName(ip[0:strings.LastIndex(ip, ":")])
if strings.LastIndex(ip, ":") > 0 {
s.Data["ip"] = utils.GetHostByName(ip[0:])
}
s.Data["p"] = server.Bridge.TunnelPort
s.Data["proxyPort"] = beego.AppConfig.String("hostPort")
s.Layout = "public/layout.html"

100
web/controllers/client.go Normal file
View File

@@ -0,0 +1,100 @@
package controllers
import (
"github.com/cnlh/easyProxy/server"
"github.com/cnlh/easyProxy/utils"
)
type ClientController struct {
BaseController
}
func (s *ClientController) Client() {
if s.Ctx.Request.Method == "GET" {
s.Data["menu"] = "client"
s.SetInfo("客户端管理")
s.display("client/list")
return
}
start, length := s.GetAjaxParams()
list, cnt := server.GetClientList(start, length)
s.AjaxTable(list, cnt, cnt)
}
//添加客户端
func (s *ClientController) Add() {
if s.Ctx.Request.Method == "GET" {
s.Data["menu"] = "client"
s.SetInfo("新增")
s.display()
} else {
t := &utils.Client{
VerifyKey: utils.GetRandomString(16),
Id: server.CsvDb.GetClientId(),
Status: true,
Remark: s.GetString("Remark"),
Cnf: &utils.ServerConfig{
U: s.GetString("u"),
P: s.GetString("p"),
Compress: s.GetString("compress"),
Crypt: s.GetBoolNoErr("crypt"),
Mux: s.GetBoolNoErr("mux"),
},
}
server.CsvDb.NewClient(t)
s.AjaxOk("添加成功")
}
}
//修改客户端
func (s *ClientController) Edit() {
if s.Ctx.Request.Method == "GET" {
s.Data["menu"] = "client"
id := s.GetIntNoErr("id")
if c, err := server.CsvDb.GetClient(id); err != nil {
s.error()
} else {
s.Data["c"] = c
}
s.SetInfo("修改")
s.display()
} else {
id := s.GetIntNoErr("Id")
if c, err := server.CsvDb.GetClient(id); err != nil {
s.error()
} else {
c.Remark = s.GetString("Remark")
c.Cnf.U = s.GetString("u")
c.Cnf.P = s.GetString("p")
c.Cnf.Compress = s.GetString("compress")
c.Cnf.Crypt = s.GetBoolNoErr("crypt")
c.Cnf.Mux = s.GetBoolNoErr("mux")
server.CsvDb.UpdateClient(c)
}
s.AjaxOk("修改成功")
}
}
//更改状态
func (s *ClientController) ChangeStatus() {
id := s.GetIntNoErr("id")
if client, err := server.CsvDb.GetClient(id); err == nil {
client.Status = s.GetBoolNoErr("status")
if client.Status == false {
server.DelClientConnect(client.Id)
}
s.AjaxOk("修改成功")
}
s.AjaxErr("修改失败")
}
//删除客户端
func (s *ClientController) Del() {
id := s.GetIntNoErr("id")
if err := server.CsvDb.DelClient(id); err != nil {
s.AjaxErr("删除失败")
}
server.DelTunnelAndHostByClientId(id)
server.DelClientConnect(id)
s.AjaxOk("删除成功")
}

View File

@@ -10,9 +10,14 @@ type IndexController struct {
}
func (s *IndexController) Index() {
s.SetInfo("使用说明")
s.Data["data"] = server.GetDashboardData()
s.SetInfo("dashboard")
s.display("index/index")
}
func (s *IndexController) Help() {
s.SetInfo("使用说明")
s.display("index/help")
}
func (s *IndexController) Tcp() {
s.SetInfo("tcp隧道管理")
@@ -44,30 +49,44 @@ func (s *IndexController) Host() {
s.display("index/list")
}
func (s *IndexController) All() {
s.Data["menu"] = "client"
clientId := s.GetString("client_id")
s.Data["client_id"] = clientId
s.SetInfo("客户端" + clientId + "的所有隧道")
s.display("index/list")
}
func (s *IndexController) GetServerConfig() {
start, length := s.GetAjaxParams()
taskType := s.GetString("type")
list, cnt := server.GetServerConfig(start, length, taskType)
clientId := s.GetIntNoErr("client_id")
list, cnt := server.GetServerConfig(start, length, taskType, clientId)
s.AjaxTable(list, cnt, cnt)
}
func (s *IndexController) Add() {
if s.Ctx.Request.Method == "GET" {
s.Data["type"] = s.GetString("type")
s.Data["client_id"] = s.GetString("client_id")
s.SetInfo("新增")
s.display()
} else {
t := &server.ServerConfig{
TcpPort: s.GetIntNoErr("port"),
Mode: s.GetString("type"),
Target: s.GetString("target"),
VerifyKey: utils.GetRandomString(16),
U: s.GetString("u"),
P: s.GetString("p"),
Compress: s.GetString("compress"),
Crypt: utils.GetBoolByStr(s.GetString("crypt")),
Mux: utils.GetBoolByStr(s.GetString("mux")),
IsRun: 0,
t := &utils.ServerConfig{
TcpPort: s.GetIntNoErr("port"),
Mode: s.GetString("type"),
Target: s.GetString("target"),
U: s.GetString("u"),
P: s.GetString("p"),
Compress: s.GetString("compress"),
Crypt: s.GetBoolNoErr("crypt"),
Mux: s.GetBoolNoErr("mux"),
IsRun: 0,
Id: server.CsvDb.GetTaskId(),
ClientId: s.GetIntNoErr("client_id"),
UseClientCnf: s.GetBoolNoErr("use_client"),
Start: 1,
Remark: s.GetString("remark"),
}
server.CsvDb.NewTask(t)
if err := server.AddTask(t); err != nil {
@@ -79,9 +98,9 @@ func (s *IndexController) Add() {
}
func (s *IndexController) Edit() {
id := s.GetIntNoErr("id")
if s.Ctx.Request.Method == "GET" {
vKey := s.GetString("vKey")
if t, err := server.CsvDb.GetTask(vKey); err != nil {
if t, err := server.CsvDb.GetTask(id); err != nil {
s.error()
} else {
s.Data["t"] = t
@@ -89,44 +108,46 @@ func (s *IndexController) Edit() {
s.SetInfo("修改")
s.display()
} else {
vKey := s.GetString("vKey")
if t, err := server.CsvDb.GetTask(vKey); err != nil {
if t, err := server.CsvDb.GetTask(id); err != nil {
s.error()
} else {
t.TcpPort = s.GetIntNoErr("port")
t.Mode = s.GetString("type")
t.Target = s.GetString("target")
t.Id = id
t.ClientId = s.GetIntNoErr("client_id")
t.U = s.GetString("u")
t.P = s.GetString("p")
t.Compress = s.GetString("compress")
t.Crypt = utils.GetBoolByStr(s.GetString("crypt"))
t.Mux = utils.GetBoolByStr(s.GetString("mux"))
t.Crypt = s.GetBoolNoErr("crypt")
t.Mux = s.GetBoolNoErr("mux")
t.UseClientCnf = s.GetBoolNoErr("use_client")
t.Remark = s.GetString("remark")
server.CsvDb.UpdateTask(t)
server.StopServer(t.VerifyKey)
server.StartTask(t.VerifyKey)
}
s.AjaxOk("修改成功")
}
}
func (s *IndexController) Stop() {
vKey := s.GetString("vKey")
if err := server.StopServer(vKey); err != nil {
id := s.GetIntNoErr("id")
if err := server.StopServer(id); err != nil {
s.AjaxErr("停止失败")
}
s.AjaxOk("停止成功")
}
func (s *IndexController) Del() {
vKey := s.GetString("vKey")
if err := server.DelTask(vKey); err != nil {
id := s.GetIntNoErr("id")
if err := server.DelTask(id); err != nil {
s.AjaxErr("删除失败")
}
s.AjaxOk("删除成功")
}
func (s *IndexController) Start() {
vKey := s.GetString("vKey")
if err := server.StartTask(vKey); err != nil {
id := s.GetIntNoErr("id")
if err := server.StartTask(id); err != nil {
s.AjaxErr("开启失败")
}
s.AjaxOk("开启成功")
@@ -134,13 +155,14 @@ func (s *IndexController) Start() {
func (s *IndexController) HostList() {
if s.Ctx.Request.Method == "GET" {
s.Data["vkey"] = s.GetString("vkey")
s.Data["client_id"] = s.GetString("client_id")
s.Data["menu"] = "host"
s.SetInfo("域名列表")
s.display("index/hlist")
} else {
start, length := s.GetAjaxParams()
vkey := s.GetString("vkey")
list, cnt := server.CsvDb.GetHostList(start, length, vkey)
clientId := s.GetIntNoErr("client_id")
list, cnt := server.CsvDb.GetHostList(start, length, clientId)
s.AjaxTable(list, cnt, cnt)
}
}
@@ -155,16 +177,18 @@ func (s *IndexController) DelHost() {
func (s *IndexController) AddHost() {
if s.Ctx.Request.Method == "GET" {
s.Data["vkey"] = s.GetString("vkey")
s.Data["client_id"] = s.GetString("client_id")
s.Data["menu"] = "host"
s.SetInfo("新增")
s.display("index/hadd")
} else {
h := &server.HostList{
Vkey: s.GetString("vkey"),
h := &utils.HostList{
ClientId: s.GetIntNoErr("client_id"),
Host: s.GetString("host"),
Target: s.GetString("target"),
HeaderChange: s.GetString("header"),
HostChange: s.GetString("hostchange"),
Remark: s.GetString("remark"),
}
server.CsvDb.NewHost(h)
s.AjaxOk("添加成功")
@@ -172,26 +196,26 @@ func (s *IndexController) AddHost() {
}
func (s *IndexController) EditHost() {
host := s.GetString("host")
if s.Ctx.Request.Method == "GET" {
host := s.GetString("host")
if h, t, err := server.GetKeyByHost(host); err != nil {
s.Data["menu"] = "host"
if h, _, err := server.GetKeyByHost(host); err != nil {
s.error()
} else {
s.Data["t"] = t
s.Data["h"] = h
}
s.SetInfo("修改")
s.display("index/hedit")
} else {
host := s.GetString("host")
if h, _, err := server.GetKeyByHost(host); err != nil {
s.error()
} else {
h.Vkey = s.GetString("vkey")
h.ClientId = s.GetIntNoErr("client_id")
h.Host = s.GetString("nhost")
h.Target = s.GetString("target")
h.HeaderChange = s.GetString("header")
h.HostChange = s.GetString("hostchange")
h.Remark = s.GetString("remark")
server.CsvDb.UpdateHost(h)
}
s.AjaxOk("修改成功")

View File

@@ -9,4 +9,5 @@ func init() {
beego.Router("/", &controllers.IndexController{}, "*:Index")
beego.AutoRouter(&controllers.IndexController{})
beego.AutoRouter(&controllers.LoginController{})
beego.AutoRouter(&controllers.ClientController{})
}

3477
web/static/js/chart.js Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -4,10 +4,10 @@
*
* To rebuild or modify this file with the latest versions of the included
* software please visit:
* https://datatables.net/download/#bs4/jq-3.3.1/dt-1.10.18
* https://datatables.net/download/#bs4/jq-3.3.1/dt-1.10.18/r-2.2.2
*
* Included libraries:
* jQuery 3 3.3.1, DataTables 1.10.18
* jQuery 3 3.3.1, DataTables 1.10.18, Responsive 2.2.2
*/
/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */
@@ -164,7 +164,7 @@ Z(n.defaults.column);n.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender
aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,searchDelay:null,sPaginationType:"two_button",
iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,bAjaxDataGet:!0,jqXHR:null,json:k,oAjaxData:k,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==y(this)?1*this._iRecordsTotal:
this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==y(this)?1*this._iRecordsDisplay:this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,d=this.aiDisplay.length,e=this.oFeatures,f=e.bPaginate;return e.bServerSide?!1===f||-1===a?b+d:Math.min(b+a,this._iRecordsDisplay):!f||c>d||-1===a?d:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{},rowIdFn:null,rowId:null};n.ext=x={buttons:{},
classes:{},build:"bs4/jq-3.3.1/dt-1.10.18",errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:n.fnVersionCheck,iApiIndex:0,oJUIClasses:{},sVersion:n.version};h.extend(x,{afnFiltering:x.search,aTypes:x.type.detect,ofnSearch:x.type.search,oSort:x.type.order,afnSortData:x.order,aoFeatures:x.feature,oApi:x.internal,oStdClasses:x.classes,oPagination:x.pager});
classes:{},build:"bs4/jq-3.3.1/dt-1.10.18/r-2.2.2",errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:n.fnVersionCheck,iApiIndex:0,oJUIClasses:{},sVersion:n.version};h.extend(x,{afnFiltering:x.search,aTypes:x.type.detect,ofnSearch:x.type.search,oSort:x.type.order,afnSortData:x.order,aoFeatures:x.feature,oApi:x.internal,oStdClasses:x.classes,oPagination:x.pager});
h.extend(n.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",
sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",
sJUIHeader:"",sJUIFooter:""});var Kb=n.ext.pager;h.extend(Kb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},numbers:function(a,b){return[ia(a,b)]},simple_numbers:function(a,b){return["previous",ia(a,b),"next"]},full_numbers:function(a,b){return["first","previous",ia(a,b),"next","last"]},first_last_numbers:function(a,b){return["first",ia(a,b),"last"]},_numbers:ia,numbers_length:7});h.extend(!0,n.ext.renderer,{pageButton:{_:function(a,b,c,d,e,
@@ -192,3 +192,43 @@ renderer:"bootstrap"});b.extend(f.ext.classes,{sWrapper:"dataTables_wrapper dt-b
{"class":t.sPageButton+" "+g,id:0===r&&"string"===typeof c?a.sTableId+"_"+c:null}).append(b("<a>",{href:"#","aria-controls":a.sTableId,"aria-label":u[c],"data-dt-idx":p,tabindex:a.iTabIndex,"class":"page-link"}).html(e)).appendTo(d),a.oApi._fnBindAction(i,{action:c},m),p++)}},i;try{i=b(h).find(d.activeElement).data("dt-idx")}catch(v){}q(b(h).empty().html('<ul class="pagination"/>').children("ul"),s);i!==m&&b(h).find("[data-dt-idx="+i+"]").focus()};return f});
/*!
Responsive 2.2.2
2014-2018 SpryMedia Ltd - datatables.net/license
*/
(function(d){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(l){return d(l,window,document)}):"object"===typeof exports?module.exports=function(l,j){l||(l=window);if(!j||!j.fn.dataTable)j=require("datatables.net")(l,j).$;return d(j,l,l.document)}:d(jQuery,window,document)})(function(d,l,j,q){function t(a,b,c){var e=b+"-"+c;if(n[e])return n[e];for(var d=[],a=a.cell(b,c).node().childNodes,b=0,c=a.length;b<c;b++)d.push(a[b]);return n[e]=d}function r(a,b,d){var e=b+
"-"+d;if(n[e]){for(var a=a.cell(b,d).node(),d=n[e][0].parentNode.childNodes,b=[],f=0,g=d.length;f<g;f++)b.push(d[f]);d=0;for(f=b.length;d<f;d++)a.appendChild(b[d]);n[e]=q}}var o=d.fn.dataTable,i=function(a,b){if(!o.versionCheck||!o.versionCheck("1.10.10"))throw"DataTables Responsive requires DataTables 1.10.10 or newer";this.s={dt:new o.Api(a),columns:[],current:[]};this.s.dt.settings()[0].responsive||(b&&"string"===typeof b.details?b.details={type:b.details}:b&&!1===b.details?b.details={type:!1}:
b&&!0===b.details&&(b.details={type:"inline"}),this.c=d.extend(!0,{},i.defaults,o.defaults.responsive,b),a.responsive=this,this._constructor())};d.extend(i.prototype,{_constructor:function(){var a=this,b=this.s.dt,c=b.settings()[0],e=d(l).width();b.settings()[0]._responsive=this;d(l).on("resize.dtr orientationchange.dtr",o.util.throttle(function(){var b=d(l).width();b!==e&&(a._resize(),e=b)}));c.oApi._fnCallbackReg(c,"aoRowCreatedCallback",function(e){-1!==d.inArray(!1,a.s.current)&&d(">td, >th",
e).each(function(e){e=b.column.index("toData",e);!1===a.s.current[e]&&d(this).css("display","none")})});b.on("destroy.dtr",function(){b.off(".dtr");d(b.table().body()).off(".dtr");d(l).off("resize.dtr orientationchange.dtr");d.each(a.s.current,function(b,e){!1===e&&a._setColumnVis(b,!0)})});this.c.breakpoints.sort(function(a,b){return a.width<b.width?1:a.width>b.width?-1:0});this._classLogic();this._resizeAuto();c=this.c.details;!1!==c.type&&(a._detailsInit(),b.on("column-visibility.dtr",function(){a._timer&&
clearTimeout(a._timer);a._timer=setTimeout(function(){a._timer=null;a._classLogic();a._resizeAuto();a._resize();a._redrawChildren()},100)}),b.on("draw.dtr",function(){a._redrawChildren()}),d(b.table().node()).addClass("dtr-"+c.type));b.on("column-reorder.dtr",function(){a._classLogic();a._resizeAuto();a._resize()});b.on("column-sizing.dtr",function(){a._resizeAuto();a._resize()});b.on("preXhr.dtr",function(){var e=[];b.rows().every(function(){this.child.isShown()&&e.push(this.id(true))});b.one("draw.dtr",
function(){a._resizeAuto();a._resize();b.rows(e).every(function(){a._detailsDisplay(this,false)})})});b.on("init.dtr",function(){a._resizeAuto();a._resize();d.inArray(false,a.s.current)&&b.columns.adjust()});this._resize()},_columnsVisiblity:function(a){var b=this.s.dt,c=this.s.columns,e,f,g=c.map(function(a,b){return{columnIdx:b,priority:a.priority}}).sort(function(a,b){return a.priority!==b.priority?a.priority-b.priority:a.columnIdx-b.columnIdx}),h=d.map(c,function(e,c){return!1===b.column(c).visible()?
"not-visible":e.auto&&null===e.minWidth?!1:!0===e.auto?"-":-1!==d.inArray(a,e.includeIn)}),m=0;e=0;for(f=h.length;e<f;e++)!0===h[e]&&(m+=c[e].minWidth);e=b.settings()[0].oScroll;e=e.sY||e.sX?e.iBarWidth:0;m=b.table().container().offsetWidth-e-m;e=0;for(f=h.length;e<f;e++)c[e].control&&(m-=c[e].minWidth);var s=!1;e=0;for(f=g.length;e<f;e++){var k=g[e].columnIdx;"-"===h[k]&&(!c[k].control&&c[k].minWidth)&&(s||0>m-c[k].minWidth?(s=!0,h[k]=!1):h[k]=!0,m-=c[k].minWidth)}g=!1;e=0;for(f=c.length;e<f;e++)if(!c[e].control&&
!c[e].never&&!1===h[e]){g=!0;break}e=0;for(f=c.length;e<f;e++)c[e].control&&(h[e]=g),"not-visible"===h[e]&&(h[e]=!1);-1===d.inArray(!0,h)&&(h[0]=!0);return h},_classLogic:function(){var a=this,b=this.c.breakpoints,c=this.s.dt,e=c.columns().eq(0).map(function(a){var b=this.column(a),e=b.header().className,a=c.settings()[0].aoColumns[a].responsivePriority;a===q&&(b=d(b.header()).data("priority"),a=b!==q?1*b:1E4);return{className:e,includeIn:[],auto:!1,control:!1,never:e.match(/\bnever\b/)?!0:!1,priority:a}}),
f=function(a,b){var c=e[a].includeIn;-1===d.inArray(b,c)&&c.push(b)},g=function(d,c,g,k){if(g)if("max-"===g){k=a._find(c).width;c=0;for(g=b.length;c<g;c++)b[c].width<=k&&f(d,b[c].name)}else if("min-"===g){k=a._find(c).width;c=0;for(g=b.length;c<g;c++)b[c].width>=k&&f(d,b[c].name)}else{if("not-"===g){c=0;for(g=b.length;c<g;c++)-1===b[c].name.indexOf(k)&&f(d,b[c].name)}}else e[d].includeIn.push(c)};e.each(function(a,e){for(var c=a.className.split(" "),f=!1,i=0,l=c.length;i<l;i++){var j=d.trim(c[i]);
if("all"===j){f=!0;a.includeIn=d.map(b,function(a){return a.name});return}if("none"===j||a.never){f=!0;return}if("control"===j){f=!0;a.control=!0;return}d.each(b,function(a,b){var d=b.name.split("-"),c=j.match(RegExp("(min\\-|max\\-|not\\-)?("+d[0]+")(\\-[_a-zA-Z0-9])?"));c&&(f=!0,c[2]===d[0]&&c[3]==="-"+d[1]?g(e,b.name,c[1],c[2]+c[3]):c[2]===d[0]&&!c[3]&&g(e,b.name,c[1],c[2]))})}f||(a.auto=!0)});this.s.columns=e},_detailsDisplay:function(a,b){var c=this,e=this.s.dt,f=this.c.details;if(f&&!1!==f.type){var g=
f.display(a,b,function(){return f.renderer(e,a[0],c._detailsObj(a[0]))});(!0===g||!1===g)&&d(e.table().node()).triggerHandler("responsive-display.dt",[e,a,g,b])}},_detailsInit:function(){var a=this,b=this.s.dt,c=this.c.details;"inline"===c.type&&(c.target="td:first-child, th:first-child");b.on("draw.dtr",function(){a._tabIndexes()});a._tabIndexes();d(b.table().body()).on("keyup.dtr","td, th",function(a){a.keyCode===13&&d(this).data("dtr-keyboard")&&d(this).click()});var e=c.target;d(b.table().body()).on("click.dtr mousedown.dtr mouseup.dtr",
"string"===typeof e?e:"td, th",function(c){if(d(b.table().node()).hasClass("collapsed")&&d.inArray(d(this).closest("tr").get(0),b.rows().nodes().toArray())!==-1){if(typeof e==="number"){var g=e<0?b.columns().eq(0).length+e:e;if(b.cell(this).index().column!==g)return}g=b.row(d(this).closest("tr"));c.type==="click"?a._detailsDisplay(g,false):c.type==="mousedown"?d(this).css("outline","none"):c.type==="mouseup"&&d(this).blur().css("outline","")}})},_detailsObj:function(a){var b=this,c=this.s.dt;return d.map(this.s.columns,
function(e,d){if(!e.never&&!e.control)return{title:c.settings()[0].aoColumns[d].sTitle,data:c.cell(a,d).render(b.c.orthogonal),hidden:c.column(d).visible()&&!b.s.current[d],columnIndex:d,rowIndex:a}})},_find:function(a){for(var b=this.c.breakpoints,c=0,e=b.length;c<e;c++)if(b[c].name===a)return b[c]},_redrawChildren:function(){var a=this,b=this.s.dt;b.rows({page:"current"}).iterator("row",function(c,e){b.row(e);a._detailsDisplay(b.row(e),!0)})},_resize:function(){var a=this,b=this.s.dt,c=d(l).width(),
e=this.c.breakpoints,f=e[0].name,g=this.s.columns,h,m=this.s.current.slice();for(h=e.length-1;0<=h;h--)if(c<=e[h].width){f=e[h].name;break}var i=this._columnsVisiblity(f);this.s.current=i;e=!1;h=0;for(c=g.length;h<c;h++)if(!1===i[h]&&!g[h].never&&!g[h].control&&!1===!b.column(h).visible()){e=!0;break}d(b.table().node()).toggleClass("collapsed",e);var k=!1,j=0;b.columns().eq(0).each(function(b,c){!0===i[c]&&j++;i[c]!==m[c]&&(k=!0,a._setColumnVis(b,i[c]))});k&&(this._redrawChildren(),d(b.table().node()).trigger("responsive-resize.dt",
[b,this.s.current]),0===b.page.info().recordsDisplay&&d("td",b.table().body()).eq(0).attr("colspan",j))},_resizeAuto:function(){var a=this.s.dt,b=this.s.columns;if(this.c.auto&&-1!==d.inArray(!0,d.map(b,function(a){return a.auto}))){d.isEmptyObject(n)||d.each(n,function(b){b=b.split("-");r(a,1*b[0],1*b[1])});a.table().node();var c=a.table().node().cloneNode(!1),e=d(a.table().header().cloneNode(!1)).appendTo(c),f=d(a.table().body()).clone(!1,!1).empty().appendTo(c),g=a.columns().header().filter(function(b){return a.column(b).visible()}).to$().clone(!1).css("display",
"table-cell").css("min-width",0);d(f).append(d(a.rows({page:"current"}).nodes()).clone(!1)).find("th, td").css("display","");if(f=a.table().footer()){var f=d(f.cloneNode(!1)).appendTo(c),h=a.columns().footer().filter(function(b){return a.column(b).visible()}).to$().clone(!1).css("display","table-cell");d("<tr/>").append(h).appendTo(f)}d("<tr/>").append(g).appendTo(e);"inline"===this.c.details.type&&d(c).addClass("dtr-inline collapsed");d(c).find("[name]").removeAttr("name");d(c).css("position","relative");
c=d("<div/>").css({width:1,height:1,overflow:"hidden",clear:"both"}).append(c);c.insertBefore(a.table().node());g.each(function(c){c=a.column.index("fromVisible",c);b[c].minWidth=this.offsetWidth||0});c.remove()}},_setColumnVis:function(a,b){var c=this.s.dt,e=b?"":"none";d(c.column(a).header()).css("display",e);d(c.column(a).footer()).css("display",e);c.column(a).nodes().to$().css("display",e);d.isEmptyObject(n)||c.cells(null,a).indexes().each(function(a){r(c,a.row,a.column)})},_tabIndexes:function(){var a=
this.s.dt,b=a.cells({page:"current"}).nodes().to$(),c=a.settings()[0],e=this.c.details.target;b.filter("[data-dtr-keyboard]").removeData("[data-dtr-keyboard]");"number"===typeof e?a.cells(null,e,{page:"current"}).nodes().to$().attr("tabIndex",c.iTabIndex).data("dtr-keyboard",1):("td:first-child, th:first-child"===e&&(e=">td:first-child, >th:first-child"),d(e,a.rows({page:"current"}).nodes()).attr("tabIndex",c.iTabIndex).data("dtr-keyboard",1))}});i.breakpoints=[{name:"desktop",width:Infinity},{name:"tablet-l",
width:1024},{name:"tablet-p",width:768},{name:"mobile-l",width:480},{name:"mobile-p",width:320}];i.display={childRow:function(a,b,c){if(b){if(d(a.node()).hasClass("parent"))return a.child(c(),"child").show(),!0}else{if(a.child.isShown())return a.child(!1),d(a.node()).removeClass("parent"),!1;a.child(c(),"child").show();d(a.node()).addClass("parent");return!0}},childRowImmediate:function(a,b,c){if(!b&&a.child.isShown()||!a.responsive.hasHidden())return a.child(!1),d(a.node()).removeClass("parent"),
!1;a.child(c(),"child").show();d(a.node()).addClass("parent");return!0},modal:function(a){return function(b,c,e){if(c)d("div.dtr-modal-content").empty().append(e());else{var f=function(){g.remove();d(j).off("keypress.dtr")},g=d('<div class="dtr-modal"/>').append(d('<div class="dtr-modal-display"/>').append(d('<div class="dtr-modal-content"/>').append(e())).append(d('<div class="dtr-modal-close">&times;</div>').click(function(){f()}))).append(d('<div class="dtr-modal-background"/>').click(function(){f()})).appendTo("body");
d(j).on("keyup.dtr",function(a){27===a.keyCode&&(a.stopPropagation(),f())})}a&&a.header&&d("div.dtr-modal-content").prepend("<h2>"+a.header(b)+"</h2>")}}};var n={};i.renderer={listHiddenNodes:function(){return function(a,b,c){var e=d('<ul data-dtr-index="'+b+'" class="dtr-details"/>'),f=!1;d.each(c,function(b,c){c.hidden&&(d('<li data-dtr-index="'+c.columnIndex+'" data-dt-row="'+c.rowIndex+'" data-dt-column="'+c.columnIndex+'"><span class="dtr-title">'+c.title+"</span> </li>").append(d('<span class="dtr-data"/>').append(t(a,
c.rowIndex,c.columnIndex))).appendTo(e),f=!0)});return f?e:!1}},listHidden:function(){return function(a,b,c){return(a=d.map(c,function(a){return a.hidden?'<li data-dtr-index="'+a.columnIndex+'" data-dt-row="'+a.rowIndex+'" data-dt-column="'+a.columnIndex+'"><span class="dtr-title">'+a.title+'</span> <span class="dtr-data">'+a.data+"</span></li>":""}).join(""))?d('<ul data-dtr-index="'+b+'" class="dtr-details"/>').append(a):!1}},tableAll:function(a){a=d.extend({tableClass:""},a);return function(b,
c,e){b=d.map(e,function(a){return'<tr data-dt-row="'+a.rowIndex+'" data-dt-column="'+a.columnIndex+'"><td>'+a.title+":</td> <td>"+a.data+"</td></tr>"}).join("");return d('<table class="'+a.tableClass+' dtr-details" width="100%"/>').append(b)}}};i.defaults={breakpoints:i.breakpoints,auto:!0,details:{display:i.display.childRow,renderer:i.renderer.listHidden(),target:0,type:"inline"},orthogonal:"display"};var p=d.fn.dataTable.Api;p.register("responsive()",function(){return this});p.register("responsive.index()",
function(a){a=d(a);return{column:a.data("dtr-index"),row:a.parent().data("dtr-index")}});p.register("responsive.rebuild()",function(){return this.iterator("table",function(a){a._responsive&&a._responsive._classLogic()})});p.register("responsive.recalc()",function(){return this.iterator("table",function(a){a._responsive&&(a._responsive._resizeAuto(),a._responsive._resize())})});p.register("responsive.hasHidden()",function(){var a=this.context[0];return a._responsive?-1!==d.inArray(!1,a._responsive.s.current):
!1});p.registerPlural("columns().responsiveHidden()","column().responsiveHidden()",function(){return this.iterator("column",function(a,b){return a._responsive?a._responsive.s.current[b]:!1},1)});i.version="2.2.2";d.fn.dataTable.Responsive=i;d.fn.DataTable.Responsive=i;d(j).on("preInit.dt.dtr",function(a,b){if("dt"===a.namespace&&(d(b.nTable).hasClass("responsive")||d(b.nTable).hasClass("dt-responsive")||b.oInit.responsive||o.defaults.responsive)){var c=b.oInit.responsive;!1!==c&&new i(b,d.isPlainObject(c)?
c:{})}});return i});
/*!
Bootstrap 4 integration for DataTables' Responsive
©2016 SpryMedia Ltd - datatables.net/license
*/
(function(c){"function"===typeof define&&define.amd?define(["jquery","datatables.net-bs4","datatables.net-responsive"],function(a){return c(a,window,document)}):"object"===typeof exports?module.exports=function(a,b){a||(a=window);if(!b||!b.fn.dataTable)b=require("datatables.net-bs4")(a,b).$;b.fn.dataTable.Responsive||require("datatables.net-responsive")(a,b);return c(b,a,a.document)}:c(jQuery,window,document)})(function(c){var a=c.fn.dataTable,b=a.Responsive.display,g=b.modal,e=c('<div class="modal fade dtr-bs-modal" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><div class="modal-body"/></div></div></div>');
b.modal=function(a){return function(b,d,f){if(c.fn.modal){if(!d){if(a&&a.header){var d=e.find("div.modal-header"),h=d.find("button").detach();d.empty().append('<h4 class="modal-title">'+a.header(b)+"</h4>").append(h)}e.find("div.modal-body").empty().append(f());e.appendTo("body").modal()}}else g(b,d,f)}};return a.Responsive});

View File

@@ -1,27 +1,48 @@
(function () {
"use strict";
"use strict";
var treeviewMenu = $('.app-menu');
var treeviewMenu = $('.app-menu');
// Toggle Sidebar
$('[data-toggle="sidebar"]').click(function(event) {
event.preventDefault();
$('.app').toggleClass('sidenav-toggled');
});
// Toggle Sidebar
$('[data-toggle="sidebar"]').click(function (event) {
event.preventDefault();
$('.app').toggleClass('sidenav-toggled');
});
// Activate sidebar treeview toggle
$("[data-toggle='treeview']").click(function(event) {
event.preventDefault();
if(!$(this).parent().hasClass('is-expanded')) {
treeviewMenu.find("[data-toggle='treeview']").parent().removeClass('is-expanded');
}
$(this).parent().toggleClass('is-expanded');
});
// Activate sidebar treeview toggle
$("[data-toggle='treeview']").click(function (event) {
event.preventDefault();
if (!$(this).parent().hasClass('is-expanded')) {
treeviewMenu.find("[data-toggle='treeview']").parent().removeClass('is-expanded');
}
$(this).parent().toggleClass('is-expanded');
});
// Set initial active toggle
$("[data-toggle='treeview.'].is-expanded").parent().toggleClass('is-expanded');
// Set initial active toggle
$("[data-toggle='treeview.'].is-expanded").parent().toggleClass('is-expanded');
//Activate bootstrip tooltips
// $("[data-toggle='tooltip']").tooltip();
//Activate bootstrip tooltips
// $("[data-toggle='tooltip']").tooltip();
})();
function change(limit) {
var size = "";
if (limit < 0.1 * 1024) {
size = limit.toFixed(2) + "B"
} else if (limit < 0.1 * 1024 * 1024) {
size = (limit / 1024).toFixed(2) + "KB"
} else if (limit < 0.1 * 1024 * 1024 * 1024) {
size = (limit / (1024 * 1024)).toFixed(2) + "MB"
} else {
size = (limit / (1024 * 1024 * 1024)).toFixed(2) + "GB"
}
var sizeStr = size + "";
var index = sizeStr.indexOf(".");
var dou = sizeStr.substr(index + 1, 2)
if (dou == "00") {
return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2)
}
return size;
}

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>easyProxy error</title>
</head>
<body>
404 not found,power by easyProxy
</body>
</html>

67
web/views/client/add.html Executable file
View File

@@ -0,0 +1,67 @@
<div class="row tile">
<div class="col-md-6 col-md-auto">
<div>
<h3 class="tile-title">添加</h3>
<div class="tile-body">
<form>
<div class="form-group" id="target">
<label class="control-label">备注</label>
<input class="form-control" type="text" name="Remark" placeholder="客户端备注">
</div>
<div class="form-group" id="u">
<label class="control-label">验证用户名(仅socks5,web穿透支持)</label>
<input class="form-control" type="text" name="u" placeholder="不填则无需验证">
</div>
<div class="form-group" id="p">
<label class="control-label">验证密码(仅socks5,web穿透支持)</label>
<input class="form-control" type="text" name="p" placeholder="不填则无需验证">
</div>
<div class="form-group" id="compress">
<label class="control-label">数据压缩方式</label>
<select class="form-control" name="compress">
<option value="">不压缩</option>
<option value="snappy">snappy</option>
</select>
</div>
<div class="form-group" id="compress">
<label class="control-label">是否加密传输</label>
<select class="form-control" name="crypt">
<option value="0">不加密</option>
<option value="1">加密</option>
</select>
</div>
<div class="form-group" id="compress">
<label class="control-label">是否TCP复用</label>
<select class="form-control" name="crypt">
<option value="0">不启用</option>
<option value="1">启用</option>
</select>
</div>
</form>
</div>
<div class="tile-footer">
&nbsp;&nbsp;<button class="btn btn-success" href="#" id="add"><i
class="fa fa-fw fa-lg fa-eye"></i>添加
</button>
</div>
</div>
</div>
</div>
</main>
<script>
$(function () {
$("#add").on("click", function () {
$.ajax({
type: "POST",
url: "/client/add",
data: $("form").serializeArray(),
success: function (res) {
alert(res.msg)
if (res.status) {
history.back(-1)
}
}
})
})
})
</script>

71
web/views/client/edit.html Executable file
View File

@@ -0,0 +1,71 @@
<div class="row tile">
<div class="col-md-6 col-md-auto">
<div>
<h3 class="tile-title">添加</h3>
<div class="tile-body">
<form>
<input type="hidden" name="Id" value="{{.c.Id}}">
<div class="form-group" id="target">
<label class="control-label">备注</label>
<input class="form-control" value="{{.c.Remark}}" type="text" name="Remark" placeholder="客户端备注">
</div>
<div class="form-group" id="u">
<label class="control-label">验证用户名(仅socks5,web穿透支持)</label>
<input class="form-control" value="{{.c.Cnf.U}}" type="text" name="u"
placeholder="不填则无需验证">
</div>
<div class="form-group" id="p">
<label class="control-label">验证密码(仅socks5,web穿透支持)</label>
<input class="form-control" value="{{.c.Cnf.P}}" type="text" name="p"
placeholder="不填则无需验证">
</div>
<div class="form-group" id="compress">
<label class="control-label">数据压缩方式(所有模式均支持)</label>
<select class="form-control" name="compress">
<option {{if eq "" .c.Cnf.Compress}}selected{{end}} value="">不压缩</option>
<option {{if eq "snappy" .c.Cnf.Compress}}selected{{end}} value="snappy">snappy压缩</option>
</select>
</div>
<div class="form-group" id="compress">
<label class="control-label">是否加密传输(所有模式均支持)</label>
<select class="form-control" name="crypt">
<option {{if eq false .c.Cnf.Crypt}}selected{{end}} value="0">不加密</option>
<option {{if eq true .c.Cnf.Crypt}}selected{{end}} value="1">加密</option>
</select>
</div>
<div class="form-group" id="compress">
<label class="control-label">是否启用TCP复用(所有模式均支持)</label>
<select class="form-control" name="mux">
<option {{if eq false .c.Cnf.Mux}}selected{{end}} value="0">不启用</option>
<option {{if eq true .c.Cnf.Mux}}selected{{end}} value="1">启用</option>
</select>
</div>
</form>
</div>
<div class="tile-footer">
&nbsp;&nbsp;<button class="btn btn-success" href="#" id="add"><i
class="fa fa-fw fa-lg fa-eye"></i>保存
</button>
</div>
</div>
</div>
</div>
</main>
<script>
$(function () {
$("#add").on("click", function () {
$.ajax({
type: "POST",
url: "/client/edit",
data: $("form").serializeArray(),
success: function (res) {
alert(res.msg)
if (res.status) {
history.back(-1)
}
}
})
})
})
</script>

234
web/views/client/list.html Executable file
View File

@@ -0,0 +1,234 @@
<div class="row">
<div class="col-md-12">
<div class="tile">
<div class="tile-body">
<table class="table table-hover table-bordered" id="sampleTable">
<thead>
<tr>
<th>Id</th>
<th>验证密钥</th>
<th>客户端地址</th>
<th>备注</th>
<th>压缩方式</th>
<th>加密</th>
<th>多路复用</th>
<th>用户名</th>
<th>密码</th>
<th>状态</th>
<th>客户端连接状态</th>
<th>出口流量</th>
<th>入口流量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
<script type="text/javascript">
function del(id) {
if (confirm("确定要删除数据吗?")) {
$.ajax({
type: "POST",
url: "/client/del",
data: {"id": id},
success: function (res) {
alert(res.msg)
if (res.status) {
document.location.reload();
}
}
})
}
}
function start(id) {
if (confirm("确定要开始任务吗?")) {
$.ajax({
type: "POST",
url: "/client/changestatus",
data: {"id": id, "status": 1},
success: function (res) {
alert(res.msg)
if (res.status) {
document.location.reload();
}
}
})
}
}
function stop(id) {
if (confirm("确定要暂停吗?")) {
$.ajax({
type: "POST",
url: "/client/changestatus",
data: {
"id": id, "status": 0
},
success:
function (res) {
alert(res.msg)
if (res.status) {
document.location.reload();
}
}
})
}
}
function edit(id) {
window.location.href = "/client/edit?id=" + id
}
function add() {
window.location.href = "/client/add"
}
function show_tunnel_list(id) {
window.location.href = "/index/all?client_id=" + id
}
function show_host_list(id) {
window.location.href = "/index/hostlist?client_id=" + id
}
$(document)
.ready(function () {
var table = $('#sampleTable').DataTable({
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.childRowImmediate
}
},
dom: 'Bfrtip',
processing: true,
serverSide: true,
autoWidth: false,
ordering: false,
ajax: {
url: '/client/client',
type: 'POST'
},
dom: '<"top"fl><"toolbar">rt<"bottom"ip><"clear">',
columns: [ //这个是显示到界面上的个数据 格式为 {data:'显示的字段名'}
{data: 'Id'},
{data: 'VerifyKey'},
{data: "Addr"},
{data: "Remark"},
{data: "Addr"},
{data: "Addr"},
{data: "Addr"},
{data: "Addr"},
{data: "Remark"},
{data: "Status"},
{data: "IsConnect"},
{data: "Addr"},
{data: "Addr"},
{data: ""},
],
bFilter: false,
columnDefs: [{
targets: -1,
render: function (data, type, row, meta) {
if (row.Status == true) {
btn = "<button onclick=\"stop('" + row.Id + "')\" class=\"btn btn-secondary btn-sm\" type=\"button\">关闭</button>"
} else {
btn = "<button onclick=\"start('" + row.Id + "')\" class=\"btn btn-success btn-sm\" type=\"button\">打开</button>"
}
btn_del = '<button onclick="del(\'' + row.Id + '\')" class="btn btn-danger btn-sm">删除</button> '
btn_edit = '<button onclick="edit(\'' + row.Id + '\')" class="btn btn-primary btn-sm">查看编辑</button> '
btn_list = '<button onclick="show_tunnel_list(\'' + row.Id + '\')" class="btn btn-info btn-sm">隧道</button> '
btn_host = '<button onclick="show_host_list(\'' + row.Id + '\')" class="btn btn-info btn-sm">域名</button> '
return '<div class="btn-group" role="group" aria-label="..."> ' + btn + btn_del + btn_edit + btn_host + btn_list + '</div>'
}
},
{
targets: -5,
render: function (data, type, row, meta) {
if (data == false) {
return "<span class=\"badge badge-pill badge-secondary\">暂停</span>"
} else {
return "<span class=\"badge badge-pill badge-success\">正常</span>"
}
}
},
{
targets: -4,
render: function (data, type, row, meta) {
if (data == false) {
return "<span class=\"badge badge-pill badge-secondary\">未连接</span>"
} else {
return "<span class=\"badge badge-pill badge-success\">已连接</span>"
}
}
},
{
targets: -2,
render: function (data, type, row, meta) {
return change(row.Flow.InletFlow)
}
},
{
targets: -3,
render: function (data, type, row, meta) {
return change(row.Flow.ExportFlow)
}
},
{
targets: -6,
render: function (data, type, row, meta) {
return row.Cnf.P
}
},
{
targets: -7,
render: function (data, type, row, meta) {
return row.Cnf.U
}
},
{
targets: -8,
render: function (data, type, row, meta) {
if (row.Cnf.Mux == "0") {
return "不启用"
} else {
return "启用"
}
}
}
,
{
targets: -9,
render: function (data, type, row, meta) {
if (row.Cnf.Crypt == "0") {
return "不加密"
} else {
return "加密"
}
}
}
,
{
targets: -10,
render: function (data, type, row, meta) {
return row.Cnf.Compress
}
}
],
buttons: []
});
$("#sampleTable_length").html('<button class="btn btn-primary" onclick="add()" type="button">新增</button>')
})
;
</script>

View File

@@ -19,9 +19,12 @@
<option {{if eq "udpServer" .type}}selected{{end}} value="udpServer">udp隧道</option>
<option {{if eq "socks5Server" .type}}selected{{end}} value="socks5Server">socks5代理</option>
<option {{if eq "httpProxyServer" .type}}selected{{end}} value="httpProxyServer">http代理
<option {{if eq "hostServer" .type}}selected{{end}} value="hostServer">host客户端</option>
</select>
</div>
<div class="form-group">
<label class="control-label">备注</label>
<input class="form-control" type="text" name="remark" placeholder="备注">
</div>
<div class="form-group" id="port">
<label class="control-label">监听的端口</label>
<input class="form-control" type="text" name="port" placeholder="公网服务器对外访问端口例如8024">
@@ -30,6 +33,18 @@
<label class="control-label">内网目标(ip:端口)</label>
<input class="form-control" type="text" name="target" placeholder="内网代理地址例如10.1.50.203:22">
</div>
<div class="form-group" id="client_id">
<label class="control-label">客户端ID</label>
<input value="{{.client_id}}" class="form-control" type="text" name="client_id"
placeholder="客户端id">
</div>
<div class="form-group" id="use_client">
<label class="control-label">是否使用客户端配置</label>
<select class="form-control" name="use_client">
<option value="1"></option>
<option value="0"></option>
</select>
</div>
<div class="form-group" id="compress">
<label class="control-label">数据压缩方式</label>
<select class="form-control" name="compress">
@@ -37,16 +52,16 @@
<option value="snappy">snappy</option>
</select>
</div>
<div class="form-group" id="compress">
<div class="form-group" id="crypt">
<label class="control-label">是否加密传输</label>
<select class="form-control" name="crypt">
<option value="0">不加密</option>
<option value="1">加密</option>
</select>
</div>
<div class="form-group" id="compress">
<div class="form-group" id="mux">
<label class="control-label">是否TCP复用</label>
<select class="form-control" name="crypt">
<select class="form-control" name="mux">
<option value="0">不启用</option>
<option value="1">启用</option>
</select>
@@ -77,7 +92,7 @@
arr["udpServer"] = ["type", "port", "target", "compress", "udp隧道模式提供一条udp隧道适用于dns、内网dns访问等添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后访问公网服务器的设定端口则相当于访问内网目标地址的udp目标端口"]
arr["socks5Server"] = ["type", "port", "compress", "u", "p", "socks5代理模式内网socks5代理配合proxifer可如同使用vpn一样访问内网设备或资源添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后在外网环境下本机配置socks5代理即访问内网设备或者资源 "]
arr["httpProxyServer"] = ["type", "port", "compress", "u", "p", " http代理模式内网http代理可访问内网网站添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后在外网环境下本机配置http代理即访问内网站点"]
arr["hostServer"] = ["type", "compress", "u", "p", "域名分发模式使用域名代理内网服务适用于小程序开发、公众号开发、站点演示等添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后使用nginx将请求反向代理到本程序再进行域名配置即可解析"]
arrClientHide = ["compress", "u", "p", "crypt", "mux"]
function resetForm() {
for (var i = 0; i < arr["all"].length; i++) {
@@ -90,10 +105,28 @@
$("#info").html(arr[o][arr[o].length - 1])
}
function resetClientCnf() {
for (var i = 0; i < arrClientHide.length; i++) {
$("#" + arrClientHide[i]).css("display", "block")
}
op = $("#use_client option:selected").val()
if (op == 1) {
for (var i = 0; i < arrClientHide.length; i++) {
$("#" + arrClientHide[i]).css("display", "none")
}
}
}
$(function () {
resetForm()
resetClientCnf()
$("#type").on("change", function () {
resetForm()
resetClientCnf()
})
$("#use_client").on("change", function () {
resetForm()
resetClientCnf()
})
$("#add").on("click", function () {
$.ajax({

View File

@@ -4,17 +4,21 @@
<h3 class="tile-title">添加</h3>
<div class="tile-body">
<form>
<input type="hidden" name="vKey" value="{{.t.VerifyKey}}">
<input type="hidden" name="id" value="{{.t.Id}}">
<div class="form-group">
<label class="control-label">模式</label>
<select class="form-control" name="type" id="type">
<option {{if eq "tunnelServer" .t.Mode}}selected{{end}} value="tunnelServer">tcp隧道</option>
<option {{if eq "udpServer" .t.Mode}}selected{{end}} value="udpServer">udp隧道</option>
<option {{if eq "socks5Server" .t.Mode}}selected{{end}} value="socks5Server">socks5代理</option>
<option {{if eq "socks5Server" .t.Mode}}selected{{end}} value="socks5Server">socks5代理
</option>
<option {{if eq "httpProxyServer" .t.Mode}}selected{{end}} value="httpProxyServer">http代理
<option {{if eq "hostServer" .t.Mode}}selected{{end}} value="hostServer">host客户端</option>
</select>
</div>
<div class="form-group">
<label class="control-label">备注</label>
<input class="form-control" value="{{.t.Remark}}" type="text" name="remark" placeholder="备注">
</div>
<div class="form-group" id="port">
<label class="control-label">监听的端口</label>
<input class="form-control" value="{{.t.TcpPort}}" type="text" name="port"
@@ -25,6 +29,18 @@
<input class="form-control" value="{{.t.Target}}" type="text" name="target"
placeholder="内网隧道目标例如10.1.50.203:22">
</div>
<div class="form-group" id="client_id">
<label class="control-label">客户端ID</label>
<input class="form-control" value="{{.t.ClientId}}" type="text" name="client_id"
placeholder="客户端id">
</div>
<div class="form-group" id="use_client">
<label class="control-label">是否使用客户端配置</label>
<select class="form-control" name="use_client">
<option {{if eq false .t.UseClientCnf}}selected{{end}} value="0"></option>
<option {{if eq true .t.UseClientCnf}}selected{{end}} value="1"></option>
</select>
</div>
<div class="form-group" id="compress">
<label class="control-label">数据压缩方式(所有模式均支持)</label>
<select class="form-control" name="compress">
@@ -32,14 +48,14 @@
<option {{if eq "snappy" .t.Compress}}selected{{end}} value="snappy">snappy压缩</option>
</select>
</div>
<div class="form-group" id="compress">
<div class="form-group" id="crypt">
<label class="control-label">是否加密传输(所有模式均支持)</label>
<select class="form-control" name="crypt">
<option {{if eq false .t.Crypt}}selected{{end}} value="0">不加密</option>
<option {{if eq true .t.Crypt}}selected{{end}} value="1">加密</option>
</select>
</div>
<div class="form-group" id="compress">
<div class="form-group" id="mux">
<label class="control-label">是否启用TCP复用(所有模式均支持)</label>
<select class="form-control" name="mux">
<option {{if eq false .t.Mux}}selected{{end}} value="0">不启用</option>
@@ -74,7 +90,7 @@
arr["udpServer"] = ["type", "port", "target", "compress"]
arr["socks5Server"] = ["type", "port", "compress", "u", "p"]
arr["httpProxyServer"] = ["type", "port", "compress", "u", "p"]
arr["hostServer"] = ["type", "compress", "u", "p"]
arrClientHide = ["compress", "u", "p", "crypt", "mux"]
function resetForm() {
for (var i = 0; i < arr["all"].length; i++) {
@@ -86,10 +102,28 @@
}
}
function resetClientCnf() {
for (var i = 0; i < arrClientHide.length; i++) {
$("#" + arrClientHide[i]).css("display", "block")
}
op = $("#use_client option:selected").val()
if (op == 1) {
for (var i = 0; i < arrClientHide.length; i++) {
$("#" + arrClientHide[i]).css("display", "none")
}
}
}
$(function () {
resetForm()
resetClientCnf()
$("#type").on("change", function () {
resetForm()
resetClientCnf()
})
$("#use_client").on("change", function () {
resetForm()
resetClientCnf()
})
$("#add").on("click", function () {
$.ajax({

View File

@@ -4,18 +4,26 @@
<h3 class="tile-title">添加</h3>
<div class="tile-body">
<form>
<input type="hidden" name="vkey" value="{{.vkey}}">
<div class="form-group">
<label class="control-label">备注</label>
<input class="form-control" type="text" name="remark" placeholder="备注">
</div>
<div class="form-group">
<label class="control-label">域名</label>
<input class="form-control" type="text" name="host" placeholder="域名">
</div>
<div class="form-group">
<label class="control-label">客户端id</label>
<input value="{{.client_id}}" class="form-control" type="text" name="client_id" placeholder="客户端id">
</div>
<div class="form-group">
<label class="control-label">内网目标</label>
<input class="form-control" type="text" name="target" placeholder="内网隧道目标例如10.1.50.203:22">
</div>
<div class="form-group" id="header">
<label class="control-label">header头修改(冒号分隔多个请换行填写)</label>
<textarea class="form-control" rows="4" type="text" name="header" placeholder="Cache-Control: no-cache"></textarea>
<textarea class="form-control" rows="4" type="text" name="header"
placeholder="Cache-Control: no-cache"></textarea>
</div>
<div class="form-group" id="hostchange">
<label class="control-label">host修改</label>

View File

@@ -4,12 +4,20 @@
<h3 class="tile-title">修改</h3>
<div class="tile-body">
<form>
<input type="hidden" name="vkey" value="{{.t.VerifyKey}}">
<div class="form-group">
<label class="control-label">域名</label>
<input class="form-control" value="{{.h.Host}}" type="hidden" name="host" placeholder="域名">
<input class="form-control" value="{{.h.Host}}" type="text" name="nhost" placeholder="域名">
</div>
<div class="form-group">
<label class="control-label">备注</label>
<input class="form-control" value="{{.h.Remark}}" type="text" name="remark" placeholder="备注">
</div>
<div class="form-group">
<label class="control-label">客户端id</label>
<input class="form-control" value="{{.h.ClientId}}" type="text" name="client_id"
placeholder="客户端id">
</div>
<div class="form-group">
<label class="control-label">内网目标</label>
<input class="form-control" value="{{.h.Target}}" type="text" name="target"

146
web/views/index/help.html Normal file
View File

@@ -0,0 +1,146 @@
<div class="row">
<div class="col-md-12">
<div class="tile">
<iframe src="https://ghbtns.com/github-btn.html?user=cnlh&repo=easyProxy&type=star&count=true&size=large"
frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
<iframe src="https://ghbtns.com/github-btn.html?user=cnlh&repo=easyProxy&type=watch&count=true&size=large&v=2"
frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
<iframe src="https://ghbtns.com/github-btn.html?user=cnlh&repo=easyProxy&type=fork&count=true&size=large"
frameborder="0" scrolling="0" width="158px" height="30px"></iframe>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="tile">
<h3 class="tile-title">域名代理模式</h3>
<p>
<b>适用范围</b> 小程序开发微信公众号开发产品演示
</p>
<p>
<b>假设场景</b>
<li>有一个域名proxy.com有一台公网机器ip为{{.ip}}</li>
<li>两个内网开发站点127.0.0.1:81127.0.0.1:82</li>
<li>想通过a.proxy.com访问127.0.0.1:81通过b.proxy.com访问127.0.0.1:82</li>
</p>
<p><b>使用步骤</b></p>
<ul>
<li>将a.proxy.comb.proxy.com解析到公网服务器{{.ip}}</li>
<li>使用nginx监听这两个个域名并配置ssl等</li>
<li>在nginx配置中添加反向代理或直接将http监听端口改为80<br>
<pre><code>
server {
listen 80;
server_name a.proxy.com b.proxy.com;#也可以是泛解析*.proxy.com
#ssl等配置
<b>location / {
proxy_pass http://127.0.0.1:{{.proxyPort}};
}</b>
}
</code></pre>
</li>
<li>在域名代理管理中添加一个客户端选择压缩方式保存 <a href="/index/add?type=hostServer">立即添加</a></li>
{{/*<li>在域名代理管理中找到新加的客户端(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
<li>点击该客户端的域名管理添加两条规则规则1域名a.proxy.com内网目标127.0.0.1:812域名b.proxy.com内网目标127.0.0.1:82</li>
<li>现在访问a.proxy.comb.proxy.com即可成功</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="tile">
<h3 class="tile-title">tcp隧道模式</h3>
<p>
<b>适用范围</b> ssh远程桌面等tcp连接场景
</p>
<p>
<b>假设场景</b> 想通过访问公网服务器{{.ip}}的8001端口连接内网机器10.1.50.101的22端口实现ssh连接
</p>
<p><b>使用步骤</b></p>
<ul>
<li>在tcp隧道管理中添加一条隧道填写监听的端口8001内网目标ip和目标端口10.1.50.101:22选择压缩方式保存 <a
href="/index/add?type=tunnelServer">立即添加</a></li>
{{/*<li>在tcp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
<li>访问公网服务器ip{{.ip}}:填写的监听端口(8001)相当于访问内网ip(10.1.50.101):目标端口(22)例如ssh -p 8001 root@{{.ip}}</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
</div>
</div>
<div class="col-md-6">
<div class="tile">
<h3 class="tile-title">udp隧道模式</h3>
<p>
<b>适用范围</b> 内网dns解析等udp连接场景
</p>
<p>
<b>假设场景</b> 内网有一台dns10.1.50.102:53在非内网环境下想使用该dns公网服务器为{{.ip}}
</p>
<p><b>使用步骤</b></p>
<ul>
<li>在udp隧道管理中添加一条隧道填写监听的端口8002内网目标ip和目标端口10.1.50.102:53选择压缩方式保存 <a
href="/index/add?type=udpServer">立即添加</a></li>
{{/*<li>在udp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
<li>修改本机dns为{{.ip}}则相当于使用10.1.50.202作为dns服务器</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="tile">
<h3 class="tile-title">socks5代理模式</h3>
<p>
<b>适用范围</b> 在外网环境下如同使用vpn一样访问内网设备或者资源
</p>
<p>
<b>假设场景</b> 想将公网服务器{{.ip}}的8003端口作为socks5代理达到访问内网任意设备或者资源的效果
</p>
<p><b>使用步骤</b></p>
<ul>
<li>在socks5隧道管理中添加一条隧道填写监听的端口8003验证用户名和密码自行选择建议先不填部分客户端不支持proxifer支持选择压缩方式保存 <a
href="/index/add?type=sock5Server">立即添加</a></li>
{{/*<li>在socks5代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
<li>在外网环境的本机配置socks5代理ip为公网服务器ip{{.ip}}端口为填写的监听端口(8003)即可畅享内网了</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
</div>
</div>
<div class="col-md-6">
<div class="tile">
<h3 class="tile-title">http代理模式</h3>
<p>
<b>适用范围</b> 在外网环境下访问内网站点
</p>
<p>
<b>假设场景</b> 想将公网服务器{{.ip}}的8004端口作为http代理访问内网网站
</p>
<p><b>使用步骤</b></p>
<ul>
<li>在http隧道管理中添加一条隧道填写监听的端口8004选择压缩方式保存 <a
href="/index/add?type=httpProxyServer">立即添加</a></li>
<li>在http代理管理列表中找到新加的隧道(查看其vkey或者客户端命令)任意内网机器执行客户端命令</li>
<li>在外网环境的本机配置http代理ip为公网服务器ip{{.ip}}端口为填写的监听端口(8004)即可访问了</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="tile">
<p>
<b>多客户端模式</b>
<li>服务端启动./proxy_server</li>
<li>客户端启动./proxy_client -server={{.ip}}:{{.p}} -vkey=xxx见管理列表的客户端启动模式</li>
</p>
<p><b>支持客户端同时建立多条隧道例如单个通道时命令为./proxy_client -server={{.ip}}:{{.p}}
-vkey=ccc如果要支持另外一个隧道则对应的执行命令为./proxy_client
-server={{.ip}}:{{.p}} -vkey=ccc,ddd即用逗号分隔开多个vkey适用于所有模式</b></p>
</div>
</div>
</div>
</main>

View File

@@ -5,9 +5,13 @@
<table class="table table-hover table-bordered" id="sampleTable">
<thead>
<tr>
<th>客户端key</th>
<th>客户端id</th>
<th>备注</th>
<th>host</th>
<th>内网目标</th>
<th>host改写</th>
<th>出口流量</th>
<th>入口流量</th>
<th>操作</th>
</tr>
</thead>
@@ -39,7 +43,7 @@
}
function add() {
window.location.href = "/index/addhost?vkey={{.vkey}}"
window.location.href = "/index/addhost?vkey={{.task_id}}&client_id={{.client_id}}"
}
function edit(host) {
@@ -48,6 +52,11 @@
$(document).ready(function () {
var table = $('#sampleTable').DataTable({
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.childRowImmediate
}
},
dom: 'Bfrtip',
processing: true,
serverSide: true,
@@ -59,9 +68,13 @@
},
dom: '<"top"fl><"toolbar">rt<"bottom"ip><"clear">',
columns: [ //这个是显示到界面上的个数据 格式为 {data:'显示的字段名'}
{data: 'Vkey'},
{data: 'ClientId'},
{data: 'Remark'},
{data: 'Host'},
{data: 'Target'},
{data: 'HostChange'},
{data: 'HostChange'},
{data: 'HostChange'},
{data: 'Target'},
],
bFilter: false,
@@ -74,7 +87,19 @@
'<button onclick="edit(\'' + row.Host + '\')" type="button" class="btn btn-primary btn-sm">编辑查看</button> '
+ ' </div>'
}
}
},
{
targets: -2,
render: function (data, type, row, meta) {
return change(row.Flow.InletFlow)
}
},
{
targets: -3,
render: function (data, type, row, meta) {
return change(row.Flow.ExportFlow)
}
}
],
buttons: []
});

View File

@@ -1,147 +1,104 @@
<div class="row">
<div class="col-md-12">
<div class="tile">
<iframe src="https://ghbtns.com/github-btn.html?user=cnlh&repo=easyProxy&type=star&count=true&size=large"
frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
<iframe src="https://ghbtns.com/github-btn.html?user=cnlh&repo=easyProxy&type=watch&count=true&size=large&v=2"
frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
<iframe src="https://ghbtns.com/github-btn.html?user=cnlh&repo=easyProxy&type=fork&count=true&size=large"
frameborder="0" scrolling="0" width="158px" height="30px"></iframe>
<div class="col-md-3">
<div class="widget-small warning coloured-icon"><i class="icon fa fa-html5 fa-3x"></i>
<div class="info">
<h4>HTTP端口</h4>
<p><b>{{.p}}</b></p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="tile">
<h3 class="tile-title">域名代理模式</h3>
<p>
<b>适用范围</b> 小程序开发微信公众号开发产品演示
</p>
<p>
<b>假设场景</b>
<li>有一个域名proxy.com有一台公网机器ip为{{.ip}}</li>
<li>两个内网开发站点127.0.0.1:81127.0.0.1:82</li>
<li>想通过a.proxy.com访问127.0.0.1:81通过b.proxy.com访问127.0.0.1:82</li>
</p>
<p><b>使用步骤</b></p>
<ul>
<li>将a.proxy.comb.proxy.com解析到公网服务器{{.ip}}</li>
<li>使用nginx监听这两个个域名并配置ssl等</li>
<li>在nginx配置中添加反向代理或直接将http监听端口改为80<br>
<pre><code>
server {
listen 80;
server_name a.proxy.com b.proxy.com;#也可以是泛解析*.proxy.com
#ssl等配置
<b>location / {
proxy_pass http://127.0.0.1:{{.proxyPort}};
}</b>
}
</code></pre>
</li>
<li>在域名代理管理中添加一个客户端选择压缩方式保存 <a href="/index/add?type=hostServer">立即添加</a></li>
{{/*<li>在域名代理管理中找到新加的客户端(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
<li>点击该客户端的域名管理添加两条规则规则1域名a.proxy.com内网目标127.0.0.1:812域名b.proxy.com内网目标127.0.0.1:82</li>
<li>现在访问a.proxy.comb.proxy.com即可成功</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
<div class="col-md-3">
<div class="widget-small danger coloured-icon"><i class="icon fa fa-home fa-3x"></i>
<div class="info">
<h4>域名解析数</h4>
<p><b>{{.data.hostCount}}</b></p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="widget-small primary coloured-icon"><i class="icon fa fa-users fa-3x"></i>
<div class="info">
<h4>总客户端数</h4>
<p><b>{{.data.clientCount}}</b></p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="widget-small info coloured-icon"><i class="icon fa fa-user-secret fa-3x"></i>
<div class="info">
<h4>在线客户端数</h4>
<p><b>{{.data.clientOnlineCount}}</b></p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="tile">
<h3 class="tile-title">tcp隧道模式</h3>
<p>
<b>适用范围</b> ssh远程桌面等tcp连接场景
</p>
<p>
<b>假设场景</b> 想通过访问公网服务器{{.ip}}的8001端口连接内网机器10.1.50.101的22端口实现ssh连接
</p>
<p><b>使用步骤</b></p>
<ul>
<li>在tcp隧道管理中添加一条隧道填写监听的端口8001内网目标ip和目标端口10.1.50.101:22选择压缩方式保存 <a
href="/index/add?type=tunnelServer">立即添加</a></li>
{{/*<li>在tcp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
<li>访问公网服务器ip{{.ip}}:填写的监听端口(8001)相当于访问内网ip(10.1.50.101):目标端口(22)例如ssh -p 8001 root@{{.ip}}</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
<h3 class="tile-title">流量</h3>
<div class="embed-responsive embed-responsive-16by9">
<canvas class="embed-responsive-item" id="flow"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="tile">
<h3 class="tile-title">udp隧道模式</h3>
<p>
<b>适用范围</b> 内网dns解析等udp连接场景
</p>
<p>
<b>假设场景</b> 内网有一台dns10.1.50.102:53在非内网环境下想使用该dns公网服务器为{{.ip}}
</p>
<p><b>使用步骤</b></p>
<ul>
<li>在udp隧道管理中添加一条隧道填写监听的端口8002内网目标ip和目标端口10.1.50.102:53选择压缩方式保存 <a
href="/index/add?type=udpServer">立即添加</a></li>
{{/*<li>在udp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
<li>修改本机dns为{{.ip}}则相当于使用10.1.50.202作为dns服务器</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
<h3 class="tile-title">代理类型</h3>
<div class="embed-responsive embed-responsive-16by9">
<canvas class="embed-responsive-item" id="types"></canvas>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="tile">
<h3 class="tile-title">socks5代理模式</h3>
<p>
<b>适用范围</b> 在外网环境下如同使用vpn一样访问内网设备或者资源
</p>
<p>
<b>假设场景</b> 想将公网服务器{{.ip}}的8003端口作为socks5代理达到访问内网任意设备或者资源的效果
</p>
<p><b>使用步骤</b></p>
<ul>
<li>在socks5隧道管理中添加一条隧道填写监听的端口8003验证用户名和密码自行选择建议先不填部分客户端不支持proxifer支持选择压缩方式保存 <a
href="/index/add?type=sock5Server">立即添加</a></li>
{{/*<li>在socks5代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
<li>在外网环境的本机配置socks5代理ip为公网服务器ip{{.ip}}端口为填写的监听端口(8003)即可畅享内网了</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
</div>
</div>
<div class="col-md-6">
<div class="tile">
<h3 class="tile-title">http代理模式</h3>
<p>
<b>适用范围</b> 在外网环境下访问内网站点
</p>
<p>
<b>假设场景</b> 想将公网服务器{{.ip}}的8004端口作为http代理访问内网网站
</p>
<p><b>使用步骤</b></p>
<ul>
<li>在http隧道管理中添加一条隧道填写监听的端口8004选择压缩方式保存 <a
href="/index/add?type=httpProxyServer">立即添加</a></li>
<li>在http代理管理列表中找到新加的隧道(查看其vkey或者客户端命令)任意内网机器执行客户端命令</li>
<li>在外网环境的本机配置http代理ip为公网服务器ip{{.ip}}端口为填写的监听端口(8004)即可访问了</li>
</ul>
<p>上文中提到公网ip{{.ip}}为系统自动识别如果是在测试环境中请自行对应默认启动方式为单客户端模式默认内网客户端已经启动</p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="tile">
<p>
<b>多客户端模式</b>
<li>服务端启动./proxy_server</li>
<li>客户端启动./proxy_client -server={{.ip}}:{{.p}} -vkey=xxx见管理列表的客户端启动模式</li>
</p>
<p><b>支持客户端同时建立多条隧道例如单个通道时命令为./proxy_client -server={{.ip}}:{{.p}} -vkey=ccc如果要支持另外一个隧道则对应的执行命令为./proxy_client
-server={{.ip}}:{{.p}} -vkey=ccc,ddd即用逗号分隔开多个vkey适用于所有模式</b></p>
</div>
</div>
</div>
</main>
<script>
var pdataFlow = [
{
value: {{.data.inletFlowCount}},
color: "#46BFBD",
highlight: "#5AD3D1",
label: "入口流量"
},
{
value: {{.data.exportFlowCount}},
color: "#FDB45C",
highlight: "#FFC870",
label: "出口流量"
}
]
var pdataTypes = [
{
value: {{.data.tunnelServerCount}},
color: "#46BFBD",
highlight: "#5AD3D1",
label: "tcp隧道"
},
{
value: {{.data.socks5ServerCount}},
color: "#85FEAA",
highlight: "#85FEAA",
label: "socks5隧道"
},
{
value: {{.data.httpProxyServerCount}},
color: "#4B653C",
highlight: "#4B653C",
label: "http代理"
},
{
value: {{.data.udpServerCount}},
color: "#90653C",
highlight: "#90653C",
label: "udp代理"
},
{
value: {{.data.hostCount}},
color: "#FDB45C",
highlight: "#FDB45C",
label: "域名解析"
}
]
var ctxp = $("#flow").get(0).getContext("2d");
var pieChart = new Chart(ctxp).Pie(pdataFlow);
var ctxd = $("#types").get(0).getContext("2d");
var doughnutChart = new Chart(ctxd).Doughnut(pdataTypes);
</script>

View File

@@ -5,10 +5,11 @@
<table class="table table-hover table-bordered" id="sampleTable">
<thead>
<tr>
{{/*<th>模式</th>*/}}
<th>Id</th>
<th>备注</th>
<th>客户端Id</th>
<th>监听端口</th>
<th>内网目标</th>
<th>多客户端模式客户端执行命令</th>
<th>压缩方式</th>
<th>加密传输</th>
<th>TCP多路复用</th>
@@ -16,11 +17,12 @@
<th>密码</th>
<th>客户端状态</th>
<th>状态</th>
<th>出口流量</th>
<th>入口流量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
@@ -30,12 +32,12 @@
</main>
<script type="text/javascript">
function del(vKey) {
function del(id) {
if (confirm("确定要删除数据吗?")) {
$.ajax({
type: "POST",
url: "/index/del",
data: {"vKey": vKey},
data: {"id": id},
success: function (res) {
alert(res.msg)
if (res.status) {
@@ -46,12 +48,12 @@
}
}
function start(vKey) {
function start(id) {
if (confirm("确定要开始任务吗?")) {
$.ajax({
type: "POST",
url: "/index/start",
data: {"vKey": vKey},
data: {"id": id},
success: function (res) {
alert(res.msg)
if (res.status) {
@@ -62,12 +64,12 @@
}
}
function stop(vKey) {
function stop(id) {
if (confirm("确定要暂停吗?")) {
$.ajax({
type: "POST",
url: "/index/stop",
data: {"vKey": vKey},
data: {"id": id},
success: function (res) {
alert(res.msg)
if (res.status) {
@@ -78,35 +80,38 @@
}
}
function edit(vKey) {
window.location.href = "/index/edit?vKey=" + vKey
function edit(id) {
window.location.href = "/index/edit?id=" + id
}
function add() {
window.location.href = "/index/add?type=" +{{.type}}
window.location.href = "/index/add?type={{.type}}" + "&client_id={{.client_id}}"
}
function hostList(vkey) {
window.location.href = "/index/hostlist?vkey=" + vkey
}
$(document).ready(function () {
var table = $('#sampleTable').DataTable({
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.childRowImmediate
}
},
dom: 'Bfrtip',
processing: true,
serverSide: true,
autoWidth: false,
ordering: false,
ajax: {
url: '/index/getserverconfig?type={{.type}}',
url: '/index/getserverconfig?type={{.type}}' + "&client_id=" +{{.client_id}},
type: 'POST'
},
dom: '<"top"fl><"toolbar">rt<"bottom"ip><"clear">',
columns: [ //这个是显示到界面上的个数据 格式为 {data:'显示的字段名'}
// {data: 'Mode'},
{data: 'Id'},
{data: 'Remark'},
{data: 'ClientId'},
{data: 'TcpPort'},
{data: 'Target'},
{data: 'VerifyKey'},
{data: 'Compress'},
{data: 'Crypt'},
{data: 'Mux'},
@@ -114,6 +119,8 @@
{data: 'P'},
{data: 'ClientStatus'},
{data: 'IsRun'},
{data: 'ExportFlow'},
{data: 'InletFlow'},
{data: "Id"}
],
bFilter: false,
@@ -121,27 +128,18 @@
targets: -1,
render: function (data, type, row, meta) {
if (row.IsRun == 1) {
btn = "<button onclick=\"stop('" + row.VerifyKey + "')\" class=\"btn btn-secondary btn-sm\" type=\"button\">关闭</button>"
btn = "<button onclick=\"stop('" + row.Id + "')\" class=\"btn btn-secondary btn-sm\" type=\"button\">关闭</button>"
} else {
btn = "<button onclick=\"start('" + row.VerifyKey + "')\" class=\"btn btn-success btn-sm\" type=\"button\">打开</button>"
}
btn_edit = '<button onclick="edit(\'' + row.VerifyKey + '\')" type="button" class="btn btn-primary btn-sm">查看编辑</button> '
if ({{.type}} == "hostServer"
)
{
btn_host = '<button onclick="hostList(\'' + row.VerifyKey + '\')" type="button" class="btn btn-info btn-sm">域名管理</button> '
}
else
{
btn_host = ""
btn = "<button onclick=\"start('" + row.Id + "')\" class=\"btn btn-success btn-sm\" type=\"button\">打开</button>"
}
btn_edit = '<button onclick="edit(\'' + row.Id + '\')" type="button" class="btn btn-primary btn-sm">查看编辑</button> '
return '<div class="btn-group" role="group" aria-label="..."> ' +
'<button onclick="del(\'' + row.VerifyKey + '\')" type="button" class="btn btn-danger btn-sm">删除</button>' +
btn_edit + btn + btn_host + ' </div>'
'<button onclick="del(\'' + row.Id + '\')" type="button" class="btn btn-danger btn-sm">删除</button>' +
btn_edit + btn + ' </div>'
}
},
{
targets: -2,
targets: -4,
render: function (data, type, row, meta) {
if (data == 0) {
return "<span class=\"badge badge-pill badge-secondary\">暂停</span>"
@@ -151,7 +149,7 @@
}
},
{
targets: -7,
targets: -9,
render: function (data, type, row, meta) {
if (data == "0") {
return "不加密"
@@ -161,7 +159,7 @@
}
},
{
targets: -6,
targets: -8,
render: function (data, type, row, meta) {
if (data == "0") {
return "不启用"
@@ -169,17 +167,9 @@
return "启用"
}
}
}
,
{
targets: 2,
render: function (data, type, row, meta) {
return "./proxy_client -server={{.ip}}:{{.p}} -vkey=" + data
// return data
}
},
{
targets: -3,
targets: -5,
render: function (data, type, row, meta) {
if (data == 0) {
return "<span class=\"badge badge-pill badge-secondary\">离线</span>"
@@ -187,6 +177,18 @@
return "<span class=\"badge badge-pill badge-success\">在线</span>"
}
}
},
{
targets: -2,
render: function (data, type, row, meta) {
return change(row.Flow.InletFlow)
}
},
{
targets: -3,
render: function (data, type, row, meta) {
return change(row.Flow.ExportFlow)
}
}
],
buttons: []

View File

@@ -41,8 +41,10 @@
</div>
<ul class="app-menu">
<li><a class="app-menu__item {{if eq "index" .menu}}active{{end}}" href="/"><i
class="app-menu__icon fa fa-dashboard"></i><span class="app-menu__label">使用说明</span></a></li>
<li><a class="app-menu__item {{if eq "host" .menu}}active{{end}}" href="/index/host"><i
class="app-menu__icon fa fa-dashboard"></i><span class="app-menu__label">dashboard</span></a></li>
<li><a class="app-menu__item {{if eq "client" .menu}}active{{end}}" href="/client/client"><i
class="app-menu__icon fa fa-dot-circle-o"></i><span class="app-menu__label">客户端管理</span></a></li>
<li><a class="app-menu__item {{if eq "host" .menu}}active{{end}}" href="/index/hostlist"><i
class="app-menu__icon fa fa-lemon-o"></i><span class="app-menu__label">域名代理</span></a></li>
<li><a class="app-menu__item {{if eq "tcp" .menu}}active{{end}}" href="/index/tcp"><i
class="app-menu__icon fa fa-life-buoy"></i><span class="app-menu__label">tcp隧道管理</span></a></li>
@@ -52,12 +54,14 @@
class="app-menu__icon fa fa-lightbulb-o"></i><span class="app-menu__label">socks5代理</span></a></li>
<li><a class="app-menu__item {{if eq "http" .menu}}active{{end}}" href="/index/http"><i
class="app-menu__icon fa fa-magic"></i><span class="app-menu__label">http代理</span></a></li>
<li><a class="app-menu__item {{if eq "help" .menu}}active{{end}}" href="/index/help"><i
class="app-menu__icon fa fa-question-circle"></i><span class="app-menu__label">使用说明</span></a></li>
</ul>
</aside>
<script type="text/javascript" src="/static/js/datatables.min.js"></script>
<script src="/static/js/main.js"></script>
<script src="/static/js/chart.js"></script>
<main class="app-content">
<div class="app-title">
<div>