客户端服务端分离

This commit is contained in:
刘河
2019-01-09 20:33:00 +08:00
parent dcd21f211d
commit 1f61b99387
46 changed files with 1062 additions and 1431 deletions

118
web/controllers/base.go Executable file
View File

@@ -0,0 +1,118 @@
package controllers
import (
"github.com/astaxie/beego"
"github.com/cnlh/easyProxy/server"
"github.com/cnlh/easyProxy/utils"
"strconv"
"strings"
)
type BaseController struct {
beego.Controller
controllerName string
actionName string
}
//初始化参数
func (s *BaseController) Prepare() {
controllerName, actionName := s.GetControllerAndAction()
s.controllerName = strings.ToLower(controllerName[0 : len(controllerName)-10])
s.actionName = strings.ToLower(actionName)
if s.GetSession("auth") != true {
s.Redirect("/login/index", 302)
}
}
//加载模板
func (s *BaseController) display(tpl ...string) {
var tplname string
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, ":")])
s.Data["p"] = server.Bridge.TunnelPort
s.Data["proxyPort"] = beego.AppConfig.String("hostPort")
s.Layout = "public/layout.html"
s.TplName = tplname
}
//错误
func (s *BaseController) error() {
s.Layout = "public/layout.html"
s.TplName = "public/error.html"
}
//去掉没有err返回值的int
func (s *BaseController) GetIntNoErr(key string, def ...int) int {
strv := s.Ctx.Input.Query(key)
if len(strv) == 0 && len(def) > 0 {
return def[0]
}
val, _ := strconv.Atoi(strv)
return val
}
//获取去掉错误的bool值
func (s *BaseController) GetBoolNoErr(key string, def ...bool) bool {
strv := s.Ctx.Input.Query(key)
if len(strv) == 0 && len(def) > 0 {
return def[0]
}
val, _ := strconv.ParseBool(strv)
return val
}
//ajax正确返回
func (s *BaseController) AjaxOk(str string) {
s.Data["json"] = ajax(str, 1)
s.ServeJSON()
s.StopRun()
}
//ajax错误返回
func (s *BaseController) AjaxErr(str string) {
s.Data["json"] = ajax(str, 0)
s.ServeJSON()
s.StopRun()
}
//组装ajax
func ajax(str string, status int) (map[string]interface{}) {
json := make(map[string]interface{})
json["status"] = status
json["msg"] = str
return json
}
//ajax table返回
func (s *BaseController) AjaxTable(list interface{}, cnt int, recordsTotal int) {
json := make(map[string]interface{})
json["data"] = list
json["draw"] = s.GetIntNoErr("draw")
json["err"] = ""
json["recordsTotal"] = recordsTotal
json["recordsFiltered"] = cnt
s.Data["json"] = json
s.ServeJSON()
s.StopRun()
}
//ajax table参数
func (s *BaseController) GetAjaxParams() (start, limit int) {
s.Ctx.Input.Bind(&start, "start")
s.Ctx.Input.Bind(&limit, "length")
return
}
func (s *BaseController) SetInfo(name string) {
s.Data["name"] = name
}
func (s *BaseController) SetType(name string) {
s.Data["type"] = name
}

170
web/controllers/index.go Executable file
View File

@@ -0,0 +1,170 @@
package controllers
import (
"github.com/cnlh/easyProxy/server"
"github.com/cnlh/easyProxy/utils"
)
type IndexController struct {
BaseController
}
func (s *IndexController) Index() {
s.SetInfo("使用说明")
s.display("index/index")
}
func (s *IndexController) Tcp() {
s.SetInfo("tcp隧道管理")
s.SetType("tunnelServer")
s.display("index/list")
}
func (s *IndexController) Udp() {
s.SetInfo("udp隧道管理")
s.SetType("udpServer")
s.display("index/list")
}
func (s *IndexController) Socks5() {
s.SetInfo("socks5管理")
s.SetType("socks5Server")
s.display("index/list")
}
func (s *IndexController) Http() {
s.SetInfo("http代理管理")
s.SetType("httpProxyServer")
s.display("index/list")
}
func (s *IndexController) Host() {
s.SetInfo("host模式管理")
s.SetType("hostServer")
s.display("index/list")
}
func (s *IndexController) GetServerConfig() {
start, length := s.GetAjaxParams()
taskType := s.GetString("type")
list, cnt := server.GetServerConfig(start, length, taskType)
s.AjaxTable(list, cnt, cnt)
}
func (s *IndexController) Add() {
if s.Ctx.Request.Method == "GET" {
s.Data["type"] = s.GetString("type")
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,
}
server.CsvDb.NewTask(t)
if err := server.AddTask(t); err != nil {
s.AjaxErr(err.Error())
} else {
s.AjaxOk("添加成功")
}
}
}
func (s *IndexController) Edit() {
if s.Ctx.Request.Method == "GET" {
vKey := s.GetString("vKey")
if t, err := server.CsvDb.GetTask(vKey); err != nil {
s.error()
} else {
s.Data["t"] = t
}
s.SetInfo("修改")
s.display()
} else {
vKey := s.GetString("vKey")
if t, err := server.CsvDb.GetTask(vKey); err != nil {
s.error()
} else {
t.TcpPort = s.GetIntNoErr("port")
t.Mode = s.GetString("type")
t.Target = s.GetString("target")
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"))
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 {
s.AjaxErr("停止失败")
}
s.AjaxOk("停止成功")
}
func (s *IndexController) Del() {
vKey := s.GetString("vKey")
if err := server.DelTask(vKey); err != nil {
s.AjaxErr("删除失败")
}
s.AjaxOk("删除成功")
}
func (s *IndexController) Start() {
vKey := s.GetString("vKey")
if err := server.StartTask(vKey); err != nil {
s.AjaxErr("开启失败")
}
s.AjaxOk("开启成功")
}
func (s *IndexController) HostList() {
if s.Ctx.Request.Method == "GET" {
s.Data["vkey"] = s.GetString("vkey")
s.SetInfo("域名列表")
s.display("index/hlist")
} else {
start, length := s.GetAjaxParams()
vkey := s.GetString("vkey")
list, cnt := server.CsvDb.GetHostList(start, length, vkey)
s.AjaxTable(list, cnt, cnt)
}
}
func (s *IndexController) DelHost() {
host := s.GetString("host")
if err := server.CsvDb.DelHost(host); err != nil {
s.AjaxErr("删除失败")
}
s.AjaxOk("删除成功")
}
func (s *IndexController) AddHost() {
if s.Ctx.Request.Method == "GET" {
s.Data["vkey"] = s.GetString("vkey")
s.SetInfo("新增")
s.display("index/hadd")
} else {
h := &server.HostList{
Vkey: s.GetString("vkey"),
Host: s.GetString("host"),
Target: s.GetString("target"),
}
server.CsvDb.NewHost(h)
s.AjaxOk("添加成功")
}
}

27
web/controllers/login.go Executable file
View File

@@ -0,0 +1,27 @@
package controllers
import (
"github.com/astaxie/beego"
)
type LoginController struct {
beego.Controller
}
func (self *LoginController) Index() {
self.TplName = "login/index.html"
}
func (self *LoginController) Verify() {
if self.GetString("psd") == beego.AppConfig.String("password") {
self.SetSession("auth", true)
self.Data["json"] = map[string]interface{}{"status": 1, "msg": "验证成功"}
self.ServeJSON()
} else {
self.Data["json"] = map[string]interface{}{"status": 0, "msg": "验证失败"}
self.ServeJSON()
}
}
func (self *LoginController) Out() {
self.SetSession("auth", false)
self.Redirect("/login/index", 302)
}

12
web/routers/router.go Executable file
View File

@@ -0,0 +1,12 @@
package routers
import (
"github.com/astaxie/beego"
"github.com/cnlh/easyProxy/web/controllers"
)
func init() {
beego.Router("/", &controllers.IndexController{}, "*:Index")
beego.AutoRouter(&controllers.IndexController{})
beego.AutoRouter(&controllers.LoginController{})
}

4
web/static/css/font-awesome.min.css vendored Executable file

File diff suppressed because one or more lines are too long

15240
web/static/css/main.css Executable file

File diff suppressed because it is too large Load Diff

BIN
web/static/fonts/FontAwesome.otf Executable file

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
web/static/img/48.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
web/static/img/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

194
web/static/js/datatables.min.js vendored Executable file

File diff suppressed because one or more lines are too long

27
web/static/js/main.js Executable file
View File

@@ -0,0 +1,27 @@
(function () {
"use strict";
var treeviewMenu = $('.app-menu');
// 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');
});
// Set initial active toggle
$("[data-toggle='treeview.'].is-expanded").parent().toggleClass('is-expanded');
//Activate bootstrip tooltips
// $("[data-toggle='tooltip']").tooltip();
})();

112
web/views/index/add.html Executable file
View File

@@ -0,0 +1,112 @@
<div class="row tile">
<div class="col-lg-12">
<div class="bs-component">
<div class="alert alert-dismissible alert-success">
<button class="close" type="button" data-dismiss="alert">×</button>
<span id="info"></span>
</div>
</div>
</div>
<div class="col-md-6 col-md-auto">
<div>
<h3 class="tile-title">添加</h3>
<div class="tile-body">
<form>
<div class="form-group">
<label class="control-label">模式</label>
<select class="form-control" name="type" id="type">
<option {{if eq "tunnelServer" .type}}selected{{end}} value="tunnelServer">tcp隧道</option>
<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" id="port">
<label class="control-label">监听的端口</label>
<input class="form-control" type="text" name="port" placeholder="公网服务器对外访问端口例如8024">
</div>
<div class="form-group" id="target">
<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="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>
<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>
</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>
var arr = []
arr["all"] = ["type", "port", "compress", "u", "p", "target"]
arr["tunnelServer"] = ["type", "port", "target", "compress", "u", "p", "tcp隧道模式提供一条tcp隧道适用于ssh、远程桌面等添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的目标端口"]
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将请求反向代理到本程序再进行域名配置即可解析"]
function resetForm() {
for (var i = 0; i < arr["all"].length; i++) {
$("#" + arr["all"][i]).css("display", "none")
}
o = $("#type option:selected").val()
for (var i = 0; i < arr[o].length - 1; i++) {
$("#" + arr[o][i]).css("display", "block")
}
$("#info").html(arr[o][arr[o].length - 1])
}
$(function () {
resetForm()
$("#type").on("change", function () {
resetForm()
})
$("#add").on("click", function () {
$.ajax({
type: "POST",
url: "/index/add",
data: $("form").serializeArray(),
success: function (res) {
alert(res.msg)
if (res.status) {
history.back(-1)
}
}
})
})
})
</script>

108
web/views/index/edit.html Executable file
View File

@@ -0,0 +1,108 @@
<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="vKey" value="{{.t.VerifyKey}}">
<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 "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" id="port">
<label class="control-label">监听的端口</label>
<input class="form-control" value="{{.t.TcpPort}}" type="text" name="port"
placeholder="公网服务器对外访问端口例如8024">
</div>
<div class="form-group" id="target">
<label class="control-label">内网目标(仅tcpudp隧道模式需填写)</label>
<input class="form-control" value="{{.t.Target}}" type="text" name="target"
placeholder="内网隧道目标例如10.1.50.203:22">
</div>
<div class="form-group" id="compress">
<label class="control-label">数据压缩方式(所有模式均支持)</label>
<select class="form-control" name="compress">
<option {{if eq "" .t.Compress}}selected{{end}} value="">不压缩</option>
<option {{if eq "snappy" .t.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 .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">
<label class="control-label">是否启用TCP复用(所有模式均支持)</label>
<select class="form-control" name="mux">
<option {{if eq false .t.Mux}}selected{{end}} value="0">不启用</option>
<option {{if eq true .t.Mux}}selected{{end}} value="1">启用</option>
</select>
</div>
<div class="form-group" id="u">
<label class="control-label">验证用户名(仅socks5,web穿透支持)</label>
<input class="form-control" value="{{.t.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="{{.t.P}}" type="text" name="p"
placeholder="不填则无需验证">
</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>
var arr = []
arr["all"] = ["type", "port", "compress", "u", "p", "target"]
arr["tunnelServer"] = ["type", "port", "target", "u", "p", "compress"]
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"]
function resetForm() {
for (var i = 0; i < arr["all"].length; i++) {
$("#" + arr["all"][i]).css("display", "none")
}
o = $("#type option:selected").val()
for (var i = 0; i < arr[o].length; i++) {
$("#" + arr[o][i]).css("display", "block")
}
}
$(function () {
resetForm()
$("#type").on("change", function () {
resetForm()
})
$("#add").on("click", function () {
$.ajax({
type: "POST",
url: "/index/edit",
data: $("form").serializeArray(),
success: function (res) {
alert(res.msg)
if (res.status) {
history.back(-1)
}
}
})
})
})
</script>

43
web/views/index/hadd.html Executable file
View File

@@ -0,0 +1,43 @@
<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="vkey" value="{{.vkey}}">
<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">内网目标</label>
<input class="form-control" type="text" name="target" placeholder="内网隧道目标例如10.1.50.203:22">
</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: "/index/addhost",
data: $("form").serializeArray(),
success: function (res) {
alert(res.msg)
if (res.status) {
history.back(-1)
}
}
})
})
})
</script>

81
web/views/index/hlist.html Executable file
View File

@@ -0,0 +1,81 @@
<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>客户端key</th>
<th>host</th>
<th>内网目标</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
<script type="text/javascript">
function del(host) {
if (confirm("确定要删除数据吗?")) {
$.ajax({
type: "POST",
url: "/index/delhost",
data: {"host": host},
success: function (res) {
alert(res.msg)
if (res.status) {
document.location.reload();
}
}
})
}
}
function add() {
window.location.href = "/index/addhost?vkey={{.vkey}}"
}
$(document).ready(function () {
var table = $('#sampleTable').DataTable({
dom: 'Bfrtip',
processing: true,
serverSide: true,
autoWidth: false,
ordering: false,
ajax: {
url: window.location,
type: 'POST'
},
dom: '<"top"fl><"toolbar">rt<"bottom"ip><"clear">',
columns: [ //这个是显示到界面上的个数据 格式为 {data:'显示的字段名'}
{data: 'Vkey'},
{data: 'Host'},
{data: 'Target'},
{data: 'Target'},
],
bFilter: false,
columnDefs: [{
targets: -1,
render: function (data, type, row, meta) {
return '<div class="btn-group" role="group" aria-label="..."> ' +
'<button onclick="del(\'' + row.Host + '\')" type="button" class="btn btn-danger btn-sm">删除</button>' +
' </div>'
}
}
],
buttons: []
});
$("#sampleTable_length").html('<button class="btn btn-primary" onclick="add()" type="button">新增</button>')
})
;
</script>

152
web/views/index/index.html Executable file
View File

@@ -0,0 +1,152 @@
<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_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_set_header Connection "";
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>服务端启动./easyProxy -mode=webServer -tcpport=8284</li>
<li>客户端启动./easyProxy -server={{.ip}}:{{.p}} -vkey=xxx见管理列表的客户端启动模式</li>
</p>
<p><b>支持客户端同时建立多条隧道例如单个通道时命令为./easyProxy -server={{.ip}}:{{.p}} -vkey=ccc如果要支持另外一个隧道则对应的执行命令为./easyProxy
-server={{.ip}}:{{.p}} -vkey=ccc,ddd即用逗号分隔开多个vkey适用于所有模式</b></p>
</div>
</div>
</div>
</main>

199
web/views/index/list.html Executable file
View File

@@ -0,0 +1,199 @@
<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>模式</th>*/}}
<th>监听端口</th>
<th>内网目标</th>
<th>多客户端模式客户端执行命令</th>
<th>压缩方式</th>
<th>加密传输</th>
<th>TCP多路复用</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(vKey) {
if (confirm("确定要删除数据吗?")) {
$.ajax({
type: "POST",
url: "/index/del",
data: {"vKey": vKey},
success: function (res) {
alert(res.msg)
if (res.status) {
document.location.reload();
}
}
})
}
}
function start(vKey) {
if (confirm("确定要开始任务吗?")) {
$.ajax({
type: "POST",
url: "/index/start",
data: {"vKey": vKey},
success: function (res) {
alert(res.msg)
if (res.status) {
document.location.reload();
}
}
})
}
}
function stop(vKey) {
if (confirm("确定要暂停吗?")) {
$.ajax({
type: "POST",
url: "/index/stop",
data: {"vKey": vKey},
success: function (res) {
alert(res.msg)
if (res.status) {
document.location.reload();
}
}
})
}
}
function edit(vKey) {
window.location.href = "/index/edit?vKey=" + vKey
}
function add() {
window.location.href = "/index/add?type=" +{{.type}}
}
function hostList(vkey) {
window.location.href = "/index/hostlist?vkey=" + vkey
}
$(document).ready(function () {
var table = $('#sampleTable').DataTable({
dom: 'Bfrtip',
processing: true,
serverSide: true,
autoWidth: false,
ordering: false,
ajax: {
url: '/index/getserverconfig?type={{.type}}',
type: 'POST'
},
dom: '<"top"fl><"toolbar">rt<"bottom"ip><"clear">',
columns: [ //这个是显示到界面上的个数据 格式为 {data:'显示的字段名'}
// {data: 'Mode'},
{data: 'TcpPort'},
{data: 'Target'},
{data: 'VerifyKey'},
{data: 'Compress'},
{data: 'Crypt'},
{data: 'Mux'},
{data: 'U'},
{data: 'P'},
{data: 'ClientStatus'},
{data: 'IsRun'},
{data: "Id"}
],
bFilter: false,
columnDefs: [{
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>"
} 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 = ""
}
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>'
}
},
{
targets: -2,
render: function (data, type, row, meta) {
if (data == 0) {
return "<span class=\"badge badge-pill badge-secondary\">暂停</span>"
} else {
return "<span class=\"badge badge-pill badge-success\">正常</span>"
}
}
},
{
targets: -7,
render: function (data, type, row, meta) {
if (data == "0") {
return "不加密"
} else {
return "加密"
}
}
},
{
targets: -6,
render: function (data, type, row, meta) {
if (data == "0") {
return "不启用"
} else {
return "启用"
}
}
}
,
{
targets: 2,
render: function (data, type, row, meta) {
return "./easyProxy -server={{.ip}}:{{.p}} -vkey=" + data
// return data
}
},
{
targets: -3,
render: function (data, type, row, meta) {
if (data == 0) {
return "<span class=\"badge badge-pill badge-secondary\">离线</span>"
} else {
return "<span class=\"badge badge-pill badge-success\">在线</span>"
}
}
}
],
buttons: []
});
$("#sampleTable_length").html('<button class="btn btn-primary" onclick="add()" type="button">新增</button>')
})
;
</script>

60
web/views/login/index.html Executable file
View File

@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/static/img/favicon.ico">
<!-- Main CSS-->
<link rel="stylesheet" type="text/css" href="/static/css/main.css">
<!-- Font-icon css-->
<link rel="stylesheet" type="text/css" href="/static/css/font-awesome.min.css">
<title>easyProxy内网穿透</title>
</head>
<body>
<section class="material-half-bg">
<div class="cover"></div>
</section>
<section class="login-content">
<div class="logo">
<h1></h1>
</div>
<div class="login-box">
<form class="login-form" onsubmit="return false">
<h3 class="login-head"><i class="fa fa-lg fa-fw fa-user"></i>内网穿透管理登陆</h3>
<div class="form-group">
<label class="control-label">密码</label>
<input class="form-control" name="psd" type="password" placeholder="" autofocus>
</div>
<div class="form-group btn-container">
<button onclick="login()" class="btn btn-primary btn-block"><i class="fa fa-sign-in fa-lg fa-fw"></i>登陆
</button>
</div>
</form>
</div>
</section>
<script type="text/javascript" src="/static/js/pdfmake.min.js"></script>
<script type="text/javascript" src="/static/js/vfs_fonts.js"></script>
<script type="text/javascript" src="/static/js/datatables.min.js"></script>
<script src="/static/js/main.js"></script>
<script type="text/javascript">
// Login Page Flipbox control
function login() {
$.ajax({
type: "POST",
url: "/login/verify",
data: $("form").serializeArray(),
success: function (res) {
if (res.status) {
window.location.href = "/index/index"
} else {
alert(res.msg)
}
}
})
return false
}
</script>
</body>
</html>

5
web/views/public/error.html Executable file
View File

@@ -0,0 +1,5 @@
<div class="page-error tile">
<h1><i class="fa fa-exclamation-circle"></i> Error 404: Page not found</h1>
<p>The page you have requested is not found.</p>
<p><a class="btn btn-primary" href="javascript:window.history.back();">Go Back</a></p>
</div>

72
web/views/public/layout.html Executable file
View File

@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="description"
content="Vali is a responsive and free admin theme built with Bootstrap 4, SASS and PUG.js. It's fully customizable and modular.">
<link rel="shortcut icon" href="/static/img/favicon.ico">
<title>easyProxy内网穿透</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Font-icon css-->
<link rel="stylesheet" type="text/css" href="/static/css/font-awesome.min.css">
<!-- Main CSS-->
<link rel="stylesheet" type="text/css" href="/static/css/main.css">
</head>
<body class="app sidebar-mini rtl">
<!-- Navbar-->
<header class="app-header">
<a class="app-header__logo" href="/"></a>
<!-- Sidebar toggle button--><a class="app-sidebar__toggle" href="#" data-toggle="sidebar"
aria-label="Hide Sidebar"></a>
<ul class="app-nav">
<!--Notification Menu-->
<!-- User Menu-->
<li class="dropdown"><a class="app-nav__item" href="/login/out" data-toggle="dropdown"
aria-label="Open Profile Menu"><i class="fa fa-sign-out fa-lg"></i>退出登陆</a>
</li>
</ul>
</header>
<!-- Sidebar menu-->
<div class="app-sidebar__overlay" data-toggle="sidebar"></div>
<aside class="app-sidebar">
<div class="app-sidebar__user"><img class="app-sidebar__user-avatar"
src="/static/img/48.jpg"
alt="User Image">
<div>
<p class="app-sidebar__user-name">System</p>
<p class="app-sidebar__user-designation">欢迎使用</p>
</div>
</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-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>
<li><a class="app-menu__item {{if eq "udp" .menu}}active{{end}}" href="/index/udp"><i
class="app-menu__icon fa fa-laptop"></i><span class="app-menu__label">udp隧道管理</span></a></li>
<li><a class="app-menu__item {{if eq "socks5" .menu}}active{{end}}" href="/index/socks5"><i
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>
</ul>
</aside>
<script type="text/javascript" src="/static/js/datatables.min.js"></script>
<script src="/static/js/main.js"></script>
<main class="app-content">
<div class="app-title">
<div>
<h1><i class="fa fa-th-list"></i> {{.name}}</h1>
<a href="javascript:history.back(-1);"><i class="fa fa-mail-reply"></i>返回</a>
</div>
</div>
{{.LayoutContent}}
</body>
</html>