mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-07-01 20:42:07 +00:00
feature / 12
12 / log xray api port 12 / getXuiLatestVersion 12 / caching
This commit is contained in:
parent
8aabe1b049
commit
377a6b6786
7 changed files with 168 additions and 5 deletions
52
caching/caching.go
Normal file
52
caching/caching.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package caching
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
memoryCache *cache.Cache
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCache() *Cache {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
return &Cache{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Cache) Init() (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
s.Flush()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s.memoryCache = cache.New(10*time.Minute, 10*time.Minute)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Cache) Flush() error {
|
||||||
|
if s.memoryCache != nil {
|
||||||
|
s.memoryCache.Flush()
|
||||||
|
}
|
||||||
|
s.cancel()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Cache) GetCtx() context.Context {
|
||||||
|
return s.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Cache) Memory() *cache.Cache {
|
||||||
|
return s.memoryCache
|
||||||
|
}
|
1
go.mod
1
go.mod
|
@ -59,6 +59,7 @@ require (
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.22.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.22.0 // indirect
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/pires/go-proxyproto v0.8.0 // indirect
|
github.com/pires/go-proxyproto v0.8.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -119,6 +119,8 @@ github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||||
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
|
|
28
main.go
28
main.go
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
"x-ui/database"
|
"x-ui/database"
|
||||||
|
"x-ui/caching"
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
"x-ui/sub"
|
"x-ui/sub"
|
||||||
"x-ui/web"
|
"x-ui/web"
|
||||||
|
@ -61,6 +62,17 @@ func runWebServer() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cacheInstance *caching.Cache
|
||||||
|
cacheInstance = caching.NewCache()
|
||||||
|
global.SetCache(cacheInstance)
|
||||||
|
err = cacheInstance.Init()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Cache initialization error: %v", err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
log.Println("Cache initialized")
|
||||||
|
}
|
||||||
|
|
||||||
sigCh := make(chan os.Signal, 1)
|
sigCh := make(chan os.Signal, 1)
|
||||||
// Trap shutdown signals
|
// Trap shutdown signals
|
||||||
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM)
|
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM)
|
||||||
|
@ -79,6 +91,10 @@ func runWebServer() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug("Error stopping sub server:", err)
|
logger.Debug("Error stopping sub server:", err)
|
||||||
}
|
}
|
||||||
|
err = cacheInstance.Flush()
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug("Error clearing cache:", err)
|
||||||
|
}
|
||||||
|
|
||||||
server = web.NewServer()
|
server = web.NewServer()
|
||||||
global.SetWebServer(server)
|
global.SetWebServer(server)
|
||||||
|
@ -98,10 +114,20 @@ func runWebServer() {
|
||||||
}
|
}
|
||||||
log.Println("Sub server restarted successfully.")
|
log.Println("Sub server restarted successfully.")
|
||||||
|
|
||||||
|
cacheInstance = caching.NewCache()
|
||||||
|
global.SetCache(cacheInstance)
|
||||||
|
err = cacheInstance.Init()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Cache re-initialization error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Cache cleared.")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
server.Stop()
|
server.Stop()
|
||||||
subServer.Stop()
|
subServer.Stop()
|
||||||
log.Println("Shutting down servers.")
|
cacheInstance.Flush()
|
||||||
|
log.Println("Shutting down servers and cache clearing.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,13 @@ import (
|
||||||
_ "unsafe"
|
_ "unsafe"
|
||||||
|
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
webServer WebServer
|
webServer WebServer
|
||||||
subServer SubServer
|
subServer SubServer
|
||||||
|
caching Cache
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebServer interface {
|
type WebServer interface {
|
||||||
|
@ -21,6 +23,11 @@ type SubServer interface {
|
||||||
GetCtx() context.Context
|
GetCtx() context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Cache interface {
|
||||||
|
Memory() *cache.Cache
|
||||||
|
GetCtx() context.Context
|
||||||
|
}
|
||||||
|
|
||||||
func SetWebServer(s WebServer) {
|
func SetWebServer(s WebServer) {
|
||||||
webServer = s
|
webServer = s
|
||||||
}
|
}
|
||||||
|
@ -36,3 +43,11 @@ func SetSubServer(s SubServer) {
|
||||||
func GetSubServer() SubServer {
|
func GetSubServer() SubServer {
|
||||||
return subServer
|
return subServer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetCache(c Cache) {
|
||||||
|
caching = c
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCache() Cache {
|
||||||
|
return caching
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
"x-ui/database"
|
"x-ui/database"
|
||||||
|
@ -22,6 +23,7 @@ import (
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
"x-ui/util/sys"
|
"x-ui/util/sys"
|
||||||
"x-ui/xray"
|
"x-ui/xray"
|
||||||
|
"x-ui/web/global"
|
||||||
|
|
||||||
"github.com/shirou/gopsutil/v4/cpu"
|
"github.com/shirou/gopsutil/v4/cpu"
|
||||||
"github.com/shirou/gopsutil/v4/disk"
|
"github.com/shirou/gopsutil/v4/disk"
|
||||||
|
@ -61,8 +63,10 @@ type Status struct {
|
||||||
State ProcessState `json:"state"`
|
State ProcessState `json:"state"`
|
||||||
ErrorMsg string `json:"errorMsg"`
|
ErrorMsg string `json:"errorMsg"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
ApiPort string `json:"apiPort"`
|
|
||||||
} `json:"xray"`
|
} `json:"xray"`
|
||||||
|
XUI struct {
|
||||||
|
LatestVersion string `json:"latestVersion"`
|
||||||
|
} `json:"xui"`
|
||||||
Uptime uint64 `json:"uptime"`
|
Uptime uint64 `json:"uptime"`
|
||||||
Loads []float64 `json:"loads"`
|
Loads []float64 `json:"loads"`
|
||||||
TcpCount int `json:"tcpCount"`
|
TcpCount int `json:"tcpCount"`
|
||||||
|
@ -95,6 +99,14 @@ type ServerService struct {
|
||||||
inboundService InboundService
|
inboundService InboundService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractValue(body string, key string) string {
|
||||||
|
keystr := "\"" + key + "\":[^,;\\]}]*"
|
||||||
|
r, _ := regexp.Compile(keystr)
|
||||||
|
match := r.FindString(body)
|
||||||
|
keyValMatch := strings.Split(match, ":")
|
||||||
|
return strings.TrimSpace(strings.ReplaceAll(keyValMatch[1], "\"", ""))
|
||||||
|
}
|
||||||
|
|
||||||
func getPublicIP(url string) string {
|
func getPublicIP(url string) string {
|
||||||
var host string
|
var host string
|
||||||
host = os.Getenv("XUI_SERVER_IP")
|
host = os.Getenv("XUI_SERVER_IP")
|
||||||
|
@ -121,7 +133,37 @@ func getPublicIP(url string) string {
|
||||||
return ipString
|
return ipString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getXuiLatestVersion() string {
|
||||||
|
cache := global.GetCache().Memory()
|
||||||
|
if data, found := cache.Get("xui_latest_tag_name"); found {
|
||||||
|
if tag, ok := data.(string); ok {
|
||||||
|
return string(tag)
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
url := "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest"
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
json, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := extractValue(string(json), "tag_name")
|
||||||
|
cache.Set("xui_latest_tag_name", tag, 60*time.Minute)
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
||||||
|
cache := global.GetCache().Memory()
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
status := &Status{
|
status := &Status{
|
||||||
T: now,
|
T: now,
|
||||||
|
@ -224,8 +266,27 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
||||||
logger.Warning("get udp connections failed:", err)
|
logger.Warning("get udp connections failed:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if data, found := cache.Get("xui_public_ipv4"); found {
|
||||||
|
if ipv4, ok := data.(string); ok {
|
||||||
|
status.PublicIP.IPv4 = string(ipv4)
|
||||||
|
} else {
|
||||||
|
status.PublicIP.IPv4 = "N/A"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
status.PublicIP.IPv4 = getPublicIP("https://api.ipify.org")
|
status.PublicIP.IPv4 = getPublicIP("https://api.ipify.org")
|
||||||
|
cache.Set("xui_public_ipv4", status.PublicIP.IPv4, 720*time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data, found := cache.Get("xui_public_ipv6"); found {
|
||||||
|
if ipv6, ok := data.(string); ok {
|
||||||
|
status.PublicIP.IPv6 = string(ipv6)
|
||||||
|
} else {
|
||||||
|
status.PublicIP.IPv6 = "N/A"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
status.PublicIP.IPv6 = getPublicIP("https://api6.ipify.org")
|
status.PublicIP.IPv6 = getPublicIP("https://api6.ipify.org")
|
||||||
|
cache.Set("xui_public_ipv6", status.PublicIP.IPv6, 720*time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
if s.xrayService.IsXrayRunning() {
|
if s.xrayService.IsXrayRunning() {
|
||||||
status.Xray.State = Running
|
status.Xray.State = Running
|
||||||
|
@ -240,7 +301,9 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
||||||
status.Xray.ErrorMsg = s.xrayService.GetXrayResult()
|
status.Xray.ErrorMsg = s.xrayService.GetXrayResult()
|
||||||
}
|
}
|
||||||
status.Xray.Version = s.xrayService.GetXrayVersion()
|
status.Xray.Version = s.xrayService.GetXrayVersion()
|
||||||
status.Xray.ApiPort = s.xrayService.GetXrayApiPort()
|
|
||||||
|
status.XUI.LatestVersion = getXuiLatestVersion()
|
||||||
|
|
||||||
var rtm runtime.MemStats
|
var rtm runtime.MemStats
|
||||||
runtime.ReadMemStats(&rtm)
|
runtime.ReadMemStats(&rtm)
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,10 @@ func (s *XrayService) RestartXray(isForce bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if isForce {
|
||||||
|
logger.Debug("Xray Api Port: ", strconv.Itoa(p.GetAPIPort()))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue