1.3.1 主要新增反向代理功能模块.
This commit is contained in:
parent
da023ceb77
commit
ff20532615
12
README.md
12
README.md
|
@ -40,6 +40,13 @@
|
|||
- 其它细节功能自己慢慢发现...
|
||||
- 没有文档,后台各处的提示信息已经足够多.
|
||||
- 支持的DNS服务商和DDNS-GO一样,有Alidns(阿里云),百度云,Cloudflare,Dnspod(腾讯云),华为云.自定义(Callback)内置有每步,No-IP,Dynv6,Dynu模版,一键填充,仅需修改相应用户密码或者token即可快速接入.
|
||||
- 3.Http反向代理
|
||||
- 支持HttpBasic认证
|
||||
- 支持IP黑白名单
|
||||
- 支持UserAgent黑白名单
|
||||
- 日志记录最近访问情况
|
||||
- 一键开关子规则
|
||||
- 前端域名与后端地址 支持一对一,一对多(均衡负载),多对多(下一级反向代理)
|
||||
|
||||
- 将要实现的功能
|
||||
- 有建议可联系作者.
|
||||
|
@ -155,6 +162,11 @@
|
|||
|
||||

|
||||
|
||||
#### Http反向代理
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#开发编译
|
||||
|
|
|
@ -22,7 +22,7 @@ func GetAppInfo() *AppInfo {
|
|||
}
|
||||
|
||||
func InitAppInfo(version, date string) {
|
||||
appInfo.AppName = "Lucky(大吉)"
|
||||
appInfo.AppName = "Lucky"
|
||||
appInfo.Version = version
|
||||
appInfo.Date = date
|
||||
appInfo.OS = runtime.GOOS
|
||||
|
|
|
@ -1,10 +1,30 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package config
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BlackListItem WhiteListItem
|
||||
|
||||
func (w *BlackListItem) Contains(ip string) bool {
|
||||
netIP := net.ParseIP(ip)
|
||||
if netIP == nil {
|
||||
return false
|
||||
}
|
||||
if w.NetIP != nil {
|
||||
return w.NetIP.Equal(netIP)
|
||||
}
|
||||
|
||||
if w.Cidr != nil {
|
||||
return w.Cidr.Contains(netIP)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type BlackListConfigure struct {
|
||||
BlackList []BlackListItem `json:"BlackList"` //黑名单列表
|
||||
}
|
||||
|
@ -25,10 +45,44 @@ func GetBlackList() []BlackListItem {
|
|||
return resList
|
||||
}
|
||||
|
||||
func BlackListInit() {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
var netIP net.IP
|
||||
var cidr *net.IPNet
|
||||
|
||||
for i := range programConfigure.BlackListConfigure.BlackList {
|
||||
netIP = nil
|
||||
cidr = nil
|
||||
if strings.Contains(programConfigure.BlackListConfigure.BlackList[i].IP, "/") {
|
||||
_, cidr, _ = net.ParseCIDR(programConfigure.BlackListConfigure.BlackList[i].IP)
|
||||
} else {
|
||||
netIP = net.ParseIP(programConfigure.BlackListConfigure.BlackList[i].IP)
|
||||
}
|
||||
programConfigure.BlackListConfigure.BlackList[i].Cidr = cidr
|
||||
programConfigure.BlackListConfigure.BlackList[i].NetIP = netIP
|
||||
}
|
||||
}
|
||||
|
||||
func BlackListAdd(ip string, activelifeDuration int32) (string, error) {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
|
||||
var err error
|
||||
var netIP net.IP = nil
|
||||
var cidr *net.IPNet = nil
|
||||
if strings.Contains(ip, "/") {
|
||||
_, cidr, err = net.ParseCIDR(ip)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("网段格式有误,转换出错:%s", err.Error())
|
||||
}
|
||||
} else {
|
||||
netIP = net.ParseIP(ip)
|
||||
if netIP == nil {
|
||||
return "", fmt.Errorf("IP格式有误")
|
||||
}
|
||||
}
|
||||
|
||||
if activelifeDuration <= 0 {
|
||||
activelifeDuration = 666666
|
||||
}
|
||||
|
@ -41,7 +95,7 @@ func BlackListAdd(ip string, activelifeDuration int32) (string, error) {
|
|||
return EffectiveTimeStr, Save()
|
||||
}
|
||||
}
|
||||
item := BlackListItem{IP: ip, EffectiveTime: EffectiveTimeStr}
|
||||
item := BlackListItem{IP: ip, EffectiveTime: EffectiveTimeStr, NetIP: netIP, Cidr: cidr}
|
||||
programConfigure.BlackListConfigure.BlackList = append(programConfigure.BlackListConfigure.BlackList, item)
|
||||
return EffectiveTimeStr, Save()
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gdy666/lucky/base"
|
||||
"github.com/gdy666/lucky/socketproxy"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/fileutils"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/stringsp"
|
||||
)
|
||||
|
@ -18,6 +18,9 @@ import (
|
|||
const defaultAdminAccount = "666"
|
||||
const defaultAdminPassword = "666"
|
||||
const defaultAdminListenPort = 16601
|
||||
const defaultLogSize = 2048
|
||||
const minLogSize = 1024
|
||||
const maxLogSize = 40960
|
||||
|
||||
var runMode = "prod"
|
||||
var version = "0.0.0"
|
||||
|
@ -41,10 +44,10 @@ func FlushLoginRandomKey() {
|
|||
}
|
||||
|
||||
type ConfigureRelayRule struct {
|
||||
Name string `json:"Name"`
|
||||
Configurestr string `json:"Configurestr"`
|
||||
Enable bool `json:"Enable"`
|
||||
Options base.RelayRuleOptions `json:"Options"`
|
||||
Name string `json:"Name"`
|
||||
Configurestr string `json:"Configurestr"`
|
||||
Enable bool `json:"Enable"`
|
||||
Options socketproxy.RelayRuleOptions `json:"Options"`
|
||||
}
|
||||
|
||||
type BaseConfigure struct {
|
||||
|
@ -54,15 +57,17 @@ type BaseConfigure struct {
|
|||
AdminPassword string `json:"AdminPassword"` //登录密码
|
||||
AllowInternetaccess bool `json:"AllowInternetaccess"` //允许外网访问
|
||||
GlobalMaxConnections int64 `json:"GlobalMaxConnections"` //全局最大连接数
|
||||
LogMaxSize int `json:"LogMaxSize"` //日志记录最大条数
|
||||
}
|
||||
|
||||
type ProgramConfigure struct {
|
||||
BaseConfigure BaseConfigure `json:"BaseConfigure"`
|
||||
RelayRuleList []ConfigureRelayRule `json:"RelayRuleList"`
|
||||
WhiteListConfigure WhiteListConfigure `json:"WhiteListConfigure"`
|
||||
BlackListConfigure BlackListConfigure `json:"BlackListConfigure"`
|
||||
DDNSConfigure DDNSConfigure `json:"DDNSConfigure"` //DDNS 参数设置
|
||||
DDNSTaskList []DDNSTask `json:"DDNSTaskList"`
|
||||
BaseConfigure BaseConfigure `json:"BaseConfigure"`
|
||||
RelayRuleList []ConfigureRelayRule `json:"RelayRuleList"`
|
||||
WhiteListConfigure WhiteListConfigure `json:"WhiteListConfigure"`
|
||||
BlackListConfigure BlackListConfigure `json:"BlackListConfigure"`
|
||||
DDNSConfigure DDNSConfigure `json:"DDNSConfigure"` //DDNS 参数设置
|
||||
DDNSTaskList []DDNSTask `json:"DDNSTaskList"` //DDNS任务列表
|
||||
ReverseProxyRuleList []ReverseProxyRule `json:"ReverseProxyRuleList"` //反向代理规则列表
|
||||
}
|
||||
|
||||
var programConfigureMutex sync.RWMutex
|
||||
|
@ -147,8 +152,14 @@ func SetBaseConfigure(conf *BaseConfigure) error {
|
|||
defer programConfigureMutex.Unlock()
|
||||
programConfigure.BaseConfigure = *conf
|
||||
|
||||
base.SetGlobalMaxConnections(conf.GlobalMaxConnections)
|
||||
base.SetGlobalMaxProxyCount(conf.ProxyCountLimit)
|
||||
socketproxy.SetGlobalMaxConnections(conf.GlobalMaxConnections)
|
||||
socketproxy.SetGlobalMaxProxyCount(conf.ProxyCountLimit)
|
||||
|
||||
if conf.LogMaxSize < minLogSize {
|
||||
conf.LogMaxSize = minLogSize
|
||||
} else if conf.LogMaxSize > maxLogSize {
|
||||
conf.LogMaxSize = maxLogSize
|
||||
}
|
||||
|
||||
return Save()
|
||||
}
|
||||
|
@ -190,17 +201,23 @@ func Read(filePath string) (err error) {
|
|||
}
|
||||
|
||||
if pc.BaseConfigure.GlobalMaxConnections <= 0 {
|
||||
pc.BaseConfigure.GlobalMaxConnections = base.DEFAULT_GLOBAL_MAX_CONNECTIONS
|
||||
pc.BaseConfigure.GlobalMaxConnections = socketproxy.DEFAULT_GLOBAL_MAX_CONNECTIONS
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.ProxyCountLimit <= 0 {
|
||||
pc.BaseConfigure.ProxyCountLimit = base.DEFAULT_MAX_PROXY_COUNT
|
||||
pc.BaseConfigure.ProxyCountLimit = socketproxy.DEFAULT_MAX_PROXY_COUNT
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.AdminWebListenPort <= 0 {
|
||||
pc.BaseConfigure.AdminWebListenPort = 16601
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.LogMaxSize < minLogSize {
|
||||
pc.BaseConfigure.LogMaxSize = minLogSize
|
||||
} else if pc.BaseConfigure.LogMaxSize > maxLogSize {
|
||||
pc.BaseConfigure.LogMaxSize = maxLogSize
|
||||
}
|
||||
|
||||
programConfigure = pc
|
||||
|
||||
return nil
|
||||
|
@ -273,7 +290,8 @@ func loadDefaultConfigure(
|
|||
AdminAccount: defaultAdminAccount,
|
||||
AdminPassword: defaultAdminPassword,
|
||||
ProxyCountLimit: proxyCountLimit,
|
||||
AllowInternetaccess: false}
|
||||
AllowInternetaccess: false,
|
||||
LogMaxSize: defaultLogSize}
|
||||
|
||||
whiteListConfigure := WhiteListConfigure{BaseConfigure: WhiteListBaseConfigure{ActivelifeDuration: 36, BasicAccount: defaultAdminAccount, BasicPassword: defaultAdminPassword}}
|
||||
|
||||
|
@ -282,11 +300,11 @@ func loadDefaultConfigure(
|
|||
pc.WhiteListConfigure = whiteListConfigure
|
||||
|
||||
if pc.BaseConfigure.GlobalMaxConnections <= 0 {
|
||||
pc.BaseConfigure.GlobalMaxConnections = base.DEFAULT_GLOBAL_MAX_CONNECTIONS
|
||||
pc.BaseConfigure.GlobalMaxConnections = socketproxy.DEFAULT_GLOBAL_MAX_CONNECTIONS
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.ProxyCountLimit <= 0 {
|
||||
pc.BaseConfigure.ProxyCountLimit = base.DEFAULT_MAX_PROXY_COUNT
|
||||
pc.BaseConfigure.ProxyCountLimit = socketproxy.DEFAULT_MAX_PROXY_COUNT
|
||||
}
|
||||
|
||||
if pc.BaseConfigure.AdminWebListenPort <= 0 {
|
||||
|
|
|
@ -0,0 +1,728 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/ginutils"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/logsbuffer"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var reverseProxyLogsStore map[string]*logsbuffer.LogsBuffer
|
||||
var reverseProxyLogsStoreMu sync.Mutex
|
||||
|
||||
var reverseProxyServerStore sync.Map
|
||||
var reverseProxyServerStoreMu sync.Mutex
|
||||
|
||||
func init() {
|
||||
reverseProxyLogsStore = make(map[string]*logsbuffer.LogsBuffer)
|
||||
}
|
||||
|
||||
func CreateReverseProxyLogbuffer(key string, buffSize int) *logsbuffer.LogsBuffer {
|
||||
reverseProxyLogsStoreMu.Lock()
|
||||
defer reverseProxyLogsStoreMu.Unlock()
|
||||
var buf *logsbuffer.LogsBuffer
|
||||
var ok bool
|
||||
if buf, ok = reverseProxyLogsStore[key]; !ok {
|
||||
buf = &logsbuffer.LogsBuffer{}
|
||||
buf.SetBufferSize(buffSize)
|
||||
reverseProxyLogsStore[key] = buf
|
||||
} else if buf.GetBufferSize() != buffSize {
|
||||
buf.SetBufferSize(buffSize)
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// TidyReverseProxyCache 整理反向代理日志缓存
|
||||
func TidyReverseProxyCache() {
|
||||
ruleList := GetReverseProxyRuleList()
|
||||
var keyListBuffer strings.Builder
|
||||
for _, rule := range ruleList {
|
||||
keyListBuffer.WriteString(rule.DefaultProxy.Key)
|
||||
keyListBuffer.WriteString(",")
|
||||
for _, sr := range rule.ProxyList {
|
||||
keyListBuffer.WriteString(sr.Key)
|
||||
keyListBuffer.WriteString(",")
|
||||
}
|
||||
}
|
||||
|
||||
keyListStr := keyListBuffer.String()
|
||||
reverseProxyLogsStoreMu.Lock()
|
||||
defer reverseProxyLogsStoreMu.Unlock()
|
||||
|
||||
var needDeleteKeys []string
|
||||
for k := range reverseProxyLogsStore {
|
||||
if !strings.Contains(keyListStr, k) {
|
||||
needDeleteKeys = append(needDeleteKeys, k)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range needDeleteKeys {
|
||||
delete(reverseProxyLogsStore, needDeleteKeys[i])
|
||||
reverseProxyServerStore.Delete(needDeleteKeys[i])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type SubReverProxyRule struct {
|
||||
Key string `json:"Key"`
|
||||
|
||||
initOnce sync.Once
|
||||
Locations []string `json:"Locations"` //长度大于1时均衡负载
|
||||
locationMutex *sync.Mutex `json:"-"`
|
||||
locationsCount int `json:"-"`
|
||||
locationIndex uint64 `json:"-"`
|
||||
|
||||
EnableAccessLog bool `json:"EnableAccessLog"` //开启日志
|
||||
LogLevel int `json:"LogLevel"` //日志输出级别
|
||||
LogOutputToConsole bool `json:"LogOutputToConsole"` //日志输出到终端
|
||||
AccessLogMaxNum int `json:"AccessLogMaxNum"` //最大条数
|
||||
WebListShowLastLogMaxCount int `json:"WebListShowLastLogMaxCount"` //前端列表显示最新日志最大条数
|
||||
RequestInfoLogFormat string `json:"RequestInfoLogFormat"` //请求信息在日志中的格式
|
||||
|
||||
ForwardedByClientIP bool `json:"ForwardedByClientIP"`
|
||||
TrustedCIDRsStrList []string `json:"TrustedCIDRsStrList"`
|
||||
RemoteIPHeaders []string `json:"RemoteIPHeaders"` //识别客户端原始IP的Http请求头
|
||||
TrustedProxyCIDRs []*net.IPNet `json:"-"`
|
||||
|
||||
AddRemoteIPToHeader bool `json:"AddRemoteIPToHeader"` //追加客户端连接IP到指定Header
|
||||
AddRemoteIPHeaderKey string `json:"AddRemoteIPHeaderKey"`
|
||||
|
||||
EnableBasicAuth bool `json:"EnableBasicAuth"` //启用BasicAuth认证
|
||||
BasicAuthUser string `json:"BasicAuthUser"` //如果配置此参数,暴露出去的 HTTP 服务需要采用 Basic Auth 的鉴权才能访问
|
||||
BasicAuthPasswd string `json:"BasicAuthPasswd"` //结合 BasicAuthUser 使用
|
||||
|
||||
SafeIPMode string `json:"SafeIPMode"` //IP过滤模式 黑白名单
|
||||
SafeUserAgentMode string `json:"SafeUserAgentMode"` //UserAgent 过滤模式 黑白名单
|
||||
UserAgentfilter []string `json:"UserAgentfilter"` //UserAgent 过滤内容
|
||||
|
||||
CustomRobotTxt bool `json:"CustomRobotTxt"`
|
||||
RobotTxt string `json:"RobotTxt"`
|
||||
//------------------
|
||||
logsBuffer *logsbuffer.LogsBuffer
|
||||
logrus *logrus.Logger
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
type ReverseProxyRule struct {
|
||||
RuleName string `json:"RuleName"`
|
||||
RuleKey string `json:"RuleKey"`
|
||||
Enable bool `json:"Enable"`
|
||||
ListenIP string `json:"ListenIP"`
|
||||
ListenPort int `json:"ListenPort"`
|
||||
EnableTLS bool `json:"EnableTLS"`
|
||||
Network string `json:"Network"`
|
||||
|
||||
DefaultProxy struct {
|
||||
SubReverProxyRule
|
||||
} `json:"DefaultProxy"`
|
||||
|
||||
ProxyList []ReverseProxy `json:"ProxyList"`
|
||||
domainsMap *sync.Map
|
||||
initOnec sync.Once
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) Init() {
|
||||
r.initOnec.Do(func() {
|
||||
r.initDomainsMap()
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) Logf(level logrus.Level, c *gin.Context, format string, v ...any) {
|
||||
clientIP := r.ClientIP(c)
|
||||
remoteIP := c.RemoteIP()
|
||||
method := c.Request.Method
|
||||
host := c.Request.Host
|
||||
//hostname, hostport := httputils.SplitHostPort(c.Request.Host)
|
||||
url := c.Request.URL.String()
|
||||
//path := c.Request.URL.Path
|
||||
|
||||
r.GetLogrus().WithFields(logrus.Fields{
|
||||
"ClientIP": clientIP,
|
||||
"RemoteIP": remoteIP,
|
||||
"Method": method,
|
||||
"Host": host,
|
||||
// "Hostname": hostname,
|
||||
// "Hostport": hostport,
|
||||
"URL": url,
|
||||
//"path": path,
|
||||
"UserAgent": c.Request.UserAgent(),
|
||||
}).Logf(level, format, v...)
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) HandlerReverseProxy(remote *url.URL, path string, c *gin.Context) {
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(remote)
|
||||
proxy.Director = func(req *http.Request) {
|
||||
req.Header = c.Request.Header
|
||||
req.Host = remote.Host
|
||||
req.URL.Scheme = remote.Scheme
|
||||
req.URL.Host = remote.Host
|
||||
req.URL.Path = path
|
||||
if r.AddRemoteIPToHeader && r.AddRemoteIPHeaderKey != "" {
|
||||
cip := r.ClientIP(c)
|
||||
req.Header.Add(r.AddRemoteIPHeaderKey, cip)
|
||||
}
|
||||
}
|
||||
proxy.ErrorLog = r.GetLogger()
|
||||
proxy.ServeHTTP(c.Writer, c.Request)
|
||||
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) PrintfToConsole(entry *logrus.Entry) error {
|
||||
if !r.LogOutputToConsole {
|
||||
return nil
|
||||
}
|
||||
|
||||
s, _ := entry.String()
|
||||
log.Print(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) GetLogrus() *logrus.Logger {
|
||||
if r.logrus == nil {
|
||||
r.logrus = logrus.New()
|
||||
r.logrus.SetLevel(logrus.Level(r.LogLevel))
|
||||
r.GetLogsBuffer().SetFireCallback(r.PrintfToConsole)
|
||||
r.logrus.SetOutput(r.GetLogsBuffer())
|
||||
r.logrus.SetFormatter(&logrus.JSONFormatter{
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
DisableTimestamp: true,
|
||||
DisableHTMLEscape: true,
|
||||
DataKey: "ExtInfo",
|
||||
})
|
||||
|
||||
r.logrus.AddHook(r.GetLogsBuffer())
|
||||
|
||||
}
|
||||
return r.logrus
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) GetLogger() *log.Logger {
|
||||
if r.logger == nil {
|
||||
r.logger = log.New(r.GetLogsBuffer(), "", log.LstdFlags)
|
||||
}
|
||||
return r.logger
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) GetLogsBuffer() *logsbuffer.LogsBuffer {
|
||||
if r.logsBuffer == nil {
|
||||
r.logsBuffer = CreateReverseProxyLogbuffer(r.Key, r.AccessLogMaxNum)
|
||||
}
|
||||
return r.logsBuffer
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) checkupClientIP(ip string) bool {
|
||||
return SafeCheck(r.SafeIPMode, ip)
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) checkupUserAgent(ua string) bool {
|
||||
|
||||
isContains := false
|
||||
for _, c := range r.UserAgentfilter {
|
||||
if strings.Contains(ua, c) {
|
||||
isContains = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch r.SafeUserAgentMode {
|
||||
case "whitelist":
|
||||
return isContains
|
||||
case "blacklist":
|
||||
return !isContains
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) ReverseProxyHandler(c *gin.Context) {
|
||||
path := c.Param("proxyPath")
|
||||
hostName, _ := httputils.SplitHostPort(c.Request.Host)
|
||||
rule, ok := r.GetSubRuleByDomain(hostName)
|
||||
|
||||
var subRule *SubReverProxyRule = nil
|
||||
if ok && rule.Enable {
|
||||
subRule = &rule.SubReverProxyRule
|
||||
} else {
|
||||
subRule = &r.DefaultProxy.SubReverProxyRule
|
||||
}
|
||||
|
||||
if !subRule.checkupClientIP(subRule.ClientIP(c)) { //IP检查
|
||||
subRule.Logf(logrus.WarnLevel, c, "IP[%s]禁止访问,当前Ip检查模式[%s]", subRule.ClientIP(c), subRule.SafeIPMode)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if !subRule.checkupUserAgent(c.Request.UserAgent()) {
|
||||
subRule.Logf(logrus.WarnLevel, c, "IP[%s]UA[%s]禁止访问,当前UA检查模式[%s]", subRule.ClientIP(c), c.Request.UserAgent(), subRule.SafeUserAgentMode)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if !subRule.BasicAuthHandler(c) {
|
||||
subRule.Logf(logrus.WarnLevel, c, "BasicAuth认证不通过")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if subRule.CustomRobotTxt && c.Request.RequestURI == "/robots.txt" {
|
||||
if c.Request.Method != "GET" && c.Request.Method != "HEAD" {
|
||||
status := http.StatusOK
|
||||
if c.Request.Method != "OPTIONS" {
|
||||
status = http.StatusMethodNotAllowed
|
||||
}
|
||||
c.Header("Allow", "GET,HEAD,OPTIONS")
|
||||
c.AbortWithStatus(status)
|
||||
return
|
||||
}
|
||||
c.Data(http.StatusOK, "text/plain", []byte(subRule.RobotTxt))
|
||||
subRule.Logf(logrus.InfoLevel, c, "触发自定义robots.txt")
|
||||
return
|
||||
}
|
||||
|
||||
location := subRule.GetLocation()
|
||||
if location == "" && subRule.Key == r.RuleKey {
|
||||
subRule.Logf(logrus.InfoLevel, c, "域名[%s]没有对应后端地址,默认后端地址没有设置", hostName)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if subRule.Key == r.RuleKey {
|
||||
subRule.Logf(logrus.InfoLevel, c, "[%s] 指向默认后端地址[%s%s]", hostName, location, c.Request.URL.String())
|
||||
} else {
|
||||
subRule.Logf(logrus.InfoLevel, c, "[%s] 指向后端地址[%s%s]", hostName, location, c.Request.URL.String())
|
||||
}
|
||||
|
||||
remote, err := url.Parse(location)
|
||||
if err != nil {
|
||||
subRule.Logf(logrus.ErrorLevel, c, "后端地址转换出错:%s", err.Error())
|
||||
c.JSON(http.StatusBadGateway, gin.H{"ret": 1, "msg": fmt.Sprintf("后端地址[%s] 转换出错:%s", location, err.Error())})
|
||||
return
|
||||
}
|
||||
subRule.HandlerReverseProxy(remote, path, c)
|
||||
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) GetSubRuleByDomain(domain string) (*ReverseProxy, bool) {
|
||||
val, ok := r.domainsMap.Load(domain)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return val.(*ReverseProxy), true
|
||||
}
|
||||
|
||||
type ReverseProxy struct {
|
||||
SubReverProxyRule
|
||||
Enable bool `json:"Enable"`
|
||||
Remark string `json:"Remark"`
|
||||
Domains []string `json:"Domains"` //自定义域名
|
||||
|
||||
}
|
||||
|
||||
func GetSubRuleByKey(ruleKey, proxyKey string) *SubReverProxyRule {
|
||||
//rule := getSubRuleByKey()
|
||||
|
||||
rule := GetReverseProxyRuleByKey(ruleKey)
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
//fmt.Printf("FFF ruleKey:%s proxyKey:%s\n", ruleKey, proxyKey)
|
||||
|
||||
if proxyKey == "default" {
|
||||
|
||||
return &rule.DefaultProxy.SubReverProxyRule
|
||||
}
|
||||
|
||||
for i := range rule.ProxyList {
|
||||
if rule.ProxyList[i].Key == proxyKey {
|
||||
return &rule.ProxyList[i].SubReverProxyRule
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) GetServer() *http.Server {
|
||||
s, loaded := reverseProxyServerStore.Load(r.RuleKey)
|
||||
if !loaded {
|
||||
return nil
|
||||
}
|
||||
return s.(*http.Server)
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) SetServer(s *http.Server) {
|
||||
if s == nil {
|
||||
reverseProxyServerStore.Delete(r.RuleKey)
|
||||
return
|
||||
}
|
||||
reverseProxyServerStore.Store(r.RuleKey, s)
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) ServerStart() error {
|
||||
// r.smu.Lock()
|
||||
// defer r.smu.Unlock()
|
||||
reverseProxyServerStoreMu.Lock()
|
||||
defer reverseProxyServerStoreMu.Unlock()
|
||||
server := r.GetServer()
|
||||
|
||||
if server != nil {
|
||||
return fmt.Errorf("RuleServer[%s]已经启动,请勿重复启动", r.Addr())
|
||||
}
|
||||
ginR := gin.New()
|
||||
|
||||
ginR.Any("/*proxyPath", r.ReverseProxyHandler)
|
||||
server = &http.Server{
|
||||
Addr: r.Addr(),
|
||||
Handler: ginR,
|
||||
}
|
||||
|
||||
ln, err := net.Listen(r.Network, r.Addr())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var serveResult error
|
||||
|
||||
go func() {
|
||||
serveResult = server.Serve(ln)
|
||||
}()
|
||||
|
||||
<-time.After(time.Millisecond * 300)
|
||||
|
||||
defer func() {
|
||||
if serveResult == nil {
|
||||
//setPreReverseProxyHttpServer(r.RuleKey, r.server)
|
||||
r.SetServer(server)
|
||||
}
|
||||
}()
|
||||
|
||||
return serveResult
|
||||
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) ServerStop() {
|
||||
reverseProxyServerStoreMu.Lock()
|
||||
defer reverseProxyServerStoreMu.Unlock()
|
||||
server := r.GetServer()
|
||||
if server == nil {
|
||||
return
|
||||
}
|
||||
server.Close()
|
||||
r.SetServer(nil)
|
||||
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) initDomainsMap() error {
|
||||
r.domainsMap = &sync.Map{}
|
||||
for i := range r.ProxyList {
|
||||
for j := range r.ProxyList[i].Domains {
|
||||
_, loaded := r.domainsMap.LoadOrStore(r.ProxyList[i].Domains[j], &r.ProxyList[i])
|
||||
if loaded {
|
||||
return fmt.Errorf("前端域名[%s]冲突", r.ProxyList[i].Domains[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) initOnceExec() {
|
||||
r.initOnce.Do(func() {
|
||||
r.locationsCount = len(r.Locations)
|
||||
r.InitTrustedProxyCIDRs()
|
||||
r.locationMutex = &sync.Mutex{}
|
||||
})
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) GetLocation() string {
|
||||
r.initOnceExec()
|
||||
r.locationMutex.Lock()
|
||||
defer func() {
|
||||
r.locationIndex++
|
||||
r.locationMutex.Unlock()
|
||||
}()
|
||||
|
||||
if r.locationsCount == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return r.Locations[r.locationIndex%uint64(r.locationsCount)]
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) BasicAuthHandler(c *gin.Context) bool {
|
||||
if !r.EnableBasicAuth || r.BasicAuthUser == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
realm := "Basic realm=" + strconv.Quote("Authorization Required")
|
||||
pairs := ginutils.ProcessAccounts(gin.Accounts{r.BasicAuthUser: r.BasicAuthPasswd})
|
||||
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 false
|
||||
}
|
||||
c.Set("user", user)
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) InitTrustedProxyCIDRs() error {
|
||||
var res []*net.IPNet
|
||||
for i := range r.TrustedCIDRsStrList {
|
||||
if strings.TrimSpace(r.TrustedCIDRsStrList[i]) == "" {
|
||||
continue
|
||||
}
|
||||
_, cidr, err := net.ParseCIDR(r.TrustedCIDRsStrList[i])
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%s]网段格式有误", r.TrustedCIDRsStrList[i])
|
||||
}
|
||||
res = append(res, cidr)
|
||||
}
|
||||
r.TrustedProxyCIDRs = res
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) ClientIP(c *gin.Context) string {
|
||||
remoteIP := net.ParseIP(c.RemoteIP())
|
||||
if remoteIP == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
trusted := r.isTrustedProxy(remoteIP)
|
||||
|
||||
if trusted && r.ForwardedByClientIP && r.RemoteIPHeaders != nil {
|
||||
for _, headerName := range r.RemoteIPHeaders {
|
||||
ip, valid := r.validateHeader(c.Request.Header.Get(headerName))
|
||||
if valid {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return remoteIP.String()
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) validateHeader(header string) (clientIP string, valid bool) {
|
||||
if header == "" {
|
||||
return "", false
|
||||
}
|
||||
items := strings.Split(header, ",")
|
||||
for i := len(items) - 1; i >= 0; i-- {
|
||||
ipStr := strings.TrimSpace(items[i])
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if (i == 0) || (!r.isTrustedProxy(ip)) {
|
||||
return ipStr, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (r *SubReverProxyRule) isTrustedProxy(ip net.IP) bool {
|
||||
r.initOnceExec()
|
||||
|
||||
if r.TrustedProxyCIDRs == nil {
|
||||
return false
|
||||
}
|
||||
for _, cidr := range r.TrustedProxyCIDRs {
|
||||
if cidr.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) Addr() string {
|
||||
return fmt.Sprintf("%s:%d", r.ListenIP, r.ListenPort)
|
||||
}
|
||||
|
||||
type LogItem struct {
|
||||
ProxyKey string
|
||||
ClientIP string
|
||||
LogContent string
|
||||
LogTime string
|
||||
}
|
||||
|
||||
// 2006-01-02 15:04:05
|
||||
func ReverseProxyLogConvert(lg *logsbuffer.LogItem) any {
|
||||
l := LogItem{
|
||||
LogContent: lg.Content,
|
||||
LogTime: time.Unix(lg.Timestamp/int64(time.Second), 0).Format("2006-01-02 15:04:05")}
|
||||
return l
|
||||
}
|
||||
|
||||
func (r *ReverseProxyRule) GetLastLogs() map[string][]any {
|
||||
res := make(map[string][]any)
|
||||
res["default"] = r.DefaultProxy.GetLogsBuffer().GetLastLogs(ReverseProxyLogConvert, r.DefaultProxy.WebListShowLastLogMaxCount)
|
||||
|
||||
for i := range r.ProxyList {
|
||||
res[r.ProxyList[i].Key] = r.ProxyList[i].GetLogsBuffer().GetLastLogs(
|
||||
ReverseProxyLogConvert, r.ProxyList[i].WebListShowLastLogMaxCount)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
func GetReverseProxyRuleList() []*ReverseProxyRule {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
|
||||
var resList []*ReverseProxyRule
|
||||
|
||||
for i := range programConfigure.ReverseProxyRuleList {
|
||||
programConfigure.ReverseProxyRuleList[i].Init()
|
||||
rule := programConfigure.ReverseProxyRuleList[i]
|
||||
resList = append(resList, &rule)
|
||||
}
|
||||
return resList
|
||||
}
|
||||
|
||||
func GetReverseProxyRuleByKey(ruleKey string) *ReverseProxyRule {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
ruleIndex := -1
|
||||
|
||||
for i := range programConfigure.ReverseProxyRuleList {
|
||||
if programConfigure.ReverseProxyRuleList[i].RuleKey == ruleKey {
|
||||
ruleIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if ruleIndex == -1 {
|
||||
return nil
|
||||
}
|
||||
res := programConfigure.ReverseProxyRuleList[ruleIndex]
|
||||
return &res
|
||||
}
|
||||
|
||||
func ReverseProxyRuleListAdd(rule *ReverseProxyRule) error {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
|
||||
programConfigure.ReverseProxyRuleList = append(programConfigure.ReverseProxyRuleList, *rule)
|
||||
return Save()
|
||||
}
|
||||
|
||||
func ReverseProxyRuleListDelete(ruleKey string) error {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
|
||||
ruleIndex := -1
|
||||
|
||||
for i := range programConfigure.ReverseProxyRuleList {
|
||||
if programConfigure.ReverseProxyRuleList[i].RuleKey == ruleKey {
|
||||
ruleIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ruleIndex == -1 {
|
||||
return fmt.Errorf("找不到需要删除的DDNS任务")
|
||||
}
|
||||
|
||||
programConfigure.ReverseProxyRuleList = DeleteReverseProxyRuleListlice(programConfigure.ReverseProxyRuleList, ruleIndex)
|
||||
return Save()
|
||||
}
|
||||
|
||||
func EnableReverseProxyRuleByKey(ruleKey string, enable bool) error {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
ruleIndex := -1
|
||||
|
||||
for i := range programConfigure.DDNSTaskList {
|
||||
if programConfigure.ReverseProxyRuleList[i].RuleKey == ruleKey {
|
||||
ruleIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if ruleIndex == -1 {
|
||||
return fmt.Errorf("开关反向代理规则失败,ruleKey %s 未找到", ruleKey)
|
||||
}
|
||||
programConfigure.ReverseProxyRuleList[ruleIndex].Enable = enable
|
||||
|
||||
return Save()
|
||||
}
|
||||
|
||||
func EnableReverseProxySubRule(ruleKey, proxyKey string, enable bool) error {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
ruleIndex := -1
|
||||
|
||||
for i := range programConfigure.DDNSTaskList {
|
||||
if programConfigure.ReverseProxyRuleList[i].RuleKey == ruleKey {
|
||||
ruleIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if ruleIndex == -1 {
|
||||
return fmt.Errorf("开关反向代理子规则失败,ruleKey %s 未找到", ruleKey)
|
||||
}
|
||||
|
||||
proxyIndex := -1
|
||||
for i := range programConfigure.ReverseProxyRuleList[ruleIndex].ProxyList {
|
||||
if programConfigure.ReverseProxyRuleList[ruleIndex].ProxyList[i].Key == proxyKey {
|
||||
proxyIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if proxyIndex == -1 {
|
||||
return fmt.Errorf("开关反向代理子规则失败,proxyKey %s 未找到", proxyKey)
|
||||
}
|
||||
|
||||
programConfigure.ReverseProxyRuleList[ruleIndex].ProxyList[proxyIndex].Enable = enable
|
||||
|
||||
return Save()
|
||||
|
||||
}
|
||||
|
||||
func UpdateReverseProxyRulet(rule ReverseProxyRule) error {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
ruleIndex := -1
|
||||
|
||||
for i := range programConfigure.DDNSTaskList {
|
||||
if programConfigure.ReverseProxyRuleList[i].RuleKey == rule.RuleKey {
|
||||
ruleIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ruleIndex == -1 {
|
||||
return fmt.Errorf("找不到需要更新的反向代理规则")
|
||||
}
|
||||
|
||||
// rule.RuleKey = programConfigure.ReverseProxyRuleList[ruleIndex].RuleKey
|
||||
programConfigure.ReverseProxyRuleList[ruleIndex] = rule
|
||||
|
||||
return Save()
|
||||
}
|
||||
|
||||
func DeleteReverseProxyRuleListlice(a []ReverseProxyRule, deleteIndex int) []ReverseProxyRule {
|
||||
j := 0
|
||||
for i := range a {
|
||||
if i != deleteIndex {
|
||||
a[j] = a[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return a[:j]
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package config
|
||||
|
||||
import (
|
||||
|
@ -22,17 +22,17 @@ func whiteListCheck(ip string) bool {
|
|||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
if programConfigure == nil {
|
||||
//log.Printf("AAAA")
|
||||
return false
|
||||
}
|
||||
|
||||
for _, item := range programConfigure.WhiteListConfigure.WhiteList {
|
||||
if item.IP != ip {
|
||||
|
||||
if !item.Contains(ip) {
|
||||
continue
|
||||
}
|
||||
|
||||
itemEffectiveTime, err := time.ParseInLocation("2006-01-02 15:04:05", item.EffectiveTime, time.Local)
|
||||
if err != nil {
|
||||
//log.Printf("BBBB")
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ func blackListCheck(ip string) bool {
|
|||
}
|
||||
|
||||
for _, item := range programConfigure.BlackListConfigure.BlackList {
|
||||
if item.IP != ip {
|
||||
if !item.Contains(ip) {
|
||||
continue
|
||||
}
|
||||
itemEffectiveTime, err := time.ParseInLocation("2006-01-02 15:04:05", item.EffectiveTime, time.Local)
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package config
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WhiteListConfigure struct {
|
||||
BaseConfigure WhiteListBaseConfigure `json:"BaseConfigure"`
|
||||
|
@ -9,8 +14,25 @@ type WhiteListConfigure struct {
|
|||
}
|
||||
|
||||
type WhiteListItem struct {
|
||||
IP string `json:"IP"`
|
||||
EffectiveTime string `json:"Effectivetime"` //有效时间
|
||||
IP string `json:"IP"`
|
||||
EffectiveTime string `json:"Effectivetime"` //有效时间
|
||||
NetIP net.IP `json:"-"`
|
||||
Cidr *net.IPNet `json:"-"`
|
||||
}
|
||||
|
||||
func (w *WhiteListItem) Contains(ip string) bool {
|
||||
netIP := net.ParseIP(ip)
|
||||
if netIP == nil {
|
||||
return false
|
||||
}
|
||||
if w.NetIP != nil {
|
||||
return w.NetIP.Equal(netIP)
|
||||
}
|
||||
|
||||
if w.Cidr != nil {
|
||||
return w.Cidr.Contains(netIP)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type WhiteListBaseConfigure struct {
|
||||
|
@ -52,10 +74,44 @@ func GetWhiteList() []WhiteListItem {
|
|||
return resList
|
||||
}
|
||||
|
||||
func WhiteListInit() {
|
||||
programConfigureMutex.RLock()
|
||||
defer programConfigureMutex.RUnlock()
|
||||
var netIP net.IP
|
||||
var cidr *net.IPNet
|
||||
|
||||
for i := range programConfigure.WhiteListConfigure.WhiteList {
|
||||
netIP = nil
|
||||
cidr = nil
|
||||
if strings.Contains(programConfigure.WhiteListConfigure.WhiteList[i].IP, "/") {
|
||||
_, cidr, _ = net.ParseCIDR(programConfigure.WhiteListConfigure.WhiteList[i].IP)
|
||||
} else {
|
||||
netIP = net.ParseIP(programConfigure.WhiteListConfigure.WhiteList[i].IP)
|
||||
}
|
||||
programConfigure.WhiteListConfigure.WhiteList[i].Cidr = cidr
|
||||
programConfigure.WhiteListConfigure.WhiteList[i].NetIP = netIP
|
||||
}
|
||||
}
|
||||
|
||||
func WhiteListAdd(ip string, activelifeDuration int32) (string, error) {
|
||||
programConfigureMutex.Lock()
|
||||
defer programConfigureMutex.Unlock()
|
||||
|
||||
var err error
|
||||
var netIP net.IP = nil
|
||||
var cidr *net.IPNet = nil
|
||||
if strings.Contains(ip, "/") {
|
||||
_, cidr, err = net.ParseCIDR(ip)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("网段格式有误,转换出错:%s", err.Error())
|
||||
}
|
||||
} else {
|
||||
netIP = net.ParseIP(ip)
|
||||
if netIP == nil {
|
||||
return "", fmt.Errorf("IP格式有误")
|
||||
}
|
||||
}
|
||||
|
||||
if activelifeDuration <= 0 {
|
||||
activelifeDuration = programConfigure.WhiteListConfigure.BaseConfigure.ActivelifeDuration
|
||||
}
|
||||
|
@ -68,7 +124,7 @@ func WhiteListAdd(ip string, activelifeDuration int32) (string, error) {
|
|||
return EffectiveTimeStr, Save()
|
||||
}
|
||||
}
|
||||
item := WhiteListItem{IP: ip, EffectiveTime: EffectiveTimeStr}
|
||||
item := WhiteListItem{IP: ip, EffectiveTime: EffectiveTimeStr, NetIP: netIP, Cidr: cidr}
|
||||
programConfigure.WhiteListConfigure.WhiteList = append(programConfigure.WhiteListConfigure.WhiteList, item)
|
||||
return EffectiveTimeStr, Save()
|
||||
}
|
||||
|
|
|
@ -166,6 +166,10 @@ func (dnspod *Dnspod) getRecordList(domain *ddnscore.Domain, typ string) (result
|
|||
params.Add("sub_domain", domain.GetSubDomain())
|
||||
params.Add("format", "json")
|
||||
|
||||
if !params.Has("record_line") {
|
||||
params.Add("record_line", "默认")
|
||||
}
|
||||
|
||||
client, e := dnspod.CreateHTTPClient()
|
||||
if e != nil {
|
||||
err = e
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
package ddns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gdy666/lucky/ddnscore.go"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
|
||||
)
|
||||
|
||||
type godaddyRecord struct {
|
||||
Data string `json:"data"`
|
||||
Name string `json:"name"`
|
||||
TTL int `json:"ttl"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type godaddyRecords []godaddyRecord
|
||||
|
||||
type GoDaddy struct {
|
||||
DNSCommon
|
||||
TTL int
|
||||
header http.Header
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// Init 初始化
|
||||
func (gd *GoDaddy) Init(task *ddnscore.DDNSTaskInfo) {
|
||||
gd.DNSCommon.Init(task)
|
||||
// if task.TTL == "" {
|
||||
// // 默认600s
|
||||
// gd.TTL = 600
|
||||
// } else {
|
||||
// gd.TTL = task.TTL
|
||||
// }
|
||||
if task.TTL == "" {
|
||||
// 默认300s
|
||||
gd.TTL = 600
|
||||
} else {
|
||||
ttl, err := strconv.Atoi(task.TTL)
|
||||
if err != nil {
|
||||
gd.TTL = 600
|
||||
} else {
|
||||
gd.TTL = ttl
|
||||
}
|
||||
}
|
||||
gd.header = map[string][]string{
|
||||
"Authorization": {fmt.Sprintf("sso-key %s:%s", task.DNS.ID, task.DNS.Secret)},
|
||||
"Content-Type": {"application/json"},
|
||||
}
|
||||
//g.throttle, _ = util.GetThrottle(55)
|
||||
gd.client, _ = gd.CreateHTTPClient()
|
||||
|
||||
gd.SetCreateUpdateDomainFunc(gd.createUpdateDomain)
|
||||
}
|
||||
|
||||
func (gd *GoDaddy) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) {
|
||||
|
||||
_, err := gd.sendReq(http.MethodPut, recordType, domain, &godaddyRecords{godaddyRecord{
|
||||
Data: ipAddr,
|
||||
Name: domain.SubDomain,
|
||||
TTL: gd.TTL,
|
||||
Type: recordType,
|
||||
}})
|
||||
if err != nil {
|
||||
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (gd *GoDaddy) sendReq(method string, rType string, domain *ddnscore.Domain, data any) (*godaddyRecords, error) {
|
||||
|
||||
var body *bytes.Buffer
|
||||
if data != nil {
|
||||
if buffer, err := json.Marshal(data); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
body = bytes.NewBuffer(buffer)
|
||||
}
|
||||
}
|
||||
path := fmt.Sprintf("https://api.godaddy.com/v1/domains/%s/records/%s/%s",
|
||||
domain.DomainName, rType, domain.SubDomain)
|
||||
req, err := http.NewRequest(method, path, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = gd.header
|
||||
|
||||
resp, err := gd.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &godaddyRecords{}
|
||||
|
||||
httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
|
||||
|
||||
return result, nil
|
||||
}
|
|
@ -101,8 +101,6 @@ func DDNSTaskInfoMapUpdateDomainInfo(task *DDNSTaskInfo) {
|
|||
state.(*DDNSTaskState).Domains = task.TaskState.Domains
|
||||
}
|
||||
|
||||
//func DDNSTaskInfo
|
||||
|
||||
func DDNSTaskInfoMapDelete(key string) {
|
||||
taskInfoMapMutex.Lock()
|
||||
defer taskInfoMapMutex.Unlock()
|
||||
|
@ -126,8 +124,7 @@ func GetDDNSTaskInfoList() []*DDNSTaskInfo {
|
|||
var res []*DDNSTaskInfo
|
||||
for i := range ddnsTaskList {
|
||||
ti := CreateDDNSTaskInfo(ddnsTaskList[i])
|
||||
res = append(res, &ti)
|
||||
taskInfoMap.Store(ddnsTaskList[i].TaskKey, &ti.TaskState)
|
||||
res = append(res, ti)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
@ -140,25 +137,23 @@ func GetDDNSTaskInfoByKey(key string) *DDNSTaskInfo {
|
|||
return nil
|
||||
}
|
||||
info := CreateDDNSTaskInfo(ddnsConf)
|
||||
return &info
|
||||
return info
|
||||
}
|
||||
|
||||
func CreateDDNSTaskInfo(task *config.DDNSTask) DDNSTaskInfo {
|
||||
func CreateDDNSTaskInfo(task *config.DDNSTask) *DDNSTaskInfo {
|
||||
var res DDNSTaskInfo
|
||||
res.DDNSTask = *task
|
||||
info, ok := taskInfoMap.Load(task.TaskKey)
|
||||
if ok {
|
||||
res.TaskState = *info.(*DDNSTaskState)
|
||||
} else {
|
||||
var ds DDNSTaskState
|
||||
ds.Init(res.Domains)
|
||||
res.TaskState.Init(res.Domains)
|
||||
if task.Enable {
|
||||
ds.SetDomainUpdateStatus(UpdateWaiting, "")
|
||||
res.TaskState.SetDomainUpdateStatus(UpdateWaiting, "")
|
||||
} else {
|
||||
ds.SetDomainUpdateStatus(UpdateStop, "")
|
||||
res.TaskState.SetDomainUpdateStatus(UpdateStop, "")
|
||||
}
|
||||
|
||||
res.TaskState = ds
|
||||
taskInfoMap.Store(task.TaskKey, &res.TaskState)
|
||||
}
|
||||
return res
|
||||
return &res
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/gdy666/lucky/config"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/netinterfaces"
|
||||
)
|
||||
|
||||
// Ipv4Reg IPv4正则
|
||||
|
@ -50,7 +51,7 @@ func (d *DDNSTaskInfo) CheckIPChange() (ipAddr string, change bool) {
|
|||
func (d *DDNSTaskInfo) getIpv4Addr() (result string) {
|
||||
// 判断从哪里获取IP
|
||||
if d.GetType == "netInterface" {
|
||||
result = GetIPFromNetInterface("IPv4", d.NetInterface, d.IPReg)
|
||||
result = netinterfaces.GetIPFromNetInterface("IPv4", d.NetInterface, d.IPReg)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -111,7 +112,7 @@ func (d *DDNSTaskInfo) getIpv4Addr() (result string) {
|
|||
func (d *DDNSTaskInfo) getIpv6Addr() (result string) {
|
||||
// 判断从哪里获取IP
|
||||
if d.GetType == "netInterface" {
|
||||
result = GetIPFromNetInterface("IPv6", d.NetInterface, d.IPReg)
|
||||
result = netinterfaces.GetIPFromNetInterface("IPv6", d.NetInterface, d.IPReg)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
19
go.mod
19
go.mod
|
@ -9,8 +9,9 @@ require (
|
|||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/guonaihong/gout v0.3.1
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/shirou/gopsutil/v3 v3.22.7
|
||||
golang.org/x/net v0.0.0-20220809012201-f428fae20770
|
||||
github.com/shirou/gopsutil/v3 v3.22.8
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
golang.org/x/net v0.0.0-20220921203646-d300de134e69
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -18,24 +19,24 @@ require (
|
|||
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.11.0 // indirect
|
||||
github.com/goccy/go-json v0.9.10 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
||||
github.com/goccy/go-json v0.9.11 // 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-20220517141722-cf486979b281 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.5.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-20220722155217-630584e8d5aa // indirect
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // indirect
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
|
|
43
go.sum
43
go.sum
|
@ -19,19 +19,20 @@ github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa
|
|||
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.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
|
||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc=
|
||||
github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
github.com/goccy/go-json v0.9.11/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.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/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/guonaihong/gout v0.3.1 h1:pj/44Jw0TTmcHF2RjMaCWhKPwCH98YuQejbN15Hts/o=
|
||||
|
@ -49,10 +50,11 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|||
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/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281 h1:aczX6NMOtt6L4YT0fQvKkDK6LZEtdOso9sUH89V1+P0=
|
||||
github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281/go.mod h1:lc+czkgO/8F7puNki5jk8QyujbfK1LOT7Wl0ON2hxyk=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY=
|
||||
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -61,8 +63,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
|||
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/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
|
||||
github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -74,15 +76,16 @@ github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:Om
|
|||
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.7 h1:flKnuCMfUUrO+oAvwAd6GKZgnPzr098VA/UJ14nhJd4=
|
||||
github.com/shirou/gopsutil/v3 v3.22.7/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
|
||||
github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHSzD9Y=
|
||||
github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
|
@ -101,8 +104,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
|
||||
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
|
@ -113,8 +116,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220809012201-f428fae20770 h1:dIi4qVdvjZEjiMDv7vhokAZNGnz3kepwuXqFKYDdDMs=
|
||||
golang.org/x/net v0.0.0-20220809012201-f428fae20770/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220921203646-d300de134e69 h1:hUJpGDpnfwdJW8iNypFjmSY0sCBEL+spFTZ2eO+Sfps=
|
||||
golang.org/x/net v0.0.0-20220921203646-d300de134e69/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
|
@ -132,8 +135,10 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs=
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
|
27
main.go
27
main.go
|
@ -10,18 +10,19 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gdy666/lucky/base"
|
||||
"github.com/gdy666/lucky/config"
|
||||
"github.com/gdy666/lucky/ddns"
|
||||
"github.com/gdy666/lucky/reverseproxy"
|
||||
"github.com/gdy666/lucky/rule"
|
||||
"github.com/gdy666/lucky/socketproxy"
|
||||
)
|
||||
|
||||
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)")
|
||||
udpPackageSize = flag.Int("ups", socketproxy.UDP_DEFAULT_PACKAGE_SIZE, "udp package max size")
|
||||
smc = flag.Int64("smc", socketproxy.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")
|
||||
|
@ -36,6 +37,11 @@ var (
|
|||
|
||||
var runTime time.Time
|
||||
|
||||
func init() {
|
||||
var cstZone = time.FixedZone("CST", 8*3600) // 东八
|
||||
time.Local = cstZone
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
config.InitAppInfo(version, date)
|
||||
|
@ -55,16 +61,19 @@ func main() {
|
|||
|
||||
gcf := config.GetConfig()
|
||||
|
||||
config.BlackListInit()
|
||||
config.WhiteListInit()
|
||||
|
||||
//fmt.Printf("*gcf:%v\n", *gcf)
|
||||
|
||||
base.SetSafeCheck(config.SafeCheck)
|
||||
base.SetGlobalMaxConnections(gcf.BaseConfigure.GlobalMaxConnections)
|
||||
base.SetGlobalMaxProxyCount(gcf.BaseConfigure.ProxyCountLimit)
|
||||
socketproxy.SetSafeCheck(config.SafeCheck)
|
||||
socketproxy.SetGlobalMaxConnections(gcf.BaseConfigure.GlobalMaxConnections)
|
||||
socketproxy.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)
|
||||
RunAdminWeb(gcf.BaseConfigure.AdminWebListenPort, gcf.BaseConfigure.LogMaxSize)
|
||||
|
||||
runTime = time.Now()
|
||||
|
||||
|
@ -89,6 +98,8 @@ func main() {
|
|||
ddns.Run(time.Duration(ddnsConf.FirstCheckDelay)*time.Second, time.Duration(ddnsConf.Intervals)*time.Second)
|
||||
}
|
||||
|
||||
reverseproxy.InitReverseProxyServer()
|
||||
|
||||
//ddns.RunTimer(time.Second, time.Second*30)
|
||||
|
||||
//initProxyList()
|
||||
|
@ -113,7 +124,7 @@ func main() {
|
|||
}
|
||||
|
||||
func LoadRuleListFromCMD(args []string) {
|
||||
options := base.RelayRuleOptions{UDPPackageSize: *udpPackageSize,
|
||||
options := socketproxy.RelayRuleOptions{UDPPackageSize: *udpPackageSize,
|
||||
SingleProxyMaxConnections: *smc,
|
||||
UDPProxyPerformanceMode: *upm,
|
||||
UDPShortMode: *udpshort}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 95 KiB |
|
@ -0,0 +1,76 @@
|
|||
package reverseproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/gdy666/lucky/config"
|
||||
)
|
||||
|
||||
func InitReverseProxyServer() {
|
||||
ruleList := config.GetReverseProxyRuleList()
|
||||
for ruleIndex := range ruleList {
|
||||
if ruleList[ruleIndex].Enable {
|
||||
startRes := ruleList[ruleIndex].ServerStart()
|
||||
if startRes == nil {
|
||||
log.Printf("启动反向代理服务[%s]成功", ruleList[ruleIndex].Addr())
|
||||
} else {
|
||||
log.Printf("启动反向代理服务[%s]失败:%s", ruleList[ruleIndex].Addr(), startRes.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func EnableRuleByKey(key string, enable bool) error {
|
||||
|
||||
rule := config.GetReverseProxyRuleByKey(key)
|
||||
|
||||
if rule == nil {
|
||||
return fmt.Errorf("GetReverseProxyRuleByKey not found:%s", key)
|
||||
}
|
||||
|
||||
if enable {
|
||||
err := rule.ServerStart()
|
||||
if err != nil {
|
||||
log.Printf("启用反向代理规则[%s]出错:%s", rule.Addr(), err.Error())
|
||||
config.EnableReverseProxyRuleByKey(key, false)
|
||||
return fmt.Errorf("启用反向代理规则[%s]出错:%s", rule.Addr(), err.Error())
|
||||
} else {
|
||||
log.Printf("启用反向代理规则[%s]成功", rule.Addr())
|
||||
}
|
||||
} else {
|
||||
rule.ServerStop()
|
||||
log.Printf("停用反向代理规则[%s]成功", rule.Addr())
|
||||
}
|
||||
return config.EnableReverseProxyRuleByKey(key, enable)
|
||||
}
|
||||
|
||||
type RuleInfo struct {
|
||||
config.ReverseProxyRule
|
||||
AccessLogs map[string][]any
|
||||
}
|
||||
|
||||
func GetProxyRuleListInfo() *[]RuleInfo {
|
||||
ruleList := config.GetReverseProxyRuleList()
|
||||
var res []RuleInfo
|
||||
for i := range ruleList {
|
||||
//ti := createProxyRuleInfo(nil, ruleList[i])
|
||||
var ri RuleInfo
|
||||
ri.ReverseProxyRule = *ruleList[i]
|
||||
ri.AccessLogs = ruleList[i].GetLastLogs()
|
||||
res = append(res, ri)
|
||||
}
|
||||
return &res
|
||||
}
|
||||
|
||||
func GetAccessLogs(ruleKey, proxyKey string, pageSize, page int) (int, []any) {
|
||||
var res []any
|
||||
total := 0
|
||||
|
||||
subRule := config.GetSubRuleByKey(ruleKey, proxyKey)
|
||||
if subRule == nil {
|
||||
return 0, res
|
||||
}
|
||||
total, res = subRule.GetLogsBuffer().GetLogsByLimit(config.ReverseProxyLogConvert, pageSize, page)
|
||||
return total, res
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/gdy666/lucky/base"
|
||||
"github.com/gdy666/lucky/config"
|
||||
"github.com/gdy666/lucky/socketproxy"
|
||||
)
|
||||
|
||||
var globalRelayRules *[]RelayRule
|
||||
|
@ -177,14 +177,14 @@ func EnableAllRelayRule() error {
|
|||
}
|
||||
|
||||
for i := range *globalRelayRules {
|
||||
if GetGlobalEnableProxyCount()+(*globalRelayRules)[i].GetProxyCount() <= base.GetGlobalMaxProxyCount() {
|
||||
if GetGlobalEnableProxyCount()+(*globalRelayRules)[i].GetProxyCount() <= socketproxy.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 GetGlobalEnableProxyCount()+(*globalRelayRules)[i].GetProxyCount() > socketproxy.DEFAULT_MAX_PROXY_COUNT {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("\n\t超出代理数最大限制,规则[%s]未启用", (*globalRelayRules)[i].MainConfigure)
|
||||
} else {
|
||||
|
@ -245,7 +245,7 @@ func DeleteRuleSlice(a []RelayRule, deleteIndex int) []RelayRule {
|
|||
return a[:j]
|
||||
}
|
||||
|
||||
//syncRuleListToConfigure 同步规则列表到配置
|
||||
// syncRuleListToConfigure 同步规则列表到配置
|
||||
func syncRuleListToConfigure() error {
|
||||
var ruleList []config.ConfigureRelayRule
|
||||
for i := range *globalRelayRules {
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package rule
|
||||
|
||||
import (
|
||||
"github.com/gdy666/lucky/base"
|
||||
)
|
||||
import "github.com/gdy666/lucky/socketproxy"
|
||||
|
||||
type RelayRuleProxyInfo struct {
|
||||
Proxy string `json:"Proxy"`
|
||||
|
@ -58,7 +56,7 @@ func GetRelayRuleList() (*[]RelayRule, map[string][]RelayRuleProxyInfo) {
|
|||
// return info
|
||||
// }
|
||||
|
||||
func GetProxyInfo(p base.Proxy) map[string]string {
|
||||
func GetProxyInfo(p socketproxy.Proxy) map[string]string {
|
||||
pi := make(map[string]string)
|
||||
pi["proxyType"] = p.GetProxyType()
|
||||
pi["key"] = p.GetKey()
|
||||
|
|
|
@ -8,23 +8,23 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gdy666/lucky/base"
|
||||
"github.com/gdy666/lucky/socketproxy"
|
||||
)
|
||||
|
||||
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:"-"`
|
||||
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 socketproxy.RelayRuleOptions `json:"Options"`
|
||||
SubRuleList []SubRelayRule `json:"SubRuleList"`
|
||||
From string `json:"From"`
|
||||
IsEnable bool `json:"Enable"`
|
||||
proxyList *[]socketproxy.Proxy `json:"-"`
|
||||
}
|
||||
|
||||
type SubRelayRule struct {
|
||||
|
@ -63,8 +63,8 @@ func (r *RelayRule) Disable() {
|
|||
}
|
||||
}
|
||||
|
||||
func GetRelayRulesFromCMD(configureList []string, options *base.RelayRuleOptions) (relayRules *[]RelayRule, err error) {
|
||||
//proxyMap := make(map[string]base.Proxy)
|
||||
func GetRelayRulesFromCMD(configureList []string, options *socketproxy.RelayRuleOptions) (relayRules *[]RelayRule, err error) {
|
||||
//proxyMap := make(map[string]socketproxy.Proxy)
|
||||
|
||||
var relayRuleList []RelayRule
|
||||
|
||||
|
@ -95,7 +95,7 @@ func (r *RelayRule) CreateMainConfigure() (configure string) {
|
|||
return configure
|
||||
}
|
||||
|
||||
func CreateRuleByConfigureAndOptions(name, configureStr string, options base.RelayRuleOptions) (rule *RelayRule, err error) {
|
||||
func CreateRuleByConfigureAndOptions(name, configureStr string, options socketproxy.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)
|
||||
|
@ -115,12 +115,12 @@ func CreateRuleByConfigureAndOptions(name, configureStr string, options base.Rel
|
|||
// }
|
||||
// }
|
||||
|
||||
var pl []base.Proxy
|
||||
var pl []socketproxy.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,
|
||||
p, e := socketproxy.CreateProxy(r.SubRuleList[i].ProxyType,
|
||||
r.SubRuleList[i].BindIP,
|
||||
r.SubRuleList[i].TargetHost,
|
||||
nil,
|
||||
|
@ -137,7 +137,7 @@ func CreateRuleByConfigureAndOptions(name, configureStr string, options base.Rel
|
|||
continue
|
||||
}
|
||||
|
||||
p, e := base.CreateProxy(r.SubRuleList[i].ProxyType,
|
||||
p, e := socketproxy.CreateProxy(r.SubRuleList[i].ProxyType,
|
||||
r.SubRuleList[i].BindIP,
|
||||
r.SubRuleList[i].TargetHost,
|
||||
&r.BalanceTargetAddressList,
|
||||
|
@ -375,13 +375,13 @@ func checkProxyType(proxyTypeList []string) error {
|
|||
}
|
||||
|
||||
// CheckProxyConflict 冲突检查
|
||||
func CheckProxyConflict(proxyList *[]base.Proxy, proxyType, listenIP string, listenPort int) error {
|
||||
proxyMap := make(map[string]base.Proxy)
|
||||
func CheckProxyConflict(proxyList *[]socketproxy.Proxy, proxyType, listenIP string, listenPort int) error {
|
||||
proxyMap := make(map[string]socketproxy.Proxy)
|
||||
for i, p := range *proxyList {
|
||||
proxyMap[p.GetKey()] = (*proxyList)[i]
|
||||
}
|
||||
|
||||
key := base.GetProxyKey(proxyType, listenIP, listenPort)
|
||||
key := socketproxy.GetProxyKey(proxyType, listenIP, listenPort)
|
||||
if _, ok := proxyMap[key]; ok {
|
||||
return fmt.Errorf("绑定的地址和端口存在冲突![%s]", key)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package socketproxy
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
|
@ -1,5 +1,5 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package socketproxy
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,5 +1,5 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package socketproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,5 +1,5 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package socketproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -116,7 +116,6 @@ func (p *TCPUDPProxyCommonConf) GetListentAddress() string {
|
|||
} else {
|
||||
p.listentAddress = fmt.Sprintf("%s:%d", p.listenIP, p.listenPort)
|
||||
}
|
||||
|
||||
}
|
||||
return p.listentAddress
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//Copyright 2022 gdy, 272288813@qq.com
|
||||
package base
|
||||
// Copyright 2022 gdy, 272288813@qq.com
|
||||
package socketproxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -173,7 +173,7 @@ func (p *UDPProxy) StopProxy() {
|
|||
|
||||
}
|
||||
|
||||
//ReadFromTargetOnce one clientAddr only read once,short mode eg: udp dns
|
||||
// ReadFromTargetOnce one clientAddr only read once,short mode eg: udp dns
|
||||
func (p *UDPProxy) ReadFromTargetOnce() bool {
|
||||
if p.targetPort == 53 || p.ShortMode {
|
||||
return true
|
|
@ -0,0 +1,63 @@
|
|||
package ginutils
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type BasicAuthPair struct {
|
||||
value string
|
||||
user string
|
||||
}
|
||||
|
||||
type BasicAuthPairs []BasicAuthPair
|
||||
|
||||
func ProcessAccounts(accounts gin.Accounts) BasicAuthPairs {
|
||||
length := len(accounts)
|
||||
assert1(length > 0, "Empty list of authorized credentials")
|
||||
pairs := make(BasicAuthPairs, 0, length)
|
||||
for user, password := range accounts {
|
||||
assert1(user != "", "User can not be empty")
|
||||
value := authorizationHeader(user, password)
|
||||
pairs = append(pairs, BasicAuthPair{
|
||||
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 BasicAuthPairs) 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,59 @@
|
|||
package ginutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
|
@ -1,25 +1,26 @@
|
|||
package web
|
||||
package ginutils
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func HandlerStaticFiles() gin.HandlerFunc {
|
||||
fileServer := http.FileServer(http.FS(stafs))
|
||||
func HandlerStaticFiles(files fs.FS) gin.HandlerFunc {
|
||||
fileServer := http.FileServer(http.FS(files))
|
||||
return func(c *gin.Context) {
|
||||
staticFile := isStaticFile(http.FS(stafs), c.Request.URL.Path, true)
|
||||
staticFile := isStaticFile(http.FS(files), c.Request.URL.Path, true)
|
||||
if staticFile {
|
||||
fileServer.ServeHTTP(c.Writer, c.Request)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
func isStaticFile(fs http.FileSystem, name string, redirect bool) (isFile bool) {
|
||||
const indexPage = "/index.html"
|
||||
if strings.HasSuffix(name, indexPage) {
|
|
@ -1,17 +1,13 @@
|
|||
package ginutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
//Cors 处理跨域请求,支持options访问
|
||||
// Cors 处理跨域请求,支持options访问
|
||||
func Cors(params ...interface{}) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
|
@ -31,57 +27,7 @@ func Cors(params ...interface{}) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
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 获取子域名部分
|
||||
// GetChildDomain 获取子域名部分
|
||||
func GetChildDomain(host string) string {
|
||||
hostSplitList := strings.Split(host, ".")
|
||||
listLen := len(hostSplitList)
|
||||
|
|
|
@ -24,6 +24,36 @@ func init() {
|
|||
globalTransportMap = make(map[string]*http.Transport)
|
||||
}
|
||||
|
||||
func SplitHostPort(hostPort string) (host, port string) {
|
||||
host = hostPort
|
||||
|
||||
colon := strings.LastIndexByte(host, ':')
|
||||
if colon != -1 && validOptionalPort(host[colon:]) {
|
||||
host, port = host[:colon], host[colon+1:]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
host = host[1 : len(host)-1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validOptionalPort(port string) bool {
|
||||
if port == "" {
|
||||
return true
|
||||
}
|
||||
if port[0] != ':' {
|
||||
return false
|
||||
}
|
||||
for _, b := range port[1:] {
|
||||
if b < '0' || b > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func GetAndParseJSONResponseFromHttpResponse(resp *http.Response, result interface{}) error {
|
||||
bytes, err := GetBytesFromHttpResponse(resp)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
package logsbuffer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type LogsBuffer struct {
|
||||
bufferSize int
|
||||
preTimeStamp int64
|
||||
timeStampIndex int64
|
||||
logsStore []LogItem
|
||||
mu sync.RWMutex
|
||||
fireCallback func(entry *logrus.Entry) error
|
||||
}
|
||||
|
||||
type LogItem struct {
|
||||
Timestamp int64
|
||||
Content string
|
||||
Data map[string]any
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) AddLog(t time.Time, msg string, data map[string]any) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
if l.preTimeStamp == t.UnixNano() {
|
||||
l.timeStampIndex++
|
||||
} else {
|
||||
l.timeStampIndex = 0
|
||||
}
|
||||
|
||||
li := LogItem{Timestamp: t.UnixNano() + l.timeStampIndex, Content: strings.TrimSpace(msg), Data: data}
|
||||
l.logsStore = append(l.logsStore, li)
|
||||
l.preTimeStamp = t.UnixNano()
|
||||
if len(l.logsStore) > l.bufferSize+16 {
|
||||
l.logsStore = l.logsStore[len(l.logsStore)-l.bufferSize:]
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) Fire(entry *logrus.Entry) error {
|
||||
|
||||
entryStr, err := entry.String()
|
||||
if err != nil {
|
||||
return fmt.Errorf("entry.String() err:%s", err.Error())
|
||||
}
|
||||
|
||||
l.AddLog(entry.Time, entryStr, entry.Data)
|
||||
if l.fireCallback != nil {
|
||||
return l.fireCallback(entry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) SetFireCallback(f func(entry *logrus.Entry) error) {
|
||||
l.fireCallback = f
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) Levels() []logrus.Level {
|
||||
return logrus.AllLevels
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) Write(p []byte) (n int, err error) {
|
||||
|
||||
l.AddLog(time.Now(), string(p), nil)
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) GetLogs(logItemConvertFunc func(*LogItem) any, fTimestamp int64) []any {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
var logs []any
|
||||
for i := range l.logsStore {
|
||||
if l.logsStore[i].Timestamp <= fTimestamp {
|
||||
continue
|
||||
}
|
||||
lg := l.getLogItem(logItemConvertFunc, l.logsStore[i])
|
||||
|
||||
logs = append(logs, lg)
|
||||
}
|
||||
return logs
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) GetLastLogs(logItemConvertFunc func(*LogItem) any, maxCount int) []any {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
logCount := len(l.logsStore)
|
||||
var resRaw []LogItem
|
||||
if maxCount >= logCount {
|
||||
resRaw = l.logsStore[0:]
|
||||
} else {
|
||||
resRaw = l.logsStore[logCount-maxCount:]
|
||||
}
|
||||
var logs []any
|
||||
for i := range resRaw {
|
||||
lg := l.getLogItem(logItemConvertFunc, resRaw[i])
|
||||
logs = append(logs, lg)
|
||||
}
|
||||
return logs
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) GetLogsByLimit(logItemConvertFunc func(*LogItem) any, pageSize, page int) (int, []any) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
logCount := len(l.logsStore)
|
||||
var resRaw []LogItem
|
||||
|
||||
firstIndex := (page - 1) * pageSize
|
||||
endIndex := firstIndex + pageSize
|
||||
|
||||
if firstIndex < logCount {
|
||||
if endIndex >= logCount {
|
||||
resRaw = l.logsStore[firstIndex:]
|
||||
} else {
|
||||
resRaw = l.logsStore[firstIndex:endIndex]
|
||||
}
|
||||
}
|
||||
|
||||
var logs []any
|
||||
for i := range resRaw {
|
||||
lg := l.getLogItem(logItemConvertFunc, resRaw[i])
|
||||
logs = append(logs, lg)
|
||||
}
|
||||
return logCount, logs
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) getLogItem(logItemConvertFunc func(*LogItem) any, li LogItem) any {
|
||||
var lg any
|
||||
if logItemConvertFunc == nil {
|
||||
lg = li
|
||||
} else {
|
||||
lg = logItemConvertFunc(&li)
|
||||
}
|
||||
return lg
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) ClearLog() {
|
||||
l.logsStore = l.logsStore[:0]
|
||||
}
|
||||
|
||||
func Create(size int) *LogsBuffer {
|
||||
lb := &LogsBuffer{bufferSize: size}
|
||||
return lb
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) SetBufferSize(size int) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.bufferSize = size
|
||||
if len(l.logsStore) > l.bufferSize {
|
||||
l.logsStore = l.logsStore[len(l.logsStore)-l.bufferSize:]
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) GetBufferSize() int {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
return l.bufferSize
|
||||
}
|
||||
|
||||
func (l *LogsBuffer) GetLogCount() int {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
return len(l.logsStore)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package ddnscore
|
||||
package netinterfaces
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
@ -119,7 +119,7 @@ func GetIPFromNetInterface(ipType, netinterface, ipreg string) string {
|
|||
for i := range addressList {
|
||||
matched, err := regexp.MatchString(ipreg, addressList[i])
|
||||
if matched && err == nil {
|
||||
log.Printf("正则匹配上")
|
||||
//log.Printf("正则匹配上")
|
||||
return addressList[i]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package slice
|
||||
|
||||
func DeleteAnyListlice(a []any, deleteIndex int) []any {
|
||||
j := 0
|
||||
for i := range a {
|
||||
if i != deleteIndex {
|
||||
a[j] = a[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return a[:j]
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package stringsp
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
KB = uint(1024)
|
||||
MB = uint64(1024 * 1024)
|
||||
GB = uint64(1024 * 1024 * 1024)
|
||||
TB = uint64(1024 * 1024 * 1024 * 1024)
|
||||
EB = uint64(1024 * 1024 * 1024 * 1024 * 1024)
|
||||
)
|
||||
|
||||
func BinaryUnitToStr(binaryUnit uint64) (size string) {
|
||||
switch {
|
||||
case binaryUnit < 1024:
|
||||
return fmt.Sprintf("%.2fB", float64(binaryUnit)/float64(1))
|
||||
case binaryUnit < MB: //1024*1024
|
||||
return fmt.Sprintf("%.2fKB", float64(binaryUnit)/float64(KB))
|
||||
case binaryUnit < GB: //1024 * 1024 * 1024
|
||||
return fmt.Sprintf("%.2fMB", float64(binaryUnit)/float64(MB))
|
||||
case binaryUnit < TB: //1024 * 1024 * 1024 * 1024
|
||||
return fmt.Sprintf("%.2fGB", float64(binaryUnit)/float64(GB))
|
||||
case binaryUnit < EB: //1024 * 1024 * 1024 * 1024 * 1024
|
||||
return fmt.Sprintf("%.2fTB", float64(binaryUnit)/float64(TB))
|
||||
default:
|
||||
return fmt.Sprintf("%.2fEB", float64(binaryUnit)/float64(EB))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package stringsp
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetHostAndPathFromURL(urlstr string) (string, string, string, string, error) {
|
||||
if !strings.HasPrefix(urlstr, "http") {
|
||||
urlstr = "http://" + urlstr
|
||||
}
|
||||
u, err := url.Parse(urlstr)
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
return u.Scheme, u.Hostname(), u.Port(), u.Path, nil
|
||||
}
|
4
web.go
4
web.go
|
@ -9,8 +9,8 @@ import (
|
|||
"log"
|
||||
)
|
||||
|
||||
func RunAdminWeb(listenPort int) {
|
||||
func RunAdminWeb(listenPort, logMaxSize int) {
|
||||
listen := fmt.Sprintf(":%d", listenPort)
|
||||
go web.RunAdminWeb(listen)
|
||||
go web.RunAdminWeb(listen, logMaxSize)
|
||||
log.Printf("AdminWeb listen on %s", listen)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ declare module '@vue/runtime-core' {
|
|||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||
|
@ -29,6 +31,7 @@ declare module '@vue/runtime-core' {
|
|||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
|
@ -45,6 +48,7 @@ declare module '@vue/runtime-core' {
|
|||
Pmenu: typeof import('./src/components/Pmenu.vue')['default']
|
||||
PSet: typeof import('./src/components/PSet.vue')['default']
|
||||
RelaySet: typeof import('./src/components/RelaySet.vue')['default']
|
||||
ReverseProxy: typeof import('./src/components/ReverseProxy.vue')['default']
|
||||
Status: typeof import('./src/components/Status.vue')['default']
|
||||
WhiteLists: typeof import('./src/components/WhiteLists.vue')['default']
|
||||
WhiteListSet: typeof import('./src/components/WhiteListSet.vue')['default']
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -7,9 +7,9 @@
|
|||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0" />
|
||||
|
||||
|
||||
<title>Lucky(大吉)</title>
|
||||
<script type="module" crossorigin src="/assets/index.baa1812e.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.ddd95d4c.css">
|
||||
<title>Lucky</title>
|
||||
<script type="module" crossorigin src="/assets/index.2c06576c.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.6859b28a.css">
|
||||
</head>
|
||||
<body style="margin:0">
|
||||
<div id="app"></div>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0" />
|
||||
|
||||
|
||||
<title>Lucky(大吉)</title>
|
||||
<title>Lucky</title>
|
||||
</head>
|
||||
<body style="margin:0">
|
||||
<div id="app"></div>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,6 +25,7 @@
|
|||
<BlackLists v-if="global.currentPage.value=='#blacklists'?true:false"></BlackLists>
|
||||
<DDNS v-if="global.currentPage.value=='#ddnstasklist'?true:false"></DDNS>
|
||||
<DDNSSet v-if="global.currentPage.value=='#ddnsset'?true:false"></DDNSSet>
|
||||
<ReverseProxy v-if="global.currentPage.value=='#reverseproxylist'?true:false"></ReverseProxy>
|
||||
<About v-if="global.currentPage.value=='#about'?true:false"></About>
|
||||
</el-main>
|
||||
|
||||
|
@ -49,6 +50,7 @@ import WhiteListSet from './components/WhiteListSet.vue';
|
|||
import WhiteLists from './components/WhiteLists.vue';
|
||||
import BlackLists from './components/BlackLists.vue';
|
||||
import DDNS from './components/DDNS.vue';
|
||||
import ReverseProxy from './components/reverseproxy.vue';
|
||||
|
||||
import {apiGetVersion} from "./apis/utils.js"
|
||||
import DDNSSet from './components/DDNSSet.vue';
|
||||
|
|
|
@ -337,4 +337,70 @@ export function apiGetConfigure() {
|
|||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function apiAddReverseProxyRule(data) {
|
||||
return httpRequest({
|
||||
url: '/api/reverseproxyrule',
|
||||
method: 'post',
|
||||
headers:{'Authorization':GetToken()},
|
||||
data:data
|
||||
})
|
||||
}
|
||||
|
||||
export function apiGeReverseProxyRuleList() {
|
||||
return httpRequest({
|
||||
url: '/api/reverseproxyrules',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiAlterReverseProxyRule(data) {
|
||||
return httpRequest({
|
||||
url: '/api/reverseproxyrule',
|
||||
method: 'put',
|
||||
headers:{'Authorization':GetToken()},
|
||||
data:data,
|
||||
params:{_:new Date().valueOf()}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiDeleteReverseProxyRule(ruleKey) {
|
||||
return httpRequest({
|
||||
url: '/api/reverseproxyrule',
|
||||
method: 'delete',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{_:new Date().valueOf(),key:ruleKey}
|
||||
})
|
||||
}
|
||||
|
||||
export function apiReverseProxyRuleEnable(ruleKey,proxyKey,enable) {
|
||||
return httpRequest({
|
||||
url: '/api/reverseproxyrule/enable',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{
|
||||
_:new Date().valueOf(),
|
||||
enable:enable,
|
||||
ruleKey:ruleKey,
|
||||
proxyKey:proxyKey}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function apiReverseProxyRuleLogs(ruleKey,proxyKey,pageSize,page) {
|
||||
return httpRequest({
|
||||
url: '/api/reverseproxyrule/logs',
|
||||
method: 'get',
|
||||
headers:{'Authorization':GetToken()},
|
||||
params:{
|
||||
_:new Date().valueOf(),
|
||||
ruleKey:ruleKey,
|
||||
proxyKey:proxyKey,
|
||||
pageSize:pageSize,
|
||||
page:page}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,18 +4,13 @@
|
|||
borderRadius: 'base',
|
||||
}">
|
||||
|
||||
<!-- <el-affix position="bottom" :offset="0" class="affix-container">
|
||||
<el-button type="primary" @click="showAddOrAlterWhiteListDialog('add')">DDNS任务添加 <el-icon>
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-affix> -->
|
||||
|
||||
|
||||
<el-scrollbar height="100%">
|
||||
|
||||
<div class="itemradius" :style="{
|
||||
borderRadius: 'base',
|
||||
}" v-for="task,taskIndex in taskList">
|
||||
}" v-for="task, taskIndex in taskList">
|
||||
|
||||
<el-descriptions :column="4" border>
|
||||
|
||||
|
@ -39,7 +34,7 @@
|
|||
</el-tooltip>
|
||||
|
||||
|
||||
<el-button type="primary" @click="showAddOrAlterWhiteListDialog('alter', task)">编辑</el-button>
|
||||
<el-button type="primary" @click="showAddOrAlterDDNSTaskDialog('alter', task)">编辑</el-button>
|
||||
<el-button type="danger" @click="deleteTask(task)">删除</el-button>
|
||||
|
||||
</el-descriptions-item>
|
||||
|
@ -69,7 +64,7 @@
|
|||
|
||||
<el-descriptions-item label="公网IP">
|
||||
|
||||
<el-tooltip :placement="taskIndex==0?'bottom':'top'" effect="dark" :trigger-keys="[]"
|
||||
<el-tooltip :placement="taskIndex == 0 ? 'bottom' : 'top'" effect="dark" :trigger-keys="[]"
|
||||
content="">
|
||||
<template #content>
|
||||
<span v-html="getIPHistroyListHtml(task.TaskState.IPAddrHistory)"></span>
|
||||
|
@ -108,7 +103,7 @@
|
|||
|
||||
<el-descriptions-item label="WebHook 触发时间" :span="task.TaskState.WebhookCallTime == '' ? 3 : 1">
|
||||
|
||||
<el-tooltip :placement="taskIndex==0?'bottom':'top'" effect="dark" :trigger-keys="[]"
|
||||
<el-tooltip :placement="taskIndex == 0 ? 'bottom' : 'top'" effect="dark" :trigger-keys="[]"
|
||||
content="">
|
||||
<template #content>
|
||||
<span
|
||||
|
@ -116,7 +111,7 @@
|
|||
</template>
|
||||
<el-button color="#409eff" size="default">
|
||||
{{ task.TaskState.WebhookCallTime == "" ? '从未触发' :
|
||||
task.TaskState.WebhookCallTime
|
||||
task.TaskState.WebhookCallTime
|
||||
}}
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
@ -126,7 +121,7 @@
|
|||
<el-descriptions-item label="WebHook 触发结果"
|
||||
v-if="task.TaskState.WebhookCallTime == '' ? false : true"
|
||||
:span="task.TaskState.WebhookCallErrorMsg == '' ? 2 : 1">
|
||||
<el-tooltip :placement="taskIndex==0?'bottom':'top'" effect="dark" :trigger-keys="[]"
|
||||
<el-tooltip :placement="taskIndex == 0 ? 'bottom' : 'top'" effect="dark" :trigger-keys="[]"
|
||||
content="">
|
||||
<template #content>
|
||||
<span
|
||||
|
@ -177,28 +172,28 @@
|
|||
<el-descriptions-item label="域名">
|
||||
<el-button color="#409eff" size="default"
|
||||
@click="copyDomain(domain.SubDomain, domain.DomainName)">
|
||||
{{ domain.SubDomain ==''?domain.DomainName:domain.SubDomain + "." + domain.DomainName }}
|
||||
{{ domain.SubDomain == '' ? domain.DomainName : domain.SubDomain + "." + domain.DomainName }}
|
||||
</el-button>
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="同步结果">
|
||||
|
||||
<el-tooltip :placement="taskIndex==0?'bottom':'top'" effect="dark" :trigger-keys="[]"
|
||||
<el-tooltip :placement="taskIndex == 0 ? 'bottom' : 'top'" effect="dark" :trigger-keys="[]"
|
||||
content="">
|
||||
<template #content>
|
||||
<span v-html="GetSyncUpdateHistroyListHtml(domain.UpdateHistroy)"></span>
|
||||
|
||||
</template>
|
||||
|
||||
<el-button :type="domain.UpdateStatus == '失败' ? 'danger' : task.Enable?'success':'info'"
|
||||
<el-button :type="domain.UpdateStatus == '失败' ? 'danger' : task.Enable ? 'success' : 'info'"
|
||||
size="small">
|
||||
{{ task.Enable?domain.UpdateStatus:'停止同步' }}
|
||||
{{ task.Enable ? domain.UpdateStatus : '停止同步' }}
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="最后检测时间" :span="domain.Message == '' ? 2 : 1">
|
||||
<el-tooltip :placement="taskIndex==0?'bottom':'top'" effect="dark" :trigger-keys="[]"
|
||||
<el-tooltip :placement="taskIndex == 0 ? 'bottom' : 'top'" effect="dark" :trigger-keys="[]"
|
||||
content="">
|
||||
<template #content>
|
||||
<span v-html="GetSyncUpdateHistroyListHtml(domain.UpdateHistroy)"></span>
|
||||
|
@ -226,7 +221,7 @@
|
|||
</el-scrollbar>
|
||||
|
||||
<el-affix position="bottom" :offset="30" class="affix-container">
|
||||
<el-button type="primary" @click="showAddOrAlterWhiteListDialog('add', null)">添加DDNS任务 <el-icon>
|
||||
<el-button type="primary" @click="showAddOrAlterDDNSTaskDialog('add', null)">添加DDNS任务 <el-icon>
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
|
@ -259,6 +254,7 @@
|
|||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
<div v-show="DDNSForm.Enable">
|
||||
|
||||
|
||||
|
@ -266,7 +262,6 @@
|
|||
<div class="fromitemDivRadius">
|
||||
<p>DNS服务商设置</p>
|
||||
|
||||
|
||||
<div class="fromitemChildDivRadius">
|
||||
|
||||
<el-form-item label="DNS服务商" label-width="auto">
|
||||
|
@ -404,7 +399,7 @@
|
|||
|
||||
<div v-show="DDNSForm.DNS.Callback.Server == 'other' ? false : true">
|
||||
|
||||
<div v-if="DDNSForm.DNS.Callback.Server=='meibu'" style="font-size: small;">
|
||||
<div v-if="DDNSForm.DNS.Callback.Server == 'meibu'" style="font-size: small;">
|
||||
<el-tooltip content="注意:每步 IPv4和IPv6的接口不相同,免费二级域名不能同时支持IPv4和IPv6" placement="top">
|
||||
<el-form-item label-width="auto">
|
||||
<el-link type="primary" style="font-size: small;"
|
||||
|
@ -416,7 +411,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div v-if="DDNSForm.DNS.Callback.Server=='noip'" style="font-size: small;">
|
||||
<div v-if="DDNSForm.DNS.Callback.Server == 'noip'" style="font-size: small;">
|
||||
<el-form-item label-width="auto">
|
||||
<el-link type="primary" style="font-size: small;" href="https://www.noip.com"
|
||||
target="_blank">
|
||||
|
@ -425,7 +420,7 @@
|
|||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div v-if="DDNSForm.DNS.Callback.Server=='dynv6'" style="font-size: small;">
|
||||
<div v-if="DDNSForm.DNS.Callback.Server == 'dynv6'" style="font-size: small;">
|
||||
<el-form-item label-width="auto">
|
||||
<el-link type="primary" style="font-size: small;" href="https://dynv6.com/"
|
||||
target="_blank">
|
||||
|
@ -439,7 +434,7 @@
|
|||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div v-if="DDNSForm.DNS.Callback.Server=='dynu'" style="font-size: small;">
|
||||
<div v-if="DDNSForm.DNS.Callback.Server == 'dynu'" style="font-size: small;">
|
||||
<el-form-item label-width="auto">
|
||||
<el-link type="primary" style="font-size: small;" href="https://www.dynu.com/"
|
||||
target="_blank">
|
||||
|
@ -924,22 +919,22 @@
|
|||
</el-tooltip>
|
||||
|
||||
|
||||
<div v-show="!DDNSForm.WebhookDisableCallbackSuccessContentCheck">
|
||||
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
|
||||
<template #content>用于判断记录Webhook 接口是否成功调用<br />
|
||||
多种表示成功的不同字符串请分多行写<br />
|
||||
支持的变量 <br />
|
||||
#{ipAddr} : 当前公网IP<br />
|
||||
#{successDomains} : 更新/添加成功的域名列表,域名之间用,号分隔<br />
|
||||
#{successDomainsLine} : 更新/添加成功的域名列表,域名之间用'\n'分隔<br />
|
||||
#{failedDomains} : 更新/添加失败的域名列表,域名之间用,号分隔<br />
|
||||
#{failedDomainsLine} : 更新/添加失败的域名列表,域名之间用'\n'分隔</template>
|
||||
<el-form-item label="接口调用成功包含的字符串" label-width="auto">
|
||||
<el-input v-model="DDNSFormWebhookSuccessContentArea"
|
||||
:autosize="{ minRows: 3, maxRows: 5 }" type="textarea" autocomplete="off"
|
||||
placeholder="" />
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
<div v-show="!DDNSForm.WebhookDisableCallbackSuccessContentCheck">
|
||||
<el-tooltip class="box-item" effect="dark" :trigger-keys="[]" content="">
|
||||
<template #content>用于判断记录Webhook 接口是否成功调用<br />
|
||||
多种表示成功的不同字符串请分多行写<br />
|
||||
支持的变量 <br />
|
||||
#{ipAddr} : 当前公网IP<br />
|
||||
#{successDomains} : 更新/添加成功的域名列表,域名之间用,号分隔<br />
|
||||
#{successDomainsLine} : 更新/添加成功的域名列表,域名之间用'\n'分隔<br />
|
||||
#{failedDomains} : 更新/添加失败的域名列表,域名之间用,号分隔<br />
|
||||
#{failedDomainsLine} : 更新/添加失败的域名列表,域名之间用'\n'分隔</template>
|
||||
<el-form-item label="接口调用成功包含的字符串" label-width="auto">
|
||||
<el-input v-model="DDNSFormWebhookSuccessContentArea"
|
||||
:autosize="{ minRows: 3, maxRows: 5 }" type="textarea"
|
||||
autocomplete="off" placeholder="" />
|
||||
</el-form-item>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -996,8 +991,8 @@
|
|||
<span class="dialog-footer">
|
||||
<el-button @click="addDDNSDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="exeAddOrAlterDDNSOption">{{ DDNSFormOptionType == "add" ? '添加' :
|
||||
'修改'
|
||||
}}
|
||||
'修改'
|
||||
}}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
|
@ -1042,12 +1037,12 @@ var taskList = ref([{
|
|||
NetInterface: "",
|
||||
IPReg: "",
|
||||
Domains: [""],
|
||||
HttpClientTimeout:20,
|
||||
HttpClientTimeout: 20,
|
||||
DNS: {
|
||||
Name: "alidns",
|
||||
ID: "",
|
||||
Secret: "",
|
||||
ForceInterval:600,
|
||||
ForceInterval: 600,
|
||||
ResolverDoaminCheck: false,
|
||||
DNSServerList: [''],
|
||||
HttpClientProxyType: "",
|
||||
|
@ -1060,7 +1055,7 @@ var taskList = ref([{
|
|||
Headers: [""],
|
||||
RequestBody: "",
|
||||
Server: "",
|
||||
DisableCallbackSuccessContentCheck:false,
|
||||
DisableCallbackSuccessContentCheck: false,
|
||||
CallbackSuccessContent: [""]
|
||||
},
|
||||
|
||||
|
@ -1070,7 +1065,7 @@ var taskList = ref([{
|
|||
WebhookMethod: "",
|
||||
WebhookHeaders: [""],
|
||||
WebhookRequestBody: "",
|
||||
WebhookDisableCallbackSuccessContentCheck:false,
|
||||
WebhookDisableCallbackSuccessContentCheck: false,
|
||||
WebhookSuccessContent: [""],
|
||||
WebhookProxy: "",
|
||||
WebhookProxyAddr: "",
|
||||
|
@ -1082,23 +1077,24 @@ var taskList = ref([{
|
|||
WebhookCallTime: "",
|
||||
WebhookCallResult: false,
|
||||
WebhookCallErrorMsg: "",
|
||||
IPAddrHistory:[{IPaddr:"",RecordTime:""}],
|
||||
WebhookCallHistroy:[{CallTime:"",CallResult:""}],
|
||||
IPAddrHistory: [{ IPaddr: "", RecordTime: "" }],
|
||||
WebhookCallHistroy: [{ CallTime: "", CallResult: "" }],
|
||||
IpAddr: "",
|
||||
Domains: [{
|
||||
Domains: [{
|
||||
DomainName: "",
|
||||
SubDomain: "",
|
||||
UpdateStatus: "",
|
||||
LastUpdateStatusTime: "",
|
||||
Message: "",
|
||||
UpdateHistroy:[{UpdateStatus:"",UpdateTime:""}] }]
|
||||
SubDomain: "",
|
||||
UpdateStatus: "",
|
||||
LastUpdateStatusTime: "",
|
||||
Message: "",
|
||||
UpdateHistroy: [{ UpdateStatus: "", UpdateTime: "" }]
|
||||
}]
|
||||
},
|
||||
}]);
|
||||
taskList.value.splice(0, 1)
|
||||
|
||||
|
||||
const rhtml = ref("")
|
||||
const webhookSelect= ref("")
|
||||
const webhookSelect = ref("")
|
||||
const WebhookServerListArea = ref("")
|
||||
|
||||
rhtml.value = ` <el-link type="info">info</el-link>`
|
||||
|
@ -1266,8 +1262,8 @@ const WebhookServerList = [
|
|||
label: '企业微信',
|
||||
},
|
||||
{
|
||||
value:'custom',
|
||||
label:'自定义',
|
||||
value: 'custom',
|
||||
label: '自定义',
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -1320,91 +1316,91 @@ const CallbackMethodList = [
|
|||
]
|
||||
|
||||
|
||||
const getIPHistroyListHtml = (ipHistroy)=>{
|
||||
const getIPHistroyListHtml = (ipHistroy) => {
|
||||
let res = ""
|
||||
|
||||
for (let i in ipHistroy){
|
||||
let ipText = ipHistroy[i].IPaddr;
|
||||
if (ipText==""){
|
||||
for (let i in ipHistroy) {
|
||||
let ipText = ipHistroy[i].IPaddr;
|
||||
if (ipText == "") {
|
||||
ipText = "获取IP失败"
|
||||
}
|
||||
|
||||
res += ipHistroy[i].RecordTime +" "+ ipText + '<br />'
|
||||
res += ipHistroy[i].RecordTime + " " + ipText + '<br />'
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
const GetSyncUpdateHistroyListHtml = (updateHistroy)=>{
|
||||
let res = ""
|
||||
const GetSyncUpdateHistroyListHtml = (updateHistroy) => {
|
||||
let res = ""
|
||||
|
||||
for (let i in updateHistroy){
|
||||
let state = updateHistroy[i].UpdateStatus;
|
||||
for (let i in updateHistroy) {
|
||||
let state = updateHistroy[i].UpdateStatus;
|
||||
|
||||
res += updateHistroy[i].UpdateTime +" "+ state + '<br />'
|
||||
res += updateHistroy[i].UpdateTime + " " + state + '<br />'
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
const getWebhookCallHistroyListHtml = (histroy)=>{
|
||||
const getWebhookCallHistroyListHtml = (histroy) => {
|
||||
let res = "仅记录程序本次启动以来的Webhook调用记录<br />"
|
||||
|
||||
for (let i in histroy){
|
||||
let result = histroy[i].CallResult;
|
||||
res += histroy[i].CallTime +" "+ result + '<br />'
|
||||
for (let i in histroy) {
|
||||
let result = histroy[i].CallResult;
|
||||
res += histroy[i].CallTime + " " + result + '<br />'
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
const WebhookServerSelectChange = (server : string)=>{
|
||||
switch (server){
|
||||
const WebhookServerSelectChange = (server: string) => {
|
||||
switch (server) {
|
||||
case "dingding":
|
||||
let dingding_msg = {
|
||||
msgtype:"markdown",
|
||||
markdown:{
|
||||
title:"DDNS域名同步反馈",
|
||||
text:'#### DDNS域名同步反馈 \n - IP地址:#{ipAddr} \n - 域名更新成功列表:#{successDomainsLine}\n - 域名更新失败列表:#{failedDomainsLine}\n - Webhook触发时间: \n #{time}'
|
||||
msgtype: "markdown",
|
||||
markdown: {
|
||||
title: "DDNS域名同步反馈",
|
||||
text: '#### DDNS域名同步反馈 \n - IP地址:#{ipAddr} \n - 域名更新成功列表:#{successDomainsLine}\n - 域名更新失败列表:#{failedDomainsLine}\n - Webhook触发时间: \n #{time}'
|
||||
},
|
||||
}
|
||||
WebhookServerListArea.value = JSON.stringify(dingding_msg,null,2);
|
||||
}
|
||||
WebhookServerListArea.value = JSON.stringify(dingding_msg, null, 2);
|
||||
break;
|
||||
case 'feishu':
|
||||
let feishu_msg = {
|
||||
msg_type:"post",
|
||||
content:{
|
||||
post:{
|
||||
zh_cn:{
|
||||
title:"DDNS域名同步反馈",
|
||||
content:[
|
||||
[{tag:"text",text:"IP地址:#{ipAddr}"}],
|
||||
[{tag:"text",text:"域名更新成功列表:#{successDomainsLine}"}],
|
||||
[{tag:"text",text:"域名更新失败列表:#{failedDomainsLine}"}],
|
||||
[{tag:"text",text:"Webhook触发时间: \n#{time}"}],
|
||||
]
|
||||
msg_type: "post",
|
||||
content: {
|
||||
post: {
|
||||
zh_cn: {
|
||||
title: "DDNS域名同步反馈",
|
||||
content: [
|
||||
[{ tag: "text", text: "IP地址:#{ipAddr}" }],
|
||||
[{ tag: "text", text: "域名更新成功列表:#{su.ccessDomainsLine}" }],
|
||||
[{ tag: "text", text: "域名更新失败列表:#{failedDomainsLine}" }],
|
||||
[{ tag: "text", text: "Webhook触发时间: \n#{time}" }],
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WebhookServerListArea.value= JSON.stringify(feishu_msg,null,2)
|
||||
WebhookServerListArea.value = JSON.stringify(feishu_msg, null, 2)
|
||||
break
|
||||
case 'weixinpro':
|
||||
let weixin_msg = {
|
||||
msgtype:"markdown",
|
||||
markdown:{
|
||||
content:'#### DDNS域名同步反馈 \n##### IP地址:\n#{ipAddr} \n##### 域名更新成功列表:\n#{successDomainsLine}\n##### 域名更新失败列表:\n#{failedDomainsLine}\n##### Webhook触发时间: \n#{time}'
|
||||
msgtype: "markdown",
|
||||
markdown: {
|
||||
content: '#### DDNS域名同步反馈 \n##### IP地址:\n#{ipAddr} \n##### 域名更新成功列表:\n#{successDomainsLine}\n##### 域名更新失败列表:\n#{failedDomainsLine}\n##### Webhook触发时间: \n#{time}'
|
||||
}
|
||||
}
|
||||
WebhookServerListArea.value= JSON.stringify(weixin_msg,null,2)
|
||||
WebhookServerListArea.value = JSON.stringify(weixin_msg, null, 2)
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
const copyDomain = ( SubDomain: string,domain: string) => {
|
||||
const copyDomain = (SubDomain: string, domain: string) => {
|
||||
|
||||
let content = SubDomain ==''?domain:SubDomain + "." + domain;
|
||||
let content = SubDomain == '' ? domain : SubDomain + "." + domain;
|
||||
|
||||
CopyTotoClipboard(content)
|
||||
MessageShow('success', '域名 ' + content + ' 已复制到剪切板')
|
||||
|
@ -1581,12 +1577,12 @@ const DDNSForm = ref(
|
|||
NetInterface: "",
|
||||
IPReg: "",
|
||||
Domains: [""],
|
||||
HttpClientTimeout:60,
|
||||
HttpClientTimeout: 60,
|
||||
DNS: {
|
||||
Name: "alidns",
|
||||
ID: "",
|
||||
Secret: "",
|
||||
ForceInterval:3600,
|
||||
ForceInterval: 3600,
|
||||
ResolverDoaminCheck: false,
|
||||
DNSServerList: [""],
|
||||
HttpClientProxyType: "",
|
||||
|
@ -1599,17 +1595,17 @@ const DDNSForm = ref(
|
|||
Headers: [""],
|
||||
RequestBody: "",
|
||||
Server: "",
|
||||
DisableCallbackSuccessContentCheck:false,
|
||||
DisableCallbackSuccessContentCheck: false,
|
||||
CallbackSuccessContent: [""],
|
||||
},
|
||||
},
|
||||
WebhookEnable: false,
|
||||
WebhookCallOnGetIPfail:false,
|
||||
WebhookCallOnGetIPfail: false,
|
||||
WebhookURL: "",
|
||||
WebhookMethod: "",
|
||||
WebhookHeaders: [""],
|
||||
WebhookRequestBody: "",
|
||||
WebhookDisableCallbackSuccessContentCheck:false,
|
||||
WebhookDisableCallbackSuccessContentCheck: false,
|
||||
WebhookSuccessContent: [""],
|
||||
WebhookProxy: "",
|
||||
WebhookProxyAddr: "",
|
||||
|
@ -1629,12 +1625,12 @@ const preDDNSFrom = ref(
|
|||
NetInterface: "",
|
||||
IPReg: "",
|
||||
Domains: [""],
|
||||
HttpClientTimeout:20,
|
||||
HttpClientTimeout: 20,
|
||||
DNS: {
|
||||
Name: "alidns",
|
||||
ID: "",
|
||||
Secret: "",
|
||||
ForceInterval:3600,
|
||||
ForceInterval: 3600,
|
||||
ResolverDoaminCheck: false,
|
||||
DNSServerList: [''],
|
||||
HttpClientProxyType: "",
|
||||
|
@ -1647,17 +1643,17 @@ const preDDNSFrom = ref(
|
|||
Headers: [""],
|
||||
RequestBody: "",
|
||||
Server: "",
|
||||
DisableCallbackSuccessContentCheck:false,
|
||||
DisableCallbackSuccessContentCheck: false,
|
||||
CallbackSuccessContent: [""],
|
||||
},
|
||||
},
|
||||
WebhookEnable: false,
|
||||
WebhookCallOnGetIPfail:false,
|
||||
WebhookCallOnGetIPfail: false,
|
||||
WebhookURL: "",
|
||||
WebhookMethod: "",
|
||||
WebhookHeaders: [""],
|
||||
WebhookRequestBody: "",
|
||||
WebhookDisableCallbackSuccessContentCheck:false,
|
||||
WebhookDisableCallbackSuccessContentCheck: false,
|
||||
WebhookSuccessContent: [""],
|
||||
WebhookProxy: "",
|
||||
WebhookProxyAddr: "",
|
||||
|
@ -1678,10 +1674,10 @@ const DDNSFormOptionType = ref("")
|
|||
const checkIPv4URLList = ["https://4.ipw.cn", "http://v4.ip.zxinc.org/getip", "https://myip4.ipip.net", "https://www.taobao.com/help/getip.php", "https://ddns.oray.com/checkip", "https://ip.3322.net", "https://v4.myip.la"]
|
||||
const checkIPv6URLList = ["https://6.ipw.cn", "https://ipv6.ddnspod.com", "http://v6.ip.zxinc.org/getip", "https://speed.neu6.edu.cn/getIP.php", "https://v6.ident.me", "https://v6.myip.la"]
|
||||
|
||||
const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
|
||||
const showAddOrAlterDDNSTaskDialog = (optionType: string, task: any) => {
|
||||
//console.log("optionType fuck:" + optionType)
|
||||
|
||||
webhookSelect.value=""
|
||||
webhookSelect.value = ""
|
||||
|
||||
queryNetinterfaces()
|
||||
DDNSFormOptionType.value = optionType
|
||||
|
@ -1697,35 +1693,35 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
|
|||
DDNSForm.value.IPReg = ""
|
||||
DDNSForm.value.Domains = [""]
|
||||
DDNSForm.value.HttpClientTimeout = 20,
|
||||
DDNSForm.value.DNS = {
|
||||
Name: "alidns",
|
||||
ID: "",
|
||||
Secret: "",
|
||||
ForceInterval:3600,
|
||||
ResolverDoaminCheck: true,
|
||||
DNSServerList: [],
|
||||
HttpClientProxyType: "",
|
||||
HttpClientProxyAddr: "",
|
||||
HttpClientProxyUser: "",
|
||||
HttpClientProxyPassword: "",
|
||||
Callback: {
|
||||
URL: "",
|
||||
Method: "get",
|
||||
Headers: [""],
|
||||
RequestBody: "",
|
||||
Server: "other",
|
||||
DisableCallbackSuccessContentCheck:false,
|
||||
CallbackSuccessContent: [],
|
||||
DDNSForm.value.DNS = {
|
||||
Name: "alidns",
|
||||
ID: "",
|
||||
Secret: "",
|
||||
ForceInterval: 3600,
|
||||
ResolverDoaminCheck: true,
|
||||
DNSServerList: [],
|
||||
HttpClientProxyType: "",
|
||||
HttpClientProxyAddr: "",
|
||||
HttpClientProxyUser: "",
|
||||
HttpClientProxyPassword: "",
|
||||
Callback: {
|
||||
URL: "",
|
||||
Method: "get",
|
||||
Headers: [""],
|
||||
RequestBody: "",
|
||||
Server: "other",
|
||||
DisableCallbackSuccessContentCheck: false,
|
||||
CallbackSuccessContent: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
DDNSForm.value.WebhookEnable = false
|
||||
DDNSForm.value.WebhookCallOnGetIPfail = false
|
||||
DDNSForm.value.WebhookURL = ""
|
||||
DDNSForm.value.WebhookMethod = "get"
|
||||
DDNSForm.value.WebhookHeaders = []
|
||||
DDNSForm.value.WebhookRequestBody = ""
|
||||
DDNSForm.value.WebhookDisableCallbackSuccessContentCheck=false,
|
||||
DDNSForm.value.WebhookSuccessContent = []
|
||||
DDNSForm.value.WebhookDisableCallbackSuccessContentCheck = false,
|
||||
DDNSForm.value.WebhookSuccessContent = []
|
||||
DDNSForm.value.WebhookProxy = ""
|
||||
DDNSForm.value.WebhookProxyAddr = ""
|
||||
DDNSForm.value.WebhookProxyUser = ""
|
||||
|
@ -1751,35 +1747,35 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
|
|||
preDDNSFrom.value.IPReg = ""
|
||||
preDDNSFrom.value.Domains = [""]
|
||||
preDDNSFrom.value.HttpClientTimeout = 20,
|
||||
preDDNSFrom.value.DNS = {
|
||||
Name: "alidns",
|
||||
ID: "",
|
||||
Secret: "",
|
||||
ForceInterval:3600,
|
||||
ResolverDoaminCheck: true,
|
||||
DNSServerList: [],
|
||||
HttpClientProxyType: "",
|
||||
HttpClientProxyAddr: "",
|
||||
HttpClientProxyUser: "",
|
||||
HttpClientProxyPassword: "",
|
||||
Callback: {
|
||||
URL: "",
|
||||
Method: "get",
|
||||
Headers: [""],
|
||||
RequestBody: "",
|
||||
DisableCallbackSuccessContentCheck:false,
|
||||
CallbackSuccessContent: [],
|
||||
Server: "other",
|
||||
preDDNSFrom.value.DNS = {
|
||||
Name: "alidns",
|
||||
ID: "",
|
||||
Secret: "",
|
||||
ForceInterval: 3600,
|
||||
ResolverDoaminCheck: true,
|
||||
DNSServerList: [],
|
||||
HttpClientProxyType: "",
|
||||
HttpClientProxyAddr: "",
|
||||
HttpClientProxyUser: "",
|
||||
HttpClientProxyPassword: "",
|
||||
Callback: {
|
||||
URL: "",
|
||||
Method: "get",
|
||||
Headers: [""],
|
||||
RequestBody: "",
|
||||
DisableCallbackSuccessContentCheck: false,
|
||||
CallbackSuccessContent: [],
|
||||
Server: "other",
|
||||
}
|
||||
}
|
||||
}
|
||||
preDDNSFrom.value.WebhookEnable = false
|
||||
preDDNSFrom.value.WebhookCallOnGetIPfail = false
|
||||
preDDNSFrom.value.WebhookURL = ""
|
||||
preDDNSFrom.value.WebhookMethod = "get"
|
||||
preDDNSFrom.value.WebhookHeaders = []
|
||||
preDDNSFrom.value.WebhookRequestBody = ""
|
||||
preDDNSFrom.value.WebhookDisableCallbackSuccessContentCheck=false,
|
||||
preDDNSFrom.value.WebhookSuccessContent = []
|
||||
preDDNSFrom.value.WebhookDisableCallbackSuccessContentCheck = false,
|
||||
preDDNSFrom.value.WebhookSuccessContent = []
|
||||
preDDNSFrom.value.WebhookProxy = ""
|
||||
preDDNSFrom.value.WebhookProxyAddr = ""
|
||||
preDDNSFrom.value.WebhookProxyUser = ""
|
||||
|
@ -1800,7 +1796,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
|
|||
Name: task.DNS.Name,
|
||||
ID: task.DNS.ID,
|
||||
Secret: task.DNS.Secret,
|
||||
ForceInterval:task.DNS.ForceInterval,
|
||||
ForceInterval: task.DNS.ForceInterval,
|
||||
ResolverDoaminCheck: task.DNS.ResolverDoaminCheck,
|
||||
DNSServerList: task.DNS.DNSServerList,
|
||||
HttpClientProxyType: task.DNS.HttpClientProxyType,
|
||||
|
@ -1813,7 +1809,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
|
|||
Headers: task.DNS.Callback.Headers,
|
||||
RequestBody: task.DNS.Callback.RequestBody,
|
||||
Server: task.DNS.Callback.Server,
|
||||
DisableCallbackSuccessContentCheck:task.DNS.Callback.DisableCallbackSuccessContentCheck,
|
||||
DisableCallbackSuccessContentCheck: task.DNS.Callback.DisableCallbackSuccessContentCheck,
|
||||
CallbackSuccessContent: task.DNS.Callback.CallbackSuccessContent
|
||||
}
|
||||
}
|
||||
|
@ -1852,7 +1848,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
|
|||
Name: task.DNS.Name,
|
||||
ID: task.DNS.ID,
|
||||
Secret: task.DNS.Secret,
|
||||
ForceInterval:task.DNS.ForceInterval,
|
||||
ForceInterval: task.DNS.ForceInterval,
|
||||
ResolverDoaminCheck: task.DNS.ResolverDoaminCheck,
|
||||
DNSServerList: task.DNS.DNSServerList,
|
||||
HttpClientProxyType: task.DNS.HttpClientProxyType,
|
||||
|
@ -1865,7 +1861,7 @@ const showAddOrAlterWhiteListDialog = (optionType: string, task: any) => {
|
|||
Headers: task.DNS.Callback.Headers,
|
||||
RequestBody: task.DNS.Callback.RequestBody,
|
||||
CallbackSuccessContent: task.DNS.Callback.CallbackSuccessContent,
|
||||
DisableCallbackSuccessContentCheck:task.DNS.Callback.DisableCallbackSuccessContentCheck,
|
||||
DisableCallbackSuccessContentCheck: task.DNS.Callback.DisableCallbackSuccessContentCheck,
|
||||
Server: task.DNS.Callback.Server,
|
||||
}
|
||||
}
|
||||
|
@ -2178,11 +2174,12 @@ const checkDDNSFormAvalid = () => {
|
|||
|
||||
|
||||
} else {
|
||||
data.DNS.Callback = {
|
||||
URL: "",
|
||||
Method: "",
|
||||
Headers: [],
|
||||
RequestBody: "", Server: "", CallbackSuccessContent: [],DisableCallbackSuccessContentCheck:false }
|
||||
data.DNS.Callback = {
|
||||
URL: "",
|
||||
Method: "",
|
||||
Headers: [],
|
||||
RequestBody: "", Server: "", CallbackSuccessContent: [], DisableCallbackSuccessContentCheck: false
|
||||
}
|
||||
}
|
||||
|
||||
if (data.WebhookEnable) {
|
||||
|
@ -2213,7 +2210,7 @@ const checkDNSData = (dns: any) => {
|
|||
return "Callback 接口地址不能为空"
|
||||
}
|
||||
|
||||
if (!dns.Callback.DisableCallbackSuccessContentCheck && dns.Callback.CallbackSuccessContent.length==0){
|
||||
if (!dns.Callback.DisableCallbackSuccessContentCheck && dns.Callback.CallbackSuccessContent.length == 0) {
|
||||
return "接口调用成功包含的字符串不能为空,如果要指定为空请禁用检测"
|
||||
}
|
||||
|
||||
|
|
|
@ -44,11 +44,15 @@ function queryLastlogs() {
|
|||
}
|
||||
|
||||
if (res.logs!=null && res.logs.length > 0) {
|
||||
if (res.logs[0].timestamp<=preLogTimestamp){
|
||||
return
|
||||
}
|
||||
// if (res.logs[0].timestamp!=preLogTimestamp){
|
||||
// console.log("fuckkkk")
|
||||
// return
|
||||
// }
|
||||
|
||||
|
||||
preLogTimestamp = res.logs[res.logs.length - 1].timestamp
|
||||
console.log("fff "+res.logs[res.logs.length - 1].log)
|
||||
console.log("追加日志 "+preLogTimestamp)
|
||||
for(var i=0;i<res.logs.length;i++){
|
||||
weblogsContent.value += res.logs[i].log +"\n"
|
||||
}
|
||||
|
@ -94,7 +98,8 @@ onUnmounted(()=>{
|
|||
border: 10px;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-wrap:break-word;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
<el-input v-model="form.AdminPassword" placeholder="管理登录密码" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="日志记录最大条数" id="logMaxSize">
|
||||
<el-input-number v-model="form.LogMaxSize" autocomplete="off" :min="1024" :max="40960" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="全局最大端口代理数量" id="proxyCountLimit">
|
||||
<el-input-number v-model="form.ProxyCountLimit" autocomplete="off" :min="1" :max="1024" />
|
||||
|
@ -150,6 +154,7 @@ const rawData = {
|
|||
ProxyCountLimit: 1,
|
||||
GlobalMaxConnections: 1,
|
||||
AllowInternetaccess: false,
|
||||
LogMaxSize:1024,
|
||||
}
|
||||
|
||||
const form = ref(rawData)
|
||||
|
@ -286,8 +291,6 @@ onMounted(() => {
|
|||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
padding: 15px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#adminListen {
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
<template #title>程序日志</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-divider style="margin-top: 0px;margin-bottom: 0px;" />
|
||||
|
||||
<el-sub-menu index="#relay">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
|
@ -38,7 +40,57 @@
|
|||
</el-icon>
|
||||
<template #title>转发规则</template>
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
|
||||
<el-sub-menu index="#reverseproxy">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<Position />
|
||||
</el-icon>
|
||||
<span>反向代理</span>
|
||||
</template>
|
||||
|
||||
<el-menu-item index="#reverseproxylist">
|
||||
<el-icon>
|
||||
<List />
|
||||
</el-icon>
|
||||
<template #title>反向代理规则列表</template>
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
|
||||
<el-sub-menu index="#ddns">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<Promotion />
|
||||
</el-icon>
|
||||
<span>动态域名</span>
|
||||
</template>
|
||||
|
||||
<el-menu-item index="#ddnstasklist">
|
||||
<el-icon>
|
||||
<List />
|
||||
</el-icon>
|
||||
<template #title>动态域名任务列表</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="#ddnsset">
|
||||
<el-icon>
|
||||
<Setting />
|
||||
</el-icon>
|
||||
<template #title>动态域名设置</template>
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
|
||||
|
||||
<el-divider style="margin-top: 0px;margin-bottom: 0px;" />
|
||||
|
||||
<el-sub-menu index="#safe">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<Guide />
|
||||
</el-icon>
|
||||
<span>IP过滤设置</span>
|
||||
</template>
|
||||
|
||||
<el-menu-item index="#whitelistset">
|
||||
<el-icon>
|
||||
|
@ -62,40 +114,7 @@
|
|||
</el-menu-item>
|
||||
|
||||
</el-sub-menu>
|
||||
|
||||
|
||||
|
||||
<el-sub-menu index="#ddns">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<Promotion />
|
||||
</el-icon>
|
||||
<span>动态域名</span>
|
||||
</template>
|
||||
|
||||
<el-menu-item index="#ddnstasklist">
|
||||
<el-icon>
|
||||
<List />
|
||||
</el-icon>
|
||||
<template #title>动态域名任务列表</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="#ddnsset">
|
||||
<el-icon>
|
||||
<Setting />
|
||||
</el-icon>
|
||||
<template #title>动态域名设置</template>
|
||||
</el-menu-item>
|
||||
|
||||
|
||||
|
||||
|
||||
</el-sub-menu>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<el-menu-item index="#set">
|
||||
<el-icon>
|
||||
|
@ -105,7 +124,7 @@
|
|||
</el-menu-item>
|
||||
|
||||
|
||||
<el-divider style="margin-top: 0px;margin-bottom: 0px;" />
|
||||
|
||||
<el-menu-item index="#about">
|
||||
<el-icon>
|
||||
<Pointer />
|
||||
|
@ -194,7 +213,7 @@ function handleSelect(key, keyPath, item, routeResult) {
|
|||
break;
|
||||
case "#logo":
|
||||
//window.open("https://github.com/gdy666/lucky", "_blank");
|
||||
location.hash ="#about"
|
||||
location.hash = "#about"
|
||||
break;
|
||||
default:
|
||||
SetHash(key)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -127,10 +127,10 @@ const flushWhiteListlife = (index, ip, life) => {
|
|||
|
||||
const addWhiteList = () => {
|
||||
|
||||
if (!isIP(addWhiteListForm.value.IP)) {
|
||||
MessageShow("error", "IP格式有误,请检查修正后再添加")
|
||||
return
|
||||
}
|
||||
// if (!isIP(addWhiteListForm.value.IP)) {
|
||||
// MessageShow("error", "IP格式有误,请检查修正后再添加")
|
||||
// return
|
||||
// }
|
||||
|
||||
apiFlushWhiteList(addWhiteListForm.value.IP, addWhiteListForm.value.Life).then((res) => {
|
||||
if (res.ret == 0) {
|
||||
|
|
|
@ -58,6 +58,7 @@ window.onpopstate = function (event){
|
|||
}
|
||||
|
||||
if(!PageExist(location.hash)){
|
||||
console.log("location.hash["+location.hash +"]no exist")
|
||||
location.hash ="#login"
|
||||
return
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export function isIP(ip :string){
|
|||
const MenuIndexList = ["#status",
|
||||
"#log","#relayset","#whitelistset",
|
||||
"#whitelists","#blacklists","#set",
|
||||
"#login","#ddns","#ddnstasklist","#ddnsset","#about"]
|
||||
"#login","#ddns","#ddnstasklist","#ddnsset","#about","#reverseproxylist"]
|
||||
|
||||
export function PageExist(page:string) {
|
||||
for(let i in MenuIndexList){
|
||||
|
@ -39,4 +39,47 @@ export function StringToArrayList(str : string){
|
|||
resList.push(item)
|
||||
}
|
||||
return resList
|
||||
}
|
||||
}
|
||||
|
||||
export function StrArrayListToBrHtml( strList : string[]){
|
||||
var resHtml = ""
|
||||
for ( let i in strList){
|
||||
resHtml += strList[i] + '<br />'
|
||||
}
|
||||
return resHtml
|
||||
}
|
||||
|
||||
export function StrArrayListToArea(strList : string[]){
|
||||
var res = ""
|
||||
for ( let i in strList){
|
||||
if(i!="0"){
|
||||
res +='\n'
|
||||
}
|
||||
res += strList[i]
|
||||
// res += strList[i] + '\n'
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
export const LogLevelList = [
|
||||
{
|
||||
value: 2,
|
||||
label: 'Error',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: 'Warn',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: 'Info',
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: 'Debug',
|
||||
},
|
||||
{
|
||||
value: 6,
|
||||
label: 'Trace',
|
||||
},
|
||||
]
|
|
@ -0,0 +1,126 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gdy666/lucky/config"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/ginutils"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
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})
|
||||
}
|
||||
|
||||
func querywhitelist(c *gin.Context) {
|
||||
resList := config.GetWhiteList()
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": resList})
|
||||
}
|
||||
|
||||
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": fmt.Sprintf("刷新IP有效期出错:%s", err.Error())})
|
||||
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 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 := ginutils.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": "IP已记录进白名单", "ip": c.ClientIP(), " effective_time": lifeTime})
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gdy666/lucky/config"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/fileutils"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/stringsp"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func configure(c *gin.Context) {
|
||||
//c config.GetConfig()
|
||||
configureBytes := config.GetConfigureBytes()
|
||||
//c.Header("Content-Type", "application/json")
|
||||
|
||||
//c.Data(http.StatusOK, "application/json", configureBytes)
|
||||
c.JSON(http.StatusOK,
|
||||
gin.H{
|
||||
"ret": 0,
|
||||
"time": time.Now().Format("060102150405"),
|
||||
"configure": string(configureBytes)},
|
||||
)
|
||||
}
|
||||
|
||||
var restoreConfigureVar *config.ProgramConfigure
|
||||
var restoreConfigureKey string
|
||||
var restoreConfigureMutex sync.Mutex
|
||||
|
||||
func restoreConfigureConfirm(c *gin.Context) {
|
||||
restoreConfigureMutex.Lock()
|
||||
defer restoreConfigureMutex.Unlock()
|
||||
key := c.Query("key")
|
||||
if key != restoreConfigureKey {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "key不一致"})
|
||||
return
|
||||
}
|
||||
|
||||
err := config.SetConfig(restoreConfigureVar)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "还原配置出错"})
|
||||
return
|
||||
}
|
||||
|
||||
rebootOnce.Do(func() {
|
||||
go func() {
|
||||
fileutils.OpenProgramOrFile(os.Args)
|
||||
os.Exit(0)
|
||||
}()
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "还原配置成功", "port": restoreConfigureVar.BaseConfigure.AdminWebListenPort})
|
||||
|
||||
}
|
||||
|
||||
func restoreConfigure(c *gin.Context) {
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("c.FormFile err:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("file.Open err:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
fileBytes, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("ioutil.ReadAll err:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
//log.Printf("file:%s\n", string(fileBytes))
|
||||
|
||||
var conf config.ProgramConfigure
|
||||
|
||||
err = json.Unmarshal(fileBytes, &conf)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("配置文件[%s]有误", file.Filename)})
|
||||
return
|
||||
}
|
||||
|
||||
if conf.BaseConfigure.AdminAccount == "" ||
|
||||
conf.BaseConfigure.AdminPassword == "" ||
|
||||
conf.BaseConfigure.AdminWebListenPort <= 0 ||
|
||||
conf.BaseConfigure.AdminWebListenPort >= 65536 ||
|
||||
conf.BaseConfigure.GlobalMaxConnections <= 0 ||
|
||||
conf.BaseConfigure.ProxyCountLimit <= 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("配置文件[%s]参数有误", file.Filename)})
|
||||
return
|
||||
}
|
||||
|
||||
restoreConfigureMutex.Lock()
|
||||
defer restoreConfigureMutex.Unlock()
|
||||
restoreConfigureVar = &conf
|
||||
restoreConfigureKey = stringsp.GetRandomStringNum(16)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "file": file.Filename, "restoreConfigureKey": restoreConfigureKey})
|
||||
|
||||
}
|
|
@ -0,0 +1,293 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gdy666/lucky/config"
|
||||
"github.com/gdy666/lucky/ddnscore.go"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func addDDNS(c *gin.Context) {
|
||||
var requestObj config.DDNSTask
|
||||
|
||||
err := c.BindJSON(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
|
||||
return
|
||||
}
|
||||
//fmt.Printf("addDDNS requestObj:%v\n", requestObj)
|
||||
err = config.CheckDDNSTaskAvalid(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
dealRequestDDNSTask(&requestObj)
|
||||
|
||||
err = config.DDNSTaskListAdd(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "DDNS任务添加出错"})
|
||||
return
|
||||
}
|
||||
|
||||
if requestObj.Enable {
|
||||
service.Message("ddns", "syncDDNSTask", requestObj.TaskKey)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
}
|
||||
|
||||
func deleteDDNSTask(c *gin.Context) {
|
||||
taskKey := c.Query("key")
|
||||
err := config.DDNSTaskListDelete(taskKey)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Errorf("删除DDNS任务出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
ddnscore.DDNSTaskInfoMapDelete(taskKey)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
}
|
||||
|
||||
func enableddns(c *gin.Context) {
|
||||
enable := c.Query("enable")
|
||||
key := c.Query("key")
|
||||
|
||||
var err error
|
||||
|
||||
if enable == "true" {
|
||||
err = ddnscore.EnableDDNSTaskByKey(key, true)
|
||||
if err == nil {
|
||||
service.Message("ddns", "syncDDNSTask", key)
|
||||
}
|
||||
} else {
|
||||
err = ddnscore.EnableDDNSTaskByKey(key, false)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("开关DDNS任务出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
}
|
||||
|
||||
func ddnsconfigure(c *gin.Context) {
|
||||
conf := config.GetDDNSConfigure()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "ddnsconfigure": conf})
|
||||
}
|
||||
|
||||
func alterDDNSTask(c *gin.Context) {
|
||||
taskKey := c.Query("key")
|
||||
var requestObj config.DDNSTask
|
||||
err := c.BindJSON(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
|
||||
return
|
||||
}
|
||||
|
||||
err = config.CheckDDNSTaskAvalid(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
dealRequestDDNSTask(&requestObj)
|
||||
|
||||
err = config.UpdateTaskToDDNSTaskList(taskKey, requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("更新DDNS任务出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
ddnscore.DDNSTaskInfoMapDelete(taskKey)
|
||||
|
||||
if requestObj.Enable {
|
||||
service.Message("ddns", "syncDDNSTask", taskKey)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
|
||||
}
|
||||
|
||||
func ddnsTaskList(c *gin.Context) {
|
||||
|
||||
conf := config.GetDDNSConfigure()
|
||||
|
||||
if !conf.Enable {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 6, "msg": "请先在设置页面启用DDNS动态域名服务"})
|
||||
return
|
||||
}
|
||||
|
||||
taskList := ddnscore.GetDDNSTaskInfoList()
|
||||
ddnscore.FLushWebLastAccessDDNSTaskListLastTime()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": taskList})
|
||||
}
|
||||
|
||||
func dealRequestDDNSTask(t *config.DDNSTask) {
|
||||
|
||||
if t.DNS.Name == "callback" {
|
||||
t.DNS.ID = ""
|
||||
t.DNS.Secret = ""
|
||||
t.DNS.Callback.URL = strings.TrimSpace(t.DNS.Callback.URL)
|
||||
//requestObj.DNS.Callback.CallbackSuccessContent = strings.TrimSpace(requestObj.DNS.Callback.CallbackSuccessContent)
|
||||
t.DNS.Callback.RequestBody = strings.TrimSpace(t.DNS.Callback.RequestBody)
|
||||
} else {
|
||||
t.DNS.Callback = config.DNSCallback{}
|
||||
}
|
||||
|
||||
if !t.DNS.ResolverDoaminCheck && len(t.DNS.DNSServerList) > 0 {
|
||||
t.DNS.DNSServerList = []string{}
|
||||
}
|
||||
|
||||
if t.DNS.ResolverDoaminCheck && (len(t.DNS.DNSServerList) == 0 || (len(t.DNS.DNSServerList) == 1 && t.DNS.DNSServerList[0] == "")) {
|
||||
if t.TaskType == "IPv6" {
|
||||
t.DNS.DNSServerList = config.DefaultIPv6DNSServerList
|
||||
} else {
|
||||
t.DNS.DNSServerList = config.DefaultIPv4DNSServerList
|
||||
}
|
||||
}
|
||||
|
||||
if t.DNS.HttpClientProxyType != "" && t.DNS.HttpClientProxyAddr == "" {
|
||||
t.DNS.HttpClientProxyType = ""
|
||||
}
|
||||
|
||||
if t.DNS.HttpClientProxyType == "" {
|
||||
t.DNS.HttpClientProxyAddr = ""
|
||||
t.DNS.HttpClientProxyUser = ""
|
||||
t.DNS.HttpClientProxyPassword = ""
|
||||
}
|
||||
|
||||
if t.GetType == "url" {
|
||||
t.NetInterface = ""
|
||||
t.IPReg = ""
|
||||
}
|
||||
|
||||
if t.GetType == "netInterface" {
|
||||
t.URL = []string{}
|
||||
}
|
||||
|
||||
if t.DNS.Name == "callback" {
|
||||
if t.DNS.Callback.DisableCallbackSuccessContentCheck {
|
||||
t.DNS.Callback.CallbackSuccessContent = []string{}
|
||||
}
|
||||
}
|
||||
|
||||
if !t.WebhookEnable {
|
||||
t.WebhookHeaders = []string{}
|
||||
t.WebhookMethod = ""
|
||||
t.WebhookRequestBody = ""
|
||||
t.WebhookURL = ""
|
||||
t.WebhookSuccessContent = []string{}
|
||||
t.WebhookProxy = ""
|
||||
t.WebhookProxyAddr = ""
|
||||
t.WebhookProxyUser = ""
|
||||
t.WebhookProxyPassword = ""
|
||||
}
|
||||
|
||||
if t.WebhookEnable {
|
||||
if t.WebhookMethod == "get" {
|
||||
t.WebhookRequestBody = ""
|
||||
}
|
||||
|
||||
if t.WebhookProxy == "" {
|
||||
t.WebhookProxyAddr = ""
|
||||
t.WebhookProxyUser = ""
|
||||
t.WebhookProxyPassword = ""
|
||||
}
|
||||
}
|
||||
|
||||
if t.DNS.ForceInterval < 60 {
|
||||
t.DNS.ForceInterval = 60
|
||||
} else if t.DNS.ForceInterval > 360000 {
|
||||
t.DNS.ForceInterval = 360000
|
||||
}
|
||||
|
||||
if t.HttpClientTimeout < 3 {
|
||||
t.HttpClientTimeout = 3
|
||||
} else if t.HttpClientTimeout > 60 {
|
||||
t.HttpClientTimeout = 60
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func alterDDNSConfigure(c *gin.Context) {
|
||||
var requestObj config.DDNSConfigure
|
||||
err := c.BindJSON(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
|
||||
return
|
||||
}
|
||||
|
||||
preConfigure := config.GetDDNSConfigure()
|
||||
|
||||
if preConfigure.Enable != requestObj.Enable {
|
||||
|
||||
//log.Printf("动态服务服务状态改变:%v", requestObj.Enable)
|
||||
if requestObj.Enable {
|
||||
service.Start("ddns")
|
||||
} else {
|
||||
service.Stop("ddns")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
err = config.SetDDNSConfigure(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": "保存配置过程发生错误,请检测相关启动配置"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0})
|
||||
}
|
||||
|
||||
func webhookTest(c *gin.Context) {
|
||||
key := c.Query("key")
|
||||
ddnsTask := ddnscore.GetDDNSTaskInfoByKey(key)
|
||||
|
||||
if ddnsTask == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("找不到key对应的DDNS任务:%s", key)})
|
||||
return
|
||||
}
|
||||
|
||||
var request struct {
|
||||
WebhookURL string `json:"WebhookURL"`
|
||||
WebhookMethod string `json:"WebhookMethod"`
|
||||
WebhookHeaders []string `json:"WebhookHeaders"`
|
||||
WebhookRequestBody string `json:"WebhookRequestBody"`
|
||||
WebhookSuccessContent []string `json:"WebhookSuccessContent"` //接口调用成功包含的内容
|
||||
WebhookProxy string `json:"WebhookProxy"` //使用DNS代理设置 ""表示禁用,"dns"表示使用dns的代理设置
|
||||
WebhookProxyAddr string `json:"WebhookProxyAddr"` //代理服务器IP
|
||||
WebhookProxyUser string `json:"WebhookProxyUser"` //代理用户
|
||||
WebhookProxyPassword string `json:"WebhookProxyPassword"` //代理密码
|
||||
}
|
||||
err := c.Bind(&request)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("请求解析出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
responseStr, err := ddnscore.WebhookTest(ddnsTask,
|
||||
request.WebhookURL,
|
||||
request.WebhookMethod,
|
||||
request.WebhookRequestBody,
|
||||
request.WebhookProxy,
|
||||
request.WebhookProxyAddr,
|
||||
request.WebhookProxyUser,
|
||||
request.WebhookProxyPassword,
|
||||
request.WebhookHeaders,
|
||||
request.WebhookSuccessContent)
|
||||
|
||||
msg := "Webhook接口调用成功"
|
||||
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": msg, "Response": responseStr})
|
||||
}
|
98
web/log.go
98
web/log.go
|
@ -1,98 +0,0 @@
|
|||
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: strings.TrimSpace(string(p))}
|
||||
|
||||
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,262 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gdy666/lucky/config"
|
||||
"github.com/gdy666/lucky/reverseproxy"
|
||||
"github.com/gdy666/lucky/thirdlib/gdylib/stringsp"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func reverseProxys(c *gin.Context) {
|
||||
proxyRuleList := reverseproxy.GetProxyRuleListInfo()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "list": proxyRuleList})
|
||||
}
|
||||
|
||||
func addReverseProxyRule(c *gin.Context) {
|
||||
var requestObj config.ReverseProxyRule
|
||||
err := c.BindJSON(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
|
||||
return
|
||||
}
|
||||
|
||||
err = checkReverseProxyRuleRequest(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = config.ReverseProxyRuleListAdd(&requestObj)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("添加反向代理规则失败:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
if requestObj.Enable {
|
||||
reverseproxy.EnableRuleByKey(requestObj.RuleKey, true)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
}
|
||||
|
||||
func alterReverseProxyRule(c *gin.Context) {
|
||||
var requestObj config.ReverseProxyRule
|
||||
err := c.BindJSON(&requestObj)
|
||||
if err != nil {
|
||||
fmt.Printf("fff:%s\n", err.Error())
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
|
||||
return
|
||||
}
|
||||
|
||||
err = checkReverseProxyRuleRequest(&requestObj)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = config.UpdateReverseProxyRulet(requestObj)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("修改反向代理规则失败:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
// needStop := false
|
||||
// if preRule != nil &&
|
||||
// (preRule.Network != requestObj.Network ||
|
||||
// preRule.ListenPort != requestObj.ListenPort ||
|
||||
// preRule.Enable != requestObj.Enable ||
|
||||
// !requestObj.Enable) {
|
||||
// needStop = true
|
||||
// }
|
||||
|
||||
// if needStop {
|
||||
|
||||
// }
|
||||
|
||||
reverseproxy.EnableRuleByKey(requestObj.RuleKey, false)
|
||||
//reverseproxy.FlushCache(requestObj.RuleKey)
|
||||
if requestObj.Enable {
|
||||
reverseproxy.EnableRuleByKey(requestObj.RuleKey, true)
|
||||
}
|
||||
|
||||
config.TidyReverseProxyCache()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
}
|
||||
|
||||
func deleteReverseProxyRule(c *gin.Context) {
|
||||
ruleKey := c.Query("key")
|
||||
|
||||
err := reverseproxy.EnableRuleByKey(ruleKey, false)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": fmt.Sprintf("删除反向代理规则出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
err = config.ReverseProxyRuleListDelete(ruleKey)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 3, "msg": fmt.Sprintf("删除反向代理规则出错:%s", err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
config.TidyReverseProxyCache()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
}
|
||||
|
||||
func enableReverseProxyRule(c *gin.Context) {
|
||||
|
||||
enableStr := c.Query("enable")
|
||||
ruleKey := c.Query("ruleKey")
|
||||
proxyKey := c.Query("proxyKey")
|
||||
|
||||
enable := false
|
||||
|
||||
if enableStr == "true" {
|
||||
enable = true
|
||||
}
|
||||
|
||||
if proxyKey == "" { //开关规则
|
||||
err := reverseproxy.EnableRuleByKey(ruleKey, enable)
|
||||
if err != nil {
|
||||
errMsg := err.Error()
|
||||
if strings.Contains(errMsg, "Only one usage of each socket address") {
|
||||
errMsg = "端口已被占用"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": errMsg})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
return
|
||||
}
|
||||
|
||||
err := config.EnableReverseProxySubRule(ruleKey, proxyKey, enable)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
//reverseproxy.FlushCache(ruleKey)
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
|
||||
}
|
||||
|
||||
func checkReverseProxyRuleRequest(rule *config.ReverseProxyRule) error {
|
||||
// if len(rule.ProxyList) <= 0 {
|
||||
// return fmt.Errorf("至少添加一条反向代理转发规则")
|
||||
// }
|
||||
var err error
|
||||
|
||||
if rule.RuleKey == "" {
|
||||
rule.RuleKey = stringsp.GetRandomString(16)
|
||||
}
|
||||
rule.DefaultProxy.Key = rule.RuleKey
|
||||
|
||||
if len(rule.DefaultProxy.Locations) > 0 {
|
||||
defaultLocations := []string{}
|
||||
for i := range rule.DefaultProxy.Locations {
|
||||
scheme, hostname, port, _, e := stringsp.GetHostAndPathFromURL(rule.DefaultProxy.Locations[i])
|
||||
if e != nil {
|
||||
return fmt.Errorf("默认目标地址[%s]格式有误", rule.DefaultProxy.Locations[i])
|
||||
}
|
||||
|
||||
if port != "" {
|
||||
port = ":" + port
|
||||
}
|
||||
|
||||
defaultLocations = append(defaultLocations, fmt.Sprintf("%s://%s%s", scheme, hostname, port))
|
||||
}
|
||||
|
||||
rule.DefaultProxy.Locations = defaultLocations
|
||||
}
|
||||
|
||||
if rule.DefaultProxy.AddRemoteIPToHeader && rule.DefaultProxy.AddRemoteIPHeaderKey == "" {
|
||||
return fmt.Errorf("追加客户端连接IP到指定Header 启用时,自定义HeaderKey不能为空")
|
||||
}
|
||||
|
||||
for i := range rule.ProxyList {
|
||||
domainsLength := len(rule.ProxyList[i].Domains)
|
||||
if domainsLength <= 0 {
|
||||
return fmt.Errorf("第 %d 条反向代理转发规则中域名不能为空", i+1)
|
||||
}
|
||||
|
||||
locationsLength := len(rule.ProxyList[i].Locations)
|
||||
if locationsLength <= 0 {
|
||||
return fmt.Errorf("第 %d 条反向代理转发规则中后端目标地址不能为空", i+1)
|
||||
}
|
||||
|
||||
for j := range rule.ProxyList[i].Domains {
|
||||
_, hostname, _, _, e := stringsp.GetHostAndPathFromURL(rule.ProxyList[i].Domains[j])
|
||||
if e != nil {
|
||||
return fmt.Errorf("第 %d 条反向代理转发规则中第 %d 条前端地址/域名[%s]格式有误", i+1, j+1, rule.ProxyList[i].Domains[j])
|
||||
}
|
||||
rule.ProxyList[i].Domains[j] = hostname
|
||||
}
|
||||
|
||||
for j := range rule.ProxyList[i].Locations {
|
||||
scheme, hostname, port, _, e := stringsp.GetHostAndPathFromURL(rule.ProxyList[i].Locations[j])
|
||||
if e != nil {
|
||||
return fmt.Errorf("第 %d 条反向代理转发规则中第 %d 条后端目标地址[%s]格式有误", i+1, j+1, rule.ProxyList[i].Locations[j])
|
||||
}
|
||||
if port != "" {
|
||||
port = ":" + port
|
||||
}
|
||||
rule.ProxyList[i].Locations[j] = fmt.Sprintf("%s://%s%s", scheme, hostname, port)
|
||||
}
|
||||
|
||||
if rule.ProxyList[i].AddRemoteIPToHeader && rule.ProxyList[i].AddRemoteIPHeaderKey == "" {
|
||||
return fmt.Errorf("第 %d 条子规则中 追加客户端连接IP到指定Header 启用时,自定义HeaderKey不能为空", i+1)
|
||||
}
|
||||
|
||||
if rule.ProxyList[i].Key == "" {
|
||||
rule.ProxyList[i].Key = stringsp.GetRandomString(16)
|
||||
}
|
||||
|
||||
for j := range rule.ProxyList[i].TrustedCIDRsStrList {
|
||||
_, _, err = net.ParseCIDR(rule.ProxyList[i].TrustedCIDRsStrList[j])
|
||||
if err != nil {
|
||||
return fmt.Errorf("第 %d 条子规则中 TrustedCIDRsStrList[%s]格式有误", i+1, rule.ProxyList[i].TrustedCIDRsStrList[j])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for i := range rule.DefaultProxy.TrustedCIDRsStrList {
|
||||
_, _, err = net.ParseCIDR(rule.DefaultProxy.TrustedCIDRsStrList[i])
|
||||
if err != nil {
|
||||
return fmt.Errorf("默认子规则中的 TrustedCIDRsStrList[%s]格式有误", rule.DefaultProxy.TrustedCIDRsStrList[i])
|
||||
}
|
||||
}
|
||||
|
||||
rule.Init()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getReverseProxyLog(c *gin.Context) {
|
||||
ruleKey := c.Query("ruleKey")
|
||||
proxyKey := c.Query("proxyKey")
|
||||
pageSize, _ := strconv.Atoi(c.Query("pageSize"))
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
page, _ := strconv.Atoi(c.Query("page"))
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
//last := c.Query("last")
|
||||
|
||||
total, logList := reverseproxy.GetAccessLogs(ruleKey, proxyKey, pageSize, page)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"ret": 0, "total": total, "page": page, "pageSize": pageSize, "logs": logList})
|
||||
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gdy666/lucky/rule"
|
||||
"github.com/gdy666/lucky/socketproxy"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
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 socketproxy.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 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
|
||||
}
|
||||
|
||||
dealRequestRule(&requestRule)
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
dealRequestRule(&requestRule)
|
||||
|
||||
//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 dealRequestRule(r *rule.RelayRule) {
|
||||
r.ListenPorts = strings.TrimSpace(r.ListenPorts)
|
||||
r.TargetPorts = strings.TrimSpace(r.TargetPorts)
|
||||
r.ListenIP = strings.TrimSpace(r.ListenIP)
|
||||
r.TargetIP = strings.TrimSpace(r.TargetIP)
|
||||
r.RelayType = strings.TrimSpace(r.RelayType)
|
||||
r.Name = strings.TrimSpace(r.Name)
|
||||
|
||||
}
|
||||
|
||||
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})
|
||||
}
|
878
web/web.go
878
web/web.go
File diff suppressed because it is too large
Load Diff
|
@ -3,5 +3,5 @@
|
|||
|
||||
package main
|
||||
|
||||
func RunAdminWeb(listenPort int) {
|
||||
func RunAdminWeb(listenPort int, logMaxSize int) {
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue