开源 Init
This commit is contained in:
commit
c7b65e278c
|
@ -0,0 +1,22 @@
|
|||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
relayports
|
||||
goports
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
.goreleaser.yaml
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
*.log
|
||||
*.upx
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
dist/
|
||||
config.json
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 gdy , 272288813@qq.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,127 @@
|
|||
# goports
|
||||
|
||||
|
||||
一个主要功能和socat类似,主要实现公网ipv6 tcp/udp 转发至 内网ipv4 tcp/udp 的小工具.
|
||||
|
||||
<!-- TOC -->
|
||||
- [goports](#goports)
|
||||
- [特性](#特性)
|
||||
- [使用](#使用)
|
||||
- [转发规则格式](#转发规则格式)
|
||||
- [其它启动参数](#其它启动参数)
|
||||
- [后台界面](#后台界面)
|
||||
- [开发编译](#开发编译)
|
||||
- [使用注意&常见问题](#使用注意&常见问题)
|
||||
<!-- /TOC -->
|
||||
|
||||
|
||||
## 特性
|
||||
|
||||
- 后端golang,前端vue3
|
||||
- 支持Windows、Linux系统,支持x86、ARM、MIPS、MIPSLE等架构
|
||||
- 支持界面化(web后台)管理转发规则,单条转发规则支持设置多个转发端口,一键开关指定转发规则
|
||||
- 单条规则支持黑白名单安全模式切换,白名单模式可以让没有安全验证的内网服务端口稍微安全一丢丢暴露到公网
|
||||
- Web后台支持查看最新100条日志
|
||||
- 另有精简版不带后台,支持命令行快捷设置转发规则,有利于空间有限的嵌入式设备运行.
|
||||
|
||||
|
||||
## 使用
|
||||
|
||||
- [百度网盘下载地址](https://pan.baidu.com/s/1NfumD9XjYU3OTeVmbu6vOQ?pwd=6666)
|
||||
百度网盘版本可能会更新比较频繁,github release更新随缘.
|
||||
- 默认后台管理地址 http://<运行设备IP>:16601
|
||||
默认登录账号: 666
|
||||
默认登录密码: 666
|
||||
|
||||
- 常规使用请用 -c <配置文件路径> 指定配置文件的路由方式运行 , -p <后台端口> 可以指定后台管理端口
|
||||
```bash
|
||||
#仅指定配置文件路径(如果配置文件不存在会自动创建),建议使用绝对路径
|
||||
goports -c 666.conf
|
||||
#同时指定后台端口 8899
|
||||
goports -c 666.conf -p 8899
|
||||
```
|
||||
|
||||
- 命令行直接运行转发规则,注意后台无法编辑修改命令行启动的转发规则,主要用在不带后台的精简版
|
||||
```bash
|
||||
#指定后台端口8899
|
||||
goports -p 8899 <转发规则1> <转发规则2> <转发规则3>...<<转发规则N>
|
||||
```
|
||||
|
||||
## 转发规则格式
|
||||
例子1
|
||||
tcp6@:22222to192.168.31.1:22
|
||||
监听 tcp6 类型的22222端口转发至192.168.31.1的22端口
|
||||
|
||||
例子2
|
||||
udp@:1194to192.168.31.36
|
||||
监听 udp(同时包含udp4和udp6)类型的1194端口转发至192.168.31.36的相同端口(1194)
|
||||
|
||||
例子3
|
||||
tcp6,udp6@:53to192.168.31.1:53
|
||||
监听 tcp6和udp6类型的53端口转发至192.168.31.1的53端口
|
||||
|
||||
如果你还是没法理解格式,那么可以通过web管理后台添加转发规则后直接在规则列表中一键复制自动生成的命令行配置
|
||||
需要注意的是这种方式导入的规则不包含规则中的其它参数部分,命令行模式的规则只支持通过设置启动参数共用相同的额外参数(一般使用影响不大,不需要理会)
|
||||
|
||||
## 其它启动参数
|
||||
使用后台管理的用户不需要理会这部分内容
|
||||
|
||||
-pcl <num>
|
||||
全局代理数量限制(默认128),每个端口转发对应一个代理,这个参数主要是为了防止用户误写规则,生成过多代理造成程序奔溃或占用资源过多,一般不需要动.
|
||||
|
||||
-gpmc <num>
|
||||
全局最大并发连接数(默认10240),设计这个参数是为防止由于未知原因被人恶意高并发访问搞挂运行设备或程序,请根据需求调整.
|
||||
|
||||
-smc <num>
|
||||
单个代理(端口)的最大并发数
|
||||
|
||||
-ups <num>
|
||||
UDP包最大长度,默认1500,一般使用情景不需要理会,有特殊使用情景再自行调整,比如内网小包性能测试.
|
||||
|
||||
-upm <bool>
|
||||
UDP代理性能模式开关,打开后,多核CPU环境下有利于改善UDP小包转发性能,默认已打开.
|
||||
|
||||
-udpshort <bool>
|
||||
UDP short模式,如果需要用到dns转发打开这个开关有助于节省资源.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#后台界面
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
#开发编译
|
||||
带后台版本编译
|
||||
|
||||
```bash
|
||||
go build -v -tags "adminweb nomsgpack" -ldflags="-s -w"
|
||||
```
|
||||
|
||||
不带后台版本
|
||||
```bash
|
||||
go build -v -tags "nomsgpack" -ldflags="-s -w"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 使用注意&常见问题
|
||||
- 如果在mips架构CPU下运行有问题请使用未压缩(UPS压缩版本),
|
||||
- 已知upx3.96版压缩后的程序在mipsle下可能无法运行,虽然已经更换了upx版本,暂时未再发现异常.
|
||||
|
||||
- 不同于防火墙端口转发规则,不要设置没有用上的端口,会增加内存的使用.
|
||||
|
||||
- 小米路由 ipv4 类型的80和443端口被占用,但只设置监听tcp6(ipv6)的80/443端口转发规则完全没问题.
|
||||
|
||||
- 如果需要使用白名单模式,请根据自身需求打开外网访问后台管理页面开关.
|
||||
|
||||
- 转发规则启用异常,端口转发没有生效时请登录后台查看日志.
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type BaseProxyConf struct {
|
||||
TrafficIn int64
|
||||
TrafficOut int64
|
||||
key string
|
||||
ProxyType string // tcp tcp4 tcp6 udp udp4 udp6
|
||||
|
||||
//TrafficMonitor bool //流量监控
|
||||
fromRule string
|
||||
}
|
||||
|
||||
func (p *BaseProxyConf) GetProxyType() string {
|
||||
return p.ProxyType
|
||||
}
|
||||
|
||||
func (p *BaseProxyConf) GetStatus() string {
|
||||
return p.ProxyType
|
||||
}
|
||||
|
||||
func (p *BaseProxyConf) SetFromRule(rule string) {
|
||||
p.fromRule = rule
|
||||
}
|
||||
|
||||
func (p *BaseProxyConf) FromRule() string {
|
||||
return p.fromRule
|
||||
}
|
||||
|
||||
func (p *BaseProxyConf) ReceiveDataCallback(nw int64) {
|
||||
atomic.AddInt64(&p.TrafficIn, nw)
|
||||
}
|
||||
|
||||
func (p *BaseProxyConf) SendDataCallback(nw int64) {
|
||||
atomic.AddInt64(&p.TrafficOut, nw)
|
||||
}
|
||||
|
||||
func (p *BaseProxyConf) GetTrafficIn() int64 {
|
||||
return atomic.LoadInt64(&p.TrafficIn)
|
||||
}
|
||||
|
||||
func (p *BaseProxyConf) GetTrafficOut() int64 {
|
||||
return atomic.LoadInt64(&p.TrafficOut)
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/pool"
|
||||
)
|
||||
|
||||
type Proxy interface {
|
||||
StartProxy()
|
||||
StopProxy()
|
||||
|
||||
ReceiveDataCallback(int64)
|
||||
SendDataCallback(int64)
|
||||
GetProxyType() string
|
||||
GetStatus() string
|
||||
GetListenIP() string
|
||||
GetListenPort() int
|
||||
GetKey() string
|
||||
GetCurrentConnections() int64
|
||||
SetFromRule(string)
|
||||
FromRule() string
|
||||
String() string
|
||||
GetTrafficIn() int64
|
||||
GetTrafficOut() int64
|
||||
SafeCheck(ip string) bool
|
||||
}
|
||||
|
||||
type RelayRuleOptions struct {
|
||||
UDPPackageSize int `json:"UDPPackageSize,omitempty"`
|
||||
SingleProxyMaxConnections int64 `json:"SingleProxyMaxConnections,omitempty"`
|
||||
UDPProxyPerformanceMode bool `json:"UDPProxyPerformanceMode,omitempty"`
|
||||
UDPShortMode bool `json:"UDPShortMode,omitempty"`
|
||||
SafeMode string `json:"SafeMode,omitempty"`
|
||||
}
|
||||
|
||||
// Join two io.ReadWriteCloser and do some operations.
|
||||
func (p *BaseProxyConf) relayData(targetServer io.ReadWriteCloser, client io.ReadWriteCloser) {
|
||||
var wait sync.WaitGroup
|
||||
pipe := func(to io.ReadWriteCloser, from io.ReadWriteCloser, writedataCallback func(int64)) {
|
||||
defer to.Close()
|
||||
defer from.Close()
|
||||
defer wait.Done()
|
||||
|
||||
nw, _ := p.copyBuffer(to, from, nil, nil)
|
||||
if writedataCallback != nil {
|
||||
writedataCallback(nw)
|
||||
}
|
||||
|
||||
// if p.TrafficMonitor {
|
||||
// buf := pool.GetBuf(8 * 1024 * 1024)
|
||||
// p.CopyBuffer(to, from, buf, writedataCallback)
|
||||
// pool.PutBuf(buf)
|
||||
// } else {
|
||||
// nw, _ := p.copyBuffer(to, from, nil, nil)
|
||||
// if writedataCallback != nil {
|
||||
// writedataCallback(nw)
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
wait.Add(2)
|
||||
go pipe(targetServer, client, p.ReceiveDataCallback)
|
||||
go pipe(client, targetServer, p.SendDataCallback)
|
||||
wait.Wait()
|
||||
}
|
||||
|
||||
func (p *BaseProxyConf) CopyBuffer(dst io.Writer, src io.Reader, buf []byte, writedataCallback func(int64)) (written int64, err error) {
|
||||
if buf != nil && len(buf) == 0 {
|
||||
panic("empty buffer in CopyBuffer")
|
||||
}
|
||||
return p.copyBuffer(dst, src, buf, writedataCallback)
|
||||
}
|
||||
|
||||
// copyBuffer is the actual implementation of Copy and CopyBuffer.
|
||||
// if buf is nil, one is allocated.
|
||||
func (p *BaseProxyConf) copyBuffer(dst io.Writer, src io.Reader, buf []byte, writedataCallback func(int64)) (written int64, err error) {
|
||||
if buf == nil {
|
||||
if wt, ok := src.(io.WriterTo); ok {
|
||||
return wt.WriteTo(dst)
|
||||
}
|
||||
|
||||
if rt, ok := dst.(io.ReaderFrom); ok {
|
||||
return rt.ReadFrom(src)
|
||||
}
|
||||
|
||||
size := 32 * 1024
|
||||
if l, ok := src.(*io.LimitedReader); ok && int64(size) > l.N {
|
||||
if l.N < 1 {
|
||||
size = 1
|
||||
} else {
|
||||
size = int(l.N)
|
||||
}
|
||||
}
|
||||
|
||||
buf = pool.GetBuf(8 * size)
|
||||
defer pool.PutBuf(buf)
|
||||
}
|
||||
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if nw < 0 || nr < nw {
|
||||
nw = 0
|
||||
if ew == nil {
|
||||
ew = errors.New("invalid write result")
|
||||
}
|
||||
}
|
||||
written += int64(nw)
|
||||
|
||||
if writedataCallback != nil {
|
||||
writedataCallback(int64(nw))
|
||||
}
|
||||
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
}
|
||||
if er != nil {
|
||||
if er != io.EOF {
|
||||
err = er
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return written, err
|
||||
}
|
||||
|
||||
func formatFileSize(fileSize int64) (size string) {
|
||||
switch {
|
||||
case fileSize < 1024:
|
||||
return fmt.Sprintf("%.2fB", float64(fileSize)/float64(1))
|
||||
case fileSize < (1024 * 1024):
|
||||
return fmt.Sprintf("%.2fKB", float64(fileSize)/float64(1024))
|
||||
case fileSize < (1024 * 1024 * 1024):
|
||||
return fmt.Sprintf("%.2fMB", float64(fileSize)/float64(1024*1024))
|
||||
case fileSize < (1024 * 1024 * 1024 * 1024):
|
||||
return fmt.Sprintf("%.2fGB", float64(fileSize)/float64(1024*1024*1024))
|
||||
case fileSize < (1024 * 1024 * 1024 * 1024 * 1024):
|
||||
return fmt.Sprintf("%.2fTB", float64(fileSize)/float64(1024*1024*1024*1024))
|
||||
default:
|
||||
return fmt.Sprintf("%.2fEB", float64(fileSize)/float64(1024*1024*1024*1024*1024))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func CreateProxy(proxyType, listenIP, targetHost string, balanceTargetAddressList *[]string, listenPort, targetPort int, options *RelayRuleOptions) (p Proxy, err error) {
|
||||
//key := GetProxyKey(proxyType, listenIP, listenPort)
|
||||
switch {
|
||||
case strings.HasPrefix(proxyType, "tcp"):
|
||||
{
|
||||
return CreateTCPProxy(proxyType, listenIP, targetHost, balanceTargetAddressList, listenPort, targetPort, options), nil
|
||||
}
|
||||
case strings.HasPrefix(proxyType, "udp"):
|
||||
{
|
||||
return CreateUDPProxy(proxyType, listenIP, targetHost, balanceTargetAddressList, listenPort, targetPort, options), nil
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("未支持的类型:%s", proxyType)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func GetProxyKey(proxyType, listenIP string, listenPort int) string {
|
||||
return fmt.Sprintf("%s@%s:%d", proxyType, listenIP, listenPort)
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type TCPProxy struct {
|
||||
TCPUDPProxyCommonConf
|
||||
//TcpSingleProxyMaxConns int64
|
||||
// tcpCurrentConns int64
|
||||
listenConn net.Listener
|
||||
listenConnMutex sync.Mutex
|
||||
|
||||
connMap map[string]net.Conn
|
||||
connMapMutex sync.Mutex
|
||||
}
|
||||
|
||||
func CreateTCPProxy(proxyType, listenIP, targetIP string, balanceTargetAddressList *[]string, listenPort, targetPort int, options *RelayRuleOptions) *TCPProxy {
|
||||
p := &TCPProxy{}
|
||||
p.ProxyType = proxyType
|
||||
p.listenIP = listenIP
|
||||
p.listenPort = listenPort
|
||||
p.targetIP = targetIP
|
||||
p.targetPort = targetPort
|
||||
if balanceTargetAddressList != nil {
|
||||
p.balanceTargetAddressList = *balanceTargetAddressList
|
||||
}
|
||||
|
||||
p.safeMode = options.SafeMode
|
||||
|
||||
p.SetMaxConnections(options.SingleProxyMaxConnections)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *TCPProxy) GetStatus() string {
|
||||
return fmt.Sprintf("%s\nactivity connections:[%d]", p.String(), p.GetCurrentConnections())
|
||||
}
|
||||
|
||||
// func (p *TCPProxy) CheckConns() bool {
|
||||
// if GetGlobalTCPConns() >= tcpGlobalMaxConnections || p.GetCurrentConnections() >= p.TcpSingleProxyMaxConns {
|
||||
// // if p.GetGlobalTCPConns() >= tcpGlobalMaxConnections {
|
||||
// // log.Println("")
|
||||
// // }
|
||||
// // if p.GetCurrentTCPConns() >= p.TcpSingleProxyMaxConns {
|
||||
// // log.Printf("超出单代理TCP限制")
|
||||
// // }
|
||||
// return false
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
|
||||
func (p *TCPProxy) StartProxy() {
|
||||
p.listenConnMutex.Lock()
|
||||
defer p.listenConnMutex.Unlock()
|
||||
if p.listenConn != nil {
|
||||
log.Printf("proxy %s is started", p.String())
|
||||
return
|
||||
}
|
||||
|
||||
if p.connMap == nil {
|
||||
p.connMap = make(map[string]net.Conn)
|
||||
}
|
||||
ln, err := net.Listen(p.ProxyType, p.GetListentAddress())
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "Only one usage of each socket address") {
|
||||
log.Printf("监听IP端口[%s]已被占用,proxy[%s]启动失败", p.GetListentAddress(), p.String())
|
||||
} else {
|
||||
log.Printf("Cannot start proxy[%s]:%s", p.String(), err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
p.listenConn = ln
|
||||
|
||||
log.Printf("[proxy][start][%s]", p.String())
|
||||
|
||||
go func() {
|
||||
for {
|
||||
newConn, err := ln.Accept()
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
break
|
||||
}
|
||||
log.Printf(" Cannot accept connection due to error %s", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
newConnAddr := newConn.RemoteAddr().String()
|
||||
if !p.SafeCheck(newConnAddr) {
|
||||
log.Printf("[%s]新连接 [%s]安全检查未通过", p.GetKey(), newConnAddr)
|
||||
newConn.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
//fmt.Printf("连接IP:[%s]\n", newConn.RemoteAddr().String())
|
||||
|
||||
//log.Printf("new tdp connection %s@%s [%s]===>%s", p.ProxyType, p.ListentAddress, newConn.RemoteAddr().String(), p.TargetAddress)
|
||||
if !p.CheckConnections() {
|
||||
//log.Printf("超出最大连接数限制\n")
|
||||
p.PrintConnectionsInfo()
|
||||
log.Printf("[%s]超出最大连接数限制,不再接受新连接", p.GetKey())
|
||||
newConn.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
p.connMapMutex.Lock()
|
||||
p.connMap[newConn.RemoteAddr().String()] = newConn
|
||||
p.connMapMutex.Unlock()
|
||||
|
||||
//atomic.AddInt64(&tcpconnections, 1)
|
||||
p.AddCurrentConnections(1)
|
||||
//fmt.Printf("当前全局TCP连接数:%d\n", p.GetGlobalTCPConns())
|
||||
go p.handle(newConn)
|
||||
}
|
||||
}()
|
||||
|
||||
//
|
||||
//p.test()
|
||||
|
||||
//go p.test()
|
||||
//go p.tcptest()
|
||||
|
||||
}
|
||||
|
||||
func (p *TCPProxy) StopProxy() {
|
||||
p.listenConnMutex.Lock()
|
||||
defer p.listenConnMutex.Unlock()
|
||||
defer func() {
|
||||
log.Printf("[proxy][stop][%s]", p.String())
|
||||
}()
|
||||
if p.listenConn == nil {
|
||||
return
|
||||
}
|
||||
|
||||
p.listenConn.Close()
|
||||
p.listenConn = nil
|
||||
|
||||
p.connMapMutex.Lock()
|
||||
for _, conn := range p.connMap {
|
||||
conn.Close()
|
||||
}
|
||||
p.connMap = make(map[string]net.Conn)
|
||||
p.connMapMutex.Unlock()
|
||||
}
|
||||
|
||||
func (p *TCPProxy) handle(conn net.Conn) {
|
||||
|
||||
//dialer := net.Dialer{Timeout: 10 * time.Second}
|
||||
//targetConn, err := dialer.Dial("tcp", p.TargetAddress)
|
||||
targetConn, err := net.Dial("tcp", p.GetTargetAddress())
|
||||
|
||||
defer func() {
|
||||
if targetConn != nil {
|
||||
targetConn.Close()
|
||||
}
|
||||
defer conn.Close()
|
||||
p.AddCurrentConnections(-1)
|
||||
|
||||
p.connMapMutex.Lock()
|
||||
delete(p.connMap, conn.RemoteAddr().String())
|
||||
p.connMapMutex.Unlock()
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
log.Printf("%s error:%s", p.String(), err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//targetConn.SetDeadline(time.Now().Add(time.Second * 3))
|
||||
|
||||
// targetTcpConn, ok := targetConn.(*net.TCPConn)
|
||||
// if ok {
|
||||
// targetTcpConn.SetReadBuffer(p.BufferSize * 1024 * 256 * 1024)
|
||||
// targetTcpConn.SetWriteBuffer(p.BufferSize * 1024 * 256 * 1024)
|
||||
// }
|
||||
|
||||
p.relayData(targetConn, conn)
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const TCP_DEFAULT_STREAM_BUFFERSIZE = 128
|
||||
|
||||
const DEFAULT_GLOBAL_MAX_CONNECTIONS = int64(10240)
|
||||
const TCPUDP_DEFAULT_SINGLE_PROXY_MAX_CONNECTIONS = int64(256)
|
||||
const DEFAULT_MAX_PROXY_COUNT = int64(128)
|
||||
|
||||
var globalMaxConnections = DEFAULT_GLOBAL_MAX_CONNECTIONS
|
||||
|
||||
var globalCurrentConnections int64 = 0
|
||||
var gloMaxProxyCount int64 = DEFAULT_MAX_PROXY_COUNT
|
||||
|
||||
var safeCheckFunc func(mode, ip string) bool
|
||||
|
||||
func SetSafeCheck(f func(mode, ip string) bool) {
|
||||
safeCheckFunc = f
|
||||
}
|
||||
|
||||
func SetGlobalMaxProxyCount(max int64) {
|
||||
atomic.StoreInt64(&gloMaxProxyCount, max)
|
||||
}
|
||||
|
||||
func GetGlobalMaxProxyCount() int64 {
|
||||
return atomic.LoadInt64(&gloMaxProxyCount)
|
||||
}
|
||||
|
||||
func SetGlobalMaxConnections(max int64) {
|
||||
atomic.StoreInt64(&globalMaxConnections, max)
|
||||
}
|
||||
|
||||
func GetGlobalMaxConnections() int64 {
|
||||
return atomic.LoadInt64(&globalMaxConnections)
|
||||
}
|
||||
|
||||
func GetSingleProxyMaxConnections(m *int64) int64 {
|
||||
if *m <= 0 {
|
||||
return TCPUDP_DEFAULT_SINGLE_PROXY_MAX_CONNECTIONS
|
||||
}
|
||||
return *m
|
||||
|
||||
}
|
||||
|
||||
func GetGlobalConnections() int64 {
|
||||
return atomic.LoadInt64(&globalCurrentConnections)
|
||||
}
|
||||
|
||||
func GloBalCOnnectionsAdd(add int64) int64 {
|
||||
return atomic.AddInt64(&globalCurrentConnections, add)
|
||||
}
|
||||
|
||||
type TCPUDPProxyCommonConf struct {
|
||||
CurrentConnectionsCount int64
|
||||
SingleProxyMaxConnections int64
|
||||
targetBalanceIndex int64
|
||||
BaseProxyConf
|
||||
listentAddress string
|
||||
listenIP string
|
||||
listenPort int
|
||||
targetIP string
|
||||
targetPort int
|
||||
targetAddress string
|
||||
|
||||
balanceTargetAddressList []string //均衡负载转发
|
||||
targetBalanceIndexMutex sync.Mutex
|
||||
|
||||
safeMode string
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) CheckConnections() bool {
|
||||
if p.GetCurrentConnections() >= GetGlobalMaxConnections() || p.GetCurrentConnections() >= p.SingleProxyMaxConnections {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) PrintConnectionsInfo() {
|
||||
log.Printf("[%s]当前连接数:[%d],单代理最大连接数限制[%d],全局最大连接数限制[%d]\n", p.GetKey(), p.GetCurrentConnections(), p.SingleProxyMaxConnections, GetGlobalMaxConnections())
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) SetMaxConnections(max int64) {
|
||||
if max <= 0 {
|
||||
p.SingleProxyMaxConnections = TCPUDP_DEFAULT_SINGLE_PROXY_MAX_CONNECTIONS
|
||||
} else {
|
||||
p.SingleProxyMaxConnections = max
|
||||
}
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) AddCurrentConnections(a int64) {
|
||||
atomic.AddInt64(&p.CurrentConnectionsCount, a)
|
||||
GloBalCOnnectionsAdd(a)
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) GetCurrentConnections() int64 {
|
||||
return atomic.LoadInt64(&p.CurrentConnectionsCount)
|
||||
}
|
||||
|
||||
func (p *TCPProxy) GetCurrentCon() int64 {
|
||||
return atomic.LoadInt64(&p.CurrentConnectionsCount)
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) GetListentAddress() string {
|
||||
if p.listentAddress == "" {
|
||||
if strings.Contains(p.listenIP, ":") {
|
||||
p.listentAddress = fmt.Sprintf("[%s]:%d", p.listenIP, p.listenPort)
|
||||
} else {
|
||||
p.listentAddress = fmt.Sprintf("%s:%d", p.listenIP, p.listenPort)
|
||||
}
|
||||
|
||||
}
|
||||
return p.listentAddress
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) GetKey() string {
|
||||
if p.key == "" {
|
||||
p.key = GetProxyKey(p.ProxyType, p.listenIP, p.listenPort)
|
||||
}
|
||||
return p.key
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) GetListenIP() string {
|
||||
return p.listenIP
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) GetListenPort() int {
|
||||
return p.listenPort
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) GetTargetAddress() string {
|
||||
if len(p.balanceTargetAddressList) == 0 {
|
||||
if p.targetAddress == "" {
|
||||
if strings.Contains(p.targetIP, ":") {
|
||||
p.targetAddress = fmt.Sprintf("[%s]:%d", p.targetIP, p.targetPort)
|
||||
} else {
|
||||
p.targetAddress = fmt.Sprintf("%s:%d", p.targetIP, p.targetPort)
|
||||
}
|
||||
}
|
||||
return p.targetAddress
|
||||
}
|
||||
|
||||
var address string
|
||||
addressListLength := int64(len(p.balanceTargetAddressList))
|
||||
p.targetBalanceIndexMutex.Lock()
|
||||
address = p.balanceTargetAddressList[p.targetBalanceIndex%addressListLength]
|
||||
p.targetBalanceIndex++
|
||||
p.targetBalanceIndexMutex.Unlock()
|
||||
|
||||
return address
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) String() string {
|
||||
if len(p.balanceTargetAddressList) == 0 {
|
||||
return fmt.Sprintf("%s@%s ===> %s", p.ProxyType, p.GetListentAddress(), p.GetTargetAddress())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s@%s ===> %v", p.ProxyType, p.GetListentAddress(), p.balanceTargetAddressList)
|
||||
}
|
||||
|
||||
func (p *TCPUDPProxyCommonConf) SafeCheck(remodeAddr string) bool {
|
||||
host, _, _ := net.SplitHostPort(remodeAddr)
|
||||
return safeCheckFunc(p.safeMode, host)
|
||||
}
|
|
@ -0,0 +1,433 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/pool"
|
||||
)
|
||||
|
||||
const UDP_DEFAULT_PACKAGE_SIZE = 1500
|
||||
|
||||
//测试
|
||||
|
||||
type UDPProxy struct {
|
||||
//BaseProxyConf
|
||||
TCPUDPProxyCommonConf
|
||||
|
||||
// targetAddr *net.UDPAddr
|
||||
listenConn *net.UDPConn
|
||||
listenConnMutex sync.Mutex
|
||||
|
||||
relayChs []chan *udpPackge
|
||||
replyCh chan *udpPackge
|
||||
|
||||
udpPackageSize int
|
||||
targetudpConnItemMap map[string]*udpMapItem
|
||||
targetudpConnItemMapMutex sync.RWMutex
|
||||
Upm bool //性能模式
|
||||
ShortMode bool
|
||||
}
|
||||
|
||||
type udpPackge struct {
|
||||
dataSize int
|
||||
data *[]byte
|
||||
remoteAddr *net.UDPAddr
|
||||
}
|
||||
|
||||
func CreateUDPProxy(proxyType, listenIP, targetIP string, balanceTargetAddressList *[]string, listenPort, targetPort int, options *RelayRuleOptions) *UDPProxy {
|
||||
p := &UDPProxy{}
|
||||
//p.Key = key
|
||||
p.ProxyType = proxyType
|
||||
p.listenIP = listenIP
|
||||
p.listenPort = listenPort
|
||||
p.targetIP = targetIP
|
||||
p.targetPort = targetPort
|
||||
if balanceTargetAddressList != nil {
|
||||
p.balanceTargetAddressList = *balanceTargetAddressList
|
||||
}
|
||||
|
||||
p.Upm = options.UDPProxyPerformanceMode
|
||||
p.ShortMode = options.UDPShortMode
|
||||
p.safeMode = options.SafeMode
|
||||
|
||||
p.SetUDPPacketSize(options.UDPPackageSize)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *UDPProxy) getHandlegoroutineNum() int {
|
||||
if p.Upm {
|
||||
return runtime.NumCPU()
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (p *UDPProxy) SetUDPPacketSize(size int) {
|
||||
if size <= 0 {
|
||||
p.udpPackageSize = UDP_DEFAULT_PACKAGE_SIZE
|
||||
return
|
||||
}
|
||||
if size > 65507 {
|
||||
p.udpPackageSize = 65507
|
||||
return
|
||||
}
|
||||
p.udpPackageSize = size
|
||||
}
|
||||
|
||||
func (p *UDPProxy) GetUDPPacketSize() int {
|
||||
|
||||
return p.udpPackageSize
|
||||
}
|
||||
|
||||
func (p *UDPProxy) StartProxy() {
|
||||
//p.init()
|
||||
p.listenConnMutex.Lock()
|
||||
defer p.listenConnMutex.Unlock()
|
||||
if p.listenConn != nil {
|
||||
return
|
||||
}
|
||||
|
||||
bindAddr, err := net.ResolveUDPAddr(p.ProxyType, p.GetListentAddress())
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Cannot start proxy[%s]:%s", p.GetKey(), err)
|
||||
return
|
||||
}
|
||||
|
||||
ln, err := net.ListenUDP(p.ProxyType, bindAddr)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), " bind: Only one usage of each socket address") {
|
||||
log.Printf("监听IP端口[%s]已被占用,proxy[%s]启动失败", p.GetListentAddress(), p.String())
|
||||
} else {
|
||||
log.Printf("Cannot start proxy[%s]:%s", p.String(), err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ln.SetReadBuffer(p.getHandlegoroutineNum() * 4 * 1024 * 1024)
|
||||
ln.SetWriteBuffer(p.getHandlegoroutineNum() * 4 * 1024 * 1024)
|
||||
|
||||
p.listenConn = ln
|
||||
|
||||
log.Printf("[proxy][start][%s]", p.String())
|
||||
|
||||
// p.targetAddr, err = net.ResolveUDPAddr(p.ProxyType, p.TargetAddress)
|
||||
// if err != nil {
|
||||
// log.Printf("net.ResolveUDPAddr[%s] error:%s", p.TargetAddress, err.Error())
|
||||
// return
|
||||
// }
|
||||
|
||||
//go p.test()
|
||||
|
||||
//p.relayCh = make(chan *udpPackge, 1024)
|
||||
p.relayChs = make([]chan *udpPackge, p.getHandlegoroutineNum())
|
||||
|
||||
for i := range p.relayChs {
|
||||
p.relayChs[i] = make(chan *udpPackge, 1024)
|
||||
}
|
||||
|
||||
p.replyCh = make(chan *udpPackge, 1024)
|
||||
if p.targetudpConnItemMap == nil {
|
||||
p.targetudpConnItemMap = make(map[string]*udpMapItem)
|
||||
}
|
||||
|
||||
for i := range p.relayChs {
|
||||
go p.Forwarder(i, p.relayChs[i])
|
||||
}
|
||||
|
||||
go p.replyDataToRemotAddress()
|
||||
go p.CheckTargetUDPConn()
|
||||
|
||||
for i := 0; i < p.getHandlegoroutineNum(); i++ {
|
||||
go p.ListenFunc(ln)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *UDPProxy) StopProxy() {
|
||||
p.listenConnMutex.Lock()
|
||||
defer p.listenConnMutex.Unlock()
|
||||
defer func() {
|
||||
p.targetudpConnItemMapMutex.Lock()
|
||||
for _, v := range p.targetudpConnItemMap {
|
||||
v.conn.Close()
|
||||
}
|
||||
p.targetudpConnItemMap = nil
|
||||
p.targetudpConnItemMap = make(map[string]*udpMapItem)
|
||||
p.targetudpConnItemMapMutex.Unlock()
|
||||
log.Printf("[proxy][stop][%s]", p.String())
|
||||
}()
|
||||
|
||||
if p.listenConn == nil {
|
||||
return
|
||||
}
|
||||
p.listenConn.Close()
|
||||
p.listenConn = nil
|
||||
|
||||
}
|
||||
|
||||
//ReadFromTargetOnce one clientAddr only read once,short mode eg: udp dns
|
||||
func (p *UDPProxy) ReadFromTargetOnce() bool {
|
||||
if p.targetPort == 53 || p.ShortMode {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *UDPProxy) GetStatus() string {
|
||||
return fmt.Sprintf("%s max packet size[%d]", p.String(), p.GetUDPPacketSize())
|
||||
}
|
||||
|
||||
func (p *UDPProxy) ListenFunc(ln *net.UDPConn) {
|
||||
|
||||
inDatabuf := pool.GetBuf(p.GetUDPPacketSize())
|
||||
defer pool.PutBuf(inDatabuf)
|
||||
i := uint64(0)
|
||||
for {
|
||||
if p.listenConn == nil {
|
||||
break
|
||||
}
|
||||
|
||||
inDatabufSize, clientAddr, err := ln.ReadFromUDP(inDatabuf)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), `smaller than the datagram`) {
|
||||
log.Printf("%s ReadFromUDP error,the udp packet size is smaller than the datagram,please use flag '-ups xxx'set udp packet size \n", p.String())
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "use of closed network connection") {
|
||||
log.Printf(" %s ReadFromUDP error:\n%s \n", p.String(), err.Error())
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
//fmt.Printf("inDatabufSize:%d\n", inDatabufSize)
|
||||
|
||||
newConnAddr := clientAddr.String()
|
||||
if !p.SafeCheck(newConnAddr) {
|
||||
log.Printf("[%s]新连接 [%s]安全检查未通过", p.GetKey(), newConnAddr)
|
||||
continue
|
||||
}
|
||||
|
||||
// var newConOk bool
|
||||
// p.targetudpConnItemMapMutex.RLock()
|
||||
// _, newConOk = p.targetudpConnItemMap[clientAddr.String()]
|
||||
// p.targetudpConnItemMapMutex.RUnlock()
|
||||
// if !newConOk {
|
||||
// log.Printf("new udp connection %s@%s [%s]===>%s", p.ProxyType, p.ListentAddress, clientAddr.String(), p.TargetAddress)
|
||||
// }
|
||||
//log.Printf("new udp connection %s@%s [%s]===>%s", p.ProxyType, p.ListentAddress, clientAddr.String(), p.TargetAddress)
|
||||
|
||||
data := pool.GetBuf(inDatabufSize)
|
||||
copy(data, inDatabuf[:inDatabufSize])
|
||||
|
||||
inUdpPack := udpPackge{dataSize: inDatabufSize, data: &data, remoteAddr: clientAddr}
|
||||
//p.relayCh <- &inUdpPack
|
||||
|
||||
p.relayChs[i%uint64(p.getHandlegoroutineNum())] <- &inUdpPack
|
||||
i++
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
type udpMapItem struct {
|
||||
conn *net.UDPConn
|
||||
lastTime time.Time
|
||||
}
|
||||
|
||||
func (p *UDPProxy) Forwarder(kk int, replych chan *udpPackge) {
|
||||
|
||||
// read from targetAddr and write clientAddr
|
||||
readFromtargetAddrFunc := func(raddr *net.UDPAddr, udpItemKey string, tgConn *net.UDPConn) {
|
||||
readBuffer := pool.GetBuf(p.GetUDPPacketSize())
|
||||
defer func() {
|
||||
pool.PutBuf(readBuffer)
|
||||
if p.ReadFromTargetOnce() {
|
||||
tgConn.Close()
|
||||
}
|
||||
p.AddCurrentConnections(-1)
|
||||
}()
|
||||
|
||||
var targetConn *net.UDPConn
|
||||
var udpItem *udpMapItem
|
||||
var ok bool
|
||||
p.AddCurrentConnections(1)
|
||||
for {
|
||||
targetConn = nil
|
||||
udpItem = nil
|
||||
|
||||
timeout := 1200 * time.Millisecond
|
||||
if p.ReadFromTargetOnce() {
|
||||
timeout = 30 * time.Millisecond
|
||||
}
|
||||
|
||||
if p.ReadFromTargetOnce() {
|
||||
targetConn = tgConn
|
||||
} else {
|
||||
p.targetudpConnItemMapMutex.RLock()
|
||||
udpItem, ok = p.targetudpConnItemMap[udpItemKey]
|
||||
if !ok {
|
||||
p.targetudpConnItemMapMutex.RUnlock()
|
||||
return
|
||||
}
|
||||
p.targetudpConnItemMapMutex.RUnlock()
|
||||
targetConn = udpItem.conn
|
||||
}
|
||||
|
||||
targetConn.SetReadDeadline(time.Now().Add(timeout))
|
||||
n, _, err := targetConn.ReadFromUDP(readBuffer)
|
||||
if err != nil {
|
||||
errStr := err.Error()
|
||||
|
||||
if strings.Contains(errStr, `i/o timeout`) && !p.ReadFromTargetOnce() {
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(errStr, `use of closed network connection`) {
|
||||
log.Printf("targetConn ReadFromUDP error:%s", err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data := pool.GetBuf(n)
|
||||
copy(data, readBuffer[:n])
|
||||
udpMsg := udpPackge{dataSize: n, data: &data, remoteAddr: raddr}
|
||||
|
||||
if err = errors.PanicToError(func() {
|
||||
select {
|
||||
case p.replyCh <- &udpMsg:
|
||||
default:
|
||||
}
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !p.ReadFromTargetOnce() {
|
||||
p.targetudpConnItemMapMutex.Lock()
|
||||
udpItem, ok := p.targetudpConnItemMap[udpItemKey]
|
||||
if ok {
|
||||
udpItem.lastTime = time.Now()
|
||||
}
|
||||
p.targetudpConnItemMapMutex.Unlock()
|
||||
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if p.ReadFromTargetOnce() {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
var targetConn *net.UDPConn
|
||||
|
||||
// read from readCh
|
||||
for udpMsg := range replych {
|
||||
err = nil
|
||||
targetConn = nil
|
||||
|
||||
//if p.ReadFromTargetOnce()
|
||||
p.targetudpConnItemMapMutex.Lock()
|
||||
udpConnItem, ok := p.targetudpConnItemMap[udpMsg.remoteAddr.String()]
|
||||
if !ok || p.ReadFromTargetOnce() { //??
|
||||
|
||||
tgAddr, err := net.ResolveUDPAddr(p.ProxyType, p.GetTargetAddress())
|
||||
if err != nil {
|
||||
log.Printf("net.ResolveUDPAddr[%s] error:%s", p.GetTargetAddress(), err.Error())
|
||||
p.targetudpConnItemMapMutex.Unlock()
|
||||
pool.PutBuf(*udpMsg.data)
|
||||
continue
|
||||
}
|
||||
|
||||
targetConn, err = net.DialUDP("udp", nil, tgAddr)
|
||||
|
||||
if err != nil {
|
||||
p.targetudpConnItemMapMutex.Unlock()
|
||||
pool.PutBuf(*udpMsg.data)
|
||||
continue
|
||||
}
|
||||
targetConn.SetWriteBuffer(4 * 1024 * 1024)
|
||||
targetConn.SetReadBuffer(4 * 1024 * 1024)
|
||||
|
||||
if !ok && !p.ReadFromTargetOnce() {
|
||||
p.AddCurrentConnections(1)
|
||||
newItem := udpMapItem{conn: targetConn, lastTime: time.Now()}
|
||||
p.targetudpConnItemMap[udpMsg.remoteAddr.String()] = &newItem
|
||||
udpConnItem = &newItem
|
||||
}
|
||||
|
||||
} else {
|
||||
udpConnItem.lastTime = time.Now()
|
||||
targetConn = udpConnItem.conn
|
||||
}
|
||||
p.targetudpConnItemMapMutex.Unlock()
|
||||
|
||||
p.ReceiveDataCallback(int64(udpMsg.dataSize))
|
||||
_, err = targetConn.Write(*udpMsg.data)
|
||||
if err != nil {
|
||||
targetConn.Close()
|
||||
}
|
||||
pool.PutBuf(*udpMsg.data)
|
||||
|
||||
if !ok || p.ReadFromTargetOnce() {
|
||||
go readFromtargetAddrFunc(udpMsg.remoteAddr, udpMsg.remoteAddr.String(), targetConn)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *UDPProxy) replyDataToRemotAddress() {
|
||||
for msg := range p.replyCh {
|
||||
_, err := p.listenConn.WriteToUDP(*(msg.data), msg.remoteAddr)
|
||||
pool.PutBuf(*msg.data)
|
||||
if err != nil {
|
||||
log.Printf("udpConn.WriteToUDP error:%s", err.Error())
|
||||
continue
|
||||
}
|
||||
p.SendDataCallback(int64(msg.dataSize))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *UDPProxy) CheckTargetUDPConn() {
|
||||
for {
|
||||
<-time.After(time.Second * 1)
|
||||
// connCout := atomic.LoadInt64(&p.targetudpConnCount)
|
||||
// if connCout <= 0 {
|
||||
// continue
|
||||
// }
|
||||
if p.GetCurrentConnections() <= 0 {
|
||||
continue
|
||||
}
|
||||
p.targetudpConnItemMapMutex.Lock()
|
||||
|
||||
var deleteList []string
|
||||
|
||||
for k, v := range p.targetudpConnItemMap {
|
||||
if time.Since(v.lastTime) >= 30*time.Second {
|
||||
v.conn.Close()
|
||||
deleteList = append(deleteList, k)
|
||||
}
|
||||
}
|
||||
|
||||
//fmt.Printf("map:%v\t deleteList:%v\n", p.targetudpConnItemMap, deleteList)
|
||||
|
||||
for i := range deleteList {
|
||||
delete(p.targetudpConnItemMap, deleteList[i])
|
||||
//log.Printf("删除targetudpConnItemMap [%s]\n", deleteList[i])
|
||||
//atomic.AddInt64(&p.targetudpConnCount, -1)
|
||||
p.AddCurrentConnections(-1)
|
||||
}
|
||||
|
||||
p.targetudpConnItemMapMutex.Unlock()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
cd ./web/goports-adminviews
|
||||
npm run build
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"AdminWebListen": ":16601",
|
||||
"ProxyCountLimit": 128,
|
||||
"GlobalMaxConnections": 10240,
|
||||
"RelayRuleList": [
|
||||
{
|
||||
"Name": "FTP",
|
||||
"Configurestr": "tcp@:20000-20010,20021to192.168.31.180",
|
||||
"Enable": true,
|
||||
"Options": {
|
||||
"UDPPackageSize": 1500,
|
||||
"SingleProxyMaxConnections": 256
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Socket5 均衡负载",
|
||||
"Configurestr": "tcp@:6666to192.168.31.180:7890,192.168.31.148:7890",
|
||||
"Enable": true,
|
||||
"Options": {
|
||||
"UDPPackageSize": 1500,
|
||||
"SingleProxyMaxConnections": 256
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "FileBrowser",
|
||||
"Configurestr": "tcp@:28888to192.168.31.180",
|
||||
"Enable": true,
|
||||
"Options": {
|
||||
"UDPPackageSize": 1500,
|
||||
"SingleProxyMaxConnections": 256
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "DNS转发",
|
||||
"Configurestr": "tcp,udp@:53to192.168.31.1",
|
||||
"Enable": true,
|
||||
"Options": {
|
||||
"UDPPackageSize": 1496,
|
||||
"SingleProxyMaxConnections": 256,
|
||||
"UDPProxyPerformanceMode": true,
|
||||
"UDPShortMode": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
type BlackListItem WhiteListItem
|
||||
|
||||
type BlackListConfigure struct {
|
||||
BlackList []BlackListItem `json:"BlackList"` //黑名单列表
|
||||
}
|
||||
|
||||
func GetBlackList() []BlackListItem {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
|
||||
BlackListFlush(false)
|
||||
|
||||
var resList []BlackListItem
|
||||
if programConfigure == nil {
|
||||
return resList
|
||||
}
|
||||
for i := range programConfigure.BlackListConfigure.BlackList {
|
||||
resList = append(resList, programConfigure.BlackListConfigure.BlackList[i])
|
||||
}
|
||||
return resList
|
||||
}
|
||||
|
||||
func BlackListAdd(ip string, activelifeDuration int32) (string, error) {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
|
||||
if activelifeDuration <= 0 {
|
||||
activelifeDuration = 666666
|
||||
}
|
||||
|
||||
EffectiveTimeStr := time.Now().Add(time.Hour * time.Duration(activelifeDuration)).Format("2006-01-02 15:04:05")
|
||||
|
||||
for i, ipr := range programConfigure.BlackListConfigure.BlackList {
|
||||
if ipr.IP == ip {
|
||||
programConfigure.BlackListConfigure.BlackList[i].EffectiveTime = EffectiveTimeStr
|
||||
return EffectiveTimeStr, Save()
|
||||
}
|
||||
}
|
||||
item := BlackListItem{IP: ip, EffectiveTime: EffectiveTimeStr}
|
||||
programConfigure.BlackListConfigure.BlackList = append(programConfigure.BlackListConfigure.BlackList, item)
|
||||
return EffectiveTimeStr, Save()
|
||||
}
|
||||
|
||||
func BlackListDelete(ip string) error {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
|
||||
removeCount := 0
|
||||
CONTINUECHECK:
|
||||
removeIndex := -1
|
||||
|
||||
for i, ipr := range programConfigure.BlackListConfigure.BlackList {
|
||||
if ipr.IP == ip {
|
||||
removeIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if removeIndex >= 0 {
|
||||
removeCount++
|
||||
programConfigure.BlackListConfigure.BlackList = DeleteBlackListlice(programConfigure.BlackListConfigure.BlackList, removeIndex)
|
||||
goto CONTINUECHECK
|
||||
}
|
||||
if removeCount == 0 {
|
||||
return nil
|
||||
}
|
||||
return Save()
|
||||
}
|
||||
|
||||
func BlackListFlush(lock bool) error {
|
||||
if lock {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
}
|
||||
|
||||
removeCount := 0
|
||||
|
||||
CONTINUECHECK:
|
||||
removeIndex := -1
|
||||
|
||||
for i, ipr := range programConfigure.BlackListConfigure.BlackList {
|
||||
ipat, err := time.ParseInLocation("2006-01-02 15:04:05", ipr.EffectiveTime, time.Local)
|
||||
if err != nil { //有效时间格式有误,当失效处理
|
||||
removeIndex = i
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if time.Since(ipat) > 0 {
|
||||
removeIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if removeIndex >= 0 {
|
||||
removeCount++
|
||||
programConfigure.BlackListConfigure.BlackList = DeleteBlackListlice(programConfigure.BlackListConfigure.BlackList, removeIndex)
|
||||
goto CONTINUECHECK
|
||||
}
|
||||
|
||||
if removeCount == 0 {
|
||||
return nil
|
||||
}
|
||||
return Save()
|
||||
}
|
||||
|
||||
func DeleteBlackListlice(a []BlackListItem, deleteIndex int) []BlackListItem {
|
||||
j := 0
|
||||
for i := range a {
|
||||
if i != deleteIndex {
|
||||
a[j] = a[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return a[:j]
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ljymc/goports/base"
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/fileutils"
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/stringsp"
|
||||
)
|
||||
|
||||
const defaultAdminAccount = "666"
|
||||
const defaultAdminPassword = "666"
|
||||
const defaultAdminListenPort = 16601
|
||||
|
||||
var runMode = "prod"
|
||||
var version = "0.0.0"
|
||||
|
||||
func SetVersion(v string) {
|
||||
version = v
|
||||
}
|
||||
|
||||
func GetVersion() string {
|
||||
return version
|
||||
}
|
||||
|
||||
var loginRandomkey = ""
|
||||
|
||||
func GetLoginRandomKey() string {
|
||||
return loginRandomkey
|
||||
}
|
||||
|
||||
func FlushLoginRandomKey() {
|
||||
loginRandomkey = stringsp.GetRandomString(16)
|
||||
}
|
||||
|
||||
type ConfigureRelayRule struct {
|
||||
Name string `json:"Name"`
|
||||
Configurestr string `json:"Configurestr"`
|
||||
Enable bool `json:"Enable"`
|
||||
Options base.RelayRuleOptions `json:"Options"`
|
||||
}
|
||||
|
||||
type BaseConfigure struct {
|
||||
AdminWebListenPort int `json:"AdminWebListenPort"` //管理后台端口
|
||||
ProxyCountLimit int64 `json:"ProxyCountLimit"` //全局代理数量限制
|
||||
AdminAccount string `json:"AdminAccount"` //登录账号
|
||||
AdminPassword string `json:"AdminPassword"` //登录密码
|
||||
AllowInternetaccess bool `json:"AllowInternetaccess"` //允许外网访问
|
||||
GlobalMaxConnections int64 `json:"GlobalMaxConnections"` //全局最大并发连接数
|
||||
}
|
||||
|
||||
type ProgramConfigure struct {
|
||||
BaseConfigure BaseConfigure `json:"BaseConfigure"`
|
||||
RelayRuleList []ConfigureRelayRule `json:"RelayRuleList"`
|
||||
WhiteListConfigure WhiteListConfigure `json:"WhiteListConfigure"`
|
||||
BlackListConfigure BlackListConfigure `json:"BlackListConfigure"`
|
||||
}
|
||||
|
||||
var programConfigureMutex sync.RWMutex
|
||||
var programConfigure *ProgramConfigure
|
||||
var configurePath string
|
||||
|
||||
//var readConfigureFileOnce sync.Once
|
||||
var checkConfigureFileOnce sync.Once
|
||||
var configureFileSign int8 = -1
|
||||
|
||||
func GetAuthAccount() map[string]string {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
accountInfo := make(map[string]string)
|
||||
accountInfo[programConfigure.BaseConfigure.AdminAccount] = programConfigure.BaseConfigure.AdminPassword
|
||||
return accountInfo
|
||||
}
|
||||
|
||||
func SetConfigRuleList(ruleList *[]ConfigureRelayRule) {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
programConfigure.RelayRuleList = *ruleList
|
||||
}
|
||||
|
||||
func GetRunMode() string {
|
||||
return runMode
|
||||
}
|
||||
|
||||
func SetRunMode(mode string) {
|
||||
runMode = mode
|
||||
}
|
||||
|
||||
func GetConfig() *ProgramConfigure {
|
||||
return programConfigure
|
||||
}
|
||||
|
||||
func GetBaseConfigure() BaseConfigure {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
baseConf := programConfigure.BaseConfigure
|
||||
return baseConf
|
||||
}
|
||||
|
||||
//保存基础配置
|
||||
func SetBaseConfigure(conf *BaseConfigure) error {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
programConfigure.BaseConfigure = *conf
|
||||
|
||||
base.SetGlobalMaxConnections(conf.GlobalMaxConnections)
|
||||
base.SetGlobalMaxProxyCount(conf.ProxyCountLimit)
|
||||
|
||||
return Save()
|
||||
}
|
||||
|
||||
func Read(filePath string) (err error) {
|
||||
|
||||
pc, err := readProgramConfigure(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.GlobalMaxConnections <= 0 {
|
||||
pc.BaseConfigure.GlobalMaxConnections = base.DEFAULT_GLOBAL_MAX_CONNECTIONS
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.ProxyCountLimit <= 0 {
|
||||
pc.BaseConfigure.ProxyCountLimit = base.DEFAULT_MAX_PROXY_COUNT
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.AdminWebListenPort <= 0 {
|
||||
pc.BaseConfigure.AdminWebListenPort = 16601
|
||||
}
|
||||
|
||||
programConfigure = pc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadDefault(proxyCountLimit int64,
|
||||
adminWebListenPort int,
|
||||
globalMaxConnections int64) {
|
||||
|
||||
programConfigure = loadDefaultConfigure(proxyCountLimit, adminWebListenPort, globalMaxConnections)
|
||||
}
|
||||
|
||||
func Save() (err error) {
|
||||
//log.Printf("Save配置\n")
|
||||
if configureFileSign == 0 {
|
||||
return fmt.Errorf("配置文件不存在,不作保存")
|
||||
}
|
||||
defer func() {
|
||||
checkConfigureFileOnce.Do(func() {
|
||||
if err == nil {
|
||||
configureFileSign = 1
|
||||
} else {
|
||||
configureFileSign = 0
|
||||
}
|
||||
})
|
||||
|
||||
}()
|
||||
|
||||
err = saveProgramConfig(programConfigure, configurePath)
|
||||
return
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
func readProgramConfigure(filePath string) (conf *ProgramConfigure, err error) {
|
||||
if filePath == "" {
|
||||
return nil, fmt.Errorf("未指定配置文件路径")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(filePath, "/") {
|
||||
filePath = fmt.Sprintf("%s/%s", fileutils.GetCurrentDirectory(), filePath)
|
||||
}
|
||||
|
||||
configurePath = filePath
|
||||
|
||||
//fmt.Printf("filePath:%s\n", configurePath)
|
||||
|
||||
fileContent, err := fileutils.ReadTextFromFile(configurePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取配置文件出错:%s", err.Error())
|
||||
}
|
||||
var pc ProgramConfigure
|
||||
|
||||
err = json.Unmarshal([]byte(fileContent), &pc)
|
||||
if err != nil {
|
||||
log.Fatalf("解析配置文件出错:%s", err.Error())
|
||||
return nil, fmt.Errorf("解析配置文件出错:%s", err.Error())
|
||||
}
|
||||
|
||||
return &pc, nil
|
||||
}
|
||||
|
||||
func loadDefaultConfigure(
|
||||
proxyCountLimit int64,
|
||||
adminWebListenPort int,
|
||||
globalMaxConnections int64) *ProgramConfigure {
|
||||
|
||||
baseConfigure := BaseConfigure{AdminWebListenPort: adminWebListenPort,
|
||||
GlobalMaxConnections: globalMaxConnections,
|
||||
AdminAccount: defaultAdminAccount,
|
||||
AdminPassword: defaultAdminPassword,
|
||||
ProxyCountLimit: proxyCountLimit,
|
||||
AllowInternetaccess: false}
|
||||
|
||||
whiteListConfigure := WhiteListConfigure{BaseConfigure: WhiteListBaseConfigure{ActivelifeDuration: 36, BasicAccount: defaultAdminAccount, BasicPassword: defaultAdminPassword}}
|
||||
|
||||
var pc ProgramConfigure
|
||||
pc.BaseConfigure = baseConfigure
|
||||
pc.WhiteListConfigure = whiteListConfigure
|
||||
|
||||
if pc.BaseConfigure.GlobalMaxConnections <= 0 {
|
||||
pc.BaseConfigure.GlobalMaxConnections = base.DEFAULT_GLOBAL_MAX_CONNECTIONS
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.ProxyCountLimit <= 0 {
|
||||
pc.BaseConfigure.ProxyCountLimit = base.DEFAULT_MAX_PROXY_COUNT
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.AdminWebListenPort <= 0 {
|
||||
pc.BaseConfigure.AdminWebListenPort = defaultAdminListenPort
|
||||
}
|
||||
|
||||
return &pc
|
||||
}
|
||||
|
||||
func saveProgramConfig(programConfigure *ProgramConfigure, filePath string) error {
|
||||
resBytes, err := json.MarshalIndent(*programConfigure, "", "\t")
|
||||
if err != nil {
|
||||
return fmt.Errorf("json.Marshal:%s", err.Error())
|
||||
}
|
||||
return fileutils.SaveTextToFile(string(resBytes), filePath)
|
||||
}
|
||||
|
||||
func CheckTCPPortAvalid(port int) bool {
|
||||
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
ln.Close()
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
func SafeCheck(mode, ip string) bool {
|
||||
switch mode {
|
||||
case "whitelist":
|
||||
return whiteListCheck(ip)
|
||||
case "blacklist":
|
||||
return blackListCheck(ip)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func whiteListCheck(ip string) bool {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
if programConfigure == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, item := range programConfigure.WhiteListConfigure.WhiteList {
|
||||
if item.IP != ip {
|
||||
continue
|
||||
}
|
||||
itemEffectiveTime, err := time.ParseInLocation("2006-01-02 15:04:05", item.EffectiveTime, time.Local)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if time.Since(itemEffectiveTime) < 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func blackListCheck(ip string) bool {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
if programConfigure == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, item := range programConfigure.BlackListConfigure.BlackList {
|
||||
if item.IP != ip {
|
||||
continue
|
||||
}
|
||||
itemEffectiveTime, err := time.ParseInLocation("2006-01-02 15:04:05", item.EffectiveTime, time.Local)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if time.Since(itemEffectiveTime) < 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
type WhiteListConfigure struct {
|
||||
BaseConfigure WhiteListBaseConfigure `json:"BaseConfigure`
|
||||
WhiteList []WhiteListItem `json:"WhiteList"` //白名单列表
|
||||
}
|
||||
|
||||
type WhiteListItem struct {
|
||||
IP string `json:"IP"`
|
||||
EffectiveTime string `json:"Effectivetime"` //有效时间
|
||||
}
|
||||
|
||||
type WhiteListBaseConfigure struct {
|
||||
URL string `json:"URL"`
|
||||
ActivelifeDuration int32 `json:"ActivelifeDuration"` //有效期限,小时
|
||||
BasicAccount string `json:"BasicAccount"`
|
||||
BasicPassword string `json:"BasicPassword"`
|
||||
}
|
||||
|
||||
func GetWhiteListBaseConfigure() WhiteListBaseConfigure {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
return programConfigure.WhiteListConfigure.BaseConfigure
|
||||
}
|
||||
|
||||
func SetWhiteListBaseConfigure(activelifeDuration int32, url, account, password string) error {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
programConfigure.WhiteListConfigure.BaseConfigure.URL = url
|
||||
programConfigure.WhiteListConfigure.BaseConfigure.ActivelifeDuration = activelifeDuration
|
||||
programConfigure.WhiteListConfigure.BaseConfigure.BasicAccount = account
|
||||
programConfigure.WhiteListConfigure.BaseConfigure.BasicPassword = password
|
||||
return Save()
|
||||
}
|
||||
|
||||
func GetWhiteList() []WhiteListItem {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
|
||||
WhiteListFlush(false)
|
||||
|
||||
var resList []WhiteListItem
|
||||
if programConfigure == nil {
|
||||
return resList
|
||||
}
|
||||
for i := range programConfigure.WhiteListConfigure.WhiteList {
|
||||
resList = append(resList, programConfigure.WhiteListConfigure.WhiteList[i])
|
||||
}
|
||||
return resList
|
||||
}
|
||||
|
||||
func WhiteListAdd(ip string, activelifeDuration int32) (string, error) {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
|
||||
if activelifeDuration <= 0 {
|
||||
activelifeDuration = programConfigure.WhiteListConfigure.BaseConfigure.ActivelifeDuration
|
||||
}
|
||||
|
||||
EffectiveTimeStr := time.Now().Add(time.Hour * time.Duration(activelifeDuration)).Format("2006-01-02 15:04:05")
|
||||
|
||||
for i, ipr := range programConfigure.WhiteListConfigure.WhiteList {
|
||||
if ipr.IP == ip {
|
||||
programConfigure.WhiteListConfigure.WhiteList[i].EffectiveTime = EffectiveTimeStr
|
||||
return EffectiveTimeStr, Save()
|
||||
}
|
||||
}
|
||||
item := WhiteListItem{IP: ip, EffectiveTime: EffectiveTimeStr}
|
||||
programConfigure.WhiteListConfigure.WhiteList = append(programConfigure.WhiteListConfigure.WhiteList, item)
|
||||
return EffectiveTimeStr, Save()
|
||||
}
|
||||
|
||||
func WhiteListDelete(ip string) error {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
|
||||
removeCount := 0
|
||||
CONTINUECHECK:
|
||||
removeIndex := -1
|
||||
|
||||
for i, ipr := range programConfigure.WhiteListConfigure.WhiteList {
|
||||
if ipr.IP == ip {
|
||||
removeIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if removeIndex >= 0 {
|
||||
removeCount++
|
||||
programConfigure.WhiteListConfigure.WhiteList = DeleteWhiteListlice(programConfigure.WhiteListConfigure.WhiteList, removeIndex)
|
||||
goto CONTINUECHECK
|
||||
}
|
||||
if removeCount == 0 {
|
||||
return nil
|
||||
}
|
||||
return Save()
|
||||
}
|
||||
|
||||
func WhiteListFlush(lock bool) error {
|
||||
if lock {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
}
|
||||
|
||||
removeCount := 0
|
||||
|
||||
CONTINUECHECK:
|
||||
removeIndex := -1
|
||||
|
||||
for i, ipr := range programConfigure.WhiteListConfigure.WhiteList {
|
||||
ipat, err := time.ParseInLocation("2006-01-02 15:04:05", ipr.EffectiveTime, time.Local)
|
||||
if err != nil { //有效时间格式有误,当失效处理
|
||||
removeIndex = i
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if time.Since(ipat) > 0 {
|
||||
removeIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if removeIndex >= 0 {
|
||||
removeCount++
|
||||
programConfigure.WhiteListConfigure.WhiteList = DeleteWhiteListlice(programConfigure.WhiteListConfigure.WhiteList, removeIndex)
|
||||
goto CONTINUECHECK
|
||||
}
|
||||
|
||||
if removeCount == 0 {
|
||||
return nil
|
||||
}
|
||||
return Save()
|
||||
}
|
||||
|
||||
func DeleteWhiteListlice(a []WhiteListItem, deleteIndex int) []WhiteListItem {
|
||||
j := 0
|
||||
for i := range a {
|
||||
if i != deleteIndex {
|
||||
a[j] = a[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return a[:j]
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/recoverutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
defer func() {
|
||||
recoverErr := recover()
|
||||
if recoverErr == nil {
|
||||
return
|
||||
}
|
||||
panicFile := fmt.Sprintf("闪退.log")
|
||||
recoverutil.RecoverHandler(recoverErr, true, true, panicFile)
|
||||
}()
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
module github.com/ljymc/goports
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/fatedier/golib v0.2.0
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/gzip v0.0.5
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/shirou/gopsutil/v3 v3.22.5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/ljymc/toolbox/fileutils => ../toolbox/fileutils/
|
|
@ -0,0 +1,139 @@
|
|||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatedier/golib v0.2.0 h1:8BxiUcjleBlXBYlTNUllD8KZZHaFU/NP/vP0Yu1Fkpg=
|
||||
github.com/fatedier/golib v0.2.0/go.mod h1:e2NPpBGUFsHDjXrfP1B5aK3S0+yUeVxgqfc3go3KNj0=
|
||||
github.com/gin-contrib/gzip v0.0.5 h1:mhnVU32YnnBh2LPH2iqRqsA/eR7SAqRaD388jL2s/j0=
|
||||
github.com/gin-contrib/gzip v0.0.5/go.mod h1:OPIK6HR0Um2vNmBUTlayD7qle4yVVRZT0PyhdUigrKk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/shirou/gopsutil/v3 v3.22.5 h1:atX36I/IXgFiB81687vSiBI5zrMsxcIBkP9cQMJQoJA=
|
||||
github.com/shirou/gopsutil/v3 v3.22.5/go.mod h1:so9G9VzeHt/hsd0YwqprnjHnfARAUktauykSbr+y2gA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca h1:xTaFYiPROfpPhqrfTIDXj0ri1SpfueYT951s4bAuDO8=
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,144 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/ljymc/goports/base"
|
||||
"github.com/ljymc/goports/config"
|
||||
"github.com/ljymc/goports/rule"
|
||||
)
|
||||
|
||||
var (
|
||||
listenPort = flag.Int("p", 16601, "http Admin Web listen port ")
|
||||
pcl = flag.Int64("pcl", -1, "global proxy count limit")
|
||||
gpmc = flag.Int64("gpmc", -1, "global proxy max connections,default(1024)")
|
||||
udpPackageSize = flag.Int("ups", base.UDP_DEFAULT_PACKAGE_SIZE, "udp package max size")
|
||||
smc = flag.Int64("smc", base.TCPUDP_DEFAULT_SINGLE_PROXY_MAX_CONNECTIONS, "signle proxy max connections,default(128)")
|
||||
upm = flag.Bool("upm", true, "udp proxy Performance Mode open")
|
||||
udpshort = flag.Bool("udpshort", false, "udp short mode,eg dns")
|
||||
configureFileURL = flag.String("c", "", "configure file url")
|
||||
)
|
||||
|
||||
var (
|
||||
runMode = "prod"
|
||||
version = "unknown"
|
||||
commit = "none"
|
||||
date = "unknown"
|
||||
)
|
||||
|
||||
var runTime time.Time
|
||||
|
||||
// go build && ./goports 127.0.0.199:7000,443to192.168.31.1:80,443 20100-20110to192.168.31.1:20100-20110
|
||||
func main() {
|
||||
|
||||
flag.Parse()
|
||||
|
||||
err := config.Read(*configureFileURL)
|
||||
if err != nil {
|
||||
log.Printf("%s", err.Error())
|
||||
log.Printf("载入默认配置以及命令行设定的参数")
|
||||
config.LoadDefault(*pcl, *listenPort, *gpmc)
|
||||
if len(*configureFileURL) > 0 {
|
||||
err = config.Save()
|
||||
if err != nil {
|
||||
log.Printf("保存配置到%s出错:%s", *configureFileURL, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gcf := config.GetConfig()
|
||||
|
||||
//fmt.Printf("*gcf:%v\n", *gcf)
|
||||
|
||||
base.SetSafeCheck(config.SafeCheck)
|
||||
base.SetGlobalMaxConnections(gcf.BaseConfigure.GlobalMaxConnections)
|
||||
base.SetGlobalMaxProxyCount(gcf.BaseConfigure.ProxyCountLimit)
|
||||
config.SetRunMode(runMode)
|
||||
config.SetVersion(version)
|
||||
log.Printf("RunMode:%s\n", runMode)
|
||||
log.Printf("version:%s\tcommit %s, built at %s\n", version, commit, date)
|
||||
RunAdminWeb(gcf.BaseConfigure.AdminWebListenPort)
|
||||
|
||||
runTime = time.Now()
|
||||
|
||||
// if *upm {
|
||||
// log.Printf("udp proxy Performance Mode open ")
|
||||
// }
|
||||
|
||||
//log.Printf("Gobal proxy max connections:[%d] single proxy max connections:[%d]\n", base.GetGlobalMaxConnections(), base.GetSingleProxyMaxConnections(smc))
|
||||
|
||||
if len(flag.Args()) > 0 {
|
||||
LoadRuleListFromCMD(flag.Args())
|
||||
}
|
||||
|
||||
LoadRuleFromConfigFile(gcf)
|
||||
|
||||
rule.EnableAllRelayRule() //开启规则
|
||||
//initProxyList()
|
||||
|
||||
//*****************
|
||||
// time.Sleep(time.Microsecond * 50)
|
||||
// cruuentPath, _ := fileutils.GetCurrentDirectory()
|
||||
|
||||
// panicFile := fmt.Sprintf("%s/relayport_panic.log", cruuentPath)
|
||||
// fileutils.PanicRedirect(panicFile)
|
||||
//*****************
|
||||
|
||||
//main goroutine wait
|
||||
sigs := make(chan os.Signal, 1)
|
||||
exit := make(chan bool, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-sigs
|
||||
exit <- true
|
||||
}()
|
||||
<-exit
|
||||
}
|
||||
|
||||
func LoadRuleListFromCMD(args []string) {
|
||||
options := base.RelayRuleOptions{UDPPackageSize: *udpPackageSize,
|
||||
SingleProxyMaxConnections: *smc,
|
||||
UDPProxyPerformanceMode: *upm,
|
||||
UDPShortMode: *udpshort}
|
||||
|
||||
relayRules, err := rule.GetRelayRulesFromCMD(flag.Args(), &options)
|
||||
if err != nil {
|
||||
log.Print("config.GetRelayRulesFromCMD err:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, e := rule.AddRuleToGlobalRuleList(false, (*relayRules)...)
|
||||
if e != nil {
|
||||
log.Printf("%s\n", e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func LoadRuleFromConfigFile(pc *config.ProgramConfigure) {
|
||||
if pc == nil {
|
||||
return
|
||||
}
|
||||
for i := range pc.RelayRuleList {
|
||||
relayRule, err := rule.CreateRuleByConfigureAndOptions(
|
||||
pc.RelayRuleList[i].Name,
|
||||
pc.RelayRuleList[i].Configurestr,
|
||||
pc.RelayRuleList[i].Options)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
relayRule.From = "configureFile" //规则来源
|
||||
relayRule.IsEnable = pc.RelayRuleList[i].Enable
|
||||
|
||||
_, e := rule.AddRuleToGlobalRuleList(false, *relayRule)
|
||||
if e != nil {
|
||||
log.Printf("%s\n", e)
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 131 KiB |
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
|
@ -0,0 +1,264 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ljymc/goports/base"
|
||||
"github.com/ljymc/goports/config"
|
||||
)
|
||||
|
||||
var globalRelayRules *[]RelayRule
|
||||
var globalRelayRulesMutex sync.RWMutex
|
||||
|
||||
func SetGlobalRelayRules(rl *[]RelayRule) {
|
||||
globalRelayRulesMutex.Lock()
|
||||
defer globalRelayRulesMutex.Unlock()
|
||||
globalRelayRules = rl
|
||||
syncRuleListToConfigure()
|
||||
}
|
||||
|
||||
func AddRuleToGlobalRuleList(sync bool, rl ...RelayRule) (string, error) {
|
||||
globalRelayRulesMutex.Lock()
|
||||
defer globalRelayRulesMutex.Unlock()
|
||||
|
||||
var err error
|
||||
|
||||
if globalRelayRules == nil {
|
||||
var rrl []RelayRule
|
||||
globalRelayRules = &rrl
|
||||
}
|
||||
|
||||
for i := range rl {
|
||||
isExits := false
|
||||
for j := range *globalRelayRules {
|
||||
if (*globalRelayRules)[j].MainConfigure == rl[i].MainConfigure {
|
||||
isExits = true
|
||||
if err == nil {
|
||||
err = fmt.Errorf("\n\t规则[%s]已存在,不再重复加入规则列表", rl[i].MainConfigure)
|
||||
} else {
|
||||
err = fmt.Errorf("%s\n\t规则[%s]已存在,不再重复加入规则列表", err.Error(), rl[i].MainConfigure)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isExits {
|
||||
*globalRelayRules = append(*globalRelayRules, rl[i])
|
||||
if rl[i].From != "cmd" && sync {
|
||||
syncRes := ""
|
||||
if syncErr := syncRuleListToConfigure(); syncErr != nil {
|
||||
syncRes = syncErr.Error()
|
||||
}
|
||||
return syncRes, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
func AlterRuleInGlobalRuleListByKey(key string, rule *RelayRule) (bool, error) {
|
||||
globalRelayRulesMutex.Lock()
|
||||
defer globalRelayRulesMutex.Unlock()
|
||||
|
||||
keyIndex := -1
|
||||
|
||||
for i := range *globalRelayRules {
|
||||
if (*globalRelayRules)[i].MainConfigure != key {
|
||||
continue
|
||||
}
|
||||
keyIndex = i
|
||||
break
|
||||
}
|
||||
|
||||
if keyIndex < 0 {
|
||||
return true, fmt.Errorf("修改转发规则失败,规则[%s]不存在", key)
|
||||
}
|
||||
|
||||
(*globalRelayRules)[keyIndex].Disable()
|
||||
(*globalRelayRules)[keyIndex] = *rule
|
||||
|
||||
syncErr := syncRuleListToConfigure()
|
||||
syncSuccess := true
|
||||
if syncErr != nil {
|
||||
syncSuccess = false
|
||||
}
|
||||
|
||||
return syncSuccess, nil
|
||||
}
|
||||
|
||||
func DeleteGlobalRuleByKey(key string) (bool, error) {
|
||||
globalRelayRulesMutex.Lock()
|
||||
defer globalRelayRulesMutex.Unlock()
|
||||
|
||||
if globalRelayRules == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
deleteIndex := -1
|
||||
|
||||
for i := range *globalRelayRules {
|
||||
if (*globalRelayRules)[i].MainConfigure == key {
|
||||
deleteIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if deleteIndex < 0 {
|
||||
return true, fmt.Errorf("relayRule[%s]no found,delete failed", key)
|
||||
}
|
||||
*globalRelayRules = DeleteRuleSlice(*globalRelayRules, deleteIndex)
|
||||
|
||||
syncError := syncRuleListToConfigure()
|
||||
syncSuccess := true
|
||||
if syncError != nil {
|
||||
syncSuccess = false
|
||||
}
|
||||
|
||||
return syncSuccess, nil
|
||||
}
|
||||
|
||||
func GetRuleByMainConfigure(configStr string) *RelayRule {
|
||||
globalRelayRulesMutex.RLock()
|
||||
defer globalRelayRulesMutex.RUnlock()
|
||||
if globalRelayRules == nil {
|
||||
return nil
|
||||
}
|
||||
for i := range *globalRelayRules {
|
||||
//fmt.Printf("MainConfigure %s:::%s\n", (*globalRelayRules)[i].MainConfigure, configStr)
|
||||
if (*globalRelayRules)[i].MainConfigure == configStr {
|
||||
r := (*globalRelayRules)[i]
|
||||
return &r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetGlobalEnableProxyCount() int64 {
|
||||
globalRelayRulesMutex.RLock()
|
||||
defer globalRelayRulesMutex.RUnlock()
|
||||
|
||||
if globalRelayRules == nil {
|
||||
return 0
|
||||
}
|
||||
count := int64(0)
|
||||
for _, r := range *globalRelayRules {
|
||||
if !r.IsEnable {
|
||||
continue
|
||||
}
|
||||
if r.proxyList == nil {
|
||||
continue
|
||||
}
|
||||
count += int64(len(*r.proxyList))
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// func StartAllProxy() {
|
||||
// allProxyListMutex.Lock()
|
||||
// defer allProxyListMutex.Unlock()
|
||||
// if allProxyList == nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// for _, p := range *allProxyList {
|
||||
// go p.StartProxy()
|
||||
// }
|
||||
// }
|
||||
|
||||
func EnableAllRelayRule() error {
|
||||
globalRelayRulesMutex.RLock()
|
||||
defer globalRelayRulesMutex.RUnlock()
|
||||
var err error
|
||||
|
||||
if globalRelayRules == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := range *globalRelayRules {
|
||||
if GetGlobalEnableProxyCount()+(*globalRelayRules)[i].GetProxyCount() <= base.GetGlobalMaxProxyCount() {
|
||||
if (*globalRelayRules)[i].From == "cmd" || ((*globalRelayRules)[i].From == "configureFile" && (*globalRelayRules)[i].IsEnable) {
|
||||
(*globalRelayRules)[i].Enable()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if GetGlobalEnableProxyCount()+(*globalRelayRules)[i].GetProxyCount() > base.DEFAULT_MAX_PROXY_COUNT {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("\n\t超出代理数最大限制,规则[%s]未启用", (*globalRelayRules)[i].MainConfigure)
|
||||
} else {
|
||||
err = fmt.Errorf("%s\n\t超出代理数最大限制,规则[%s]未启用", err.Error(), (*globalRelayRules)[i].MainConfigure)
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func EnableRelayRuleByKey(key string) (*RelayRule, bool, error) {
|
||||
globalRelayRulesMutex.RLock()
|
||||
defer globalRelayRulesMutex.RUnlock()
|
||||
|
||||
for i := range *globalRelayRules {
|
||||
if (*globalRelayRules)[i].MainConfigure == key {
|
||||
(*globalRelayRules)[i].Enable()
|
||||
syncErr := syncRuleListToConfigure()
|
||||
synsSuccess := true
|
||||
if syncErr != nil {
|
||||
synsSuccess = false
|
||||
}
|
||||
|
||||
return &(*globalRelayRules)[i], synsSuccess, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, true, fmt.Errorf("规则[%s]不存在,开启失败", key)
|
||||
}
|
||||
|
||||
func DisableRelayRuleByKey(key string) (*RelayRule, bool, error) {
|
||||
globalRelayRulesMutex.RLock()
|
||||
defer globalRelayRulesMutex.RUnlock()
|
||||
|
||||
for i := range *globalRelayRules {
|
||||
if (*globalRelayRules)[i].MainConfigure == key {
|
||||
(*globalRelayRules)[i].Disable()
|
||||
syncErr := syncRuleListToConfigure()
|
||||
synsSuccess := true
|
||||
if syncErr != nil {
|
||||
synsSuccess = false
|
||||
}
|
||||
return &(*globalRelayRules)[i], synsSuccess, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, true, fmt.Errorf("规则[%s]不存在,停用失败", key)
|
||||
}
|
||||
|
||||
func DeleteRuleSlice(a []RelayRule, deleteIndex int) []RelayRule {
|
||||
j := 0
|
||||
for i := range a {
|
||||
if i != deleteIndex {
|
||||
a[j] = a[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return a[:j]
|
||||
}
|
||||
|
||||
//syncRuleListToConfigure 同步规则列表到配置
|
||||
func syncRuleListToConfigure() error {
|
||||
var ruleList []config.ConfigureRelayRule
|
||||
for i := range *globalRelayRules {
|
||||
if (*globalRelayRules)[i].From == "cmd" {
|
||||
continue
|
||||
}
|
||||
rule := config.ConfigureRelayRule{
|
||||
Name: (*globalRelayRules)[i].Name,
|
||||
Configurestr: (*globalRelayRules)[i].MainConfigure,
|
||||
Enable: (*globalRelayRules)[i].IsEnable,
|
||||
Options: (*globalRelayRules)[i].Options}
|
||||
ruleList = append(ruleList, rule)
|
||||
}
|
||||
config.SetConfigRuleList(&ruleList)
|
||||
return config.Save()
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package rule
|
||||
|
||||
import (
|
||||
"github.com/ljymc/goports/base"
|
||||
)
|
||||
|
||||
type RelayRuleProxyInfo struct {
|
||||
Proxy string `json:"Proxy"`
|
||||
CurrentConnections int64 `json:"CurrentConnections"`
|
||||
TrafficIn int64 `json:"TrafficIn"`
|
||||
TrafficOut int64 `json:"TrafficOut"`
|
||||
}
|
||||
|
||||
func GetRelayRuleList() (*[]RelayRule, map[string][]RelayRuleProxyInfo) {
|
||||
globalRelayRulesMutex.RLock()
|
||||
defer globalRelayRulesMutex.RUnlock()
|
||||
var rl []RelayRule
|
||||
proxyInfoMap := make(map[string][]RelayRuleProxyInfo)
|
||||
if globalRelayRules == nil {
|
||||
return &rl, proxyInfoMap
|
||||
}
|
||||
|
||||
for i := range *globalRelayRules {
|
||||
rl = append(rl, (*globalRelayRules)[i])
|
||||
var rpl []RelayRuleProxyInfo
|
||||
if (*globalRelayRules)[i].proxyList == nil {
|
||||
proxyInfoMap[(*globalRelayRules)[i].MainConfigure] = rpl
|
||||
continue
|
||||
}
|
||||
for pindex := range *(*globalRelayRules)[i].proxyList {
|
||||
pi := RelayRuleProxyInfo{
|
||||
Proxy: (*(*globalRelayRules)[i].proxyList)[pindex].GetKey(),
|
||||
CurrentConnections: (*(*globalRelayRules)[i].proxyList)[pindex].GetCurrentConnections(),
|
||||
TrafficIn: (*(*globalRelayRules)[i].proxyList)[pindex].GetTrafficIn(),
|
||||
TrafficOut: (*(*globalRelayRules)[i].proxyList)[pindex].GetTrafficOut()}
|
||||
rpl = append(rpl, pi)
|
||||
}
|
||||
proxyInfoMap[(*globalRelayRules)[i].MainConfigure] = rpl
|
||||
}
|
||||
|
||||
return &rl, proxyInfoMap
|
||||
}
|
||||
|
||||
//GetAllProxyInfo
|
||||
// func GetAllProxyInfo() map[string]interface{} {
|
||||
// allProxyListMutex.Lock()
|
||||
// defer allProxyListMutex.Unlock()
|
||||
// info := make(map[string]interface{})
|
||||
// if allProxyList == nil {
|
||||
// return info
|
||||
// }
|
||||
// for _, p := range *allProxyList {
|
||||
// pi := GetProxyInfo(p)
|
||||
// info[p.GetKey()] = pi
|
||||
// }
|
||||
|
||||
// return info
|
||||
// }
|
||||
|
||||
func GetProxyInfo(p base.Proxy) map[string]string {
|
||||
pi := make(map[string]string)
|
||||
pi["proxyType"] = p.GetProxyType()
|
||||
pi["key"] = p.GetKey()
|
||||
pi["status"] = p.GetStatus()
|
||||
pi["fromRule"] = p.FromRule()
|
||||
return pi
|
||||
}
|
|
@ -0,0 +1,545 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ljymc/goports/base"
|
||||
)
|
||||
|
||||
type RelayRule struct {
|
||||
Name string `json:"Name"`
|
||||
MainConfigure string `json:"Mainconfigure"`
|
||||
RelayType string `json:"RelayType"`
|
||||
ListenIP string `json:"ListenIP"`
|
||||
ListenPorts string `json:"ListenPorts"`
|
||||
TargetIP string `json:"TargetIP"`
|
||||
TargetPorts string `json:"TargetPorts"`
|
||||
BalanceTargetAddressList []string `json:"BalanceTargetAddressList"`
|
||||
Options base.RelayRuleOptions `json:"Options"`
|
||||
SubRuleList []SubRelayRule `json:"SubRuleList"`
|
||||
From string `json:"From"`
|
||||
IsEnable bool `json:"Enable"`
|
||||
proxyList *[]base.Proxy `json:"-"`
|
||||
}
|
||||
|
||||
type SubRelayRule struct {
|
||||
ProxyType string `json:"ProxyType"`
|
||||
BindIP string `json:"BindIP"`
|
||||
ListenPorts []int `json:"ListenPorts"`
|
||||
TargetHost string `json:"TargetHost"`
|
||||
TargetPorts []int `json:"TargetPorts"`
|
||||
BalanceTargetAddressAddress []string `json:"BalanceTargetAddressAddress"`
|
||||
}
|
||||
|
||||
func (r *RelayRule) Enable() {
|
||||
r.IsEnable = true
|
||||
if r.proxyList == nil {
|
||||
return
|
||||
}
|
||||
for _, p := range *r.proxyList {
|
||||
p.StartProxy()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RelayRule) GetProxyCount() int64 {
|
||||
if r.proxyList == nil {
|
||||
return 0
|
||||
}
|
||||
return int64(len(*r.proxyList))
|
||||
}
|
||||
|
||||
func (r *RelayRule) Disable() {
|
||||
r.IsEnable = false
|
||||
if r.proxyList == nil {
|
||||
return
|
||||
}
|
||||
for _, p := range *r.proxyList {
|
||||
p.StopProxy()
|
||||
}
|
||||
}
|
||||
|
||||
func GetRelayRulesFromCMD(configureList []string, options *base.RelayRuleOptions) (relayRules *[]RelayRule, err error) {
|
||||
//proxyMap := make(map[string]base.Proxy)
|
||||
|
||||
var relayRuleList []RelayRule
|
||||
|
||||
for _, configure := range configureList {
|
||||
|
||||
relayRule, err := CreateRuleByConfigureAndOptions("", configure, *options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
relayRule.From = "cmd" //规则来源
|
||||
|
||||
relayRuleList = append(relayRuleList, *relayRule)
|
||||
}
|
||||
|
||||
return &relayRuleList, nil
|
||||
}
|
||||
|
||||
func (r *RelayRule) CreateMainConfigure() (configure string) {
|
||||
if len(r.BalanceTargetAddressList) > 0 {
|
||||
configure = fmt.Sprintf("%s@%s:%sto%s", r.RelayType, r.ListenIP, r.ListenPorts, strings.Join(r.BalanceTargetAddressList, ","))
|
||||
} else {
|
||||
if strings.Compare(r.ListenPorts, r.TargetPorts) == 0 {
|
||||
configure = fmt.Sprintf("%s@%s:%sto%s", r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP)
|
||||
} else {
|
||||
configure = fmt.Sprintf("%s@%s:%sto%s:%s", r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP, r.TargetPorts)
|
||||
}
|
||||
}
|
||||
return configure
|
||||
}
|
||||
|
||||
func CreateRuleByConfigureAndOptions(name, configureStr string, options base.RelayRuleOptions) (rule *RelayRule, err error) {
|
||||
var r RelayRule
|
||||
r.Options = options
|
||||
r.SubRuleList, r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP, r.TargetPorts, r.BalanceTargetAddressList, err = createSubRuleListFromConfigure(configureStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.MainConfigure = r.CreateMainConfigure()
|
||||
|
||||
// if len(r.BalanceTargetAddressList) > 0 {
|
||||
// r.MainConfigure = fmt.Sprintf("%s@%s:%sto%s", r.RelayType, r.ListenIP, r.ListenPorts, strings.Join(r.BalanceTargetAddressList, ","))
|
||||
// } else {
|
||||
// if strings.Compare(r.ListenPorts, r.TargetPorts) == 0 {
|
||||
// r.MainConfigure = fmt.Sprintf("%s@%s:%sto%s", r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP)
|
||||
// } else {
|
||||
// r.MainConfigure = fmt.Sprintf("%s@%s:%sto%s:%s", r.RelayType, r.ListenIP, r.ListenPorts, r.TargetIP, r.TargetPorts)
|
||||
// }
|
||||
// }
|
||||
|
||||
var pl []base.Proxy
|
||||
|
||||
for i := range r.SubRuleList {
|
||||
if len(r.BalanceTargetAddressList) == 0 {
|
||||
for j := range r.SubRuleList[i].ListenPorts {
|
||||
p, e := base.CreateProxy(r.SubRuleList[i].ProxyType,
|
||||
r.SubRuleList[i].BindIP,
|
||||
r.SubRuleList[i].TargetHost,
|
||||
nil,
|
||||
r.SubRuleList[i].ListenPorts[j],
|
||||
r.SubRuleList[i].TargetPorts[j],
|
||||
&options)
|
||||
if e != nil {
|
||||
log.Printf("CreateProxy error:%s", e.Error())
|
||||
continue
|
||||
}
|
||||
p.SetFromRule(r.MainConfigure)
|
||||
pl = append(pl, p)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
p, e := base.CreateProxy(r.SubRuleList[i].ProxyType,
|
||||
r.SubRuleList[i].BindIP,
|
||||
r.SubRuleList[i].TargetHost,
|
||||
&r.BalanceTargetAddressList,
|
||||
r.SubRuleList[i].ListenPorts[0],
|
||||
0,
|
||||
&options)
|
||||
if e != nil {
|
||||
log.Printf("CreateProxy error:%s", e.Error())
|
||||
continue
|
||||
}
|
||||
p.SetFromRule(r.MainConfigure)
|
||||
pl = append(pl, p)
|
||||
|
||||
}
|
||||
|
||||
r.proxyList = &pl
|
||||
|
||||
r.Name = name
|
||||
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func createSubRuleListFromConfigure(str string) (subRelyList []SubRelayRule, proxytypeListStr, listenIP, listenPortsStr, targetIP, targetePortsStr string, targetAddressList []string, err error) {
|
||||
|
||||
splitRes := strings.Split(str, "@")
|
||||
if len(splitRes) > 2 {
|
||||
err = fmt.Errorf("relay参数:%s格式有误!000", str)
|
||||
return
|
||||
}
|
||||
|
||||
proxytypeListStr = "tcp,udp"
|
||||
|
||||
relayConfig := splitRes[0]
|
||||
|
||||
if len(splitRes) == 2 {
|
||||
proxytypeListStr = splitRes[0]
|
||||
relayConfig = splitRes[1]
|
||||
}
|
||||
|
||||
proxyTypeList := getProxyTypeList(proxytypeListStr)
|
||||
err = checkProxyType(proxyTypeList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
proxytypeListStr = convertProxyTypeByList(proxyTypeList)
|
||||
|
||||
relayConfigArray := strings.Split(relayConfig, "to")
|
||||
if len(relayConfigArray) > 2 {
|
||||
err = fmt.Errorf("relay参数:%s格式有误!001", str)
|
||||
return
|
||||
}
|
||||
|
||||
var listenPorts []int
|
||||
var targetPorts []int
|
||||
|
||||
switch len(relayConfigArray) {
|
||||
case 1: //监听端口没有指定 比如 192.168.31.22:80,443,20000-20010
|
||||
tip, tPortsStr, e := getIpAndPortFromAddress(relayConfigArray[0], true, true)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("参数中目标地址部分参数[%s]格式有误", relayConfigArray[0])
|
||||
return
|
||||
}
|
||||
|
||||
targetPorts, e = portsStrToIList(tPortsStr)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("参数[%s]中的目标端口部分出错:%s", str, e.Error())
|
||||
return
|
||||
}
|
||||
targetePortsStr = tPortsStr
|
||||
listenPortsStr = tPortsStr
|
||||
listenPorts = targetPorts
|
||||
targetIP = tip
|
||||
|
||||
case 2: //监听端口有指定 比如 80,443,20000-20010to192.168.31.222 ,但目标地址的端口不一定指定
|
||||
bindAddress := relayConfigArray[0]
|
||||
bip, bPortsStr, e := getIpAndPortFromAddress(bindAddress, false, false)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("参数[%s]中的监听端口部分出错:%s", str, e.Error())
|
||||
return
|
||||
}
|
||||
listenIP = bip
|
||||
|
||||
listenPortsStr = bPortsStr
|
||||
//fmt.Printf("bip:%s bindPortsStr:%s\n", bip, bindPortsStr)
|
||||
|
||||
listenPorts, e = portsStrToIList(bPortsStr)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("参数中绑定端口部分参数[%s]格式有误", bPortsStr)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(relayConfigArray[1], ",") { //均衡负载模式
|
||||
if len(listenPorts) != 1 {
|
||||
err = fmt.Errorf("均衡负载模式一条配置指定监听一个端口")
|
||||
return
|
||||
}
|
||||
//targetAddressList :
|
||||
targetAddressList = strings.Split(relayConfigArray[1], ",")
|
||||
|
||||
} else {
|
||||
targetAddress := relayConfigArray[1]
|
||||
|
||||
tip, tPortsStr, e := getIpAndPortFromAddress(targetAddress, true, false)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("参数中目标地址部分参数[%s]格式有误", targetAddress)
|
||||
return
|
||||
}
|
||||
targetePortsStr = tPortsStr
|
||||
|
||||
targetPorts, e = portsStrToIList(tPortsStr)
|
||||
|
||||
if e != nil {
|
||||
err = fmt.Errorf("参数[%s]中的目标端口部分出错:%s", str, e.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if len(listenPorts) > 0 && len(targetPorts) == 0 {
|
||||
targetPorts = listenPorts
|
||||
targetePortsStr = listenPortsStr
|
||||
} else if len(listenPorts) == 0 && len(targetPorts) > 0 {
|
||||
listenPorts = targetPorts
|
||||
listenPortsStr = targetePortsStr
|
||||
}
|
||||
|
||||
if len(listenPorts) != len(targetPorts) {
|
||||
err = fmt.Errorf("参数[%s]中监听端口数量和目标端口数量不一致", str)
|
||||
|
||||
// fmt.Printf("listenPorts:%v\n", listenPorts)
|
||||
// fmt.Printf("targetPorts:%v\n", targetPorts)
|
||||
return
|
||||
}
|
||||
targetIP = tip
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
var SubBaseRule SubRelayRule
|
||||
SubBaseRule.BindIP = listenIP
|
||||
SubBaseRule.ListenPorts = append(SubBaseRule.ListenPorts, listenPorts...)
|
||||
|
||||
if len(targetAddressList) == 0 {
|
||||
SubBaseRule.TargetHost = targetIP
|
||||
SubBaseRule.TargetPorts = append(SubBaseRule.TargetPorts, targetPorts...)
|
||||
|
||||
} else {
|
||||
SubBaseRule.BalanceTargetAddressAddress = targetAddressList
|
||||
}
|
||||
|
||||
for i := range proxyTypeList {
|
||||
dt := SubBaseRule
|
||||
dt.ProxyType = proxyTypeList[i]
|
||||
subRelyList = append(subRelyList, dt)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func convertProxyTypeByList(proxyTypeList []string) (proxyType string) {
|
||||
for i := range proxyTypeList {
|
||||
if i == 0 {
|
||||
proxyType = proxyTypeList[i]
|
||||
continue
|
||||
}
|
||||
proxyType += "," + proxyTypeList[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getProxyTypeList(proxyTypeListStr string) (proxyTypeList []string) {
|
||||
//tmpList = strings.Split(proxyTypeListStr, ",")
|
||||
//var
|
||||
tmpMap := make(map[string]int)
|
||||
|
||||
if strings.Contains(proxyTypeListStr, "tcp4") && strings.Contains(proxyTypeListStr, "tcp6") {
|
||||
proxyTypeList = append(proxyTypeList, "tcp")
|
||||
tmpMap["tcp"] = 1
|
||||
proxyTypeListStr = strings.Replace(proxyTypeListStr, "tcp4", ",", -1)
|
||||
proxyTypeListStr = strings.Replace(proxyTypeListStr, "tcp6", ",", -1)
|
||||
}
|
||||
|
||||
if strings.Contains(proxyTypeListStr, "udp4") && strings.Contains(proxyTypeListStr, "udp6") {
|
||||
proxyTypeList = append(proxyTypeList, "udp")
|
||||
tmpMap["udp"] = 1
|
||||
proxyTypeListStr = strings.Replace(proxyTypeListStr, "udp4", ",", -1)
|
||||
proxyTypeListStr = strings.Replace(proxyTypeListStr, "udp6", ",", -1)
|
||||
}
|
||||
|
||||
tmpList := strings.Split(proxyTypeListStr, ",")
|
||||
for i := range tmpList {
|
||||
if len(tmpList[i]) <= 2 {
|
||||
continue
|
||||
}
|
||||
if _, ok := tmpMap[tmpList[i]]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
_, tcpOK := tmpMap["tcp"]
|
||||
_, udpOK := tmpMap["udp"]
|
||||
|
||||
if (tmpList[i] == "tcp4" || tmpList[i] == "tcp6") && tcpOK {
|
||||
continue
|
||||
}
|
||||
|
||||
if (tmpList[i] == "udp4" || tmpList[i] == "udp6") && udpOK {
|
||||
continue
|
||||
}
|
||||
|
||||
proxyTypeList = append(proxyTypeList, tmpList[i])
|
||||
tmpMap[tmpList[i]] = 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func checkProxyType(proxyTypeList []string) error {
|
||||
for _, proxyType := range proxyTypeList {
|
||||
|
||||
switch proxyType {
|
||||
case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
|
||||
{
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
{
|
||||
return fmt.Errorf("unsupport Proxy Type:%s", proxyType)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
//CheckProxyConflict 冲突检查
|
||||
func CheckProxyConflict(proxyList *[]base.Proxy, proxyType, listenIP string, listenPort int) error {
|
||||
proxyMap := make(map[string]base.Proxy)
|
||||
for i, p := range *proxyList {
|
||||
proxyMap[p.GetKey()] = (*proxyList)[i]
|
||||
}
|
||||
|
||||
key := base.GetProxyKey(proxyType, listenIP, listenPort)
|
||||
if _, ok := proxyMap[key]; ok {
|
||||
return fmt.Errorf("绑定的地址和端口存在冲突![%s]", key)
|
||||
}
|
||||
anyBindKey := fmt.Sprintf("%s@:%d", proxyType, listenPort)
|
||||
|
||||
if strings.Compare(key, anyBindKey) == 0 {
|
||||
for exitsKey := range proxyMap {
|
||||
if strings.HasSuffix(exitsKey, anyBindKey) {
|
||||
return fmt.Errorf("绑定的地址和端口存在冲突![%s][%s]", key, exitsKey)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if _, ok := proxyMap[anyBindKey]; ok {
|
||||
return fmt.Errorf("绑定的地址和端口存在冲突![%s]", key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//getIpAndPortsFromAddress
|
||||
func getIpAndPortFromAddress(address string, needip bool, needports bool) (ip string, ports string, err error) {
|
||||
ipAndPortIndex := strings.LastIndex(address, ":")
|
||||
|
||||
// defer func() {
|
||||
// fmt.Printf("\nFuck: [%s]--->[%s]", ip, ports)
|
||||
// }()
|
||||
|
||||
if ipAndPortIndex < 0 || (!needip && !needports) {
|
||||
|
||||
switch {
|
||||
case (!needip && needports): //地址中仅有端口
|
||||
ports = address
|
||||
case (needip && !needports): //地址中仅有ip
|
||||
{
|
||||
ip = address
|
||||
}
|
||||
case (!needip && !needports): //地址中 端口和ip都不是必须
|
||||
{
|
||||
|
||||
if ipAndPortIndex > 0 {
|
||||
ip = address[:ipAndPortIndex]
|
||||
ports = address[ipAndPortIndex+1:]
|
||||
break
|
||||
}
|
||||
|
||||
//但address非空,判断
|
||||
if address == "" {
|
||||
break
|
||||
}
|
||||
if net.ParseIP(address) != nil {
|
||||
ip = address
|
||||
} else {
|
||||
ports = address
|
||||
if strings.HasPrefix(ports, ":") {
|
||||
ports = ports[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ports = address[ipAndPortIndex+1:]
|
||||
|
||||
if ipAndPortIndex <= 1 {
|
||||
//fmt.Printf("Fuck:%s\n", ports)
|
||||
return
|
||||
}
|
||||
|
||||
addressHost := address[:ipAndPortIndex]
|
||||
addressHost = strings.Replace(addressHost, "[", "", -1)
|
||||
addressHost = strings.Replace(addressHost, "]", "", -1)
|
||||
|
||||
if net.ParseIP(addressHost) == nil {
|
||||
err = fmt.Errorf("ip[%s]格式有误", address[:ipAndPortIndex])
|
||||
return
|
||||
}
|
||||
|
||||
ip = addressHost
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//portsStrToIList
|
||||
func portsStrToIList(portsStr string) (ports []int, err error) {
|
||||
|
||||
if portsStr == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(portsStr, ",") {
|
||||
tmpStrList := strings.Split(portsStr, ",")
|
||||
for i := range tmpStrList {
|
||||
tps, e := portsStrToIList(tmpStrList[i])
|
||||
if e != nil {
|
||||
err = fmt.Errorf("端口参数处理出错:%s", e.Error())
|
||||
return
|
||||
}
|
||||
ports = append(ports, tps...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
portsStrList := strings.Split(portsStr, "-")
|
||||
if len(portsStrList) > 2 {
|
||||
err = fmt.Errorf("端口%s格式有误", portsStr)
|
||||
return
|
||||
}
|
||||
|
||||
if len(portsStrList) == 1 { //single listen port
|
||||
listenPort, e := portStrToi(portsStrList[0])
|
||||
if e != nil {
|
||||
err = fmt.Errorf("端口格式有误!%s", e.Error())
|
||||
return
|
||||
}
|
||||
ports = append(ports, listenPort)
|
||||
}
|
||||
|
||||
if len(portsStrList) == 2 {
|
||||
minListenPort, e := portStrToi(portsStrList[0])
|
||||
if e != nil {
|
||||
err = fmt.Errorf("端口格式有误!%s", portsStrList[0])
|
||||
return
|
||||
}
|
||||
maxListenPort, e := portStrToi(portsStrList[1])
|
||||
if e != nil {
|
||||
err = fmt.Errorf("端口格式有误!%s", portsStrList[1])
|
||||
return
|
||||
}
|
||||
|
||||
if maxListenPort <= minListenPort {
|
||||
err = fmt.Errorf("前一个端口[%d]要小于后一个端口[%d]", minListenPort, maxListenPort)
|
||||
return
|
||||
}
|
||||
i := minListenPort
|
||||
for {
|
||||
if i > maxListenPort {
|
||||
break
|
||||
}
|
||||
ports = append(ports, i)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func portStrToi(portStr string) (int, error) {
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("端口格式有误:%s", err.Error())
|
||||
}
|
||||
if port < 1 || port > 65535 {
|
||||
return 0, fmt.Errorf("端口[%d]超出范围", port)
|
||||
}
|
||||
return port, nil
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
package rule
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_createSubRuleListFromConfigure_1(t *testing.T) {
|
||||
argsA := "53to192.168.31.1"
|
||||
|
||||
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
|
||||
if e != nil || len(goportsList) != 2 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 1 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 1 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[0] != 53 || goportsList[0].TargetPorts[0] != 53 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[1].ProxyType != "udp" || len(goportsList[0].ListenPorts) != 1 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 1 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[1].ListenPorts[0] != 53 || goportsList[1].TargetPorts[0] != 53 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testE error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_createSubRuleListFromConfigure_2(t *testing.T) {
|
||||
argsA := "tcp@53to192.168.31.1"
|
||||
|
||||
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
|
||||
if e != nil || len(goportsList) != 1 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 1 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 1 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[0] != 53 || goportsList[0].TargetPorts[0] != 53 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_createSubRuleListFromConfigure_3(t *testing.T) {
|
||||
argsA := "udp4@53to192.168.31.1"
|
||||
|
||||
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
|
||||
if e != nil || len(goportsList) != 1 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ProxyType != "udp4" || len(goportsList[0].ListenPorts) != 1 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 1 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[0] != 53 || goportsList[0].TargetPorts[0] != 53 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_createSubRuleListFromConfigure_4_0(t *testing.T) {
|
||||
argsList := []string{"tcp@20000-20021to192.168.31.1", "tcp@192.168.31.1:20000-20021", "tcp@20000-20021to192.168.31.1:20000-20021"}
|
||||
|
||||
for _, argsA := range argsList {
|
||||
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
|
||||
if e != nil || len(goportsList) != 1 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 22 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 22 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[0] != 20000 || goportsList[0].TargetPorts[0] != 20000 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[21] != 20021 || goportsList[0].TargetPorts[21] != 20021 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_createSubRuleListFromConfigure_4_1(t *testing.T) {
|
||||
argsA := "tcp@30000-30021to192.168.31.1:20000-20021"
|
||||
|
||||
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
|
||||
if e != nil || len(goportsList) != 1 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 22 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 22 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[0] != 30000 || goportsList[0].TargetPorts[0] != 20000 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[21] != 30021 || goportsList[0].TargetPorts[21] != 20021 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_createSubRuleListFromConfigure_5_0(t *testing.T) {
|
||||
//argsA :=
|
||||
args := []string{"tcp@80,443,20000-20021to192.168.31.1:80,443,20000-20021",
|
||||
"tcp@192.168.31.1:80,443,20000-20021",
|
||||
"tcp@80,443,20000-20021to192.168.31.1"}
|
||||
|
||||
for _, argsA := range args {
|
||||
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
|
||||
if e != nil || len(goportsList) != 1 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 24 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 24 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[0] != 80 || goportsList[0].TargetPorts[0] != 80 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[23] != 20021 || goportsList[0].TargetPorts[23] != 20021 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_createSubRuleListFromConfigure_5_1(t *testing.T) {
|
||||
//argsA :=
|
||||
args := []string{"tcp@80,443,30000-30021to192.168.31.1:81,443,20000-20021"}
|
||||
|
||||
for _, argsA := range args {
|
||||
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
|
||||
if e != nil || len(goportsList) != 1 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 24 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 24 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[0] != 80 || goportsList[0].TargetPorts[0] != 81 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[23] != 30021 || goportsList[0].TargetPorts[23] != 20021 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_createSubRuleListFromConfigure_6_0(t *testing.T) {
|
||||
//argsA :=
|
||||
args := []string{"tcp6@80,443to192.168.31.1:80,443",
|
||||
"tcp6@80,443to192.168.31.1",
|
||||
"tcp6@192.168.31.1:80,443"}
|
||||
|
||||
for _, argsA := range args {
|
||||
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
|
||||
if e != nil || len(goportsList) != 1 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ProxyType != "tcp6" || len(goportsList[0].ListenPorts) != 2 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 2 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[0] != 80 || goportsList[0].TargetPorts[0] != 80 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[1] != 443 || goportsList[0].TargetPorts[1] != 443 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_createSubRuleListFromConfigure_6_1(t *testing.T) {
|
||||
//argsA :=
|
||||
args := []string{"tcp@80,443to192.168.31.1:81,443"}
|
||||
|
||||
for _, argsA := range args {
|
||||
goportsList, _, _, _, _, _, _, e := createSubRuleListFromConfigure(argsA)
|
||||
if e != nil || len(goportsList) != 1 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testA error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ProxyType != "tcp" || len(goportsList[0].ListenPorts) != 2 || goportsList[0].TargetHost != "192.168.31.1" || len(goportsList[0].TargetPorts) != 2 || len(goportsList[0].ListenPorts) != len(goportsList[0].TargetPorts) {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testB error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[0] != 80 || goportsList[0].TargetPorts[0] != 81 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testC error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
|
||||
if goportsList[0].ListenPorts[1] != 443 || goportsList[0].TargetPorts[1] != 443 {
|
||||
t.Errorf("createSubRuleListFromConfigure [%s] testD error %v", argsA, goportsList)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package fileutils
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
//获取当前路径
|
||||
func GetCurrentDirectory() string {
|
||||
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
//保存base64为文件,一般用于保存图片
|
||||
func SaveBase64AsFile(base64Str *string, fileURL string) (err error) {
|
||||
decodeStr, _ := base64.StdEncoding.DecodeString(*base64Str) //把base64写入缓存
|
||||
err = ioutil.WriteFile(fileURL, decodeStr, 0666) //buffer输出到jpg文件中(不做处理,直接写到文件)
|
||||
return
|
||||
}
|
||||
|
||||
//判断文件或文件夹是否存在
|
||||
func FileExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
//保存Text到文本
|
||||
func SaveTextToFile(text, fileURL string) error {
|
||||
dstFile, err := os.Create(fileURL)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return err
|
||||
}
|
||||
defer dstFile.Close()
|
||||
dstFile.WriteString(text + "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
//ReadTextFromFile 从文本读取内容
|
||||
func ReadTextFromFile(path string) (string, error) {
|
||||
fi, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer fi.Close()
|
||||
fd, err := ioutil.ReadAll(fi)
|
||||
return string(fd), nil
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package fileutils
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
//OpenProgramOrFile 启动程序
|
||||
func OpenProgramOrFile(argv []string) error {
|
||||
|
||||
var startArgvs []string
|
||||
|
||||
for i := range argv {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
startArgvs = append(startArgvs, argv[i])
|
||||
}
|
||||
//startArgvs = append(startArgvs, "-c")
|
||||
//startArgvs = append(startArgvs, argv...)
|
||||
|
||||
//fmt.Printf("fuck...%v \n", startArgvs)
|
||||
|
||||
//cmd := exec.Command("/bin/bash", startArgvs...)
|
||||
cmd := exec.Command(argv[0], startArgvs...)
|
||||
return cmd.Start()
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package fileutils
|
||||
|
||||
import "os/exec"
|
||||
|
||||
//OpenProgramOrFile 启动程序
|
||||
func OpenProgramOrFile(argv []string) error {
|
||||
|
||||
var startArgvs []string
|
||||
|
||||
startArgvs = append(startArgvs, "/C")
|
||||
startArgvs = append(startArgvs, "start")
|
||||
|
||||
startArgvs = append(startArgvs, argv...)
|
||||
|
||||
cmd := exec.Command("cmd.exe", startArgvs...)
|
||||
return cmd.Start()
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package ginutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
//Cors 处理跨域请求,支持options访问
|
||||
func Cors(params ...interface{}) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,access_token")
|
||||
c.Header("Access-Control-Allow-Methods", "POST, GET, PUT,OPTIONS")
|
||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
//放行所有OPTIONS方法
|
||||
if method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
}
|
||||
// 处理请求
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func GetJWTToken(tokenString, tokenKey string) (t *jwt.Token, e error) {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(tokenKey), nil
|
||||
})
|
||||
if err != nil {
|
||||
//beego.Error("Parse token:", err)
|
||||
if ve, ok := err.(*jwt.ValidationError); ok {
|
||||
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
|
||||
// That's not even a token
|
||||
return nil, errors.New("errInputData")
|
||||
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
|
||||
// Token is either expired or not active yet
|
||||
return nil, errors.New("errExpired")
|
||||
} else {
|
||||
// Couldn't handle this token
|
||||
return nil, errors.New("errInputData")
|
||||
}
|
||||
} else {
|
||||
// Couldn't handle this token
|
||||
return nil, errors.New("errInputData")
|
||||
}
|
||||
}
|
||||
if !token.Valid {
|
||||
//beego.Error("Token invalid:", tokenString)
|
||||
return nil, errors.New("errInputData")
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// info 存储的信息
|
||||
// key 加密的key
|
||||
// exp 有效期
|
||||
//GetJWTTokenString 获取Token字符串
|
||||
func GetJWTTokenString(info map[string]interface{}, key string, exp time.Duration) (string, error) {
|
||||
claims := make(jwt.MapClaims)
|
||||
claims["exp"] = time.Now().Add(exp).Unix() //token 24小时有效期
|
||||
for k := range info {
|
||||
claims[k] = info[k]
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString([]byte(key))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("生成TokenString出错:%s", err.Error())
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
//GetChildDomain 获取子域名部分
|
||||
func GetChildDomain(host string) string {
|
||||
hostSplitList := strings.Split(host, ".")
|
||||
listLen := len(hostSplitList)
|
||||
var resBuilder strings.Builder
|
||||
|
||||
for i := range hostSplitList {
|
||||
if i >= listLen-2 {
|
||||
break
|
||||
}
|
||||
if resBuilder.Len() > 0 {
|
||||
resBuilder.WriteString(".")
|
||||
}
|
||||
resBuilder.WriteString(hostSplitList[i])
|
||||
}
|
||||
|
||||
return resBuilder.String()
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package pool
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
bufPool sync.Pool
|
||||
bufPool1k sync.Pool
|
||||
bufPool2k sync.Pool
|
||||
bufPool4k sync.Pool
|
||||
bufPool8k sync.Pool
|
||||
bufPool16k sync.Pool
|
||||
)
|
||||
|
||||
const (
|
||||
k16 = 16 * 1024
|
||||
k8 = 8 * 1024
|
||||
k4 = 4 * 1024
|
||||
k2 = 2 * 1024
|
||||
k1 = 1024
|
||||
)
|
||||
|
||||
func GetBuf(size int) []byte {
|
||||
var x interface{}
|
||||
switch {
|
||||
case size >= k16:
|
||||
x = bufPool16k.Get()
|
||||
case size >= k8:
|
||||
x = bufPool8k.Get()
|
||||
case size >= k4:
|
||||
x = bufPool4k.Get()
|
||||
case size >= k2:
|
||||
x = bufPool2k.Get()
|
||||
case size >= k1:
|
||||
x = bufPool1k.Get()
|
||||
default:
|
||||
x = bufPool.Get()
|
||||
}
|
||||
if x == nil {
|
||||
return make([]byte, size)
|
||||
}
|
||||
buf := x.([]byte)
|
||||
if cap(buf) < size {
|
||||
return make([]byte, size)
|
||||
}
|
||||
return buf[:size]
|
||||
}
|
||||
|
||||
func PutBuf(buf interface{}) {
|
||||
size := cap(buf.([]byte))
|
||||
switch {
|
||||
case size >= k16:
|
||||
bufPool16k.Put(buf)
|
||||
case size >= k8:
|
||||
bufPool8k.Put(buf)
|
||||
case size >= k4:
|
||||
bufPool4k.Put(buf)
|
||||
|
||||
case size >= k2:
|
||||
bufPool2k.Put(buf)
|
||||
|
||||
case size >= k1:
|
||||
bufPool1k.Put(buf)
|
||||
default:
|
||||
bufPool.Put(buf)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package recoverutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/fileutils"
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/stderrredirect"
|
||||
)
|
||||
|
||||
//RecoverHandler 恢复处理
|
||||
func RecoverHandler(recoverErr interface{}, exit, reboot bool, panicFileURL string) {
|
||||
if recoverErr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
outputPanicV2(panicFileURL, recoverErr)
|
||||
|
||||
if reboot {
|
||||
|
||||
var argvsBuilder strings.Builder
|
||||
|
||||
for i := range os.Args {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
if argvsBuilder.Len() == 0 {
|
||||
argvsBuilder.WriteString(os.Args[i])
|
||||
} else {
|
||||
argvsBuilder.WriteString(" ")
|
||||
argvsBuilder.WriteString(os.Args[i])
|
||||
}
|
||||
}
|
||||
|
||||
fileutils.OpenProgramOrFile(os.Args) //重启程序
|
||||
//fileutil.OpenProgramOrFile(restartURIBuilder.String())
|
||||
}
|
||||
if exit {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func outputPanic(panicFileURL string, recoverErr interface{}) {
|
||||
exeName := os.Args[0] //获取程序名称
|
||||
now := time.Now() //获取当前时间
|
||||
pid := os.Getpid() //获取进程ID
|
||||
|
||||
if !strings.Contains(panicFileURL, ":") && !strings.HasPrefix(panicFileURL, "/") { //相对路径
|
||||
panicFileURL = fmt.Sprintf("%s%s%s", fileutils.GetCurrentDirectory(), string(os.PathSeparator), panicFileURL)
|
||||
}
|
||||
|
||||
fileDir := ""
|
||||
lastIndex := strings.LastIndex(panicFileURL, string(os.PathSeparator))
|
||||
if lastIndex > 0 {
|
||||
fileDir = panicFileURL[:lastIndex]
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(fileDir, 0755); err != nil {
|
||||
panic(fmt.Sprintf("创建错误重定向文件夹路径出错:%s", err.Error()))
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(panicFileURL, os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("panic重定向出错:%s", err.Error()))
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
timeStr := now.Format("2006-01-02 15:04:05") //设定时间格式
|
||||
file.WriteString("\n\n\n↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\r\n")
|
||||
file.WriteString(fmt.Sprintf("%s-%d-%s dump LOG\r\n", exeName, pid, timeStr))
|
||||
file.WriteString(fmt.Sprintf("%v\r\n", err)) //输出panic信息
|
||||
file.WriteString(string(debug.Stack())) //输出堆栈信息
|
||||
file.WriteString("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\r\n")
|
||||
|
||||
// timeStr := now.Format("20060102150405") //设定时间格式
|
||||
// fname := fmt.Sprintf("%s-%d-%s-dump.log", exeName, pid, timeStr) //保存错误信息文件名:程序名-进程ID-当前时间(年月日时分秒)
|
||||
// fmt.Println("dump to file ", fname)
|
||||
|
||||
// f, err := os.Create(fname)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// defer f.Close()
|
||||
|
||||
// if recoverErr != nil {
|
||||
// f.WriteString(fmt.Sprintf("%v\r\n", err)) //输出panic信息
|
||||
// f.WriteString("========\r\n")
|
||||
// }
|
||||
|
||||
// f.WriteString(string(debug.Stack())) //输出堆栈信息
|
||||
}
|
||||
|
||||
func outputPanicV2(panicFileURL string, recoverErr interface{}) {
|
||||
exeName := os.Args[0] //获取程序名称
|
||||
now := time.Now() //获取当前时间
|
||||
pid := os.Getpid() //获取进程ID
|
||||
setPanicRedirect := true
|
||||
if panicFileURL == "" { //空路径不设置
|
||||
setPanicRedirect = false
|
||||
}
|
||||
if !strings.Contains(panicFileURL, ":") && !strings.HasPrefix(panicFileURL, "/") { //相对路径
|
||||
panicFileURL = fmt.Sprintf("%s%s%s", fileutils.GetCurrentDirectory(), string(os.PathSeparator), panicFileURL)
|
||||
}
|
||||
|
||||
// fileDir := ""
|
||||
// lastIndex := strings.LastIndex(panicFileURL, string(os.PathSeparator))
|
||||
// if lastIndex > 0 {
|
||||
// fileDir = panicFileURL[:lastIndex]
|
||||
// }
|
||||
|
||||
// if err := os.MkdirAll(fileDir, 0755); err != nil {
|
||||
// panic(fmt.Sprintf("创建错误重定向文件夹路径出错:%s", err.Error()))
|
||||
// }
|
||||
|
||||
if setPanicRedirect {
|
||||
stderrredirect.PanicRedirect(panicFileURL)
|
||||
}
|
||||
|
||||
timeStr := now.Format("2006-01-02 15:04:05") //设定时间格式
|
||||
stderrredirect.PanicFile.WriteString("\n\n\n↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\r\n")
|
||||
stderrredirect.PanicFile.WriteString(fmt.Sprintf("%s-%d-%s dump LOG\r\n", exeName, pid, timeStr))
|
||||
// stderrredirect.PanicFile.WriteString(fmt.Sprintf("%v\r\n", err)) //输出panic信息
|
||||
stderrredirect.PanicFile.WriteString(string(debug.Stack())) //输出堆栈信息
|
||||
stderrredirect.PanicFile.WriteString("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\r\n")
|
||||
|
||||
// file, err := os.OpenFile(panicFileURL, os.O_CREATE|os.O_APPEND, 0666)
|
||||
// if err != nil {
|
||||
// panic(fmt.Sprintf("panic重定向出错:%s", err.Error()))
|
||||
// }
|
||||
|
||||
// defer file.Close()
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package stderrredirect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/fileutils"
|
||||
)
|
||||
|
||||
var PanicFile *os.File
|
||||
|
||||
var doOnce sync.Once
|
||||
|
||||
//PanicRedirect panic重定向
|
||||
func PanicRedirect(fileURL string) {
|
||||
|
||||
doOnce.Do(func() {
|
||||
if !strings.HasPrefix(fileURL, "/") { //相对路径
|
||||
fileURL = fmt.Sprintf("%s%s%s", fileutils.GetCurrentDirectory(), string(os.PathSeparator), fileURL)
|
||||
}
|
||||
|
||||
lastIndex := strings.LastIndex(fileURL, string(os.PathSeparator))
|
||||
|
||||
fileDir := ""
|
||||
|
||||
if lastIndex > 0 {
|
||||
fileDir = fileURL[:lastIndex]
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(fileDir, 0755); err != nil {
|
||||
panic(fmt.Sprintf("创建错误重定向文件夹路径出错:%s", err.Error()))
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(fileURL, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
PanicFile = file
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package stderrredirect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/fileutils"
|
||||
)
|
||||
|
||||
//错误输出重定向,用于捕获闪退信息
|
||||
|
||||
//PanicFile 记录闪退的文件
|
||||
var PanicFile *os.File
|
||||
|
||||
var doOnce sync.Once
|
||||
|
||||
//PanicRedirect panic重定向
|
||||
func PanicRedirect(fileURL string) {
|
||||
|
||||
doOnce.Do(func() {
|
||||
if !strings.Contains(fileURL, ":") { //相对路径
|
||||
fileURL = fmt.Sprintf("%s%s%s", fileutils.GetCurrentDirectory(), string(os.PathSeparator), fileURL)
|
||||
}
|
||||
|
||||
//fmt.Printf("FileURL:%s\n", fileURL)
|
||||
|
||||
lastIndex := strings.LastIndex(fileURL, string(os.PathSeparator))
|
||||
|
||||
fileDir := ""
|
||||
|
||||
if lastIndex > 0 {
|
||||
fileDir = fileURL[:lastIndex]
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(fileDir, 0755); err != nil {
|
||||
panic(fmt.Sprintf("创建错误重定向文件夹路径出错:%s", err.Error()))
|
||||
}
|
||||
|
||||
//fileDir := strings.LastIndex(fileURL, string(os.PathSeparator))
|
||||
|
||||
file, err := os.OpenFile(fileURL, os.O_CREATE|os.O_APPEND, 0666)
|
||||
PanicFile = file
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("panic重定向出错:%s", err.Error()))
|
||||
}
|
||||
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||
setStdHandle := kernel32.NewProc("SetStdHandle")
|
||||
sh := syscall.STD_ERROR_HANDLE
|
||||
v, _, err := setStdHandle.Call(uintptr(sh), uintptr(file.Fd()))
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package stringsp
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var strModel = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
var stringsBytes = []byte(strModel)
|
||||
var strModelLength = len(strModel)
|
||||
|
||||
var numStrModel = "0123456789"
|
||||
var numStrBytes = []byte(numStrModel)
|
||||
var numStrLength = len(numStrModel)
|
||||
|
||||
var randVar = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
//GetRandomString 生成随机字符串
|
||||
func GetRandomString(len int) string {
|
||||
var resBuf strings.Builder
|
||||
for i := 0; i < len; i++ {
|
||||
resBuf.WriteByte(stringsBytes[randVar.Intn(strModelLength)])
|
||||
}
|
||||
return resBuf.String()
|
||||
}
|
||||
|
||||
//GetRandomStringNum 生成随机数字字符串
|
||||
func GetRandomStringNum(len int) string {
|
||||
var resBuf strings.Builder
|
||||
for i := 0; i < len; i++ {
|
||||
resBuf.WriteByte(numStrBytes[randVar.Intn(numStrLength)])
|
||||
}
|
||||
return resBuf.String()
|
||||
|
||||
}
|
||||
|
||||
var timeStampIDMutex sync.Mutex
|
||||
var pretimeStampID int64 = 0
|
||||
|
||||
//GetTimeStampID 获取时间戳ID
|
||||
func GetTimeStampID() int64 {
|
||||
timeStampIDMutex.Lock()
|
||||
defer timeStampIDMutex.Unlock()
|
||||
id := time.Now().UnixNano()
|
||||
|
||||
CHECK:
|
||||
if id == pretimeStampID || id < pretimeStampID {
|
||||
if id < pretimeStampID {
|
||||
id = pretimeStampID + 1
|
||||
} else {
|
||||
id++
|
||||
}
|
||||
|
||||
goto CHECK
|
||||
}
|
||||
|
||||
pretimeStampID = id
|
||||
return id
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
//go:build adminweb
|
||||
// +build adminweb
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ljymc/goports/web"
|
||||
"log"
|
||||
)
|
||||
|
||||
func RunAdminWeb(listenPort int) {
|
||||
listen := fmt.Sprintf(":%d", listenPort)
|
||||
go web.RunAdminWeb(listen)
|
||||
log.Printf("AdminWeb listen on %s", listen)
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func HandlerStaticFiles() gin.HandlerFunc {
|
||||
fileServer := http.FileServer(http.FS(stafs))
|
||||
return func(c *gin.Context) {
|
||||
staticFile := isStaticFile(http.FS(stafs), c.Request.URL.Path, true)
|
||||
if staticFile {
|
||||
fileServer.ServeHTTP(c.Writer, c.Request)
|
||||
c.Abort()
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
func isStaticFile(fs http.FileSystem, name string, redirect bool) (isFile bool) {
|
||||
const indexPage = "/index.html"
|
||||
if strings.HasSuffix(name, indexPage) {
|
||||
return true
|
||||
}
|
||||
f, err := fs.Open(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.Stat()
|
||||
return err == nil
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# goports-adminviews
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0" />
|
||||
|
||||
|
||||
<title>goports-admin</title>
|
||||
</head>
|
||||
<body style="margin:0">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "goports-adminviews",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 4173"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.0.4",
|
||||
"axios": "^0.27.2",
|
||||
"element-plus": "^2.2.2",
|
||||
"vue": "^3.2.36",
|
||||
"vue-clipboard3": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^2.3.3",
|
||||
"@vue/cli-plugin-typescript": "~5.0.0",
|
||||
"babel-plugin-import": "^1.13.5",
|
||||
"sass": "^1.52.1",
|
||||
"unplugin-auto-import": "^0.8.6",
|
||||
"unplugin-vue-components": "^0.19.6",
|
||||
"vite": "^2.9.9"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
|
@ -0,0 +1,141 @@
|
|||
<template>
|
||||
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<el-header class="header" id="header" >
|
||||
<!-- <p class="title">大吉</p>
|
||||
<p class="version">version:{{version}}</p> -->
|
||||
<Pmenu class="menu"></Pmenu>
|
||||
</el-header>
|
||||
<el-container>
|
||||
<!-- <el-aside width="initial" id="aside" direction="vertical" >
|
||||
|
||||
<Pmenu v-if="global.currentPage.value=='#login'?false:true"></Pmenu>
|
||||
|
||||
</el-aside> -->
|
||||
<el-container class="body">
|
||||
<el-main id="pageContent">
|
||||
<Log v-if="global.currentPage.value=='#log'?true:false"></Log>
|
||||
<Status v-if="global.currentPage.value=='#status'?true:false"></Status>
|
||||
<Relayset v-if="global.currentPage.value=='#relayset'?true:false"></Relayset>
|
||||
<Pset v-if="global.currentPage.value=='#set'?true:false"></Pset>
|
||||
<Login v-if="global.currentPage.value=='#login'?true:false"></Login>
|
||||
<WhiteListSet v-if="global.currentPage.value=='#whitelistset'?true:false"></WhiteListSet>
|
||||
<WhiteLists v-if="global.currentPage.value=='#whitelists'?true:false"></WhiteLists>
|
||||
<BlackLists v-if="global.currentPage.value=='#blacklists'?true:false"></BlackLists>
|
||||
</el-main>
|
||||
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted,ref,inject ,computed } from 'vue'
|
||||
import Status from './components/status.vue'
|
||||
import Log from './components/log.vue';
|
||||
import Pmenu from './components/pmenu.vue';
|
||||
import Relayset from './components/relayset.vue';
|
||||
import Pset from './components/pset.vue';
|
||||
import Login from './components/login.vue';
|
||||
import WhiteListSet from './components/WhiteListSet.vue';
|
||||
import WhiteLists from './components/WhiteLists.vue';
|
||||
import BlackLists from './components/BlackLists.vue';
|
||||
|
||||
|
||||
import {apiGetVersion} from "./apis/utils.js"
|
||||
|
||||
//console.log("111")
|
||||
|
||||
const global:any = inject("global")
|
||||
|
||||
const version = ref("0.0.0")
|
||||
|
||||
const queryVersion = ()=>{
|
||||
apiGetVersion().then((res) => {
|
||||
if (res.ret == 0) {
|
||||
version.value = res.version
|
||||
return
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
//console.log("222")
|
||||
//queryVersion()
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
#pageContent{
|
||||
margin:0;
|
||||
height: 95vh;
|
||||
overflow: hidden;
|
||||
padding-left: 1px;
|
||||
padding-right: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body{
|
||||
margin: 0;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#header {
|
||||
background-color: #0d8bbb;
|
||||
height: fit-content;
|
||||
width:100%;
|
||||
padding:0
|
||||
}
|
||||
|
||||
.title {
|
||||
float:left;
|
||||
text-align: left;
|
||||
margin-left: 10%;
|
||||
font-size:25px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
float:left;
|
||||
|
||||
height: 30px;
|
||||
width:100vw;
|
||||
}
|
||||
|
||||
|
||||
.version{
|
||||
float:right;
|
||||
|
||||
}
|
||||
|
||||
.title,.version{
|
||||
width:100%;
|
||||
color: aliceblue;
|
||||
font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
/**
|
||||
* 使用方式
|
||||
* * * main.js挂载全局
|
||||
* import storage from './utils/storage';
|
||||
const app = createApp(App);
|
||||
app.config.globalProperties.$storage = storage;
|
||||
* * * vue3.0 全局使用 vue实例上的数据
|
||||
* import { defineComponent, reactive, getCurrentInstance } from "vue";
|
||||
* let { appContext } = getCurrentInstance();
|
||||
appContext.config.globalProperties.$storage.setItem("username","aaa");
|
||||
appContext.config.globalProperties.$storage.setItem("age",20);
|
||||
appContext.config.globalProperties.$storage.clearItem("age");
|
||||
appContext.config.globalProperties.$storage.clearAll();
|
||||
*/
|
||||
export default {
|
||||
getStorage () { // 先获取该项目的 命名存储空间 下的storage数据 maneger
|
||||
return JSON.parse(window.localStorage.getItem("goports") || "{}");
|
||||
},
|
||||
setItem (key, val) {
|
||||
let storage = this.getStorage()
|
||||
// console.log("setItem", storage);
|
||||
storage[key] = val; // 为当前对象添加 需要存储的值
|
||||
window.localStorage.setItem("goports", JSON.stringify(storage)) // 保存到本地
|
||||
},
|
||||
getItem (key) {
|
||||
return this.getStorage()[key]
|
||||
},
|
||||
// 清空 当前的项目下命名存储的空间 该key项的 Storage 数据
|
||||
clearItem (key) {
|
||||
let storage = this.getStorage()
|
||||
delete storage[key]
|
||||
window.localStorage.setItem(config.namespace, JSON.stringify(storage)) // 保存到本地
|
||||
},
|
||||
// 清空所有的 当前的项目下命名存储的空间 Storage 数据
|
||||
clearAll () {
|
||||
window.localStorage.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
// 导入axios实例
|
||||
import httpRequest from '@/request/index'
|
||||
import storage from './storage.js'
|
||||
|
||||
// 获取锚点
|
||||
export function GetHash() {
|
||||
return location.hash
|
||||
}
|
||||
|
||||
// 设置锚点
|
||||
export function SetHash(hash) {
|
||||
location.hash = hash
|
||||
}
|
||||
|
||||
export function GetToken(){
|
||||
//console.log("getTokenkkk: "+storage.getItem("token"))
|
||||
return storage.getItem("token")==undefined?"":storage.getItem("token");
|
||||
}
|
||||
|
||||
|
||||
// 获取状态信息
|
||||
export function apiGetStatus() {
|
||||
return httpRequest({
|
||||
url: '/api/status',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf()},
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export function apiGetLogs(pretimestamp) {
|
||||
return httpRequest({
|
||||
url: "/api/logs",
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{pre:pretimestamp,_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function apiGetRuleList() {
|
||||
return httpRequest({
|
||||
url: '/api/rulelist',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function apiAddRule(data) {
|
||||
return httpRequest({
|
||||
url: '/api/rule',
|
||||
method: 'post',
|
||||
headers:{'Authorization':GetToken()},
|
||||
data:data
|
||||
})
|
||||
}
|
||||
|
||||
export function apiDeleteRule(configure) {
|
||||
return httpRequest({
|
||||
url: '/api/rule',
|
||||
method: 'delete',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf(),rule:configure}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiAlterRule(data) {
|
||||
return httpRequest({
|
||||
url: '/api/rule',
|
||||
method: 'put',
|
||||
headers:{'Authorization':GetToken()},
|
||||
data:data
|
||||
})
|
||||
}
|
||||
|
||||
export function apiRuleEnable(key,enable) {
|
||||
return httpRequest({
|
||||
url: '/api/rule/enable',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf(),enable:enable,key:key}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiQueryBaseConfigure() {
|
||||
return httpRequest({
|
||||
url: '/api/baseconfigure',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiAlterBaseConfigure(data) {
|
||||
return httpRequest({
|
||||
url: '/api/baseconfigure',
|
||||
method: 'put',
|
||||
headers:{'Authorization':GetToken()},
|
||||
data:data
|
||||
})
|
||||
}
|
||||
|
||||
export function apiLogin(data) {
|
||||
return httpRequest({
|
||||
url: '/api/login',
|
||||
method: 'post',
|
||||
data:data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function apiRebootProgram() {
|
||||
return httpRequest({
|
||||
url: '/api/reboot_program',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function apiAlterWhiteListConfigure(data) {
|
||||
return httpRequest({
|
||||
url: '/api/whitelist/configure',
|
||||
method: 'put',
|
||||
headers:{'Authorization':GetToken()},
|
||||
data:data
|
||||
})
|
||||
}
|
||||
|
||||
export function apiGetWhiteListConfigure(data) {
|
||||
return httpRequest({
|
||||
url: '/api/whitelist/configure',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiGetWhiteList(data) {
|
||||
return httpRequest({
|
||||
url: '/api/whitelist',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf()},
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function apiFlushWhiteList(ip,life) {
|
||||
return httpRequest({
|
||||
url: '/api/whitelist/flush',
|
||||
method: 'put',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf(),ip:ip,life:life}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiDeleteWhiteList(ip,life) {
|
||||
return httpRequest({
|
||||
url: '/api/whitelist',
|
||||
method: 'delete',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf(),ip:ip},
|
||||
})
|
||||
}
|
||||
|
||||
export function apiGetBlackList(data) {
|
||||
return httpRequest({
|
||||
url: '/api/blacklist',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function apiFlushBlackList(ip,life) {
|
||||
return httpRequest({
|
||||
url: '/api/blacklist/flush',
|
||||
method: 'put',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf(),ip:ip,life:life}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiDeleteBlackList(ip,life) {
|
||||
return httpRequest({
|
||||
url: '/api/blacklist',
|
||||
method: 'delete',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf(),ip:ip}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function apiGetVersion() {
|
||||
return httpRequest({
|
||||
url: '/version',
|
||||
method: 'get',
|
||||
params:{_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiLogout() {
|
||||
return httpRequest({
|
||||
url: '/api/logout',
|
||||
method: 'put',
|
||||
headers:{'Authorization':GetToken()},
|
||||
})
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
|
||||
.PageRadius {
|
||||
height: 90vh;
|
||||
width: 100%;
|
||||
max-width: 1300px;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 0;
|
||||
margin: 20px
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition: color 0.5s, background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
.common-layout {
|
||||
.el-header,
|
||||
.el-footer,
|
||||
.el-main,
|
||||
.el-aside {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-header,
|
||||
.el-footer {
|
||||
background-color: var(--el-color-primary-light-7);
|
||||
color: var(--el-text-color-primary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-aside {
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
color: var(--el-text-color-primary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-main {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-text-color-primary);
|
||||
text-align: center;
|
||||
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69" xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
After Width: | Height: | Size: 308 B |
|
@ -0,0 +1,221 @@
|
|||
<template>
|
||||
|
||||
<div class="PageRadius" :style="{
|
||||
borderRadius: 'base',
|
||||
}">
|
||||
|
||||
<el-affix position="top" :offset="0" class="affix-container">
|
||||
<el-button type="primary" @click="showAddBlackListDialog">黑名单添加 <el-icon>
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-affix>
|
||||
|
||||
<el-scrollbar height="100%">
|
||||
|
||||
|
||||
<div class="formradius" :style="{
|
||||
borderRadius: 'base',
|
||||
}">
|
||||
|
||||
|
||||
|
||||
<el-table :data="Blacklist" style="width: 700px" height="85vh">
|
||||
<el-table-column prop="IP" label="IP" width="200" />
|
||||
<el-table-column prop="Effectivetime" label="有效时间" width="200" />
|
||||
<el-table-column fixed="right" label="操作" width="300" >
|
||||
<template #default="list">
|
||||
<el-button link type="primary" size="small"
|
||||
@click="flushBlackListEffectivetime(list.$index, Blacklist[list.$index], 0, '确认要刷新IP[' + Blacklist[list.$index].IP + ']的有效时间?')">
|
||||
刷新有效时间</el-button>
|
||||
<el-button link type="primary" size="small"
|
||||
@click="flushBlackListEffectivetime(list.$index, Blacklist[list.$index], 666666, '确认要设置IP[' + Blacklist[list.$index].IP + ']为长期有效?')">
|
||||
设置永久有效</el-button>
|
||||
<el-button link type="primary" size="small"
|
||||
@click="deleteBlackList(list.$index, Blacklist[list.$index])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</el-scrollbar>
|
||||
|
||||
|
||||
<el-dialog v-model="addBlackListDialogVisible" title="添加黑名单IP" draggable :show-close="false" width="400px">
|
||||
|
||||
<el-form :model="addBlackListForm">
|
||||
<el-form-item label="IP" label-width="auto">
|
||||
<el-input v-model="addBlackListForm.IP" autocomplete="off" />
|
||||
</el-form-item>
|
||||
<el-form-item label="有效时间(小时)" label-width="auto">
|
||||
<el-input-number v-model="addBlackListForm.Life" :min="1" :max="999999" />
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="addBlackListDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="addBlackList">添加</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import {MessageShow} from '../utils/ui'
|
||||
import {isIP} from '../utils/utils'
|
||||
|
||||
import { apiGetBlackList, apiFlushBlackList, apiDeleteBlackList } from '../apis/utils'
|
||||
var Blacklist = ref([{ IP: "", Effectivetime: "" }])
|
||||
Blacklist.value.splice(0, 1)
|
||||
|
||||
const addBlackListDialogVisible = ref(false)
|
||||
const addBlackListForm = ref({ IP: "", Life: 0 })
|
||||
|
||||
const showAddBlackListDialog = () => {
|
||||
addBlackListDialogVisible.value = true
|
||||
addBlackListForm.value.IP = ""
|
||||
addBlackListForm.value.Life = 666666
|
||||
}
|
||||
|
||||
const flushBlackListEffectivetime = (index, item, life, text) => {
|
||||
ElMessageBox.confirm(
|
||||
text,
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
flushBlackListlife(index, item.IP, life)
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
const flushBlackListlife = (index, ip, life) => {
|
||||
apiFlushBlackList(ip, life).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
Blacklist.value[index].Effectivetime = res.data
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "刷新IP[" + ip + "]有效时间出错")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
const addBlackList = () => {
|
||||
|
||||
if (!isIP(addBlackListForm.value.IP)) {
|
||||
MessageShow("error", "IP格式有误,请检查修正后再添加")
|
||||
return
|
||||
}
|
||||
|
||||
apiFlushBlackList(addBlackListForm.value.IP, addBlackListForm.value.Life).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
let item = { IP: addBlackListForm.value.IP, Effectivetime: res.data }
|
||||
Blacklist.value.push(item)
|
||||
addBlackListDialogVisible.value = false
|
||||
// MessageShow("success", "黑名单添加成功")
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "刷新IP[" + addBlackListForm.value.IP + "]有效时间出错")
|
||||
})
|
||||
}
|
||||
|
||||
const deleteBlackList = (index, item) => {
|
||||
ElMessageBox.confirm(
|
||||
'确认要删除IP [' + item.IP + "]的黑名单记录?",
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
apiDeleteBlackList(item.IP).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
Blacklist.value.splice(index, 1)
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "删除[" + item.IP + "]的黑名单记录出错")
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const queryBlackList = () => {
|
||||
apiGetBlackList().then((res) => {
|
||||
if (res.ret == 0) {
|
||||
Blacklist.value = res.data
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
//console.log("getAdminURL "+getAdminURL())
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "查询黑名单列表出错")
|
||||
})
|
||||
}
|
||||
|
||||
const keydown = (e) => {
|
||||
if (e.keyCode != 13) {
|
||||
return
|
||||
}
|
||||
if (!addBlackListDialogVisible.value) {
|
||||
return
|
||||
}
|
||||
addBlackList()
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
queryBlackList();
|
||||
window.addEventListener('keydown', keydown)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.formradius {
|
||||
border: 0px solid var(--el-border-color);
|
||||
border-radius: 0;
|
||||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
padding: 15px;
|
||||
|
||||
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,112 @@
|
|||
<template>
|
||||
<!-- <div class="logterm">
|
||||
{{weblogsContent}}
|
||||
</div> -->
|
||||
<el-scrollbar max-height="95vh" class="logtermv2" v-loading="logLoading" element-loading-background="transparent">
|
||||
{{weblogsContent}}
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {apiGetLogs } from '../apis/utils'
|
||||
import { onMounted,onUnmounted,ref,inject } from 'vue'
|
||||
import {GetHash,SetHash} from'../apis/utils.js'
|
||||
|
||||
const global:any = inject("global")
|
||||
var preLogTimestamp = ""
|
||||
var preStartTime = ""
|
||||
const weblogsContent = ref("")
|
||||
var logLoading = ref(true)
|
||||
|
||||
|
||||
|
||||
|
||||
function queryLastlogs() {
|
||||
// if(GetHash()!="#log"){
|
||||
// return ;
|
||||
// }
|
||||
|
||||
|
||||
apiGetLogs(preLogTimestamp).then((res) => {
|
||||
logLoading.value =false
|
||||
|
||||
if (preStartTime!=res.starttime){
|
||||
weblogsContent.value = ""
|
||||
preStartTime = res.starttime
|
||||
}
|
||||
|
||||
if (res.logs!=null && res.logs.length > 0) {
|
||||
if (res.logs[0].timestamp<=preLogTimestamp){
|
||||
return
|
||||
}
|
||||
|
||||
preLogTimestamp = res.logs[res.logs.length - 1].timestamp
|
||||
for(var i=0;i<res.logs.length;i++){
|
||||
weblogsContent.value += res.logs[i].log +"\n"
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
var timerID:any
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
queryLastlogs();
|
||||
timerID = setInterval(() => {
|
||||
queryLastlogs();
|
||||
}, 1000);
|
||||
})
|
||||
|
||||
onUnmounted(()=>{
|
||||
clearInterval(timerID)
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.logtermv2 {
|
||||
background-color: black;
|
||||
height: 95vh;
|
||||
width: 100%;
|
||||
color: white;
|
||||
text-align: left;
|
||||
padding-left: 3px;
|
||||
|
||||
border: 10px;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.logterm::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.logterm::-webkit-scrollbar-thumb {
|
||||
background-color: #c0c0c0;
|
||||
border-radius: 10%;
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
<template>
|
||||
|
||||
<div class="PageRadius" :style="{
|
||||
borderRadius: 'base',
|
||||
}">
|
||||
|
||||
<div class="formradius" :style="{
|
||||
borderRadius: 'base',
|
||||
}">
|
||||
|
||||
<el-form :model="form" class="SetForm" label-width="auto">
|
||||
|
||||
<el-form-item label="管理账号" id="account">
|
||||
<el-input v-model="form.Account" placeholder="管理账号" autocomplete="off" style="witdh:390px;" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="管理密码" id="account">
|
||||
<el-input show-password v-model="form.Password" placeholder="管理密码" autocomplete="off"
|
||||
style="witdh:390px;" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="rememberPasswordChecked" label="记住密码" size="large" @change="rememberPasswordCheckedChange" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
</el-form>
|
||||
|
||||
<el-button type="primary" round @click="Login">登录</el-button>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import { onMounted, onUnmounted, ref, inject } from 'vue'
|
||||
import { apiLogin } from '../apis/utils'
|
||||
import {MessageShow} from '../utils/ui'
|
||||
|
||||
|
||||
const rememberPasswordChecked = ref(true)
|
||||
|
||||
const global: any = inject("global")
|
||||
|
||||
const form = ref({
|
||||
Account: "",
|
||||
Password: ""
|
||||
})
|
||||
|
||||
|
||||
|
||||
const rememberPasswordCheckedChange= (checked )=>{
|
||||
//console.log("记住密码:"+checked)
|
||||
SaveLoginInfo(checked)
|
||||
}
|
||||
|
||||
|
||||
const SaveLoginInfo = (checked) =>{
|
||||
global.storage.setItem("rememberPassword",checked)
|
||||
if (checked){
|
||||
global.storage.setItem("loginAccount",form.value.Account)
|
||||
global.storage.setItem("loginPassword",form.value.Password)
|
||||
return
|
||||
}
|
||||
global.storage.setItem("loginAccount","")
|
||||
global.storage.setItem("loginPassword","")
|
||||
}
|
||||
|
||||
const ReadLoginInfo = ()=>{
|
||||
let rememberPassword = global.storage.getItem("rememberPassword")
|
||||
rememberPasswordChecked.value = rememberPassword == undefined || rememberPassword == false ? false : true;
|
||||
if(!rememberPassword){
|
||||
return
|
||||
}
|
||||
form.value.Account= global.storage.getItem("loginAccount")==undefined?"":global.storage.getItem("loginAccount")
|
||||
form.value.Password = global.storage.getItem("loginPassword")==undefined?"":global.storage.getItem("loginPassword")
|
||||
}
|
||||
|
||||
|
||||
const Login = () => {
|
||||
if (form.value.Account == "" || form.value.Password == "") {
|
||||
MessageShow("error", "账号或密码不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
SaveLoginInfo(rememberPasswordChecked.value)
|
||||
apiLogin(form.value).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
MessageShow("success", "登录成功")
|
||||
global.storage.setItem("token",res.token)
|
||||
global.currentPage.value = "#set"
|
||||
location.hash="#set"
|
||||
//console.log("cookies:"+res.cookies)
|
||||
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
}).catch((error) => {
|
||||
console.log("登录失败,网络请求出错:" + error)
|
||||
MessageShow("error", "登录失败,网络请求出错")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const keydown = (e) => {
|
||||
if (e.keyCode == 13) {
|
||||
Login()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', keydown)
|
||||
|
||||
|
||||
ReadLoginInfo()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.formradius {
|
||||
border: 0px solid var(--el-border-color);
|
||||
border-radius: 0;
|
||||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
padding: 15px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,233 @@
|
|||
<template>
|
||||
|
||||
<div class="PageRadius" :style="{
|
||||
borderRadius: 'base',
|
||||
}">
|
||||
|
||||
<el-scrollbar height="100%">
|
||||
|
||||
|
||||
<div class="formradius" :style="{
|
||||
borderRadius: 'base',
|
||||
}" >
|
||||
|
||||
<el-form :model="form" class="SetForm" label-width="auto">
|
||||
<el-form-item label="后台管理端口" id="adminListen">
|
||||
<el-input-number v-model="form.AdminWebListenPort" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="外网访问" id="adminListen">
|
||||
<el-switch v-model="form.AllowInternetaccess" class="mb-1"
|
||||
inline-prompt
|
||||
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
||||
width="50px"
|
||||
active-text="允许"
|
||||
inactive-text="禁止" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="管理登录账号" id="adminAccount">
|
||||
<el-input v-model="form.AdminAccount" placeholder="管理登录账号" autocomplete="off" style="witdh:390px;" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="管理登录密码" id="adminPassword">
|
||||
<el-input v-model="form.AdminPassword" placeholder="管理登录密码" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="全局最大端口代理数量" id="proxyCountLimit">
|
||||
<el-input-number v-model="form.ProxyCountLimit" autocomplete="off" :min="1" :max="1024" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="全局最大并发连接数" id="globalMaxConnections">
|
||||
<el-input-number v-model="form.GlobalMaxConnections" autocomplete="off" :min="1" :max="65535" />
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<el-button type="primary" round @click="RequestAlterConfigure">保存修改</el-button>
|
||||
<el-button type="info" round @click="resetFormData">撤销改动</el-button>
|
||||
<el-button type="danger" round @click="rebootProgram" :disabled="disableRebootButton">重启程序</el-button>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import { onMounted, onUnmounted, ref, computed, reactive } from 'vue'
|
||||
import { apiQueryBaseConfigure,apiAlterBaseConfigure,apiRebootProgram } from '../apis/utils'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
import {MessageShow} from '../utils/ui'
|
||||
|
||||
const formLabelWidth = '10vw'
|
||||
console.log("window.location.href "+window.location.href)
|
||||
console.log("window.location.port "+window.location.port)
|
||||
console.log("window.location.host "+window.location.host)
|
||||
console.log("window.location "+JSON.stringify(window.location))
|
||||
const disableRebootButton = ref(false)
|
||||
|
||||
const getAdminURL = ()=>{
|
||||
return window.location.protocol +"//"+window.location.hostname+":"+preFormData.value.AdminWebListenPort
|
||||
}
|
||||
|
||||
const rawData = {
|
||||
AdminWebListenPort: 1,
|
||||
AdminAccount: "",
|
||||
AdminPassword: "",
|
||||
ProxyCountLimit: 1,
|
||||
GlobalMaxConnections: 1,
|
||||
AllowInternetaccess: false,
|
||||
}
|
||||
|
||||
const form = ref(rawData)
|
||||
const preFormData = ref(rawData)
|
||||
|
||||
|
||||
|
||||
const resetFormData = ()=>{
|
||||
form.value.AdminWebListenPort = preFormData.value.AdminWebListenPort
|
||||
form.value.AdminAccount=preFormData.value.AdminAccount
|
||||
form.value.AdminPassword=preFormData.value.AdminPassword
|
||||
form.value.ProxyCountLimit = preFormData.value.ProxyCountLimit
|
||||
form.value.GlobalMaxConnections = preFormData.value.GlobalMaxConnections
|
||||
form.value.AllowInternetaccess = preFormData.value.AllowInternetaccess
|
||||
}
|
||||
|
||||
const syncToPreFormData = (data:any)=>{
|
||||
preFormData.value.AdminWebListenPort = data.value.AdminWebListenPort
|
||||
preFormData.value.AdminAccount=data.value.AdminAccount
|
||||
preFormData.value.AdminPassword=data.value.AdminPassword
|
||||
preFormData.value.ProxyCountLimit = data.value.ProxyCountLimit
|
||||
preFormData.value.GlobalMaxConnections = data.value.GlobalMaxConnections
|
||||
preFormData.value.AllowInternetaccess = data.value.AllowInternetaccess
|
||||
}
|
||||
|
||||
|
||||
|
||||
const rebootProgram = () => {
|
||||
disableRebootButton.value = true;
|
||||
|
||||
ElMessageBox.confirm(
|
||||
'确定要重启goports?',
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '点错了',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
apiRebootProgram().then((res) => {
|
||||
MessageShow("success", "重启成功,3秒后自动跳转到新登录连接")
|
||||
|
||||
setTimeout(()=>{
|
||||
window.location.href = getAdminURL()
|
||||
},3000)
|
||||
|
||||
//console.log("getAdminURL "+getAdminURL())
|
||||
}).catch((error) => {
|
||||
disableRebootButton.value = false;
|
||||
console.log("重启操作出错:" + error)
|
||||
MessageShow("error", "重启操作出错")
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
disableRebootButton.value = false;
|
||||
})
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
const queryConfigure = ()=>{
|
||||
apiQueryBaseConfigure().then((res) => {
|
||||
if (res.ret==0){
|
||||
form.value = res.baseconfigure
|
||||
syncToPreFormData(form)
|
||||
return
|
||||
}
|
||||
MessageShow("error", "获取基本配置出错")
|
||||
}).catch((error) => {
|
||||
console.log("获取转发规则列表出错:" + error)
|
||||
MessageShow("error", "获取基本配置出错")
|
||||
})
|
||||
}
|
||||
|
||||
const RequestAlterConfigure =()=>{
|
||||
apiAlterBaseConfigure(form.value).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
MessageShow("success", "配置修改成功")
|
||||
syncToPreFormData(form)
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
}).catch((error) => {
|
||||
console.log("配置修改失败,网络请求出错:" + error)
|
||||
MessageShow("error", "配置修改失败,网络请求出错")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
queryConfigure()
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
|
||||
.SetForm {
|
||||
margin-top: 15px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.formradius{
|
||||
border: 0px solid var(--el-border-color);
|
||||
border-radius: 0;
|
||||
margin:0 auto;
|
||||
width:fit-content;
|
||||
padding:15px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#adminListen {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
#adminAccount {
|
||||
width: 30vw;
|
||||
max-width: 360px;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
|
||||
#adminPassword {
|
||||
width: 30vw;
|
||||
max-width: 360px;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
|
||||
#proxyCountLimit {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
|
||||
#globalMaxConnections {
|
||||
width: 360px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,184 @@
|
|||
<template>
|
||||
<el-menu :default-active="activeIndex" class="el-menu-demo menu" mode="horizontal" :ellipsis="false"
|
||||
@select="handleSelect">
|
||||
|
||||
<el-sub-menu index="#menu" v-if="global.currentPage.value != '#login' ? true : false">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<Menu />
|
||||
</el-icon>
|
||||
<span>菜单</span>
|
||||
</template>
|
||||
|
||||
|
||||
<el-menu-item index="#status">
|
||||
<el-icon>
|
||||
<DataAnalysis />
|
||||
</el-icon>
|
||||
<template #title>总览</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="#log">
|
||||
<el-icon>
|
||||
<document />
|
||||
</el-icon>
|
||||
<template #title>程序日志</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-sub-menu index="#relay">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<Position />
|
||||
</el-icon>
|
||||
<span>端口转发</span>
|
||||
</template>
|
||||
|
||||
<el-menu-item index="#relayset">
|
||||
<el-icon>
|
||||
<List />
|
||||
</el-icon>
|
||||
<template #title>转发规则</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
<el-menu-item index="#whitelistset">
|
||||
<el-icon>
|
||||
<Setting />
|
||||
</el-icon>
|
||||
<template #title>白名单设置</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="#whitelists">
|
||||
<el-icon>
|
||||
<List />
|
||||
</el-icon>
|
||||
<template #title>白名单列表</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
<el-menu-item index="#blacklists">
|
||||
<el-icon>
|
||||
<List />
|
||||
</el-icon>
|
||||
<template #title>黑名单列表</template>
|
||||
</el-menu-item>
|
||||
|
||||
</el-sub-menu>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<el-menu-item index="#set">
|
||||
<el-icon>
|
||||
<setting />
|
||||
</el-icon>
|
||||
<template #title>设置</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
|
||||
<el-menu-item index="#logout">
|
||||
<el-icon>
|
||||
<Close />
|
||||
</el-icon>
|
||||
<template #title>退出登录</template>
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="flex-grow" />
|
||||
|
||||
<el-menu-item index="#logo">goports {{ version }}</el-menu-item>
|
||||
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { inject, ref, onMounted } from 'vue';
|
||||
import { SetHash, apiGetVersion } from '../apis/utils.js'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
const global: any = inject("global")
|
||||
|
||||
|
||||
const activeIndex = ref('#set')
|
||||
const version = ref("")
|
||||
|
||||
console.log("currentPage[menu]:" + global.currentPage.value)
|
||||
|
||||
|
||||
const queryVersion = () => {
|
||||
apiGetVersion().then((res) => {
|
||||
if (res.ret == 0) {
|
||||
version.value = res.version
|
||||
return
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function handleOpen(key, keyPath) {
|
||||
//console.log(key, keyPath);
|
||||
}
|
||||
function handleClose(key, keyPath) {
|
||||
//console.log(key, keyPath);
|
||||
}
|
||||
function handleSelect(key, keyPath, item, routeResult) {
|
||||
//console.log("选择菜单")
|
||||
//console.log(key, keyPath, item, routeResult);
|
||||
// switchView(key);
|
||||
switch (key) {
|
||||
case "#logout":
|
||||
ElMessageBox.confirm(
|
||||
'确定要注销登录?',
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '点错了',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
SetHash(key)
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
})
|
||||
break;
|
||||
case "#logo":
|
||||
window.open("https://github.com/ljymc/goports", "_blank");
|
||||
break;
|
||||
default:
|
||||
SetHash(key)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
queryVersion()
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.menu {
|
||||
background-color: #d9ecff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,99 @@
|
|||
<template>
|
||||
<div id="status" v-loading="logLoading" element-loading-background="transparent">
|
||||
<p class="status">总内存:{{ status.totleMem }} 已用:{{ status.usedMem }} 未用:{{ status.unusedMem }}</p>
|
||||
<p class="status">CPU全局使用率:{{ status.usedCPU }}</p>
|
||||
<p class="status">当前进程CPU使用率:{{ status.currentProcessUsedCPU }}</p>
|
||||
<p class="status">进程协程数:{{ status.goroutine }} 占用内存:{{ status.processUsedMem }}</p>
|
||||
<p class="status">goports全局连接数:{{ status.currentConnections }} </p>
|
||||
<p class="status">goports全局限制连接数:{{ status.maxConnections }}</p>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<style>
|
||||
#status {
|
||||
height: 95vh;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#proxys_status {
|
||||
font-size: 10px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
text-align: left;
|
||||
margin-left: 0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { apiGetStatus } from '../apis/utils'
|
||||
import { onMounted, onUnmounted, ref,inject } from 'vue'
|
||||
import { ElNotification } from 'element-plus'
|
||||
|
||||
const global:any = inject("global")
|
||||
|
||||
|
||||
|
||||
var logLoading = ref(true)
|
||||
|
||||
const clickTest = ()=>{
|
||||
ElNotification({
|
||||
title: 'Prompt',
|
||||
message: 'This is a message that does not automatically close',
|
||||
duration: 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
var status=ref({totleMem: '0m',
|
||||
usedMem:'0m',
|
||||
unusedMem:'0m',
|
||||
usedCPU:"0%",
|
||||
currentProcessUsedCPU:"0%",
|
||||
goroutine:"0",
|
||||
processUsedMem:"0m",
|
||||
currentConnections:0,
|
||||
maxConnections:0,
|
||||
proxysStatus:""})
|
||||
|
||||
var timerID:any
|
||||
|
||||
function flushStatus() {
|
||||
if(global.currentPage.value!="#status"){
|
||||
return ;
|
||||
}
|
||||
|
||||
apiGetStatus().then((res) => {
|
||||
logLoading.value =false
|
||||
status.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
flushStatus();
|
||||
timerID = setInterval(() => {
|
||||
flushStatus();
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
//console.log("onUnmounted status page ")
|
||||
clearInterval(timerID)
|
||||
});
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
<template>
|
||||
|
||||
<div class="PageRadius" :style="{
|
||||
borderRadius: 'base',
|
||||
}">
|
||||
<el-scrollbar height="100%">
|
||||
|
||||
|
||||
<div class="formradius" :style="{
|
||||
borderRadius: 'base',
|
||||
}">
|
||||
|
||||
<div class="whitelistConfigure">
|
||||
<el-form :model="whiteListBaseConfigureForm" class="SetForm" label-width="auto">
|
||||
|
||||
<el-form-item label="自定义URL" id="whitelisturl">
|
||||
<el-input v-model="whiteListBaseConfigureForm.URL" placeholder="自定义URL"
|
||||
autocomplete="off" style="witdh:250px;margin-bottom:4px;" />
|
||||
<el-tooltip class="box-item oneLine" effect="dark" placement="bottom"
|
||||
:content="getWhiteListURL">
|
||||
<el-button type="info" round @click="copyRelayConfigure(getWhiteListURL)"
|
||||
style="margin-right: 10px;">复制</el-button>
|
||||
</el-tooltip>
|
||||
<a>{{ getNewWhiteListURL }}</a>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="有效时长(小时)" id="whitelistActivelifeDuration">
|
||||
<el-input-number v-model="whiteListBaseConfigureForm.ActivelifeDuration"
|
||||
autocomplete="off" :min="1" :max="99999" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="认证账号" id="basicAccount">
|
||||
<el-input v-model="whiteListBaseConfigureForm.BasicAccount" placeholder="认证账号"
|
||||
autocomplete="off" style="witdh:250px;" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="认证密码" id="basicPassword">
|
||||
<el-input v-model="whiteListBaseConfigureForm.BasicPassword" placeholder="认证密码"
|
||||
autocomplete="off" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button type="primary" round @click="SaveWhiteListConfigure">保存配置</el-button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import {MessageShow} from '../utils/ui'
|
||||
import {CopyTotoClipboard} from '../utils/utils'
|
||||
import { apiAlterWhiteListConfigure, apiGetWhiteListConfigure} from '../apis/utils'
|
||||
|
||||
const whiteListBaseConfigureForm = ref({
|
||||
URL: "",
|
||||
ActivelifeDuration: 36,
|
||||
BasicAccount: "",
|
||||
BasicPassword: "",
|
||||
})
|
||||
|
||||
const preWhiteListBaseConfigureForm = ref({
|
||||
URL: "",
|
||||
ActivelifeDuration: 36,
|
||||
BasicAccount: "",
|
||||
BasicPassword: "",
|
||||
})
|
||||
|
||||
const getWhiteListURL = computed(() => {
|
||||
if (preWhiteListBaseConfigureForm.value.URL == undefined || preWhiteListBaseConfigureForm.value.URL == "") {
|
||||
return window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/wl"
|
||||
}
|
||||
return window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/wl/" + preWhiteListBaseConfigureForm.value.URL
|
||||
})
|
||||
|
||||
const getNewWhiteListURL = computed(() => {
|
||||
if (whiteListBaseConfigureForm.value.URL == undefined || whiteListBaseConfigureForm.value.URL == "") {
|
||||
return window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/wl"
|
||||
}
|
||||
return window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/wl/" + whiteListBaseConfigureForm.value.URL
|
||||
})
|
||||
|
||||
const copyRelayConfigure = (url: string) => {
|
||||
CopyTotoClipboard(url)
|
||||
MessageShow('success', '白名单认证地址 ' + url + ' 已复制到剪切板')
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const SaveWhiteListConfigure = () => {
|
||||
apiAlterWhiteListConfigure(whiteListBaseConfigureForm.value).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
MessageShow("success", "保存成功")
|
||||
preWhiteListBaseConfigureForm.value = whiteListBaseConfigureForm.value
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
//console.log("getAdminURL "+getAdminURL())
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "查询白名单设置出错")
|
||||
})
|
||||
}
|
||||
|
||||
const queryWhiteListConfigure = () => {
|
||||
apiGetWhiteListConfigure().then((res) => {
|
||||
if (res.ret == 0) {
|
||||
whiteListBaseConfigureForm.value = ref(res.data).value
|
||||
preWhiteListBaseConfigureForm.value.URL = whiteListBaseConfigureForm.value.URL
|
||||
preWhiteListBaseConfigureForm.value.ActivelifeDuration = whiteListBaseConfigureForm.value.ActivelifeDuration
|
||||
preWhiteListBaseConfigureForm.value.BasicAccount = whiteListBaseConfigureForm.value.BasicAccount
|
||||
preWhiteListBaseConfigureForm.value.BasicPassword = whiteListBaseConfigureForm.value.BasicPassword
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
//console.log("getAdminURL "+getAdminURL())
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "查询白名单设置出错")
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
queryWhiteListConfigure()
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.formradius{
|
||||
border: 0px solid var(--el-border-color);
|
||||
border-radius: 0;
|
||||
margin:0 auto;
|
||||
width:fit-content;
|
||||
padding:15px;
|
||||
|
||||
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,219 @@
|
|||
<template>
|
||||
|
||||
<div class="PageRadius" :style="{
|
||||
borderRadius: 'base',
|
||||
}">
|
||||
|
||||
<el-affix position="bottom" :offset="0" class="affix-container">
|
||||
<el-button type="primary" @click="showAddWhiteListDialog">白名单添加 <el-icon>
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-affix>
|
||||
|
||||
<el-scrollbar height="100%">
|
||||
|
||||
|
||||
<div class="formradius" :style="{
|
||||
borderRadius: 'base',
|
||||
}" >
|
||||
|
||||
|
||||
|
||||
|
||||
<el-table :data="whitelist" style="width: 700px" height="85vh">
|
||||
<el-table-column prop="IP" label="IP" width="200" />
|
||||
<el-table-column prop="Effectivetime" label="有效时间" width="200" />
|
||||
<el-table-column fixed="right" label="操作" width="300">
|
||||
<template #default="list">
|
||||
<el-button link type="primary" size="small"
|
||||
@click="flushWhiteListEffectivetime(list.$index, whitelist[list.$index], 0, '确认要刷新IP[' + whitelist[list.$index].IP + ']的有效时间?')">
|
||||
刷新有效时间</el-button>
|
||||
<el-button link type="primary" size="small"
|
||||
@click="flushWhiteListEffectivetime(list.$index, whitelist[list.$index], 666666, '确认要设置IP[' + whitelist[list.$index].IP + ']为长期有效?')">
|
||||
设置永久有效</el-button>
|
||||
<el-button link type="primary" size="small"
|
||||
@click="deleteWhiteList(list.$index, whitelist[list.$index])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</el-scrollbar>
|
||||
|
||||
|
||||
|
||||
<el-dialog v-model="addWhiteListDialogVisible" title="添加白名单IP" draggable :show-close="false" width="400px">
|
||||
|
||||
<el-form :model="addWhiteListForm">
|
||||
<el-form-item label="IP" label-width="auto">
|
||||
<el-input v-model="addWhiteListForm.IP" autocomplete="off" />
|
||||
</el-form-item>
|
||||
<el-form-item label="有效时间(小时)" label-width="auto">
|
||||
<el-input-number v-model="addWhiteListForm.Life" :min="1" :max="999999" />
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="addWhiteListDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="addWhiteList">添加</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import {MessageShow} from '../utils/ui'
|
||||
import {isIP} from '../utils/utils'
|
||||
import { apiGetWhiteList, apiFlushWhiteList, apiDeleteWhiteList, apiGetBlackList, apiFlushBlackList, apiDeleteBlackList } from '../apis/utils'
|
||||
var whitelist = ref([{ IP: "", Effectivetime: "" }])
|
||||
whitelist.value.splice(0, 1)
|
||||
|
||||
const addWhiteListDialogVisible = ref(false)
|
||||
const addWhiteListForm = ref({ IP: "", Life: 0 })
|
||||
|
||||
const showAddWhiteListDialog = () => {
|
||||
addWhiteListDialogVisible.value = true
|
||||
addWhiteListForm.value.IP = ""
|
||||
addWhiteListForm.value.Life = 24
|
||||
}
|
||||
|
||||
const flushWhiteListEffectivetime = (index, item, life, text) => {
|
||||
ElMessageBox.confirm(
|
||||
text,
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
flushWhiteListlife(index, item.IP, life)
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
const flushWhiteListlife = (index, ip, life) => {
|
||||
apiFlushWhiteList(ip, life).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
whitelist.value[index].Effectivetime = res.data
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "刷新IP[" + ip + "]有效时间出错")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
const addWhiteList = () => {
|
||||
|
||||
if (!isIP(addWhiteListForm.value.IP)) {
|
||||
MessageShow("error", "IP格式有误,请检查修正后再添加")
|
||||
return
|
||||
}
|
||||
|
||||
apiFlushWhiteList(addWhiteListForm.value.IP, addWhiteListForm.value.Life).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
let item = { IP: addWhiteListForm.value.IP, Effectivetime: res.data }
|
||||
whitelist.value.push(item)
|
||||
addWhiteListDialogVisible.value = false
|
||||
//MessageShow("success", "白名单添加成功")
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "刷新IP[" + addWhiteListForm.value.IP + "]有效时间出错")
|
||||
})
|
||||
}
|
||||
|
||||
const deleteWhiteList = (index, item) => {
|
||||
ElMessageBox.confirm(
|
||||
'确认要删除IP [' + item.IP + "]的白名单记录?",
|
||||
'Warning',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
apiDeleteWhiteList(item.IP).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
whitelist.value.splice(index, 1)
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "删除[" + item.IP + "]的白名单记录出错")
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
const queryWhiteList = () => {
|
||||
apiGetWhiteList().then((res) => {
|
||||
if (res.ret == 0) {
|
||||
whitelist.value = res.data
|
||||
return
|
||||
}
|
||||
MessageShow("error", res.msg)
|
||||
//console.log("getAdminURL "+getAdminURL())
|
||||
}).catch((error) => {
|
||||
MessageShow("error", "查询白名单列表出错")
|
||||
})
|
||||
}
|
||||
|
||||
const keydown = (e)=>{
|
||||
if (e.keyCode != 13) {
|
||||
return
|
||||
}
|
||||
if(!addWhiteListDialogVisible.value ){
|
||||
return
|
||||
}
|
||||
addWhiteList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
queryWhiteList();
|
||||
window.addEventListener('keydown', keydown)
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.formradius {
|
||||
border: 0px solid var(--el-border-color);
|
||||
border-radius: 0;
|
||||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
padding: 15px;
|
||||
|
||||
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,71 @@
|
|||
import { createApp,ref } from 'vue'
|
||||
import App from './App.vue'
|
||||
import './assets/common-layout.scss'
|
||||
import './assets/appbase.css'
|
||||
import * as ElIcon from '@element-plus/icons-vue'
|
||||
import 'element-plus/theme-chalk/el-notification.css'
|
||||
import 'element-plus/theme-chalk/el-menu.css'
|
||||
import 'element-plus/theme-chalk/el-loading.css'
|
||||
import 'element-plus/theme-chalk/el-message.css'
|
||||
import 'element-plus/theme-chalk/el-message-box.css'
|
||||
import 'element-plus/theme-chalk/el-button.css'
|
||||
import storage from './apis/storage.js'
|
||||
import {apiLogout} from './apis/utils.js'
|
||||
import {PageExist,CurrentPage} from './utils/utils'
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
for (let iconName in ElIcon){
|
||||
app.component(iconName, ElIcon[iconName])
|
||||
}
|
||||
|
||||
|
||||
app.config.globalProperties.$storage = storage;
|
||||
|
||||
|
||||
|
||||
if(!PageExist(location.hash)){
|
||||
location.hash="#status"
|
||||
}
|
||||
|
||||
//配置全局变量
|
||||
//默认页面
|
||||
var currentPage = ref(location.hash)
|
||||
|
||||
|
||||
|
||||
// if (process.env.NODE_ENV=="development"){
|
||||
// currentPage.value="#relayset"
|
||||
// location.hash="#relayset"
|
||||
// }
|
||||
|
||||
app.provide('global',{
|
||||
currentPage,
|
||||
storage,
|
||||
})
|
||||
|
||||
window.onpopstate = function (event){
|
||||
currentPage.value=location.hash
|
||||
CurrentPage.value = location.hash
|
||||
|
||||
if(location.hash == "#logout"){//注销登录
|
||||
apiLogout().then((res) => {
|
||||
}).catch((error) => {
|
||||
})
|
||||
storage.setItem("token","")
|
||||
location.hash ="#login"
|
||||
return
|
||||
}
|
||||
|
||||
if(!PageExist(location.hash)){
|
||||
location.hash ="#login"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
app.mount('#app')
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import axios from 'axios'
|
||||
|
||||
console.log("vue run mode "+process.env.NODE_ENV)
|
||||
|
||||
var baseURL = "/" //
|
||||
if (process.env.NODE_ENV=="development"){
|
||||
//开发环境下这个改为自己的接口地址
|
||||
baseURL = 'http://192.168.31.70:16601'
|
||||
}
|
||||
|
||||
|
||||
//var fuck = storage.getItem("cookies")
|
||||
//console.log("fuck:"+fuck)
|
||||
|
||||
//console.log("baseURL: "+ baseURL)
|
||||
|
||||
// 创建一个 axios 实例
|
||||
const service = axios.create({
|
||||
baseURL: baseURL, // 所有的请求地址前缀部分
|
||||
timeout: 5000, // 请求超时时间毫秒
|
||||
withCredentials: false, // 异步请求携带cookie
|
||||
headers: {
|
||||
// 设置后端需要的传参类型
|
||||
'Content-Type': 'application/json',
|
||||
//'X-Requested-With': 'XMLHttpRequest',
|
||||
},
|
||||
})
|
||||
|
||||
// 添加请求拦截器
|
||||
service.interceptors.request.use(
|
||||
function (config) {
|
||||
// 在发送请求之前做些什么
|
||||
return config
|
||||
},
|
||||
function (error) {
|
||||
// 对请求错误做些什么
|
||||
console.log(error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 添加响应拦截器
|
||||
service.interceptors.response.use(
|
||||
function (response) {
|
||||
//console.log(response)
|
||||
// 2xx 范围内的状态码都会触发该函数。
|
||||
// 对响应数据做点什么
|
||||
// dataAxios 是 axios 返回数据中的 data
|
||||
const dataAxios = response.data
|
||||
// 这个状态码是和后端约定的
|
||||
const code = dataAxios.reset
|
||||
|
||||
//console.log("dataAxios data: "+JSON.stringify(dataAxios))
|
||||
//console.log("ret: "+dataAxios.ret)
|
||||
if (dataAxios.ret!=undefined&& dataAxios.ret==-1){
|
||||
//global.currentPage.value="set"
|
||||
console.log("登录失效")
|
||||
//window.location.href="/"
|
||||
location.hash ="#login"
|
||||
//var currentPage = ref("#login")
|
||||
|
||||
}
|
||||
return dataAxios
|
||||
},
|
||||
function (error) {
|
||||
// 超出 2xx 范围的状态码都会触发该函数。
|
||||
// 对响应错误做点什么
|
||||
console.log(error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
|
@ -0,0 +1,26 @@
|
|||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
|
||||
// ElMessageBox.alert(message, {
|
||||
// confirmButtonText: '好的',
|
||||
// callback: () => {
|
||||
// },
|
||||
// })
|
||||
|
||||
|
||||
export function ShowMessageBox(message: string) {
|
||||
ElMessageBox.alert(message, {
|
||||
confirmButtonText: '好的',
|
||||
callback: () => {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function MessageShow(type:any,message: string) {
|
||||
ElMessage({
|
||||
message: message,
|
||||
type: type,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import useClipboard from 'vue-clipboard3'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export function CopyTotoClipboard(data: string) {
|
||||
useClipboard().toClipboard(data)
|
||||
}
|
||||
|
||||
const ipReg = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){6}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^::([\da-fA-F]{1,4}:){0,4}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:):([\da-fA-F]{1,4}:){0,3}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){2}:([\da-fA-F]{1,4}:){0,2}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){3}:([\da-fA-F]{1,4}:){0,1}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){4}:((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}$|^:((:[\da-fA-F]{1,4}){1,6}|:)$|^[\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,5}|:)$|^([\da-fA-F]{1,4}:){2}((:[\da-fA-F]{1,4}){1,4}|:)$|^([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,3}|:)$|^([\da-fA-F]{1,4}:){4}((:[\da-fA-F]{1,4}){1,2}|:)$|^([\da-fA-F]{1,4}:){5}:([\da-fA-F]{1,4})?$|^([\da-fA-F]{1,4}:){6}:$/
|
||||
|
||||
export function isIP(ip :string){
|
||||
return ipReg.test(ip)
|
||||
}
|
||||
|
||||
const MenuIndexList = ["#status","#log","#relayset","#whitelistset","#whitelists","#blacklists","#set","#login"]
|
||||
|
||||
export function PageExist(page:string) {
|
||||
for(let i in MenuIndexList){
|
||||
if (MenuIndexList[i]==page){
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const CurrentPage = ref("")
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { fileURLToPath, URL } from 'url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
})],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,98 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var maxWebLogsNum = 100
|
||||
|
||||
var webLogsStore []WebLog
|
||||
var webLogs WebLogs
|
||||
var startTimeStamp string
|
||||
|
||||
type WebLogs struct {
|
||||
preTimeStamp int64
|
||||
timeStampIndex int64
|
||||
}
|
||||
|
||||
// WebLogs
|
||||
type WebLog struct {
|
||||
Timestamp int64
|
||||
Log string
|
||||
}
|
||||
|
||||
func (wlog *WebLogs) Write(p []byte) (n int, err error) {
|
||||
nowTime := time.Now()
|
||||
tripContent := strings.TrimSpace(string(p)[20:])
|
||||
content := fmt.Sprintf("%s %s", nowTime.Format("2006-01-02 15:04:05"), tripContent)
|
||||
|
||||
if webLogs.preTimeStamp == nowTime.UnixNano() {
|
||||
webLogs.timeStampIndex++
|
||||
} else {
|
||||
webLogs.timeStampIndex = 0
|
||||
}
|
||||
|
||||
l := WebLog{Timestamp: nowTime.UnixNano() + webLogs.timeStampIndex, Log: content}
|
||||
|
||||
webLogsStore = append(webLogsStore, l)
|
||||
webLogs.preTimeStamp = nowTime.UnixNano()
|
||||
|
||||
if len(webLogsStore) > maxWebLogsNum {
|
||||
webLogsStore = webLogsStore[len(webLogsStore)-maxWebLogsNum:]
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// 初始化日志
|
||||
func init() {
|
||||
log.SetOutput(io.MultiWriter(&webLogs, os.Stdout))
|
||||
// log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||
|
||||
var cstZone = time.FixedZone("CST", 8*3600) // 东八
|
||||
time.Local = cstZone
|
||||
|
||||
startTimeStamp = fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
|
||||
}
|
||||
|
||||
// Logs web
|
||||
func Logs(c *gin.Context) {
|
||||
|
||||
preTimeStampStr := c.Query("pre")
|
||||
preTimeStamp, err := strconv.ParseInt(preTimeStampStr, 10, 64)
|
||||
if err != nil {
|
||||
preTimeStamp = 0
|
||||
}
|
||||
|
||||
var logs []interface{}
|
||||
for _, l := range webLogsStore {
|
||||
if l.Timestamp <= preTimeStamp {
|
||||
continue
|
||||
}
|
||||
m := make(map[string]interface{})
|
||||
m["timestamp"] = fmt.Sprintf("%d", l.Timestamp)
|
||||
m["log"] = l.Log
|
||||
logs = append(logs, m)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"ret": 0,
|
||||
"starttime": startTimeStamp,
|
||||
"logs": logs,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// ClearLog
|
||||
func ClearLog(writer http.ResponseWriter, request *http.Request) {
|
||||
webLogsStore = webLogsStore[:0]
|
||||
}
|
|
@ -0,0 +1,763 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"embed"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gin-contrib/gzip"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/ljymc/goports/base"
|
||||
"github.com/ljymc/goports/config"
|
||||
"github.com/ljymc/goports/rule"
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/fileutils"
|
||||
"github.com/ljymc/goports/thirdlib/gdylib/ginutils"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v3/process"
|
||||
)
|
||||
|
||||
//go:embed goports-adminviews/dist
|
||||
var staticFs embed.FS
|
||||
var stafs fs.FS
|
||||
var loginErrorCount = int32(0)
|
||||
var rebootOnce sync.Once
|
||||
|
||||
//store := cookie.NewStore([]byte("secret11111"))
|
||||
//var fileServer http.Handler
|
||||
//var cookieStore cookie.Store
|
||||
|
||||
func init() {
|
||||
stafs, _ = fs.Sub(staticFs, "goports-adminviews/dist")
|
||||
//cookieStore = cookie.NewStore([]byte("goports2022"))
|
||||
}
|
||||
|
||||
func RunAdminWeb(listen string) {
|
||||
|
||||
//gin.Default()
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
r := gin.New()
|
||||
|
||||
if gin.Mode() != gin.ReleaseMode {
|
||||
r.Use(gin.Logger(), gin.Recovery())
|
||||
} else {
|
||||
r.Use(gin.Recovery())
|
||||
}
|
||||
|
||||
r.Use(checkLocalIP)
|
||||
|
||||
//r.Use(sessions.Sessions("goportssession", cookieStore))
|
||||
|
||||
r.Use(gzip.Gzip(gzip.DefaultCompression))
|
||||
|
||||
// if config.GetRunMode() == "dev" {
|
||||
// r.Use(CrosHandler())
|
||||
// }
|
||||
r.Use(ginutils.Cors())
|
||||
|
||||
r.Use(HandlerStaticFiles())
|
||||
|
||||
//r.Use(sessionCheck())
|
||||
//r.StaticFS("/", http.FS(stafs))
|
||||
|
||||
authorized := r.Group("/")
|
||||
authorized.Use(tokenCheck())
|
||||
{
|
||||
authorized.GET("/api/logs", Logs)
|
||||
authorized.GET("/api/status", status)
|
||||
authorized.GET("/api/test", test)
|
||||
authorized.GET("/api/rulelist", rulelist)
|
||||
authorized.POST("/api/rule", addrule)
|
||||
authorized.DELETE("/api/rule", deleterule)
|
||||
authorized.PUT("/api/rule", alterrule)
|
||||
authorized.GET("/api/rule/enable", enablerule)
|
||||
authorized.GET("/api/baseconfigure", baseconfigure)
|
||||
authorized.PUT("/api/baseconfigure", alterBaseConfigure)
|
||||
authorized.GET("/api/reboot_program", rebootProgram)
|
||||
authorized.GET("/api/whitelist/configure", whitelistConfigure)
|
||||
authorized.PUT("/api/whitelist/configure", alterWhitelistConfigure)
|
||||
authorized.GET("/api/whitelist", querywhitelist)
|
||||
authorized.PUT("/api/whitelist/flush", flushwhitelist)
|
||||
authorized.DELETE("/api/whitelist", deletewhitelist)
|
||||
authorized.GET("/api/blacklist", queryblacklist)
|
||||
authorized.PUT("/api/blacklist/flush", flushblacklist)
|
||||
authorized.DELETE("/api/blacklist", deleteblacklist)
|
||||
r.PUT("/api/logout", logout)
|
||||
}
|
||||
r.POST("/api/login", login)
|
||||
|
||||
r.GET("/wl", whitelistBasicAuth, whilelistAdd)
|
||||
r.GET("/wl/:url", whitelistBasicAuth, whilelistAdd)
|
||||
r.GET("/version", queryVersion)
|
||||
|
||||
//r.Use(func() *gin.Context {})
|
||||
|
||||
err := r.Run(listen)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("http.ListenAndServe error:%s", err.Error())
|
||||
time.Sleep(time.Minute)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func logout(c *gin.Context) {
|
||||
config.FlushLoginRandomKey()
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "已注销登录"})
|
||||
}
|
||||
|
||||
func queryVersion(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "version": config.GetVersion()})
|
||||
}
|
||||
|
||||
func deleteblacklist(c *gin.Context) {
|
||||
ip := c.Query("ip")
|
||||
err := config.BlackListDelete(ip)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "删除黑名单指定IP出错"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0})
|
||||
}
|
||||
|
||||
func deletewhitelist(c *gin.Context) {
|
||||
ip := c.Query("ip")
|
||||
err := config.WhiteListDelete(ip)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "删除白名单指定IP出错"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0})
|
||||
}
|
||||
|
||||
func flushblacklist(c *gin.Context) {
|
||||
ip := c.Query("ip")
|
||||
activelifeDurationStr := c.Query("life")
|
||||
life, _ := strconv.Atoi(activelifeDurationStr)
|
||||
|
||||
newTime, err := config.BlackListAdd(ip, int32(life))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "刷新IP有效期出错"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": newTime})
|
||||
}
|
||||
|
||||
func flushwhitelist(c *gin.Context) {
|
||||
ip := c.Query("ip")
|
||||
activelifeDurationStr := c.Query("life")
|
||||
life, _ := strconv.Atoi(activelifeDurationStr)
|
||||
|
||||
newTime, err := config.WhiteListAdd(ip, int32(life))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "刷新IP有效期出错"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": newTime})
|
||||
}
|
||||
|
||||
func queryblacklist(c *gin.Context) {
|
||||
resList := config.GetBlackList()
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": resList})
|
||||
}
|
||||
|
||||
func querywhitelist(c *gin.Context) {
|
||||
resList := config.GetWhiteList()
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": resList})
|
||||
}
|
||||
|
||||
func whitelistBasicAuth(c *gin.Context) {
|
||||
bc := config.GetWhiteListBaseConfigure()
|
||||
whilelistURL := c.Param("url")
|
||||
if (c.Request.RequestURI == "/wl" && bc.URL != "") || whilelistURL != bc.URL {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
realm := "Basic realm=" + strconv.Quote("Authorization Required")
|
||||
pairs := processAccounts(gin.Accounts{bc.BasicAccount: bc.BasicPassword})
|
||||
user, found := pairs.searchCredential(c.GetHeader("Authorization"))
|
||||
if !found {
|
||||
// Credentials doesn't match, we return 401 and abort handlers chain.
|
||||
c.Header("WWW-Authenticate", realm)
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
c.Set("user", user)
|
||||
}
|
||||
|
||||
func whilelistAdd(c *gin.Context) {
|
||||
|
||||
lifeTime, err := config.WhiteListAdd(c.ClientIP(), 0)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "记录白名单IP出错"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": fmt.Sprintf("IP已记录进白名单"), "ip": c.ClientIP(), " effective_time": lifeTime})
|
||||
}
|
||||
|
||||
func whitelistConfigure(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": config.GetWhiteListBaseConfigure()})
|
||||
}
|
||||
|
||||
func alterWhitelistConfigure(c *gin.Context) {
|
||||
var requestObj config.WhiteListBaseConfigure
|
||||
err := c.BindJSON(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "修改请求解析出错"})
|
||||
return
|
||||
}
|
||||
|
||||
requestObj.BasicAccount = strings.TrimSpace(requestObj.BasicAccount)
|
||||
if len(requestObj.BasicAccount) == 0 || len(requestObj.BasicPassword) == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "账号或密码不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
err = config.SetWhiteListBaseConfigure(requestObj.ActivelifeDuration, requestObj.URL, requestObj.BasicAccount, requestObj.BasicPassword)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "保存白名单配置出错"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0})
|
||||
return
|
||||
}
|
||||
|
||||
func checkLocalIP(c *gin.Context) {
|
||||
clientIP := c.ClientIP()
|
||||
//fmt.Printf("clientIP:%s\n", clientIP)
|
||||
bc := config.GetBaseConfigure()
|
||||
|
||||
if !isLocalIP(clientIP) && !bc.AllowInternetaccess {
|
||||
c.JSON(http.StatusForbidden, gin.H{"ret": 1, "msg": "Forbidden Internetaccess "})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func tokenCheck() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
// if config.GetRunMode() == "dev" {
|
||||
// c.Next()
|
||||
// return
|
||||
// }
|
||||
|
||||
tokenString, _ := c.GetQuery("Authorization")
|
||||
if tokenString == "" {
|
||||
tokenString = c.GetHeader("Authorization")
|
||||
}
|
||||
|
||||
token, err := ginutils.GetJWTToken(tokenString, "strings")
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"ret": -1, "msg": "登录失效"})
|
||||
return
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"ret": -1, "msg": "登录失效"})
|
||||
return
|
||||
}
|
||||
|
||||
account := claims["account"].(string)
|
||||
password := claims["password"].(string)
|
||||
loginKey := claims["loginkey"].(string)
|
||||
|
||||
if account == "" || password == "" {
|
||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"ret": -1, "msg": "登录失效"})
|
||||
return
|
||||
}
|
||||
|
||||
bc := config.GetBaseConfigure()
|
||||
|
||||
// //fmt.Printf("session中的account:%s password:%s\n", account, password)
|
||||
if bc.AdminAccount != account || bc.AdminPassword != password || loginKey != config.GetLoginRandomKey() {
|
||||
c.AbortWithStatusJSON(http.StatusOK, gin.H{"ret": -1, "msg": "登录失效"})
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func rebootProgram(c *gin.Context) {
|
||||
rebootOnce.Do(func() {
|
||||
go func() {
|
||||
fileutils.OpenProgramOrFile(os.Args)
|
||||
os.Exit(0)
|
||||
}()
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
}
|
||||
|
||||
func login(c *gin.Context) {
|
||||
var requestObj struct {
|
||||
Account string `json:"Account"`
|
||||
Password string `json:"Password"`
|
||||
}
|
||||
err := c.BindJSON(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "登录失败,登录请求解析出错"})
|
||||
return
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(&loginErrorCount) >= 99 {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "登录错误次数太多,后台登录功能已禁用,请重启程序."})
|
||||
return
|
||||
}
|
||||
|
||||
bc := config.GetBaseConfigure()
|
||||
|
||||
if bc.AdminAccount != requestObj.Account || bc.AdminPassword != requestObj.Password {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "登录失败,账号或密码有误"})
|
||||
atomic.AddInt32(&loginErrorCount, 1)
|
||||
return
|
||||
}
|
||||
|
||||
config.FlushLoginRandomKey()
|
||||
tokenInfo := make(map[string]interface{})
|
||||
tokenInfo["account"] = requestObj.Account //用户名
|
||||
tokenInfo["password"] = requestObj.Password
|
||||
tokenInfo["loginkey"] = config.GetLoginRandomKey()
|
||||
tokenString, err := ginutils.GetJWTTokenString(tokenInfo, "strings", time.Hour*24)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "登录失败,token生成出错"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "登录成功", "token": tokenString})
|
||||
}
|
||||
|
||||
func alterBaseConfigure(c *gin.Context) {
|
||||
var requestObj config.BaseConfigure
|
||||
err := c.BindJSON(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
|
||||
return
|
||||
}
|
||||
requestObj.AdminAccount = strings.TrimSpace(requestObj.AdminAccount)
|
||||
|
||||
if len(requestObj.AdminAccount) == 0 || len(requestObj.AdminPassword) == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "账号或密码不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
preBaseConfigure := config.GetBaseConfigure()
|
||||
if preBaseConfigure.AdminWebListenPort != requestObj.AdminWebListenPort && !config.CheckTCPPortAvalid(requestObj.AdminWebListenPort) { //检测新端口
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("新的后端管理监听端口[%d]已被占用,修改设置失败", requestObj.AdminWebListenPort)})
|
||||
return
|
||||
}
|
||||
|
||||
err = config.SetBaseConfigure(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": "保存配置过程发生错误,请检测相关启动配置"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0})
|
||||
}
|
||||
|
||||
func baseconfigure(c *gin.Context) {
|
||||
conf := config.GetBaseConfigure()
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "baseconfigure": conf})
|
||||
}
|
||||
|
||||
func enablerule(c *gin.Context) {
|
||||
|
||||
enable := c.Query("enable")
|
||||
key := c.Query("key")
|
||||
|
||||
var err error
|
||||
var r *rule.RelayRule
|
||||
var syncSuccess bool
|
||||
|
||||
if enable == "true" {
|
||||
r, syncSuccess, err = rule.EnableRelayRuleByKey(key)
|
||||
} else {
|
||||
r, syncSuccess, err = rule.DisableRelayRuleByKey(key)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("开关规则出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[%s] relayRule[%s][%s]", enable, r.Name, r.MainConfigure)
|
||||
syncRes := ""
|
||||
if !syncSuccess {
|
||||
syncRes = "同步规则状态到配置文件出错"
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "", "syncres": syncRes})
|
||||
}
|
||||
|
||||
func alterrule(c *gin.Context) {
|
||||
|
||||
var requestRule rule.RelayRule
|
||||
err := c.BindJSON(&requestRule)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改请求解析出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
//fmt.Printf("balance:%v\n", requestRule.BalanceTargetAddressList)
|
||||
|
||||
preConfigureStr := requestRule.MainConfigure
|
||||
configureStr := requestRule.CreateMainConfigure()
|
||||
// configureStr := fmt.Sprintf("%s@%s:%sto%s:%s",
|
||||
// requestRule.RelayType,
|
||||
// requestRule.ListenIP, requestRule.ListenPorts,
|
||||
// requestRule.TargetIP, requestRule.TargetPorts)
|
||||
|
||||
r, err := rule.CreateRuleByConfigureAndOptions(requestRule.Name, configureStr, requestRule.Options)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改转发规则[%s]时出错:%s", preConfigureStr, err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
syncSuccess, err := rule.AlterRuleInGlobalRuleListByKey(preConfigureStr, r)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改转发规则[%s]时出错:%s", preConfigureStr, err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
r, _, err = rule.EnableRelayRuleByKey(r.MainConfigure)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": fmt.Sprintf("修改转发规则成功,但启用规则时出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
log.Printf("修改转发规则[%s][%s]成功", r.Name, r.MainConfigure)
|
||||
|
||||
synsRes := ""
|
||||
|
||||
if !syncSuccess {
|
||||
synsRes = "同步修改规则数据到配置文件出错"
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "修改转发规则成功", "syncres": synsRes})
|
||||
}
|
||||
|
||||
func deleterule(c *gin.Context) {
|
||||
ruleKey := c.Query("rule")
|
||||
|
||||
rule.DisableRelayRuleByKey(ruleKey)
|
||||
|
||||
syncSuccess, err := rule.DeleteGlobalRuleByKey(ruleKey)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("删除转发规则出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
syncRes := ""
|
||||
if !syncSuccess {
|
||||
syncRes = "同步规则信息到配置文件出错"
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "删除成功", "syncres": syncRes})
|
||||
}
|
||||
|
||||
func addrule(c *gin.Context) {
|
||||
var requestRule rule.RelayRule
|
||||
err := c.BindJSON(&requestRule)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("请求解析出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
// configureStr := fmt.Sprintf("%s@%s:%sto%s:%s",
|
||||
// requestRule.RelayType,
|
||||
// requestRule.ListenIP, requestRule.ListenPorts,
|
||||
// requestRule.TargetIP, requestRule.TargetPorts)
|
||||
configureStr := requestRule.CreateMainConfigure()
|
||||
|
||||
r, err := rule.CreateRuleByConfigureAndOptions(requestRule.Name, configureStr, requestRule.Options)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("创建转发规则出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
synsRes, err := rule.AddRuleToGlobalRuleList(true, *r)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("添加转发规则出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
r, _, err = rule.EnableRelayRuleByKey(r.MainConfigure)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": fmt.Sprintf("启用规则出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
log.Printf("添加转发规则[%s][%s]成功", r.Name, r.MainConfigure)
|
||||
|
||||
if synsRes != "" {
|
||||
synsRes = "保存配置文件出错,请检查配置文件设置"
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "添加规则并启用成功", "syncres": synsRes})
|
||||
}
|
||||
|
||||
func rulelist(c *gin.Context) {
|
||||
ruleList, proxyListInfoMap := rule.GetRelayRuleList()
|
||||
type ruleItem struct {
|
||||
Name string `json:"Name"`
|
||||
MainConfigure string `json:"Mainconfigure"`
|
||||
RelayType string `json:"RelayType"`
|
||||
ListenIP string `json:"ListenIP"`
|
||||
ListenPorts string `json:"ListenPorts"`
|
||||
TargetIP string `json:"TargetIP"`
|
||||
TargetPorts string `json:"TargetPorts"`
|
||||
BalanceTargetAddressList []string `json:"BalanceTargetAddressList"`
|
||||
Options base.RelayRuleOptions `json:"Options"`
|
||||
SubRuleList []rule.SubRelayRule `json:"SubRuleList"`
|
||||
From string `json:"From"`
|
||||
IsEnable bool `json:"Enable"`
|
||||
ProxyList []rule.RelayRuleProxyInfo `json:"ProxyList"`
|
||||
}
|
||||
|
||||
//proxyListInfoMap[(*ruleList)[i].MainConfigure]
|
||||
var data []ruleItem
|
||||
|
||||
for i := range *ruleList {
|
||||
item := ruleItem{
|
||||
Name: (*ruleList)[i].Name,
|
||||
MainConfigure: (*ruleList)[i].MainConfigure,
|
||||
RelayType: (*ruleList)[i].RelayType,
|
||||
ListenIP: (*ruleList)[i].ListenIP,
|
||||
ListenPorts: (*ruleList)[i].ListenPorts,
|
||||
TargetIP: (*ruleList)[i].TargetIP,
|
||||
TargetPorts: (*ruleList)[i].TargetPorts,
|
||||
Options: (*ruleList)[i].Options,
|
||||
SubRuleList: (*ruleList)[i].SubRuleList,
|
||||
From: (*ruleList)[i].From,
|
||||
IsEnable: (*ruleList)[i].IsEnable,
|
||||
ProxyList: proxyListInfoMap[(*ruleList)[i].MainConfigure],
|
||||
BalanceTargetAddressList: (*ruleList)[i].BalanceTargetAddressList,
|
||||
}
|
||||
data = append(data, item)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": data})
|
||||
|
||||
}
|
||||
|
||||
func test(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0})
|
||||
}
|
||||
|
||||
func status(c *gin.Context) {
|
||||
|
||||
v, _ := mem.VirtualMemory()
|
||||
|
||||
currentProcessMem := GetCurrentProcessMem()
|
||||
//fmt.Fprintf(w, "当前进程 CPU使用率:%.2f%% 协程数:%d 进程内存使用:%s 系统内存总量:%s 已用:%s 可用:%s \n", GetCurrentProcessCPUPrecent(), runtime.NumGoroutine(), formatFileSize(currentProcessMem), formatFileSize(v.Total), formatFileSize(v.Used), formatFileSize(v.Free))
|
||||
//fmt.Fprintf(w, "当前全局TCP 连接数:%d 全局TCP连接数最大限制:%d\n", core.GetGlobalTCPConns(), core.GetGlobalMaxConnections())
|
||||
|
||||
//var proxyStatusList []string
|
||||
|
||||
// for _, p := range *config.GlobalProxy {
|
||||
// //fmt.Fprintf(w, "%s\n", p.GetStatus())
|
||||
// proxyStatusList = append(proxyStatusList, p.GetStatus())
|
||||
// }
|
||||
|
||||
respMap := make(map[string]interface{})
|
||||
respMap["totleMem"] = formatFileSize(v.Total)
|
||||
respMap["usedMem"] = formatFileSize(v.Used)
|
||||
respMap["unusedMem"] = formatFileSize(v.Free)
|
||||
respMap["currentProcessUsedCPU"] = fmt.Sprintf("%.2f%%", GetCurrentProcessCPUPrecent())
|
||||
respMap["goroutine"] = fmt.Sprintf("%d", runtime.NumGoroutine())
|
||||
respMap["processUsedMem"] = formatFileSize(currentProcessMem)
|
||||
respMap["currentConnections"] = fmt.Sprintf("%d", base.GetGlobalConnections())
|
||||
respMap["maxConnections"] = fmt.Sprintf("%d", base.GetGlobalMaxConnections())
|
||||
respMap["usedCPU"] = fmt.Sprintf("%.2f%%", GetCpuPercent())
|
||||
//respMap["proxysStatus"] = proxyStatusList
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"ret": 0,
|
||||
"data": respMap,
|
||||
})
|
||||
}
|
||||
|
||||
func GetCurrentProcessMem() uint64 {
|
||||
plist, e := process.Processes()
|
||||
if e == nil {
|
||||
for _, p := range plist {
|
||||
if int(p.Pid) == os.Getpid() {
|
||||
mem, err := p.MemoryInfo()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return mem.RSS
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func GetCurrentProcessCPUPrecent() float64 {
|
||||
plist, e := process.Processes()
|
||||
if e == nil {
|
||||
for _, p := range plist {
|
||||
if int(p.Pid) == os.Getpid() {
|
||||
cpuprecent, err := p.CPUPercent()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return cpuprecent
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func GetCpuPercent() float64 {
|
||||
percent, _ := cpu.Percent(time.Second, false)
|
||||
return percent[0]
|
||||
}
|
||||
|
||||
//跨域访问:cross origin resource share
|
||||
func CrosHandler() gin.HandlerFunc {
|
||||
return func(context *gin.Context) {
|
||||
method := context.Request.Method
|
||||
//context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
origin := context.Request.Header.Get("Origin")
|
||||
context.Header("Access-Control-Allow-Origin", origin) // 设置允许访问所有域
|
||||
context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
|
||||
context.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma,token,openid,opentoken")
|
||||
//context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar")
|
||||
//context.Header("Access-Control-Allow-Methods", "*")
|
||||
//context.Header("Access-Control-Allow-Headers", "*")
|
||||
context.Header("Access-Control-Expose-Headers", "*")
|
||||
context.Header("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
context.Header("Access-Control-Max-Age", "172800")
|
||||
//context.Header("Access-Control-Allow-Credentials", "false")
|
||||
//context.Set("content-type", "application/json")
|
||||
|
||||
if method == "OPTIONS" {
|
||||
context.JSON(http.StatusOK, gin.H{
|
||||
"ret": 0,
|
||||
})
|
||||
}
|
||||
//处理请求
|
||||
context.Next()
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
func BasicAuth() gin.HandlerFunc {
|
||||
return gin.BasicAuthForRealm(config.GetAuthAccount(), "")
|
||||
}
|
||||
|
||||
func formatFileSize(fileSize uint64) (size string) {
|
||||
switch {
|
||||
case fileSize < 1024:
|
||||
return fmt.Sprintf("%.2fB", float64(fileSize)/float64(1))
|
||||
case fileSize < (1024 * 1024):
|
||||
return fmt.Sprintf("%.2fKB", float64(fileSize)/float64(1024))
|
||||
case fileSize < (1024 * 1024 * 1024):
|
||||
return fmt.Sprintf("%.2fMB", float64(fileSize)/float64(1024*1024))
|
||||
case fileSize < (1024 * 1024 * 1024 * 1024):
|
||||
return fmt.Sprintf("%.2fGB", float64(fileSize)/float64(1024*1024*1024))
|
||||
case fileSize < (1024 * 1024 * 1024 * 1024 * 1024):
|
||||
return fmt.Sprintf("%.2fTB", float64(fileSize)/float64(1024*1024*1024*1024))
|
||||
default:
|
||||
return fmt.Sprintf("%.2fEB", float64(fileSize)/float64(1024*1024*1024*1024*1024))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func isLocalIP(ipstr string) bool {
|
||||
|
||||
ip := net.ParseIP(ipstr)
|
||||
|
||||
if ip.IsLoopback() {
|
||||
return true
|
||||
}
|
||||
|
||||
ip4 := ip.To4()
|
||||
if ip4 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return ip4[0] == 10 || // 10.0.0.0/8
|
||||
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
|
||||
(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
|
||||
(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
|
||||
}
|
||||
|
||||
//***********************
|
||||
//basicAuth
|
||||
|
||||
type authPair struct {
|
||||
value string
|
||||
user string
|
||||
}
|
||||
|
||||
type authPairs []authPair
|
||||
|
||||
func processAccounts(accounts gin.Accounts) authPairs {
|
||||
length := len(accounts)
|
||||
assert1(length > 0, "Empty list of authorized credentials")
|
||||
pairs := make(authPairs, 0, length)
|
||||
for user, password := range accounts {
|
||||
assert1(user != "", "User can not be empty")
|
||||
value := authorizationHeader(user, password)
|
||||
pairs = append(pairs, authPair{
|
||||
value: value,
|
||||
user: user,
|
||||
})
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
|
||||
func authorizationHeader(user, password string) string {
|
||||
base := user + ":" + password
|
||||
return "Basic " + base64.StdEncoding.EncodeToString(StringToBytes(base))
|
||||
}
|
||||
func assert1(guard bool, text string) {
|
||||
if !guard {
|
||||
panic(text)
|
||||
}
|
||||
}
|
||||
|
||||
func StringToBytes(s string) []byte {
|
||||
return *(*[]byte)(unsafe.Pointer(
|
||||
&struct {
|
||||
string
|
||||
Cap int
|
||||
}{s, len(s)},
|
||||
))
|
||||
}
|
||||
|
||||
func (a authPairs) searchCredential(authValue string) (string, bool) {
|
||||
if authValue == "" {
|
||||
return "", false
|
||||
}
|
||||
for _, pair := range a {
|
||||
if subtle.ConstantTimeCompare(StringToBytes(pair.value), StringToBytes(authValue)) == 1 {
|
||||
return pair.user, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
//go:build !adminweb
|
||||
// +build !adminweb
|
||||
|
||||
package main
|
||||
|
||||
func RunAdminWeb(listenPort int) {
|
||||
}
|
Loading…
Reference in New Issue