mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-15 17:45:03 +03:00
fix(update): keep Xray-core running until update downloads finish (#7002)
UpdateXray stopped the core before downloading, which also stopped the Panel Traffic Outbound bridge the download needed on servers that reach GitHub only through it, leaving every client offline. The new core is now downloaded, staged and checked with run -test against the current config while the old one serves; only the rename and restart swap it (Windows still stops first). update.sh gets the bridge as XUI_UPDATE_PROXY, installs x-ui.sh from the release archive, and fetches Alpine's x-ui.rc before stopping x-ui.
This commit is contained in:
committed by
GitHub
parent
4d583cebf3
commit
6d9e836d61
@@ -933,13 +933,16 @@ const (
|
||||
maxXrayReleasesBytes = 32 << 20
|
||||
)
|
||||
|
||||
func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||
const (
|
||||
XrayURL = "https://api.github.com/repos/XTLS/Xray-core/releases"
|
||||
bufferSize = 8192
|
||||
)
|
||||
// Tests point these at a local server that stands in for GitHub.
|
||||
var (
|
||||
xrayReleasesURL = "https://api.github.com/repos/XTLS/Xray-core/releases"
|
||||
xrayDownloadURL = "https://github.com/XTLS/Xray-core/releases/download"
|
||||
)
|
||||
|
||||
req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, XrayURL, nil)
|
||||
func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||
const bufferSize = 8192
|
||||
|
||||
req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, xrayReleasesURL, nil)
|
||||
if reqErr != nil {
|
||||
return nil, reqErr
|
||||
}
|
||||
@@ -1041,7 +1044,7 @@ func (s *ServerService) downloadXRay(version string) (string, error) {
|
||||
}
|
||||
|
||||
fileName := fmt.Sprintf("Xray-%s-%s.zip", osName, arch)
|
||||
url := fmt.Sprintf("https://github.com/XTLS/Xray-core/releases/download/%s/%s", version, fileName)
|
||||
url := fmt.Sprintf("%s/%s/%s", xrayDownloadURL, version, fileName)
|
||||
client := s.settingService.NewProxiedHTTPClient(60 * time.Second)
|
||||
req, reqErr := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
|
||||
if reqErr != nil {
|
||||
@@ -1154,12 +1157,8 @@ func (s *ServerService) UpdateXray(version string) error {
|
||||
return fmt.Errorf("xray version %q is not in the fetched release list", version)
|
||||
}
|
||||
|
||||
// 1. Stop xray before doing anything
|
||||
if err := s.StopXrayService(); err != nil {
|
||||
logger.Warning("failed to stop xray before update:", err)
|
||||
}
|
||||
|
||||
// 2. Download the zip
|
||||
// Keep the running core up until the swap. The download may need its panel egress
|
||||
// bridge, and if the candidate fails the config check, clients stay on the old core.
|
||||
zipFileName, err := s.downloadXRay(version)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1181,19 +1180,20 @@ func (s *ServerService) UpdateXray(version string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. Helper to extract files
|
||||
copyZipFile := func(zipName string, fileName string) error {
|
||||
// Stage next to the target so the swap is an atomic rename on one filesystem, and
|
||||
// keep the target's extension so Windows will run the staged file for the check.
|
||||
stageZipFile := func(zipName string, fileName string) (string, error) {
|
||||
zipFile, err := reader.Open(zipName)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer zipFile.Close()
|
||||
if err := os.MkdirAll(filepath.Dir(fileName), 0o755); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
tmpFile, err := os.CreateTemp(filepath.Dir(fileName), ".xray-*")
|
||||
tmpFile, err := os.CreateTemp(filepath.Dir(fileName), ".xray-*"+filepath.Ext(fileName))
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
tmpPath := tmpFile.Name()
|
||||
ok := false
|
||||
@@ -1205,42 +1205,53 @@ func (s *ServerService) UpdateXray(version string) error {
|
||||
}()
|
||||
n, err := io.Copy(tmpFile, io.LimitReader(zipFile, maxXrayBinaryBytes+1))
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
if n > maxXrayBinaryBytes {
|
||||
return fmt.Errorf("xray binary exceeds %d bytes", maxXrayBinaryBytes)
|
||||
return "", fmt.Errorf("xray binary exceeds %d bytes", maxXrayBinaryBytes)
|
||||
}
|
||||
if err := tmpFile.Chmod(0o755); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
if err := tmpFile.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
_ = os.Remove(fileName)
|
||||
}
|
||||
if err := os.Rename(tmpPath, fileName); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
ok = true
|
||||
return nil
|
||||
return tmpPath, nil
|
||||
}
|
||||
|
||||
// 4. Extract correct binary
|
||||
zipName, target := "xray", xray.GetBinaryPath()
|
||||
if runtime.GOOS == "windows" {
|
||||
targetBinary := filepath.Join(config.GetBinFolderPath(), "xray-windows-amd64.exe")
|
||||
err = copyZipFile("xray.exe", targetBinary)
|
||||
} else {
|
||||
err = copyZipFile("xray", xray.GetBinaryPath())
|
||||
zipName, target = "xray.exe", filepath.Join(config.GetBinFolderPath(), "xray-windows-amd64.exe")
|
||||
}
|
||||
staged, err := stageZipFile(zipName, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(staged)
|
||||
|
||||
xrayConfig, err := s.xrayService.GetXrayConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := xray.CheckConfig(staged, xrayConfig); err != nil {
|
||||
return fmt.Errorf("xray %s failed the config check and was not installed: %w", version, err)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
// Windows cannot replace a running executable.
|
||||
if err := s.StopXrayService(); err != nil {
|
||||
logger.Warning("failed to stop xray before update:", err)
|
||||
}
|
||||
_ = os.Remove(target)
|
||||
}
|
||||
if err := os.Rename(staged, target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 5. Restart xray
|
||||
if err := s.xrayService.RestartXray(true); err != nil {
|
||||
logger.Error("start xray failed:", err)
|
||||
return err
|
||||
return fmt.Errorf("xray %s is installed, but the restart failed: %w", version, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user