diff options
| author | MHSanaei <mc.sanaei@gmail.com> | 2023-02-09 22:18:06 +0300 |
|---|---|---|
| committer | MHSanaei <mc.sanaei@gmail.com> | 2023-02-09 22:18:06 +0300 |
| commit | b73e4173a3c1e69e02ad6b4e3b43e425e57a5be9 (patch) | |
| tree | d95d2f5e903d97082e11eb9f9023c165b1bde388 /web/service/server.go | |
3x-ui
Diffstat (limited to 'web/service/server.go')
| -rw-r--r-- | web/service/server.go | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/web/service/server.go b/web/service/server.go new file mode 100644 index 00000000..efd985e6 --- /dev/null +++ b/web/service/server.go @@ -0,0 +1,302 @@ +package service + +import ( + "archive/zip" + "bytes" + "encoding/json" + "fmt" + "io" + "io/fs" + "net/http" + "os" + "runtime" + "time" + "x-ui/logger" + "x-ui/util/sys" + "x-ui/xray" + + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/disk" + "github.com/shirou/gopsutil/host" + "github.com/shirou/gopsutil/load" + "github.com/shirou/gopsutil/mem" + "github.com/shirou/gopsutil/net" +) + +type ProcessState string + +const ( + Running ProcessState = "running" + Stop ProcessState = "stop" + Error ProcessState = "error" +) + +type Status struct { + T time.Time `json:"-"` + Cpu float64 `json:"cpu"` + Mem struct { + Current uint64 `json:"current"` + Total uint64 `json:"total"` + } `json:"mem"` + Swap struct { + Current uint64 `json:"current"` + Total uint64 `json:"total"` + } `json:"swap"` + Disk struct { + Current uint64 `json:"current"` + Total uint64 `json:"total"` + } `json:"disk"` + Xray struct { + State ProcessState `json:"state"` + ErrorMsg string `json:"errorMsg"` + Version string `json:"version"` + } `json:"xray"` + Uptime uint64 `json:"uptime"` + Loads []float64 `json:"loads"` + TcpCount int `json:"tcpCount"` + UdpCount int `json:"udpCount"` + NetIO struct { + Up uint64 `json:"up"` + Down uint64 `json:"down"` + } `json:"netIO"` + NetTraffic struct { + Sent uint64 `json:"sent"` + Recv uint64 `json:"recv"` + } `json:"netTraffic"` +} + +type Release struct { + TagName string `json:"tag_name"` +} + +type ServerService struct { + xrayService XrayService +} + +func (s *ServerService) GetStatus(lastStatus *Status) *Status { + now := time.Now() + status := &Status{ + T: now, + } + + percents, err := cpu.Percent(0, false) + if err != nil { + logger.Warning("get cpu percent failed:", err) + } else { + status.Cpu = percents[0] + } + + upTime, err := host.Uptime() + if err != nil { + logger.Warning("get uptime failed:", err) + } else { + status.Uptime = upTime + } + + memInfo, err := mem.VirtualMemory() + if err != nil { + logger.Warning("get virtual memory failed:", err) + } else { + status.Mem.Current = memInfo.Used + status.Mem.Total = memInfo.Total + } + + swapInfo, err := mem.SwapMemory() + if err != nil { + logger.Warning("get swap memory failed:", err) + } else { + status.Swap.Current = swapInfo.Used + status.Swap.Total = swapInfo.Total + } + + distInfo, err := disk.Usage("/") + if err != nil { + logger.Warning("get dist usage failed:", err) + } else { + status.Disk.Current = distInfo.Used + status.Disk.Total = distInfo.Total + } + + avgState, err := load.Avg() + if err != nil { + logger.Warning("get load avg failed:", err) + } else { + status.Loads = []float64{avgState.Load1, avgState.Load5, avgState.Load15} + } + + ioStats, err := net.IOCounters(false) + if err != nil { + logger.Warning("get io counters failed:", err) + } else if len(ioStats) > 0 { + ioStat := ioStats[0] + status.NetTraffic.Sent = ioStat.BytesSent + status.NetTraffic.Recv = ioStat.BytesRecv + + if lastStatus != nil { + duration := now.Sub(lastStatus.T) + seconds := float64(duration) / float64(time.Second) + up := uint64(float64(status.NetTraffic.Sent-lastStatus.NetTraffic.Sent) / seconds) + down := uint64(float64(status.NetTraffic.Recv-lastStatus.NetTraffic.Recv) / seconds) + status.NetIO.Up = up + status.NetIO.Down = down + } + } else { + logger.Warning("can not find io counters") + } + + status.TcpCount, err = sys.GetTCPCount() + if err != nil { + logger.Warning("get tcp connections failed:", err) + } + + status.UdpCount, err = sys.GetUDPCount() + if err != nil { + logger.Warning("get udp connections failed:", err) + } + + if s.xrayService.IsXrayRunning() { + status.Xray.State = Running + status.Xray.ErrorMsg = "" + } else { + err := s.xrayService.GetXrayErr() + if err != nil { + status.Xray.State = Error + } else { + status.Xray.State = Stop + } + status.Xray.ErrorMsg = s.xrayService.GetXrayResult() + } + status.Xray.Version = s.xrayService.GetXrayVersion() + + return status +} + +func (s *ServerService) GetXrayVersions() ([]string, error) { + url := "https://api.github.com/repos/XTLS/Xray-core/releases" + resp, err := http.Get(url) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + buffer := bytes.NewBuffer(make([]byte, 8192)) + buffer.Reset() + _, err = buffer.ReadFrom(resp.Body) + if err != nil { + return nil, err + } + + releases := make([]Release, 0) + err = json.Unmarshal(buffer.Bytes(), &releases) + if err != nil { + return nil, err + } + versions := make([]string, 0, len(releases)) + for _, release := range releases { + versions = append(versions, release.TagName) + } + return versions, nil +} + +func (s *ServerService) downloadXRay(version string) (string, error) { + osName := runtime.GOOS + arch := runtime.GOARCH + + switch osName { + case "darwin": + osName = "macos" + } + + switch arch { + case "amd64": + arch = "64" + case "arm64": + arch = "arm64-v8a" + } + + fileName := fmt.Sprintf("Xray-%s-%s.zip", osName, arch) + url := fmt.Sprintf("https://github.com/XTLS/Xray-core/releases/download/%s/%s", version, fileName) + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() + + os.Remove(fileName) + file, err := os.Create(fileName) + if err != nil { + return "", err + } + defer file.Close() + + _, err = io.Copy(file, resp.Body) + if err != nil { + return "", err + } + + return fileName, nil +} + +func (s *ServerService) UpdateXray(version string) error { + zipFileName, err := s.downloadXRay(version) + if err != nil { + return err + } + + zipFile, err := os.Open(zipFileName) + if err != nil { + return err + } + defer func() { + zipFile.Close() + os.Remove(zipFileName) + }() + + stat, err := zipFile.Stat() + if err != nil { + return err + } + reader, err := zip.NewReader(zipFile, stat.Size()) + if err != nil { + return err + } + + s.xrayService.StopXray() + defer func() { + err := s.xrayService.RestartXray(true) + if err != nil { + logger.Error("start xray failed:", err) + } + }() + + copyZipFile := func(zipName string, fileName string) error { + zipFile, err := reader.Open(zipName) + if err != nil { + return err + } + os.Remove(fileName) + file, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_TRUNC, fs.ModePerm) + if err != nil { + return err + } + defer file.Close() + _, err = io.Copy(file, zipFile) + return err + } + + err = copyZipFile("xray", xray.GetBinaryPath()) + if err != nil { + return err + } + err = copyZipFile("geosite.dat", xray.GetGeositePath()) + if err != nil { + return err + } + err = copyZipFile("geoip.dat", xray.GetGeoipPath()) + if err != nil { + return err + } + + return nil + +} |
