lucky/web/web.go

1082 lines
30 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package web
import (
"crypto/subtle"
"embed"
"encoding/base64"
"fmt"
"io/fs"
"log"
"net"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"unsafe"
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
"github.com/gdy666/lucky/base"
"github.com/gdy666/lucky/config"
"github.com/gdy666/lucky/rule"
"github.com/gdy666/lucky/thirdlib/gdylib/fileutils"
"github.com/gdy666/lucky/thirdlib/gdylib/ginutils"
"github.com/gdy666/lucky/thirdlib/gdylib/service"
"github.com/golang-jwt/jwt"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/process"
)
//go:embed adminviews/dist
var staticFs embed.FS
var stafs fs.FS
var loginErrorCount = int32(0)
var rebootOnce sync.Once
//store := cookie.NewStore([]byte("secret11111"))
//var fileServer http.Handler
//var cookieStore cookie.Store
func init() {
stafs, _ = fs.Sub(staticFs, "adminviews/dist")
//cookieStore = cookie.NewStore([]byte("lucky2022"))
}
func RunAdminWeb(listen string) {
//gin.Default()
gin.SetMode(gin.ReleaseMode)
r := gin.New()
if gin.Mode() != gin.ReleaseMode {
r.Use(gin.Logger(), gin.Recovery())
} else {
r.Use(gin.Recovery())
}
r.Use(checkLocalIP)
//r.Use(sessions.Sessions("luckysession", cookieStore))
r.Use(gzip.Gzip(gzip.DefaultCompression))
// if config.GetRunMode() == "dev" {
// r.Use(CrosHandler())
// }
r.Use(ginutils.Cors())
r.Use(HandlerStaticFiles())
//r.Use(sessionCheck())
//r.StaticFS("/", http.FS(stafs))
authorized := r.Group("/")
authorized.Use(tokenCheck())
{
authorized.GET("/api/logs", Logs)
authorized.GET("/api/status", status)
authorized.GET("/api/test", test)
authorized.GET("/api/rulelist", rulelist)
authorized.POST("/api/rule", addrule)
authorized.DELETE("/api/rule", deleterule)
authorized.PUT("/api/rule", alterrule)
authorized.GET("/api/rule/enable", enablerule)
authorized.GET("/api/baseconfigure", baseconfigure)
authorized.PUT("/api/baseconfigure", alterBaseConfigure)
authorized.GET("/api/reboot_program", rebootProgram)
authorized.GET("/api/whitelist/configure", whitelistConfigure)
authorized.PUT("/api/whitelist/configure", alterWhitelistConfigure)
authorized.GET("/api/whitelist", querywhitelist)
authorized.PUT("/api/whitelist/flush", flushwhitelist)
authorized.DELETE("/api/whitelist", deletewhitelist)
authorized.GET("/api/blacklist", queryblacklist)
authorized.PUT("/api/blacklist/flush", flushblacklist)
authorized.DELETE("/api/blacklist", deleteblacklist)
authorized.POST("/api/ddns", addDDNS)
authorized.PUT("/api/ddns", alterDDNSTask)
authorized.GET("/api/ddnstasklist", ddnsTaskList)
authorized.DELETE("/api/ddns", deleteDDNSTask)
authorized.GET("/api/ddns/enable", enableddns)
authorized.GET("/api/ddns/configure", ddnsconfigure)
authorized.PUT("/api/ddns/configure", alterDDNSConfigure)
authorized.GET("/api/netinterfaces", netinterfaces)
authorized.GET("/api/ipregtest", IPRegTest)
authorized.POST("/api/webhooktest", webhookTest)
authorized.GET("/api/info", info)
r.PUT("/api/logout", logout)
}
r.POST("/api/login", login)
//r.GET("/FreeOSMemory", FreeOSMemory)
r.GET("/wl", whitelistBasicAuth, whilelistAdd)
r.GET("/wl/:url", whitelistBasicAuth, whilelistAdd)
r.GET("/version", queryVersion)
//r.Use(func() *gin.Context {})
err := r.Run(listen)
if err != nil {
log.Printf("http.ListenAndServe error:%s", err.Error())
time.Sleep(time.Minute)
os.Exit(1)
}
}
// func FreeOSMemory(c *gin.Context) {
// debug.FreeOSMemory()
// c.JSON(http.StatusOK, gin.H{"ret": 0})
// }
func info(c *gin.Context) {
info := config.GetAppInfo()
// var info struct {
// Version string
// OS string
// ARCH string
// Date string
// }
// info.Version =
c.JSON(http.StatusOK, gin.H{"ret": 0, "info": *info})
}
func enableddns(c *gin.Context) {
enable := c.Query("enable")
key := c.Query("key")
var err error
if enable == "true" {
err = config.EnableDDNSTaskByKey(key, true)
if err == nil {
service.Message("ddns", "syncDDNSTask", key)
}
} else {
err = config.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 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
}
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 := config.GetDDNSTaskList()
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": taskList})
}
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 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
}
if requestObj.Enable {
service.Message("ddns", "syncDDNSTask", taskKey)
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
}
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.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 logout(c *gin.Context) {
config.FlushLoginRandomKey()
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "已注销登录"})
}
func queryVersion(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"ret": 0, "version": config.GetVersion()})
}
func deleteblacklist(c *gin.Context) {
ip := c.Query("ip")
err := config.BlackListDelete(ip)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "删除黑名单指定IP出错"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func deletewhitelist(c *gin.Context) {
ip := c.Query("ip")
err := config.WhiteListDelete(ip)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "删除白名单指定IP出错"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func flushblacklist(c *gin.Context) {
ip := c.Query("ip")
activelifeDurationStr := c.Query("life")
life, _ := strconv.Atoi(activelifeDurationStr)
newTime, err := config.BlackListAdd(ip, int32(life))
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "刷新IP有效期出错"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": newTime})
}
func flushwhitelist(c *gin.Context) {
ip := c.Query("ip")
activelifeDurationStr := c.Query("life")
life, _ := strconv.Atoi(activelifeDurationStr)
newTime, err := config.WhiteListAdd(ip, int32(life))
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "刷新IP有效期出错"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": newTime})
}
func queryblacklist(c *gin.Context) {
resList := config.GetBlackList()
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": resList})
}
func querywhitelist(c *gin.Context) {
resList := config.GetWhiteList()
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": resList})
}
func whitelistBasicAuth(c *gin.Context) {
bc := config.GetWhiteListBaseConfigure()
whilelistURL := c.Param("url")
if (c.Request.RequestURI == "/wl" && bc.URL != "") || whilelistURL != bc.URL {
c.AbortWithStatus(http.StatusNotFound)
return
}
realm := "Basic realm=" + strconv.Quote("Authorization Required")
pairs := processAccounts(gin.Accounts{bc.BasicAccount: bc.BasicPassword})
user, found := pairs.searchCredential(c.GetHeader("Authorization"))
if !found {
// Credentials doesn't match, we return 401 and abort handlers chain.
c.Header("WWW-Authenticate", realm)
c.AbortWithStatus(http.StatusUnauthorized)
return
}
c.Set("user", user)
}
func whilelistAdd(c *gin.Context) {
lifeTime, err := config.WhiteListAdd(c.ClientIP(), 0)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "记录白名单IP出错"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": fmt.Sprintf("IP已记录进白名单"), "ip": c.ClientIP(), " effective_time": lifeTime})
}
func whitelistConfigure(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": config.GetWhiteListBaseConfigure()})
}
func alterWhitelistConfigure(c *gin.Context) {
var requestObj config.WhiteListBaseConfigure
err := c.BindJSON(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "修改请求解析出错"})
return
}
requestObj.BasicAccount = strings.TrimSpace(requestObj.BasicAccount)
if len(requestObj.BasicAccount) == 0 || len(requestObj.BasicPassword) == 0 {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "账号或密码不能为空"})
return
}
err = config.SetWhiteListBaseConfigure(requestObj.ActivelifeDuration, requestObj.URL, requestObj.BasicAccount, requestObj.BasicPassword)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "保存白名单配置出错"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
return
}
func checkLocalIP(c *gin.Context) {
clientIP := c.ClientIP()
//fmt.Printf("clientIP:%s\n", clientIP)
bc := config.GetBaseConfigure()
if !isLocalIP(clientIP) && !bc.AllowInternetaccess {
c.JSON(http.StatusForbidden, gin.H{"ret": 1, "msg": "Forbidden Internetaccess "})
c.Abort()
return
}
}
func tokenCheck() gin.HandlerFunc {
return func(c *gin.Context) {
// if config.GetRunMode() == "dev" {
// c.Next()
// return
// }
tokenString, _ := c.GetQuery("Authorization")
if tokenString == "" {
tokenString = c.GetHeader("Authorization")
}
token, err := ginutils.GetJWTToken(tokenString, "strings")
if err != nil {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"ret": -1, "msg": "登录失效"})
return
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"ret": -1, "msg": "登录失效"})
return
}
account := claims["account"].(string)
password := claims["password"].(string)
loginKey := claims["loginkey"].(string)
if account == "" || password == "" {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"ret": -1, "msg": "登录失效"})
return
}
bc := config.GetBaseConfigure()
// //fmt.Printf("session中的account:%s password:%s\n", account, password)
if bc.AdminAccount != account || bc.AdminPassword != password || loginKey != config.GetLoginRandomKey() {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"ret": -1, "msg": "登录失效"})
return
}
c.Next()
}
}
func rebootProgram(c *gin.Context) {
rebootOnce.Do(func() {
go func() {
fileutils.OpenProgramOrFile(os.Args)
os.Exit(0)
}()
})
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": ""})
}
func login(c *gin.Context) {
var requestObj struct {
Account string `json:"Account"`
Password string `json:"Password"`
}
err := c.BindJSON(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "登录失败,登录请求解析出错"})
return
}
if atomic.LoadInt32(&loginErrorCount) >= 99 {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "登录错误次数太多,后台登录功能已禁用,请重启程序."})
return
}
bc := config.GetBaseConfigure()
if bc.AdminAccount != requestObj.Account || bc.AdminPassword != requestObj.Password {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "登录失败,账号或密码有误"})
atomic.AddInt32(&loginErrorCount, 1)
return
}
config.FlushLoginRandomKey()
tokenInfo := make(map[string]interface{})
tokenInfo["account"] = requestObj.Account //用户名
tokenInfo["password"] = requestObj.Password
tokenInfo["loginkey"] = config.GetLoginRandomKey()
tokenString, err := ginutils.GetJWTTokenString(tokenInfo, "strings", time.Hour*24)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "登录失败,token生成出错"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "登录成功", "token": tokenString})
}
func alterBaseConfigure(c *gin.Context) {
var requestObj config.BaseConfigure
err := c.BindJSON(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "请求解析出错"})
return
}
requestObj.AdminAccount = strings.TrimSpace(requestObj.AdminAccount)
if len(requestObj.AdminAccount) == 0 || len(requestObj.AdminPassword) == 0 {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": "账号或密码不能为空"})
return
}
preBaseConfigure := config.GetBaseConfigure()
if preBaseConfigure.AdminWebListenPort != requestObj.AdminWebListenPort && !config.CheckTCPPortAvalid(requestObj.AdminWebListenPort) { //检测新端口
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("新的后端管理监听端口[%d]已被占用,修改设置失败", requestObj.AdminWebListenPort)})
return
}
err = config.SetBaseConfigure(&requestObj)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 2, "msg": "保存配置过程发生错误,请检测相关启动配置"})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func 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 baseconfigure(c *gin.Context) {
conf := config.GetBaseConfigure()
c.JSON(http.StatusOK, gin.H{"ret": 0, "baseconfigure": conf})
}
func netinterfaces(c *gin.Context) {
ipv4NetInterfaces, ipv6Netinterfaces, err := config.GetNetInterface()
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("获取网卡列表出错:%s", err.Error())})
return
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": gin.H{"IPv6NewInterfaces": ipv6Netinterfaces, "IPv4NewInterfaces": ipv4NetInterfaces}})
}
func webhookTest(c *gin.Context) {
key := c.Query("key")
ddnsTask := config.GetDDNSTaskByKey(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 := config.WebhookTest(&ddnsTask.DDNSTask,
request.WebhookURL,
request.WebhookMethod,
request.WebhookRequestBody,
request.WebhookProxy,
request.WebhookProxyAddr,
request.WebhookProxyUser,
request.WebhookProxyPassword,
request.WebhookHeaders,
request.WebhookSuccessContent)
//fmt.Printf("request:%s\n", request)
msg := "Webhook接口调用成功"
if err != nil {
msg = err.Error()
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": msg, "Response": responseStr})
}
func IPRegTest(c *gin.Context) {
iptype := c.Query("iptype")
netinterface := c.Query("netinterface")
ipreg := c.Query("ipreg")
ip := config.GetIPFromNetInterface(iptype, netinterface, ipreg)
c.JSON(http.StatusOK, gin.H{"ret": 0, "ip": ip})
}
func ddnsconfigure(c *gin.Context) {
conf := config.GetDDNSConfigure()
c.JSON(http.StatusOK, gin.H{"ret": 0, "ddnsconfigure": conf})
}
func enablerule(c *gin.Context) {
enable := c.Query("enable")
key := c.Query("key")
var err error
var r *rule.RelayRule
var syncSuccess bool
if enable == "true" {
r, syncSuccess, err = rule.EnableRelayRuleByKey(key)
} else {
r, syncSuccess, err = rule.DisableRelayRuleByKey(key)
}
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("开关规则出错:%s", err.Error())})
return
}
log.Printf("[%s] relayRule[%s][%s]", enable, r.Name, r.MainConfigure)
syncRes := ""
if !syncSuccess {
syncRes = "同步规则状态到配置文件出错"
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "", "syncres": syncRes})
}
func alterrule(c *gin.Context) {
var requestRule rule.RelayRule
err := c.BindJSON(&requestRule)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改请求解析出错:%s", err.Error())})
return
}
//fmt.Printf("balance:%v\n", requestRule.BalanceTargetAddressList)
preConfigureStr := requestRule.MainConfigure
configureStr := requestRule.CreateMainConfigure()
// configureStr := fmt.Sprintf("%s@%s:%sto%s:%s",
// requestRule.RelayType,
// requestRule.ListenIP, requestRule.ListenPorts,
// requestRule.TargetIP, requestRule.TargetPorts)
r, err := rule.CreateRuleByConfigureAndOptions(requestRule.Name, configureStr, requestRule.Options)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改转发规则[%s]时出错:%s", preConfigureStr, err.Error())})
return
}
syncSuccess, err := rule.AlterRuleInGlobalRuleListByKey(preConfigureStr, r)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("修改转发规则[%s]时出错:%s", preConfigureStr, err.Error())})
return
}
r, _, err = rule.EnableRelayRuleByKey(r.MainConfigure)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": fmt.Sprintf("修改转发规则成功,但启用规则时出错:%s", err.Error())})
return
}
log.Printf("修改转发规则[%s][%s]成功", r.Name, r.MainConfigure)
synsRes := ""
if !syncSuccess {
synsRes = "同步修改规则数据到配置文件出错"
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "修改转发规则成功", "syncres": synsRes})
}
func deleterule(c *gin.Context) {
ruleKey := c.Query("rule")
rule.DisableRelayRuleByKey(ruleKey)
syncSuccess, err := rule.DeleteGlobalRuleByKey(ruleKey)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("删除转发规则出错:%s", err.Error())})
return
}
syncRes := ""
if !syncSuccess {
syncRes = "同步规则信息到配置文件出错"
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "删除成功", "syncres": syncRes})
}
func addrule(c *gin.Context) {
var requestRule rule.RelayRule
err := c.BindJSON(&requestRule)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("请求解析出错:%s", err.Error())})
return
}
configureStr := requestRule.CreateMainConfigure()
r, err := rule.CreateRuleByConfigureAndOptions(requestRule.Name, configureStr, requestRule.Options)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("创建转发规则出错:%s", err.Error())})
return
}
synsRes, err := rule.AddRuleToGlobalRuleList(true, *r)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 1, "msg": fmt.Sprintf("添加转发规则出错:%s", err.Error())})
return
}
r, _, err = rule.EnableRelayRuleByKey(r.MainConfigure)
if err != nil {
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": fmt.Sprintf("启用规则出错:%s", err.Error())})
return
}
log.Printf("添加转发规则[%s][%s]成功", r.Name, r.MainConfigure)
if synsRes != "" {
synsRes = "保存配置文件出错,请检查配置文件设置"
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "msg": "添加规则并启用成功", "syncres": synsRes})
}
func rulelist(c *gin.Context) {
ruleList, proxyListInfoMap := rule.GetRelayRuleList()
type ruleItem struct {
Name string `json:"Name"`
MainConfigure string `json:"Mainconfigure"`
RelayType string `json:"RelayType"`
ListenIP string `json:"ListenIP"`
ListenPorts string `json:"ListenPorts"`
TargetIP string `json:"TargetIP"`
TargetPorts string `json:"TargetPorts"`
BalanceTargetAddressList []string `json:"BalanceTargetAddressList"`
Options base.RelayRuleOptions `json:"Options"`
SubRuleList []rule.SubRelayRule `json:"SubRuleList"`
From string `json:"From"`
IsEnable bool `json:"Enable"`
ProxyList []rule.RelayRuleProxyInfo `json:"ProxyList"`
}
//proxyListInfoMap[(*ruleList)[i].MainConfigure]
var data []ruleItem
for i := range *ruleList {
item := ruleItem{
Name: (*ruleList)[i].Name,
MainConfigure: (*ruleList)[i].MainConfigure,
RelayType: (*ruleList)[i].RelayType,
ListenIP: (*ruleList)[i].ListenIP,
ListenPorts: (*ruleList)[i].ListenPorts,
TargetIP: (*ruleList)[i].TargetIP,
TargetPorts: (*ruleList)[i].TargetPorts,
Options: (*ruleList)[i].Options,
SubRuleList: (*ruleList)[i].SubRuleList,
From: (*ruleList)[i].From,
IsEnable: (*ruleList)[i].IsEnable,
ProxyList: proxyListInfoMap[(*ruleList)[i].MainConfigure],
BalanceTargetAddressList: (*ruleList)[i].BalanceTargetAddressList,
}
data = append(data, item)
}
c.JSON(http.StatusOK, gin.H{"ret": 0, "data": data})
}
func test(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"ret": 0})
}
func status(c *gin.Context) {
v, _ := mem.VirtualMemory()
currentProcessMem := GetCurrentProcessMem()
//fmt.Fprintf(w, "当前进程 CPU使用率:%.2f%% 协程数:%d 进程内存使用:%s 系统内存总量:%s 已用:%s 可用:%s \n", GetCurrentProcessCPUPrecent(), runtime.NumGoroutine(), formatFileSize(currentProcessMem), formatFileSize(v.Total), formatFileSize(v.Used), formatFileSize(v.Free))
//fmt.Fprintf(w, "当前全局TCP 连接数:%d 全局TCP连接数最大限制:%d\n", core.GetGlobalTCPConns(), core.GetGlobalMaxConnections())
//var proxyStatusList []string
// for _, p := range *config.GlobalProxy {
// //fmt.Fprintf(w, "%s\n", p.GetStatus())
// proxyStatusList = append(proxyStatusList, p.GetStatus())
// }
respMap := make(map[string]interface{})
respMap["totleMem"] = formatFileSize(v.Total)
respMap["usedMem"] = formatFileSize(v.Used)
respMap["unusedMem"] = formatFileSize(v.Free)
respMap["currentProcessUsedCPU"] = fmt.Sprintf("%.2f%%", GetCurrentProcessCPUPrecent())
respMap["goroutine"] = fmt.Sprintf("%d", runtime.NumGoroutine())
respMap["processUsedMem"] = formatFileSize(currentProcessMem)
respMap["currentConnections"] = fmt.Sprintf("%d", base.GetGlobalConnections())
respMap["maxConnections"] = fmt.Sprintf("%d", base.GetGlobalMaxConnections())
respMap["usedCPU"] = fmt.Sprintf("%.2f%%", GetCpuPercent())
//respMap["proxysStatus"] = proxyStatusList
c.JSON(http.StatusOK, gin.H{
"ret": 0,
"data": respMap,
})
}
func GetCurrentProcessMem() uint64 {
plist, e := process.Processes()
if e == nil {
for _, p := range plist {
if int(p.Pid) == os.Getpid() {
mem, err := p.MemoryInfo()
if err != nil {
return 0
}
return mem.RSS
}
}
}
return 0
}
func GetCurrentProcessCPUPrecent() float64 {
plist, e := process.Processes()
if e == nil {
for _, p := range plist {
if int(p.Pid) == os.Getpid() {
cpuprecent, err := p.CPUPercent()
if err != nil {
return 0
}
return cpuprecent
}
}
}
return 0
}
func GetCpuPercent() float64 {
percent, _ := cpu.Percent(time.Second, false)
return percent[0]
}
//跨域访问cross origin resource share
func CrosHandler() gin.HandlerFunc {
return func(context *gin.Context) {
method := context.Request.Method
//context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
origin := context.Request.Header.Get("Origin")
context.Header("Access-Control-Allow-Origin", origin) // 设置允许访问所有域
context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
context.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma,token,openid,opentoken")
//context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar")
//context.Header("Access-Control-Allow-Methods", "*")
//context.Header("Access-Control-Allow-Headers", "*")
context.Header("Access-Control-Expose-Headers", "*")
context.Header("Access-Control-Allow-Credentials", "true")
context.Header("Access-Control-Max-Age", "172800")
//context.Header("Access-Control-Allow-Credentials", "false")
//context.Set("content-type", "application/json")
if method == "OPTIONS" {
context.JSON(http.StatusOK, gin.H{
"ret": 0,
})
}
//处理请求
context.Next()
}
}
//------------------------------------------------------------------------------------------------------------------
func BasicAuth() gin.HandlerFunc {
return gin.BasicAuthForRealm(config.GetAuthAccount(), "")
}
func formatFileSize(fileSize uint64) (size string) {
switch {
case fileSize < 1024:
return fmt.Sprintf("%.2fB", float64(fileSize)/float64(1))
case fileSize < (1024 * 1024):
return fmt.Sprintf("%.2fKB", float64(fileSize)/float64(1024))
case fileSize < (1024 * 1024 * 1024):
return fmt.Sprintf("%.2fMB", float64(fileSize)/float64(1024*1024))
case fileSize < (1024 * 1024 * 1024 * 1024):
return fmt.Sprintf("%.2fGB", float64(fileSize)/float64(1024*1024*1024))
case fileSize < (1024 * 1024 * 1024 * 1024 * 1024):
return fmt.Sprintf("%.2fTB", float64(fileSize)/float64(1024*1024*1024*1024))
default:
return fmt.Sprintf("%.2fEB", float64(fileSize)/float64(1024*1024*1024*1024*1024))
}
}
func isLocalIP(ipstr string) bool {
ip := net.ParseIP(ipstr)
if ip.IsLoopback() {
return true
}
ip4 := ip.To4()
if ip4 == nil {
return false
}
return ip4[0] == 10 || // 10.0.0.0/8
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
}
//***********************
//basicAuth
type authPair struct {
value string
user string
}
type authPairs []authPair
func processAccounts(accounts gin.Accounts) authPairs {
length := len(accounts)
assert1(length > 0, "Empty list of authorized credentials")
pairs := make(authPairs, 0, length)
for user, password := range accounts {
assert1(user != "", "User can not be empty")
value := authorizationHeader(user, password)
pairs = append(pairs, authPair{
value: value,
user: user,
})
}
return pairs
}
func authorizationHeader(user, password string) string {
base := user + ":" + password
return "Basic " + base64.StdEncoding.EncodeToString(StringToBytes(base))
}
func assert1(guard bool, text string) {
if !guard {
panic(text)
}
}
func StringToBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(
&struct {
string
Cap int
}{s, len(s)},
))
}
func (a authPairs) searchCredential(authValue string) (string, bool) {
if authValue == "" {
return "", false
}
for _, pair := range a {
if subtle.ConstantTimeCompare(StringToBytes(pair.value), StringToBytes(authValue)) == 1 {
return pair.user, true
}
}
return "", false
}