mirror of
https://github.com/hamster1963/HomeDash-Backend.git
synced 2025-06-21 22:41:32 +08:00
update: 初始化代码提交
This commit is contained in:
0
utility/.gitkeep
Normal file
0
utility/.gitkeep
Normal file
19
utility/bin_utils/bin_info.go
Normal file
19
utility/bin_utils/bin_info.go
Normal file
@ -0,0 +1,19 @@
|
||||
package binInfo
|
||||
|
||||
// 初始化为 unknown,如果编译时没有传入这些值,则为 unknown
|
||||
var (
|
||||
GitTag = "unknown"
|
||||
GitCommitLog = "unknown"
|
||||
GitStatus = "cleanly"
|
||||
BuildTime = "unknown"
|
||||
BuildGoVersion = "unknown"
|
||||
)
|
||||
|
||||
// VersionString 返回版本信息
|
||||
func VersionString() string {
|
||||
return "GitTag:" + GitTag + "\n" +
|
||||
"GitCommitLog:" + GitCommitLog + "\n" +
|
||||
"GitStatus:" + GitStatus + "\n" +
|
||||
"BuildTime:" + BuildTime + "\n" +
|
||||
"BuildGoVersion:" + BuildGoVersion + "\n"
|
||||
}
|
57
utility/docker_utils/docker.go
Normal file
57
utility/docker_utils/docker.go
Normal file
@ -0,0 +1,57 @@
|
||||
package docker_utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gclient"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"home-network-watcher/manifest"
|
||||
)
|
||||
|
||||
type uDockerUtils struct{}
|
||||
|
||||
var DockerUtils = &uDockerUtils{}
|
||||
|
||||
type dockerStatus struct {
|
||||
ServerCount int
|
||||
ErrorServer int
|
||||
DockerCount int
|
||||
ErrorDocker int
|
||||
}
|
||||
|
||||
// GetDockerStatus
|
||||
//
|
||||
// @dc: 获取docker状态
|
||||
// @author: hamster @date:2023/9/20 15:33:19
|
||||
func (u *uDockerUtils) GetDockerStatus(ctx context.Context) (status *dockerStatus, err error) {
|
||||
status = &dockerStatus{}
|
||||
response, err := g.Client().SetHeaderMap(manifest.DockerAuthMap).Get(context.Background(), manifest.DockerApiUrl)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func(response *gclient.Response) {
|
||||
err = response.Close()
|
||||
if err != nil {
|
||||
glog.Warning(ctx, "关闭DockerStatus", err)
|
||||
}
|
||||
}(response)
|
||||
endpointList := gconv.SliceAny(response.ReadAllString())
|
||||
status.ServerCount = len(endpointList)
|
||||
for _, endpoint := range endpointList {
|
||||
endpointJson := gjson.New(endpoint)
|
||||
// 获取全部容器数量
|
||||
serverRunningDockerCount := endpointJson.Get("Snapshots.0.RunningContainerCount").Int()
|
||||
serverStoppedDockerCount := endpointJson.Get("Snapshots.0.StoppedContainerCount").Int()
|
||||
status.DockerCount += serverRunningDockerCount + serverStoppedDockerCount
|
||||
// 判断服务器状态
|
||||
if endpointJson.Get("Status").Int() != 1 {
|
||||
status.ErrorServer++
|
||||
status.ErrorDocker += serverRunningDockerCount + serverStoppedDockerCount
|
||||
} else {
|
||||
status.ErrorDocker += serverStoppedDockerCount
|
||||
}
|
||||
}
|
||||
return status, nil
|
||||
}
|
119
utility/ha_utils/ha_entity.go
Normal file
119
utility/ha_utils/ha_entity.go
Normal file
@ -0,0 +1,119 @@
|
||||
package ha_utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"home-network-watcher/manifest"
|
||||
"time"
|
||||
)
|
||||
|
||||
type uHaUtils struct{}
|
||||
|
||||
var HaUtils = &uHaUtils{}
|
||||
|
||||
// 卧室空调
|
||||
const (
|
||||
AirConditionerEntityPattern = "climate.lumi_mcn02_1f58_air_conditioner"
|
||||
AirConditionerStatePattern = "attributes.air_conditioner.on"
|
||||
AirConditionerTempPattern = "attributes.temperature"
|
||||
)
|
||||
|
||||
// 加湿器
|
||||
const (
|
||||
HumidifierEntityPattern = "humidifier.deerma_jsq2g_d916_humidifier"
|
||||
HumidifierStatePattern = "attributes.humidifier.on"
|
||||
HumidifierHumidityPattern = "attributes.environment.relative_humidity"
|
||||
)
|
||||
|
||||
// 空气净化器
|
||||
const (
|
||||
AirPurifierEntityPattern = "fan.zhimi_mp4a_20cb_air_purifier"
|
||||
AirPurifierStatePattern = "attributes.air_purifier.on"
|
||||
AirPurifierPM25Pattern = "attributes.environment.pm2_5_density"
|
||||
)
|
||||
|
||||
// 卧室床头灯
|
||||
const (
|
||||
LightEntityPattern = "light.yeelink_bslamp2_0b02_light"
|
||||
LightStatePattern = "attributes.light.on"
|
||||
LightBrightnessPattern = "attributes.light.brightness"
|
||||
)
|
||||
|
||||
type HaEntitiesInfo struct {
|
||||
AirConditioner g.Map
|
||||
Humidifier g.Map
|
||||
AirPurifier g.Map
|
||||
Light g.Map
|
||||
}
|
||||
|
||||
// GetEntitiesInfo
|
||||
//
|
||||
// @dc: 获取实体信息
|
||||
// @author: hamster @date:2023/9/22 17:54:25
|
||||
func (u *uHaUtils) GetEntitiesInfo(ctx context.Context) (err error) {
|
||||
haClient := g.Client().SetHeaderMap(manifest.HomeAssistantAuthMap)
|
||||
baseUrl := manifest.HomeAssistantBaseUrl
|
||||
// 获取空调状态
|
||||
airConditionerMap := g.Map{}
|
||||
response, err := haClient.Get(ctx, baseUrl+AirConditionerEntityPattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Close()
|
||||
responseJson := gjson.New(response.ReadAllString())
|
||||
responseJson.SetViolenceCheck(true)
|
||||
airConditionerMap["state"] = responseJson.Get(AirConditionerStatePattern)
|
||||
airConditionerMap["temp"] = responseJson.Get(AirConditionerTempPattern)
|
||||
|
||||
// 获取加湿器状态
|
||||
humidifierMap := g.Map{}
|
||||
response, err = haClient.Get(ctx, baseUrl+HumidifierEntityPattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Close()
|
||||
responseJson = gjson.New(response.ReadAllString())
|
||||
responseJson.SetViolenceCheck(true)
|
||||
humidifierMap["state"] = responseJson.Get(HumidifierStatePattern)
|
||||
humidifierMap["humidity"] = responseJson.Get(HumidifierHumidityPattern)
|
||||
|
||||
// 获取空气净化器状态
|
||||
airPurifierMap := g.Map{}
|
||||
response, err = haClient.Get(ctx, baseUrl+AirPurifierEntityPattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Close()
|
||||
responseJson = gjson.New(response.ReadAllString())
|
||||
responseJson.SetViolenceCheck(true)
|
||||
airPurifierMap["state"] = responseJson.Get(AirPurifierStatePattern)
|
||||
airPurifierMap["pm25"] = responseJson.Get(AirPurifierPM25Pattern)
|
||||
|
||||
// 获取床头灯状态
|
||||
lightMap := g.Map{}
|
||||
response, err = haClient.Get(ctx, baseUrl+LightEntityPattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Close()
|
||||
responseJson = gjson.New(response.ReadAllString())
|
||||
responseJson.SetViolenceCheck(true)
|
||||
lightMap["state"] = responseJson.Get(LightStatePattern)
|
||||
lightMap["brightness"] = responseJson.Get(LightBrightnessPattern)
|
||||
|
||||
// 汇总信息
|
||||
HaEntitiesInfo := &HaEntitiesInfo{
|
||||
AirConditioner: airConditionerMap,
|
||||
Humidifier: humidifierMap,
|
||||
AirPurifier: airPurifierMap,
|
||||
Light: lightMap,
|
||||
}
|
||||
err = gcache.Set(ctx, manifest.HaEntitiesCacheKey, HaEntitiesInfo, 1*time.Minute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
68
utility/network_utils/coffee.go
Normal file
68
utility/network_utils/coffee.go
Normal file
@ -0,0 +1,68 @@
|
||||
package network_utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gclient"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"home-network-watcher/manifest"
|
||||
)
|
||||
|
||||
type uProxyProvider struct{}
|
||||
|
||||
var ProxyProvider = &uProxyProvider{}
|
||||
|
||||
// GetSubscribeInfo
|
||||
//
|
||||
// @dc: 获取订阅信息
|
||||
// @author: laixin @date:2023/6/6 04:15:01
|
||||
func (u *uProxyProvider) GetSubscribeInfo() (err error) {
|
||||
err, authData := getAuthData()
|
||||
if err != nil || authData == "" {
|
||||
return
|
||||
}
|
||||
proxyClient := gclient.New()
|
||||
proxyClient.SetHeaderMap(map[string]string{
|
||||
"Authorization": authData,
|
||||
})
|
||||
response, err := proxyClient.Get(context.TODO(), manifest.ProxyProviderBaseUrl, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
infoData := gconv.Map(gconv.Map(response.ReadAllString())["data"])
|
||||
if infoData["d"] == nil || infoData["transfer_enable"] == nil {
|
||||
return
|
||||
}
|
||||
usedBound := gconv.Float64(infoData["d"]) / 1024 / 1024 / 1010
|
||||
planBound := gconv.Float64(infoData["transfer_enable"]) / 1024 / 1024 / 1024
|
||||
remainBound := planBound - usedBound
|
||||
// 保留两位小数
|
||||
usedBoundStr := fmt.Sprintf("%.2f", usedBound)
|
||||
planBoundStr := fmt.Sprintf("%.2f", planBound)
|
||||
remainBoundStr := fmt.Sprintf("%.2f", remainBound)
|
||||
proxyCache := g.Map{
|
||||
"usedBound": usedBoundStr,
|
||||
"planBound": planBoundStr,
|
||||
"remainBound": remainBoundStr,
|
||||
}
|
||||
err = gcache.Set(context.TODO(), manifest.ProxySubscribeCacheKey, proxyCache, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getAuthData
|
||||
//
|
||||
// @dc: 获取代理提供商AuthData
|
||||
func getAuthData() (err error, authData string) {
|
||||
url := manifest.ProxyProviderLoginUrl
|
||||
response, err := gclient.New().Post(context.TODO(), url, manifest.ProxyProviderAuthData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return nil, gjson.New(response.ReadAllString()).Get("data.auth_data").String()
|
||||
}
|
94
utility/network_utils/home_network.go
Normal file
94
utility/network_utils/home_network.go
Normal file
@ -0,0 +1,94 @@
|
||||
package network_utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/hamster1963/360-router-data-retriever/rconfig"
|
||||
"github.com/hamster1963/360-router-data-retriever/rutils"
|
||||
"home-network-watcher/manifest"
|
||||
"time"
|
||||
)
|
||||
|
||||
type uNetworkUtils struct {
|
||||
}
|
||||
|
||||
var NetworkUtils = &uNetworkUtils{}
|
||||
|
||||
var (
|
||||
routerConfig = &rconfig.RouterConfig{
|
||||
RouterIP: manifest.HomeNetworkRouterIP,
|
||||
RouterAddress: manifest.HomeNetworkRouterAddress,
|
||||
RouterPassword: manifest.HomeNetworkRouterPassword,
|
||||
}
|
||||
routerMain rutils.SRouterController = rutils.NewRouter().InitRouter(routerConfig)
|
||||
)
|
||||
|
||||
// GetHomeNetwork
|
||||
//
|
||||
// @dc: 获取家庭路由器网速
|
||||
// @author: laixin @date:2023/4/2 19:43:13
|
||||
func (u *uNetworkUtils) GetHomeNetwork() (err error) {
|
||||
var (
|
||||
homeNetwork = g.Map{
|
||||
"time": "",
|
||||
"rxSpeedKbps": 0,
|
||||
"txSpeedKbps": 0,
|
||||
"rxSpeedMbps": 0,
|
||||
"txSpeedMbps": 0,
|
||||
}
|
||||
)
|
||||
|
||||
// 检测登陆状态
|
||||
if login, err := routerMain.CheckLogin(); err != nil || login == false {
|
||||
err := routerMain.GetRandomString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = routerMain.GenerateAesString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = routerMain.Login()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
routerSpeedInfo, err := routerMain.GetRouterSpeed()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonData := gjson.New(routerSpeedInfo)
|
||||
rxSpeed := jsonData.Get("data.down_speed") // 下载速度
|
||||
txSpeed := jsonData.Get("data.up_speed") // 上传速度
|
||||
|
||||
// 速度单位转换
|
||||
rxSpeedKbps := gconv.Float64(fmt.Sprintf("%.2f", gconv.Float64(rxSpeed)/1024))
|
||||
txSpeedKbps := gconv.Float64(fmt.Sprintf("%.2f", gconv.Float64(txSpeed)/1024))
|
||||
homeNetwork["rxSpeedKbps"] = rxSpeedKbps
|
||||
homeNetwork["txSpeedKbps"] = txSpeedKbps
|
||||
|
||||
// 转换成MB
|
||||
rxSpeedMbps := gconv.Float64(fmt.Sprintf("%.2f", gconv.Float64(rxSpeed)/1024/1024))
|
||||
txSpeedMbps := gconv.Float64(fmt.Sprintf("%.2f", gconv.Float64(txSpeed)/1024/1024))
|
||||
homeNetwork["rxSpeedMbps"] = rxSpeedMbps
|
||||
homeNetwork["txSpeedMbps"] = txSpeedMbps
|
||||
|
||||
homeNetwork["time"] = gtime.Now().String()
|
||||
deviceData, err := routerMain.GetDeviceList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
homeNetwork["deviceCount"] = len(gjson.New(deviceData).Get("client_node").Array())
|
||||
|
||||
err = gcache.Set(context.Background(), manifest.HomeNetworkCacheKey, homeNetwork, 10*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
129
utility/network_utils/node_utils.go
Normal file
129
utility/network_utils/node_utils.go
Normal file
@ -0,0 +1,129 @@
|
||||
package network_utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gclient"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"home-network-watcher/manifest"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type uNodeUtils struct{}
|
||||
|
||||
var NodeUtils = &uNodeUtils{}
|
||||
|
||||
// GetNodeInfo
|
||||
//
|
||||
// @dc: 获取节点信息
|
||||
// @author: laixin @date:2023/4/2 20:08:50
|
||||
func (u *uNodeUtils) GetNodeInfo() (err error) {
|
||||
var (
|
||||
connectedNode string
|
||||
lastChangeTime string
|
||||
nodeInfo = g.Map{
|
||||
"nodeName": "",
|
||||
"lastChangeTime": "",
|
||||
"updateTime": "",
|
||||
}
|
||||
)
|
||||
|
||||
// 获取token
|
||||
token, err := u.GetToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wsUrl := "ws://" + manifest.XrayBaseUrl + "/api/message?Authorization=" + token
|
||||
|
||||
// websocket获取节点列表
|
||||
client := gclient.NewWebSocket()
|
||||
client.HandshakeTimeout = time.Second // 设置超时时间
|
||||
client.Proxy = http.ProxyFromEnvironment // 设置代理
|
||||
client.TLSClientConfig = &tls.Config{} // 设置 tls 配置
|
||||
conn, _, err := client.Dial(wsUrl, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, data, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 打印消息类型和消息内容
|
||||
nodeList := gjson.New(string(data)).Get("body.outboundStatus").Array()
|
||||
for _, v := range nodeList {
|
||||
if !gjson.New(gconv.String(v)).Get("alive").Bool() {
|
||||
continue
|
||||
}
|
||||
// 根据"delay"获得延迟最小的节点
|
||||
if connectedNode == "" {
|
||||
connectedNode = gconv.String(v)
|
||||
} else {
|
||||
if gjson.New(connectedNode).Get("delay").Int() > gjson.New(gconv.String(v)).Get("delay").Int() {
|
||||
connectedNode = gconv.String(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 比对缓存中的节点信息
|
||||
cacheNodeInfo, _ := gcache.Get(context.Background(), manifest.ProxyNodeCacheKey)
|
||||
if cacheNodeInfo != nil {
|
||||
if gjson.New(cacheNodeInfo.String()).Get("nodeName").String() == gjson.New(connectedNode).Get("outbound_tag").String() {
|
||||
lastChangeTime = gjson.New(cacheNodeInfo).Get("lastChangeTime").String()
|
||||
} else {
|
||||
lastChangeTime = gtime.Now().String()
|
||||
}
|
||||
} else {
|
||||
lastChangeTime = gtime.Now().String()
|
||||
}
|
||||
// 获取节点信息
|
||||
nodeInfo["nodeName"] = gjson.New(connectedNode).Get("outbound_tag").String()
|
||||
nodeInfo["lastChangeTime"] = lastChangeTime
|
||||
nodeInfo["updateTime"] = gtime.Now().String()
|
||||
// 关闭连接
|
||||
_ = conn.Close()
|
||||
// 存入缓存
|
||||
err = gcache.Set(context.Background(), manifest.ProxyNodeCacheKey, nodeInfo, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetToken
|
||||
//
|
||||
// @dc: 获取v2raya面板token
|
||||
// @author: laixin @date:2023/4/2 20:13:24
|
||||
func (u *uNodeUtils) GetToken() (token string, err error) {
|
||||
var (
|
||||
url = "http://" + manifest.XrayBaseUrl + "/api/login"
|
||||
)
|
||||
|
||||
// 登陆获取token
|
||||
response, err := g.Client().Post(context.Background(), url, manifest.XrayLoginDataMap)
|
||||
defer func(response *gclient.Response) {
|
||||
err := response.Close()
|
||||
if err != nil {
|
||||
glog.Warning(context.Background(), err)
|
||||
}
|
||||
}(response)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resData := gjson.New(response.ReadAllString())
|
||||
if resData.Get("code").String() != "SUCCESS" {
|
||||
err = gerror.New("登陆失败")
|
||||
return
|
||||
}
|
||||
token = resData.Get("data.token").String()
|
||||
if token == "" {
|
||||
err = gerror.New("token获取失败")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
204
utility/network_utils/proxy_network.go
Normal file
204
utility/network_utils/proxy_network.go
Normal file
@ -0,0 +1,204 @@
|
||||
package network_utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gclient"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"home-network-watcher/manifest"
|
||||
"time"
|
||||
)
|
||||
|
||||
type uProxyNetwork struct{}
|
||||
|
||||
var ProxyNetwork = &uProxyNetwork{}
|
||||
|
||||
// GetXuiUserList
|
||||
//
|
||||
// @dc: 获取xui用户列表
|
||||
// @author: hamster @date:2023/9/19 09:29:33
|
||||
func (u *uProxyNetwork) GetXuiUserList() (err error) {
|
||||
var (
|
||||
url = manifest.XuiBaseUrl + "/xui/inbound/list"
|
||||
session map[string]string
|
||||
)
|
||||
// 尝试获取缓存中的session
|
||||
sessionData, err := gcache.Get(context.Background(), manifest.ProxySessionCacheKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sessionData.IsNil() {
|
||||
// 重新获取session
|
||||
session, err = u.GetXuiSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
session = sessionData.MapStrStr()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
post, err := g.Client().SetCookieMap(session).Post(context.Background(), url)
|
||||
defer func(post *gclient.Response) {
|
||||
err := post.Close()
|
||||
if err != nil {
|
||||
glog.Warning(context.Background(), err)
|
||||
}
|
||||
}(post)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if post.StatusCode != 200 {
|
||||
glog.Warning(context.Background(), "获取xui用户列表失败")
|
||||
return err
|
||||
}
|
||||
jsonData := gjson.New(post.ReadAllString())
|
||||
userList := jsonData.Get("obj").Array()
|
||||
// 删除敏感信息,计算总上传下载流量
|
||||
userCacheList := make([]g.Map, 0)
|
||||
upTotal := 0.00
|
||||
downTotal := 0.00
|
||||
for _, value := range userList {
|
||||
userJson := gjson.New(value)
|
||||
userCacheJson := g.Map{
|
||||
"id": userJson.Get("id").Int(),
|
||||
"enable": userJson.Get("enable").Bool(),
|
||||
"protocol": userJson.Get("protocol").String(),
|
||||
"remark": userJson.Get("remark").String(),
|
||||
// 转换为 GB
|
||||
"up": userJson.Get("up").Float64() / 1024 / 1024 / 1024,
|
||||
"down": userJson.Get("down").Float64() / 1024 / 1024 / 1024,
|
||||
}
|
||||
userCacheList = append(userCacheList, userCacheJson)
|
||||
upTotal += userJson.Get("up").Float64()
|
||||
downTotal += userJson.Get("down").Float64()
|
||||
}
|
||||
// 按照下载流量排序
|
||||
for i := 0; i < len(userCacheList); i++ {
|
||||
for j := i + 1; j < len(userCacheList); j++ {
|
||||
if userCacheList[i]["down"].(float64) < userCacheList[j]["down"].(float64) {
|
||||
userCacheList[i], userCacheList[j] = userCacheList[j], userCacheList[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
cacheXuiData := g.Map{
|
||||
"user_list": userCacheList,
|
||||
"user_count": len(userCacheList),
|
||||
"up_total": upTotal / 1024 / 1024 / 1024,
|
||||
"down_total": downTotal / 1024 / 1024 / 1024,
|
||||
}
|
||||
err = gcache.Set(context.Background(), manifest.XUIUserListCacheKey, cacheXuiData, 1*time.Minute)
|
||||
return
|
||||
}
|
||||
|
||||
// GetProxyNetwork
|
||||
//
|
||||
// @dc: 获取代理服务器的网速
|
||||
// @author: laixin @date:2023/4/2 20:06:21
|
||||
func (u *uProxyNetwork) GetProxyNetwork() (err error) {
|
||||
var (
|
||||
proxyNetwork = g.Map{
|
||||
"time": "",
|
||||
"rxSpeedKbps": 0,
|
||||
"txSpeedKbps": 0,
|
||||
"rxSpeedMbps": 0,
|
||||
"txSpeedMbps": 0,
|
||||
}
|
||||
session map[string]string
|
||||
url = manifest.XuiBaseUrl + "/server/status"
|
||||
)
|
||||
|
||||
// 尝试获取缓存中的session
|
||||
sessionData, err := gcache.Get(context.Background(), manifest.ProxySessionCacheKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sessionData.IsNil() {
|
||||
// 重新获取session
|
||||
session, err = u.GetXuiSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
session = sessionData.MapStrStr()
|
||||
}
|
||||
|
||||
// 通过xui进行网速的获取
|
||||
post, err := g.Client().SetCookieMap(session).Post(context.Background(), url)
|
||||
defer func(post *gclient.Response) {
|
||||
err := post.Close()
|
||||
if err != nil {
|
||||
glog.Warning(context.Background(), err)
|
||||
}
|
||||
}(post)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if post.StatusCode != 200 {
|
||||
glog.Warning(context.Background(), "获取网速失败")
|
||||
return err
|
||||
}
|
||||
jsonData := gjson.New(post.ReadAllString())
|
||||
rxSpeed := jsonData.Get("obj.netIO.down") // 下载速度
|
||||
txSpeed := jsonData.Get("obj.netIO.up") // 上传速度
|
||||
|
||||
// 速度单位转换
|
||||
rxSpeedKbps := gconv.Float64(fmt.Sprintf("%.2f", gconv.Float64(rxSpeed)/1024))
|
||||
txSpeedKbps := gconv.Float64(fmt.Sprintf("%.2f", gconv.Float64(txSpeed)/1024))
|
||||
proxyNetwork["rxSpeedKbps"] = rxSpeedKbps
|
||||
proxyNetwork["txSpeedKbps"] = txSpeedKbps
|
||||
|
||||
// 转换成MB
|
||||
rxSpeedMbps := gconv.Float64(fmt.Sprintf("%.2f", gconv.Float64(rxSpeed)/1024/1024))
|
||||
txSpeedMbps := gconv.Float64(fmt.Sprintf("%.2f", gconv.Float64(txSpeed)/1024/1024))
|
||||
proxyNetwork["rxSpeedMbps"] = rxSpeedMbps
|
||||
proxyNetwork["txSpeedMbps"] = txSpeedMbps
|
||||
|
||||
proxyNetwork["time"] = gtime.Now().String()
|
||||
err = gcache.Set(context.Background(), manifest.ProxyNetworkCacheKey, proxyNetwork, 10*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetXuiSession
|
||||
//
|
||||
// @dc: 获取Xui登陆session
|
||||
// @author: laixin @date:2023/4/2 20:06:21
|
||||
func (u *uProxyNetwork) GetXuiSession() (sessionMap map[string]string, err error) {
|
||||
var (
|
||||
url = manifest.XuiBaseUrl + "/login"
|
||||
)
|
||||
post, err := g.Client().Post(context.Background(), url, manifest.XuiLoginDataMap)
|
||||
defer func(post *gclient.Response) {
|
||||
err := post.Close()
|
||||
if err != nil {
|
||||
glog.Warning(context.Background(), err)
|
||||
}
|
||||
}(post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if post.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("登录失败")
|
||||
}
|
||||
if post.Header.Get("Set-Cookie") == "" {
|
||||
return nil, fmt.Errorf("获取Cookie失败")
|
||||
}
|
||||
// 将session存入缓存
|
||||
err = gcache.Set(context.Background(), manifest.ProxySessionCacheKey, post.GetCookieMap(), 15*time.Minute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return post.GetCookieMap(), nil
|
||||
}
|
143
utility/redis_test.go
Normal file
143
utility/redis_test.go
Normal file
@ -0,0 +1,143 @@
|
||||
package utility
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||
"github.com/gogf/gf/v2/database/gredis"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/google/uuid"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrLockFailed 加锁失败
|
||||
ErrLockFailed = errors.New("尝试加锁失败")
|
||||
// ErrTimeout 加锁超时
|
||||
ErrTimeout = errors.New("timeout")
|
||||
)
|
||||
|
||||
var (
|
||||
config = gredis.Config{
|
||||
Address: "120.24.211.49:6379",
|
||||
Db: 1,
|
||||
Pass: "deny1963",
|
||||
}
|
||||
group = "cache"
|
||||
ctx = gctx.New()
|
||||
)
|
||||
|
||||
type Locker struct {
|
||||
redisClient *gredis.Redis
|
||||
ttl int64
|
||||
tryInterval int
|
||||
}
|
||||
|
||||
func NewLocker(redisClient *gredis.Redis, ttl int64, tryInterval int) *Locker {
|
||||
return &Locker{redisClient: redisClient, ttl: ttl, tryInterval: tryInterval}
|
||||
}
|
||||
|
||||
type Lock struct {
|
||||
redisClient *gredis.Redis
|
||||
resource string
|
||||
uuid string
|
||||
ttl int64
|
||||
tryInterval int
|
||||
}
|
||||
|
||||
func (l *Locker) GetLock(resource string) *Lock {
|
||||
return &Lock{
|
||||
redisClient: l.redisClient,
|
||||
resource: resource,
|
||||
uuid: uuid.NewString(),
|
||||
ttl: l.ttl,
|
||||
tryInterval: l.tryInterval,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (l *Lock) TryLock(ctx context.Context) error {
|
||||
lockStatus, err := l.redisClient.SetNX(ctx, l.resource, l.uuid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lockStatus {
|
||||
err := l.redisClient.SetEX(ctx, l.resource, l.uuid, l.ttl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return ErrLockFailed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Lock) UnLock(ctx context.Context) error {
|
||||
lockStatus, err := l.redisClient.Get(ctx, l.resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lockStatus.String() != l.uuid {
|
||||
return ErrLockFailed
|
||||
}
|
||||
_, err = l.redisClient.Del(ctx, l.resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Lock) Lock(ctx context.Context) error {
|
||||
err := l.TryLock(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if err != ErrLockFailed {
|
||||
return err
|
||||
}
|
||||
glog.Info(ctx, "尝试加锁失败,开始重试")
|
||||
// 尝试加锁失败,开始重试
|
||||
ticker := time.NewTicker(time.Duration(l.tryInterval) * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ErrTimeout
|
||||
case <-ticker.C:
|
||||
if err := l.TryLock(ctx); err != nil {
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 测试方法注释
|
||||
func Test12_57_15(t *testing.T) {
|
||||
gredis.SetConfig(&config, group)
|
||||
redisClient := g.Redis(group)
|
||||
locker := NewLocker(redisClient, 6, 200)
|
||||
lock := locker.GetLock("hamster")
|
||||
|
||||
err := lock.TryLock(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = lock.Lock(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log("加锁成功")
|
||||
err = lock.UnLock(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Log("解锁成功")
|
||||
}
|
73
utility/server_utils/nezha.go
Normal file
73
utility/server_utils/nezha.go
Normal file
@ -0,0 +1,73 @@
|
||||
package server_utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"home-network-watcher/manifest"
|
||||
"math"
|
||||
)
|
||||
|
||||
type uNezha struct{}
|
||||
|
||||
var Nezha = &uNezha{}
|
||||
|
||||
// GetAllServerInfo
|
||||
//
|
||||
// @dc: 获取所有服务器信息
|
||||
// @author: hamster @date:2023/9/18 14:37:42
|
||||
func (u *uNezha) GetAllServerInfo(ctx context.Context) (err error) {
|
||||
// 1. 获取所有服务器信息
|
||||
//TODO (hamster) 2023/10/3: 优化获取服务器列表序号的方式
|
||||
serverList := []int{1, 2, 3, 4}
|
||||
nezhaClient := g.Client().SetHeader("Authorization", manifest.NezhaApiKey)
|
||||
serverDataList := make([]g.Map, 0)
|
||||
for _, value := range serverList {
|
||||
nezhaApi := manifest.NezhaApiUrl + gconv.String(value)
|
||||
response, err := nezhaClient.Get(ctx, nezhaApi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 整理数据
|
||||
cacheJson := g.Map{}
|
||||
resJsonData := gjson.New(response.ReadAllString()).Get("result.0").Map()
|
||||
serverJsonData := gjson.New(resJsonData)
|
||||
cacheJson["id"] = serverJsonData.Get("id").Int()
|
||||
cacheJson["name"] = serverJsonData.Get("name").String()
|
||||
// CPU 占用率保留两位小数
|
||||
cpuValue := serverJsonData.Get("status.CPU").Float64()
|
||||
roundedValue := math.Round(cpuValue*100) / 100
|
||||
cacheJson["cpu"] = roundedValue
|
||||
// 内存占用率保留两位小数
|
||||
memoryValue := serverJsonData.Get("host.MemTotal").Float64()
|
||||
memoryUsedValue := serverJsonData.Get("status.MemUsed").Float64()
|
||||
memoryUsedPercent := math.Round(memoryUsedValue/memoryValue*10000) / 100
|
||||
cacheJson["memory"] = memoryUsedPercent
|
||||
// 磁盘占用率保留两位小数
|
||||
diskValue := serverJsonData.Get("host.DiskTotal").Float64()
|
||||
diskUsedValue := serverJsonData.Get("status.DiskUsed").Float64()
|
||||
diskUsedPercent := math.Round(diskUsedValue/diskValue*10000) / 100
|
||||
cacheJson["disk"] = diskUsedPercent
|
||||
// 在线时长,转换为天
|
||||
uptimeValue := serverJsonData.Get("status.Uptime").Float64()
|
||||
uptimeDay := math.Round(uptimeValue/86400*100) / 100
|
||||
cacheJson["uptime"] = uptimeDay
|
||||
serverDataList = append(serverDataList, cacheJson)
|
||||
_ = response.Close()
|
||||
// 在线状态,通过last_active判断
|
||||
lastActiveValue := serverJsonData.Get("last_active").Int64()
|
||||
if gtime.Now().Unix()-lastActiveValue > 300 {
|
||||
cacheJson["status"] = "offline"
|
||||
} else {
|
||||
cacheJson["status"] = "online"
|
||||
}
|
||||
}
|
||||
err = gcache.Set(ctx, manifest.ServerDataCacheKey, serverDataList, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
34
utility/server_utils/server_cron.go
Normal file
34
utility/server_utils/server_cron.go
Normal file
@ -0,0 +1,34 @@
|
||||
package server_utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"home-network-watcher/manifest"
|
||||
"home-network-watcher/utility/docker_utils"
|
||||
"home-network-watcher/utility/uptime_utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
type uServerCron struct{}
|
||||
|
||||
var ServerCron = &uServerCron{}
|
||||
|
||||
// CronGetDockerAndMonitor
|
||||
//
|
||||
// @dc: 获取 Docker 状态与监控信息
|
||||
// @author: hamster @date:2023/9/20 16:20:11
|
||||
func (u *uServerCron) CronGetDockerAndMonitor(ctx context.Context) (err error) {
|
||||
dockerStatus, _ := docker_utils.DockerUtils.GetDockerStatus(ctx)
|
||||
serverCount, errorCount, _ := uptime_utils.Kuma.GetMonitorStatus(ctx)
|
||||
// 缓存数据
|
||||
dockerMonitor := map[string]interface{}{
|
||||
"dockerStatus": dockerStatus,
|
||||
"serverCount": serverCount,
|
||||
"errorCount": errorCount,
|
||||
}
|
||||
err = gcache.Set(ctx, manifest.DockerMonitorCacheKey, dockerMonitor, 1*time.Hour)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
114
utility/uptime_utils/kuma.go
Normal file
114
utility/uptime_utils/kuma.go
Normal file
@ -0,0 +1,114 @@
|
||||
package uptime_utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gclient"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"home-network-watcher/manifest"
|
||||
"time"
|
||||
)
|
||||
|
||||
type uKuma struct{}
|
||||
|
||||
var Kuma = &uKuma{}
|
||||
|
||||
type UptimeInfo struct {
|
||||
Status bool `json:"status"`
|
||||
Ping int `json:"ping"`
|
||||
Uptime string `json:"uptime"`
|
||||
}
|
||||
|
||||
// GetMonitorStatus
|
||||
//
|
||||
// @dc: 获取监控状态
|
||||
// @author: hamster @date:2023/9/20 14:43:18
|
||||
func (u *uKuma) GetMonitorStatus(ctx context.Context) (serverCount int, errorServer int, err error) {
|
||||
response, err := g.Client().Get(ctx, manifest.UptimeKumaApiUrl)
|
||||
defer func(response *gclient.Response) {
|
||||
err := response.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(response)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
// 获取返还数据
|
||||
heartBeatJson := gjson.New(response.ReadAllString()).Get("heartbeatList").Map()
|
||||
serverCount = len(heartBeatJson)
|
||||
for _, value := range heartBeatJson {
|
||||
pingList := gconv.SliceAny(value)
|
||||
if gjson.New(pingList[len(pingList)-1]).Get("status").Int() != 1 {
|
||||
errorServer++
|
||||
}
|
||||
}
|
||||
return serverCount, errorServer, nil
|
||||
}
|
||||
|
||||
// GetUptimeData
|
||||
//
|
||||
// @dc: 获取核心服务监控数据
|
||||
// @author: hamster @date:2023/9/21 11:21:13
|
||||
func (u *uKuma) GetUptimeData(ctx context.Context) (err error) {
|
||||
serviceMap := g.Map{
|
||||
"xui": 2,
|
||||
"v2raya": 9,
|
||||
"proxy": 8,
|
||||
"nginx": 5,
|
||||
"home": 7,
|
||||
"netflix": 15}
|
||||
response, err := g.Client().Get(ctx, manifest.UptimeKumaApiUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(response *gclient.Response) {
|
||||
err := response.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(response)
|
||||
// 获取返还数据,获取最新状态与 24 小时状态
|
||||
heartBeatJson := gjson.New(response.ReadAllString())
|
||||
for key, value := range serviceMap {
|
||||
serviceUptime := &UptimeInfo{}
|
||||
statusList := heartBeatJson.Get("heartbeatList" + "." + gconv.String(value)).Array()
|
||||
if len(statusList) == 0 {
|
||||
serviceUptime.Status = false
|
||||
serviceUptime.Ping = 0
|
||||
serviceUptime.Uptime = "0.00"
|
||||
serviceMap[key] = gconv.Map(serviceUptime)
|
||||
continue
|
||||
}
|
||||
// 获取最后一条数据
|
||||
lastHeartBeat := statusList[len(statusList)-1]
|
||||
if gconv.Int(gconv.Map(lastHeartBeat)["status"]) == 1 {
|
||||
serviceUptime.Status = true
|
||||
} else {
|
||||
serviceUptime.Status = false
|
||||
}
|
||||
serviceUptime.Ping = gconv.Int(gconv.Map(lastHeartBeat)["ping"])
|
||||
// 获取24小时可用率
|
||||
uptimeList := heartBeatJson.Get("uptimeList" + "." + gconv.String(value) + "_24").Float64()
|
||||
switch uptimeList {
|
||||
case 0:
|
||||
serviceUptime.Uptime = "0.00"
|
||||
case 1:
|
||||
serviceUptime.Uptime = "100.0"
|
||||
default:
|
||||
// 转换为2位小数百分比
|
||||
serviceUptime.Uptime = gconv.String(uptimeList * 100)
|
||||
if len(serviceUptime.Uptime) > 4 {
|
||||
serviceUptime.Uptime = serviceUptime.Uptime[:4]
|
||||
}
|
||||
}
|
||||
serviceMap[key] = gconv.Map(serviceUptime)
|
||||
}
|
||||
err = gcache.Set(ctx, manifest.UptimeCacheKey, serviceMap, 1*time.Hour)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user