| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | package service | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"archive/zip" | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/fs" | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	"mime/multipart" | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2023-03-24 13:44:26 +00:00
										 |  |  | 	"os/exec" | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2023-07-31 16:41:47 +00:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2023-03-24 13:44:26 +00:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2023-07-01 12:26:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-11 12:11:04 +00:00
										 |  |  | 	"x-ui/config" | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	"x-ui/database" | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	"x-ui/logger" | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	"x-ui/util/common" | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	"x-ui/util/sys" | 
					
						
							|  |  |  | 	"x-ui/xray" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 19:48:49 +00:00
										 |  |  | 	"github.com/shirou/gopsutil/v4/cpu" | 
					
						
							|  |  |  | 	"github.com/shirou/gopsutil/v4/disk" | 
					
						
							|  |  |  | 	"github.com/shirou/gopsutil/v4/host" | 
					
						
							|  |  |  | 	"github.com/shirou/gopsutil/v4/load" | 
					
						
							|  |  |  | 	"github.com/shirou/gopsutil/v4/mem" | 
					
						
							|  |  |  | 	"github.com/shirou/gopsutil/v4/net" | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ProcessState string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	Running ProcessState = "running" | 
					
						
							|  |  |  | 	Stop    ProcessState = "stop" | 
					
						
							|  |  |  | 	Error   ProcessState = "error" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Status struct { | 
					
						
							| 
									
										
										
										
											2023-05-25 12:18:23 +00:00
										 |  |  | 	T           time.Time `json:"-"` | 
					
						
							|  |  |  | 	Cpu         float64   `json:"cpu"` | 
					
						
							|  |  |  | 	CpuCores    int       `json:"cpuCores"` | 
					
						
							| 
									
										
										
										
											2024-05-28 13:11:46 +00:00
										 |  |  | 	LogicalPro  int       `json:"logicalPro"` | 
					
						
							| 
									
										
										
										
											2023-05-25 12:18:23 +00:00
										 |  |  | 	CpuSpeedMhz float64   `json:"cpuSpeedMhz"` | 
					
						
							|  |  |  | 	Mem         struct { | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 		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"` | 
					
						
							| 
									
										
										
										
											2023-05-24 23:41:09 +00:00
										 |  |  | 	PublicIP struct { | 
					
						
							|  |  |  | 		IPv4 string `json:"ipv4"` | 
					
						
							|  |  |  | 		IPv6 string `json:"ipv6"` | 
					
						
							|  |  |  | 	} `json:"publicIP"` | 
					
						
							| 
									
										
										
										
											2023-08-08 21:07:05 +00:00
										 |  |  | 	AppStats struct { | 
					
						
							|  |  |  | 		Threads uint32 `json:"threads"` | 
					
						
							|  |  |  | 		Mem     uint64 `json:"mem"` | 
					
						
							|  |  |  | 		Uptime  uint64 `json:"uptime"` | 
					
						
							|  |  |  | 	} `json:"appStats"` | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Release struct { | 
					
						
							|  |  |  | 	TagName string `json:"tag_name"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ServerService struct { | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	xrayService    XrayService | 
					
						
							|  |  |  | 	inboundService InboundService | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-24 23:41:09 +00:00
										 |  |  | func getPublicIP(url string) string { | 
					
						
							|  |  |  | 	resp, err := http.Get(url) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "N/A" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer resp.Body.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ip, err := io.ReadAll(resp.Body) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "N/A" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 16:20:19 +00:00
										 |  |  | 	ipString := string(ip) | 
					
						
							|  |  |  | 	if ipString == "" { | 
					
						
							|  |  |  | 		return "N/A" | 
					
						
							| 
									
										
										
										
											2023-05-24 23:41:09 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-14 16:20:19 +00:00
										 |  |  | 	return ipString | 
					
						
							| 
									
										
										
										
											2023-05-24 23:41:09 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 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] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-24 23:41:09 +00:00
										 |  |  | 	status.CpuCores, err = cpu.Counts(false) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.Warning("get cpu cores count failed:", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-28 13:11:46 +00:00
										 |  |  | 	status.LogicalPro = runtime.NumCPU() | 
					
						
							|  |  |  | 	if p != nil && p.IsRunning() { | 
					
						
							|  |  |  | 		status.AppStats.Uptime = p.GetUptime() | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		status.AppStats.Uptime = 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 12:18:23 +00:00
										 |  |  | 	cpuInfos, err := cpu.Info() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.Warning("get cpu info failed:", err) | 
					
						
							|  |  |  | 	} else if len(cpuInfos) > 0 { | 
					
						
							|  |  |  | 		cpuInfo := cpuInfos[0] | 
					
						
							|  |  |  | 		status.CpuSpeedMhz = cpuInfo.Mhz // setting CPU speed in MHz
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		logger.Warning("could not find cpu info") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-05-24 23:41:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	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") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-03-17 16:07:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	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) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-03-17 16:07:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-24 23:41:09 +00:00
										 |  |  | 	status.PublicIP.IPv4 = getPublicIP("https://api.ipify.org") | 
					
						
							|  |  |  | 	status.PublicIP.IPv6 = getPublicIP("https://api6.ipify.org") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	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() | 
					
						
							| 
									
										
										
										
											2023-08-08 21:07:05 +00:00
										 |  |  | 	var rtm runtime.MemStats | 
					
						
							|  |  |  | 	runtime.ReadMemStats(&rtm) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status.AppStats.Mem = rtm.Sys | 
					
						
							|  |  |  | 	status.AppStats.Threads = uint32(runtime.NumGoroutine()) | 
					
						
							| 
									
										
										
										
											2023-12-04 18:13:21 +00:00
										 |  |  | 	if p != nil && p.IsRunning() { | 
					
						
							| 
									
										
										
										
											2023-08-08 21:07:05 +00:00
										 |  |  | 		status.AppStats.Uptime = p.GetUptime() | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		status.AppStats.Uptime = 0 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return status | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *ServerService) GetXrayVersions() ([]string, error) { | 
					
						
							| 
									
										
										
										
											2024-10-29 12:21:14 +00:00
										 |  |  | 	const ( | 
					
						
							|  |  |  | 		XrayURL    = "https://api.github.com/repos/XTLS/Xray-core/releases" | 
					
						
							|  |  |  | 		bufferSize = 8192 | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resp, err := http.Get(XrayURL) | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer resp.Body.Close() | 
					
						
							| 
									
										
										
										
											2024-10-29 12:21:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	buffer := bytes.NewBuffer(make([]byte, bufferSize)) | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	buffer.Reset() | 
					
						
							| 
									
										
										
										
											2024-10-29 12:21:14 +00:00
										 |  |  | 	if _, err := buffer.ReadFrom(resp.Body); err != nil { | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-29 12:21:14 +00:00
										 |  |  | 	var releases []Release | 
					
						
							|  |  |  | 	if err := json.Unmarshal(buffer.Bytes(), &releases); err != nil { | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-10-29 12:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-27 07:36:27 +00:00
										 |  |  | 	var versions []string | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	for _, release := range releases { | 
					
						
							| 
									
										
										
										
											2024-10-29 12:21:14 +00:00
										 |  |  | 		tagVersion := strings.TrimPrefix(release.TagName, "v") | 
					
						
							|  |  |  | 		tagParts := strings.Split(tagVersion, ".") | 
					
						
							|  |  |  | 		if len(tagParts) != 3 { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		major, err1 := strconv.Atoi(tagParts[0]) | 
					
						
							|  |  |  | 		minor, err2 := strconv.Atoi(tagParts[1]) | 
					
						
							|  |  |  | 		patch, err3 := strconv.Atoi(tagParts[2]) | 
					
						
							|  |  |  | 		if err1 != nil || err2 != nil || err3 != nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (major == 1 && minor == 8 && patch == 24) || | 
					
						
							| 
									
										
										
										
											2025-01-01 17:36:46 +00:00
										 |  |  | 			(major >= 25) { | 
					
						
							| 
									
										
										
										
											2023-07-27 07:36:27 +00:00
										 |  |  | 			versions = append(versions, release.TagName) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return versions, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-16 22:01:14 +00:00
										 |  |  | func (s *ServerService) StopXrayService() (string error) { | 
					
						
							| 
									
										
										
										
											2023-03-24 13:44:26 +00:00
										 |  |  | 	err := s.xrayService.StopXray() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.Error("stop xray failed:", err) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-03-16 22:01:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *ServerService) RestartXrayService() (string error) { | 
					
						
							| 
									
										
										
										
											2023-03-24 13:44:26 +00:00
										 |  |  | 	s.xrayService.StopXray() | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		err := s.xrayService.RestartXray(true) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logger.Error("start xray failed:", err) | 
					
						
							| 
									
										
										
										
											2023-03-16 22:01:14 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-24 13:44:26 +00:00
										 |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2023-03-16 22:01:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 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" | 
					
						
							| 
									
										
										
										
											2024-07-08 16:19:40 +00:00
										 |  |  | 	case "armv7": | 
					
						
							|  |  |  | 		arch = "arm32-v7a" | 
					
						
							|  |  |  | 	case "armv6": | 
					
						
							|  |  |  | 		arch = "arm32-v6" | 
					
						
							|  |  |  | 	case "armv5": | 
					
						
							|  |  |  | 		arch = "arm32-v5" | 
					
						
							|  |  |  | 	case "386": | 
					
						
							|  |  |  | 		arch = "32" | 
					
						
							|  |  |  | 	case "s390x": | 
					
						
							|  |  |  | 		arch = "s390x" | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fileName := fmt.Sprintf("Xray-%s-%s.zip", osName, arch) | 
					
						
							| 
									
										
										
										
											2023-07-27 07:36:27 +00:00
										 |  |  | 	url := fmt.Sprintf("https://github.com/XTLS/Xray-core/releases/download/%s/%s", version, fileName) | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-18 09:52:07 +00:00
										 |  |  | 	err = copyZipFile("xray", xray.GetBinaryPath()) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-02-09 19:18:06 +00:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-03-24 13:44:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 16:41:47 +00:00
										 |  |  | func (s *ServerService) GetLogs(count string, level string, syslog string) []string { | 
					
						
							|  |  |  | 	c, _ := strconv.Atoi(count) | 
					
						
							|  |  |  | 	var lines []string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if syslog == "true" { | 
					
						
							|  |  |  | 		cmdArgs := []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", count, "-p", level} | 
					
						
							|  |  |  | 		// Run the command
 | 
					
						
							|  |  |  | 		cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) | 
					
						
							|  |  |  | 		var out bytes.Buffer | 
					
						
							|  |  |  | 		cmd.Stdout = &out | 
					
						
							|  |  |  | 		err := cmd.Run() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return []string{"Failed to run journalctl command!"} | 
					
						
							| 
									
										
										
										
											2023-06-16 14:55:33 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-07-31 16:41:47 +00:00
										 |  |  | 		lines = strings.Split(out.String(), "\n") | 
					
						
							| 
									
										
										
										
											2023-03-24 13:44:26 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2023-07-31 16:41:47 +00:00
										 |  |  | 		lines = logger.GetLogs(c, level) | 
					
						
							| 
									
										
										
										
											2023-03-24 13:44:26 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 16:41:47 +00:00
										 |  |  | 	return lines | 
					
						
							| 
									
										
										
										
											2023-03-24 13:44:26 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-04-11 12:11:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (s *ServerService) GetConfigJson() (interface{}, error) { | 
					
						
							| 
									
										
										
										
											2023-07-17 23:10:22 +00:00
										 |  |  | 	config, err := s.xrayService.GetXrayConfig() | 
					
						
							| 
									
										
										
										
											2023-04-11 12:11:04 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-07-17 23:10:22 +00:00
										 |  |  | 	contents, err := json.MarshalIndent(config, "", "  ") | 
					
						
							| 
									
										
										
										
											2023-04-11 12:11:04 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var jsonData interface{} | 
					
						
							| 
									
										
										
										
											2023-07-17 23:10:22 +00:00
										 |  |  | 	err = json.Unmarshal(contents, &jsonData) | 
					
						
							| 
									
										
										
										
											2023-04-11 12:11:04 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-07-27 07:36:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-11 12:11:04 +00:00
										 |  |  | 	return jsonData, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *ServerService) GetDb() ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2023-12-08 19:35:10 +00:00
										 |  |  | 	// Update by manually trigger a checkpoint operation
 | 
					
						
							|  |  |  | 	err := database.Checkpoint() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-11 12:11:04 +00:00
										 |  |  | 	// Open the file for reading
 | 
					
						
							|  |  |  | 	file, err := os.Open(config.GetDBPath()) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer file.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Read the file contents
 | 
					
						
							|  |  |  | 	fileContents, err := io.ReadAll(file) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fileContents, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-04-18 18:04:06 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | func (s *ServerService) ImportDB(file multipart.File) error { | 
					
						
							|  |  |  | 	// Check if the file is a SQLite database
 | 
					
						
							|  |  |  | 	isValidDb, err := database.IsSQLiteDB(file) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return common.NewErrorf("Error checking db file format: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !isValidDb { | 
					
						
							|  |  |  | 		return common.NewError("Invalid db file format") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 	// Reset the file reader to the beginning
 | 
					
						
							|  |  |  | 	_, err = file.Seek(0, 0) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return common.NewErrorf("Error resetting file reader: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	// Save the file as temporary file
 | 
					
						
							|  |  |  | 	tempPath := fmt.Sprintf("%s.temp", config.GetDBPath()) | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 	// Remove the existing fallback file (if any) before creating one
 | 
					
						
							|  |  |  | 	_, err = os.Stat(tempPath) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		errRemove := os.Remove(tempPath) | 
					
						
							|  |  |  | 		if errRemove != nil { | 
					
						
							|  |  |  | 			return common.NewErrorf("Error removing existing temporary db file: %v", errRemove) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Create the temporary file
 | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	tempFile, err := os.Create(tempPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return common.NewErrorf("Error creating temporary db file: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer tempFile.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 	// Remove temp file before returning
 | 
					
						
							|  |  |  | 	defer os.Remove(tempPath) | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 	// Save uploaded file to temporary file
 | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	_, err = io.Copy(tempFile, file) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return common.NewErrorf("Error saving db: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check if we can init db or not
 | 
					
						
							|  |  |  | 	err = database.InitDB(tempPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return common.NewErrorf("Error checking db: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-05 21:52:45 +00:00
										 |  |  | 	// Stop Xray
 | 
					
						
							|  |  |  | 	s.StopXrayService() | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 	// Backup the current database for fallback
 | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	fallbackPath := fmt.Sprintf("%s.backup", config.GetDBPath()) | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 	// Remove the existing fallback file (if any)
 | 
					
						
							|  |  |  | 	_, err = os.Stat(fallbackPath) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		errRemove := os.Remove(fallbackPath) | 
					
						
							|  |  |  | 		if errRemove != nil { | 
					
						
							|  |  |  | 			return common.NewErrorf("Error removing existing fallback db file: %v", errRemove) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Move the current database to the fallback location
 | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	err = os.Rename(config.GetDBPath(), fallbackPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 		return common.NewErrorf("Error backing up temporary db file: %v", err) | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 	// Remove the temporary file before returning
 | 
					
						
							|  |  |  | 	defer os.Remove(fallbackPath) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 	// Move temp to DB path
 | 
					
						
							|  |  |  | 	err = os.Rename(tempPath, config.GetDBPath()) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 		errRename := os.Rename(fallbackPath, config.GetDBPath()) | 
					
						
							|  |  |  | 		if errRename != nil { | 
					
						
							|  |  |  | 			return common.NewErrorf("Error moving db file and restoring fallback: %v", errRename) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 		return common.NewErrorf("Error moving db file: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Migrate DB
 | 
					
						
							|  |  |  | 	err = database.InitDB(config.GetDBPath()) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-05-06 00:17:57 +00:00
										 |  |  | 		errRename := os.Rename(fallbackPath, config.GetDBPath()) | 
					
						
							|  |  |  | 		if errRename != nil { | 
					
						
							|  |  |  | 			return common.NewErrorf("Error migrating db and restoring fallback: %v", errRename) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 		return common.NewErrorf("Error migrating db: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-05-05 19:52:39 +00:00
										 |  |  | 	s.inboundService.MigrateDB() | 
					
						
							| 
									
										
										
										
											2023-05-05 18:19:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Start Xray
 | 
					
						
							|  |  |  | 	err = s.RestartXrayService() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return common.NewErrorf("Imported DB but Failed to start Xray: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-18 18:04:06 +00:00
										 |  |  | func (s *ServerService) GetNewX25519Cert() (interface{}, error) { | 
					
						
							|  |  |  | 	// Run the command
 | 
					
						
							|  |  |  | 	cmd := exec.Command(xray.GetBinaryPath(), "x25519") | 
					
						
							|  |  |  | 	var out bytes.Buffer | 
					
						
							|  |  |  | 	cmd.Stdout = &out | 
					
						
							|  |  |  | 	err := cmd.Run() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lines := strings.Split(out.String(), "\n") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	privateKeyLine := strings.Split(lines[0], ":") | 
					
						
							|  |  |  | 	publicKeyLine := strings.Split(lines[1], ":") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	privateKey := strings.TrimSpace(privateKeyLine[1]) | 
					
						
							|  |  |  | 	publicKey := strings.TrimSpace(publicKeyLine[1]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	keyPair := map[string]interface{}{ | 
					
						
							|  |  |  | 		"privateKey": privateKey, | 
					
						
							|  |  |  | 		"publicKey":  publicKey, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return keyPair, nil | 
					
						
							|  |  |  | } |