From df32b4d6cc1fc50646c5d84676eab9b1a3f06e24 Mon Sep 17 00:00:00 2001 From: patterniha <71074308+patterniha@users.noreply.github.com> Date: Fri, 4 Sep 2026 06:35:10 +0330 Subject: [PATCH] Add an option to check for update without proxy (#10090) Checking for updates always dialed through the local proxy, which fails exactly when an update may be needed most - when no working server is available. Like the subscription-update menu, updating without the proxy is now possible: the Check Update window gets an "Update via proxy" toggle (persisted, on by default so existing behavior is unchanged), honored by the check-only, update and geo-file flows, and by the scheduled update check. Co-authored-by: Claude Fable 5 --- v2rayN/ServiceLib/Manager/TaskManager.cs | 2 +- .../ServiceLib/Models/Configs/ConfigItems.cs | 1 + v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 9 +++++ v2rayN/ServiceLib/Resx/ResUI.fa.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 3 ++ v2rayN/ServiceLib/Services/UpdateService.cs | 38 +++++++++---------- .../ViewModels/CheckUpdateViewModel.cs | 25 ++++++++++-- .../Views/CheckUpdateView.axaml | 10 +++++ .../Views/CheckUpdateView.axaml.cs | 1 + v2rayN/v2rayN/Views/CheckUpdateView.xaml | 10 +++++ v2rayN/v2rayN/Views/CheckUpdateView.xaml.cs | 1 + 13 files changed, 85 insertions(+), 24 deletions(-) diff --git a/v2rayN/ServiceLib/Manager/TaskManager.cs b/v2rayN/ServiceLib/Manager/TaskManager.cs index 47254a06..39b2a43e 100644 --- a/v2rayN/ServiceLib/Manager/TaskManager.cs +++ b/v2rayN/ServiceLib/Manager/TaskManager.cs @@ -136,7 +136,7 @@ public class TaskManager var updateService = new UpdateService(_config, async (success, msg) => await Task.CompletedTask); - var msgs = await updateService.CheckHasUpdateOnlyAll(_config.CheckUpdateItem.CheckPreReleaseUpdate); + var msgs = await updateService.CheckHasUpdateOnlyAll(_config.CheckUpdateItem.CheckPreReleaseUpdate, _config.CheckUpdateItem.UpdateViaProxy); foreach (var msg in msgs) { await _updateFunc?.Invoke(false, msg); diff --git a/v2rayN/ServiceLib/Models/Configs/ConfigItems.cs b/v2rayN/ServiceLib/Models/Configs/ConfigItems.cs index e912ed26..521ae84c 100644 --- a/v2rayN/ServiceLib/Models/Configs/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/Configs/ConfigItems.cs @@ -243,6 +243,7 @@ public class WebDavItem public class CheckUpdateItem { public bool CheckPreReleaseUpdate { get; set; } + public bool UpdateViaProxy { get; set; } = true; public List? SelectedCoreTypes { get; set; } } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index a37bd801..460a31e0 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -4238,6 +4238,15 @@ namespace ServiceLib.Resx { return ResourceManager.GetString("TbSettingsEnableCheckPreReleaseUpdate", resourceCulture); } } + + /// + /// 查找类似 Update via proxy 的本地化字符串。 + /// + public static string TbSettingsEnableUpdateViaProxy { + get { + return ResourceManager.GetString("TbSettingsEnableUpdateViaProxy", resourceCulture); + } + } /// /// 查找类似 Enable sorting Configurations by drag-n-drop (requires restart) 的本地化字符串。 diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa.resx b/v2rayN/ServiceLib/Resx/ResUI.fa.resx index e5662028..0e4229f6 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa.resx @@ -693,6 +693,9 @@ به روز رسانی های پیش از انتشار را بررسی شود + + به‌روزرسانی از طریق پروکسی + استثنا diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 58ca3903..b7f245dd 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -693,6 +693,9 @@ Check for pre-release + + Update via proxy + Exception diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 029cdc76..d6154607 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -693,6 +693,9 @@ 检查 Pre-Release + + 通过代理更新 + 例外 diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 6ba6c355..b2d43a18 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -693,6 +693,9 @@ 檢查 Pre-Release + + 透過代理更新 + 例外 diff --git a/v2rayN/ServiceLib/Services/UpdateService.cs b/v2rayN/ServiceLib/Services/UpdateService.cs index 11b1d3ef..42eb37ac 100644 --- a/v2rayN/ServiceLib/Services/UpdateService.cs +++ b/v2rayN/ServiceLib/Services/UpdateService.cs @@ -7,7 +7,7 @@ public partial class UpdateService(Config config, Func updat private readonly int _timeout = 30; private static readonly string _tag = "UpdateService"; - public async Task CheckUpdateGuiN(bool preRelease) + public async Task CheckUpdateGuiN(bool preRelease, bool blProxy = true) { var url = string.Empty; var fileName = string.Empty; @@ -31,7 +31,7 @@ public partial class UpdateService(Config config, Func updat }; await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, ECoreType.v2rayN)); - var result = await CheckUpdateAsync(downloadHandle, ECoreType.v2rayN, preRelease); + var result = await CheckUpdateAsync(downloadHandle, ECoreType.v2rayN, preRelease, blProxy); if (result.Success) { await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, ECoreType.v2rayN)); @@ -39,7 +39,7 @@ public partial class UpdateService(Config config, Func updat url = result.Url!; fileName = Utils.GetTempPath(Utils.GetGuid()); - await downloadHandle.DownloadFileAsync(new() { FileUrl = url, FilePath = fileName }, true, TimeSpan.FromSeconds(_timeout)); + await downloadHandle.DownloadFileAsync(new() { FileUrl = url, FilePath = fileName }, blProxy, TimeSpan.FromSeconds(_timeout)); } else { @@ -47,7 +47,7 @@ public partial class UpdateService(Config config, Func updat } } - public async Task CheckUpdateCore(ECoreType type, bool preRelease) + public async Task CheckUpdateCore(ECoreType type, bool preRelease, bool blProxy = true) { var url = string.Empty; var fileName = string.Empty; @@ -80,7 +80,7 @@ public partial class UpdateService(Config config, Func updat }; await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, type)); - var result = await CheckUpdateAsync(downloadHandle, type, preRelease); + var result = await CheckUpdateAsync(downloadHandle, type, preRelease, blProxy); if (result.Success) { await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, type)); @@ -89,7 +89,7 @@ public partial class UpdateService(Config config, Func updat url = result.Url!; var ext = url.Contains(".tar.gz") ? ".tar.gz" : Path.GetExtension(url); fileName = Utils.GetTempPath(Utils.GetGuid() + ext); - await downloadHandle.DownloadFileAsync(new() { FileUrl = url, FilePath = fileName }, true, TimeSpan.FromSeconds(_timeout)); + await downloadHandle.DownloadFileAsync(new() { FileUrl = url, FilePath = fileName }, blProxy, TimeSpan.FromSeconds(_timeout)); } else { @@ -100,7 +100,7 @@ public partial class UpdateService(Config config, Func updat } } - public async Task CheckHasUpdateOnly(ECoreType type, bool preRelease) + public async Task CheckHasUpdateOnly(ECoreType type, bool preRelease, bool blProxy = true) { if (!CoreInfoManager.Instance.IsCheckUpdateSupported(type)) { @@ -109,10 +109,10 @@ public partial class UpdateService(Config config, Func updat var downloadHandle = new DownloadService(); var checkPreRelease = CoreInfoManager.Instance.GetCheckPreRelease(type, preRelease); - return await CheckUpdateAsync(downloadHandle, type, checkPreRelease); + return await CheckUpdateAsync(downloadHandle, type, checkPreRelease, blProxy); } - public async Task> CheckHasUpdateOnlyAll(bool preRelease) + public async Task> CheckHasUpdateOnlyAll(bool preRelease, bool blProxy = true) { var msgs = new List(); foreach (var type in CoreInfoManager.Instance.GetCheckUpdateCoreTypes()) @@ -122,7 +122,7 @@ public partial class UpdateService(Config config, Func updat continue; } - var result = await CheckHasUpdateOnly(type, preRelease); + var result = await CheckHasUpdateOnly(type, preRelease, blProxy); if (result.Success && result.Version != null) { var msg = string.Format(ResUI.MsgCheckUpdateHasNewVersion, type, result.Version); @@ -137,7 +137,7 @@ public partial class UpdateService(Config config, Func updat return msgs; } - public async Task UpdateGeoFileAll() + public async Task UpdateGeoFileAll(bool blProxy = true) { var requests = new List(); requests.AddRange(GetGeoFilesRequest()); @@ -145,17 +145,17 @@ public partial class UpdateService(Config config, Func updat requests.AddRange(await GetSrsFileAllRequest()); // NOTE: srs files are more small, so we reverse the order to ensure a good download experience for the user. requests.Reverse(); - await DownloadGeoFiles(requests); + await DownloadGeoFiles(requests, blProxy); await UpdateFunc(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo")); } #region CheckUpdate private - private async Task CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease) + private async Task CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease, bool blProxy) { try { - var result = await GetRemoteVersion(downloadHandle, type, preRelease); + var result = await GetRemoteVersion(downloadHandle, type, preRelease, blProxy); if (!result.Success || result.Version is null) { return result; @@ -170,14 +170,14 @@ public partial class UpdateService(Config config, Func updat } } - private async Task GetRemoteVersion(DownloadService downloadHandle, ECoreType type, bool preRelease) + private async Task GetRemoteVersion(DownloadService downloadHandle, ECoreType type, bool preRelease, bool blProxy) { var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type); var tagName = string.Empty; if (preRelease || coreInfo?.LockedMaxVersion != null) { var url = coreInfo?.ReleaseApiUrl; - var result = await downloadHandle.TryDownloadString(url, true, Global.AppName); + var result = await downloadHandle.TryDownloadString(url, blProxy, Global.AppName); if (result.IsNullOrEmpty()) { return new UpdateResult(false, ""); @@ -209,7 +209,7 @@ public partial class UpdateService(Config config, Func updat else { var url = Path.Combine(coreInfo.Url, "latest"); - var lastUrl = await downloadHandle.UrlRedirectAsync(url, true); + var lastUrl = await downloadHandle.UrlRedirectAsync(url, blProxy); if (lastUrl == null) { return new UpdateResult(false, ""); @@ -531,7 +531,7 @@ public partial class UpdateService(Config config, Func updat }; } - private async Task DownloadGeoFiles(List requests) + private async Task DownloadGeoFiles(List requests, bool blProxy) { var tmpFilePathDict = new Dictionary(); var tmpFileRequests = new List(); @@ -586,7 +586,7 @@ public partial class UpdateService(Config config, Func updat _ = UpdateFunc(false, args.GetException().Message); }; - await downloadHandle.DownloadSmallFilesAsync(tmpFileRequests, true, TimeSpan.FromSeconds(_timeout)); + await downloadHandle.DownloadSmallFilesAsync(tmpFileRequests, blProxy, TimeSpan.FromSeconds(_timeout)); } #endregion Geo private diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs index f9b8eb19..dc0646c1 100644 --- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs @@ -13,6 +13,7 @@ public partial class CheckUpdateViewModel : MyReactiveObject public ReactiveCommand CheckUpdateCmd { get; } public ReactiveCommand CheckOnlyCmd { get; } [Reactive] public partial bool EnableCheckPreReleaseUpdate { get; set; } + [Reactive] public partial bool EnableUpdateViaProxy { get; set; } public CheckUpdateViewModel() { @@ -33,12 +34,18 @@ public partial class CheckUpdateViewModel : MyReactiveObject }); EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate; + EnableUpdateViaProxy = _config.CheckUpdateItem.UpdateViaProxy; this.WhenAnyValue( x => x.EnableCheckPreReleaseUpdate, y => y == true) .Subscribe(c => _ = OnCheckPreReleaseUpdateChanged()); + this.WhenAnyValue( + x => x.EnableUpdateViaProxy, + y => y == true) + .Subscribe(c => _ = OnUpdateViaProxyChanged()); + RefreshCheckUpdateItems(); } @@ -98,6 +105,16 @@ public partial class CheckUpdateViewModel : MyReactiveObject await SaveSelectedCoreTypes(); } + private async Task OnUpdateViaProxyChanged() + { + if (_config.CheckUpdateItem.UpdateViaProxy == EnableUpdateViaProxy) + { + return; + } + _config.CheckUpdateItem.UpdateViaProxy = EnableUpdateViaProxy; + await SaveSelectedCoreTypes(); + } + private async Task SaveSelectedCoreTypes() { _config.CheckUpdateItem.SelectedCoreTypes = @@ -145,7 +162,7 @@ public partial class CheckUpdateViewModel : MyReactiveObject } var updateService = new UpdateService(_config, async (success, msg) => await Task.CompletedTask); - var result = await updateService.CheckHasUpdateOnly(item.CoreType.Value, EnableCheckPreReleaseUpdate); + var result = await updateService.CheckHasUpdateOnly(item.CoreType.Value, EnableCheckPreReleaseUpdate, EnableUpdateViaProxy); if (result.Success && result.Version != null) { await UpdateView(item.CoreType, string.Format(ResUI.MsgCheckUpdateHasNewVersion, item.CoreType, result.Version)); @@ -230,7 +247,7 @@ public partial class CheckUpdateViewModel : MyReactiveObject UpdatedPlusPlus(null, ""); } } - await new UpdateService(_config, _updateUI).UpdateGeoFileAll() + await new UpdateService(_config, _updateUI).UpdateGeoFileAll(EnableUpdateViaProxy) .ContinueWith(t => UpdatedPlusPlus(null, "")); } @@ -245,7 +262,7 @@ public partial class CheckUpdateViewModel : MyReactiveObject UpdatedPlusPlus(_v2rayN, msg); } } - await new UpdateService(_config, _updateUI).CheckUpdateGuiN(preRelease) + await new UpdateService(_config, _updateUI).CheckUpdateGuiN(preRelease, EnableUpdateViaProxy) .ContinueWith(t => UpdatedPlusPlus(_v2rayN, "")); } @@ -263,7 +280,7 @@ public partial class CheckUpdateViewModel : MyReactiveObject if (model.CoreType.HasValue) { - await new UpdateService(_config, _updateUI).CheckUpdateCore(model.CoreType.Value, preRelease) + await new UpdateService(_config, _updateUI).CheckUpdateCore(model.CoreType.Value, preRelease, EnableUpdateViaProxy) .ContinueWith(t => UpdatedPlusPlus(model.CoreType, "")); } } diff --git a/v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml b/v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml index 61d2a1dc..ad9e7b91 100644 --- a/v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml @@ -25,6 +25,16 @@ HorizontalAlignment="Center" Orientation="Horizontal"> + + + this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.EnableUpdateViaProxy, v => v.togEnableUpdateViaProxy.IsChecked).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckOnlyCmd, v => v.btnCheckOnly).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables); }); diff --git a/v2rayN/v2rayN/Views/CheckUpdateView.xaml b/v2rayN/v2rayN/Views/CheckUpdateView.xaml index 5b6bbed7..39b7d0cd 100644 --- a/v2rayN/v2rayN/Views/CheckUpdateView.xaml +++ b/v2rayN/v2rayN/Views/CheckUpdateView.xaml @@ -22,6 +22,16 @@ DockPanel.Dock="Bottom" Orientation="Horizontal"> + + + vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.EnableUpdateViaProxy, v => v.togEnableUpdateViaProxy.IsChecked).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckOnlyCmd, v => v.btnCheckOnly).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables); });