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 <noreply@anthropic.com>
This commit is contained in:
patterniha
2026-09-04 06:35:10 +03:30
committed by GitHub
parent 9451b69246
commit df32b4d6cc
13 changed files with 85 additions and 24 deletions

View File

@@ -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);

View File

@@ -243,6 +243,7 @@ public class WebDavItem
public class CheckUpdateItem
{
public bool CheckPreReleaseUpdate { get; set; }
public bool UpdateViaProxy { get; set; } = true;
public List<string>? SelectedCoreTypes { get; set; }
}

View File

@@ -4238,6 +4238,15 @@ namespace ServiceLib.Resx {
return ResourceManager.GetString("TbSettingsEnableCheckPreReleaseUpdate", resourceCulture);
}
}
/// <summary>
/// 查找类似 Update via proxy 的本地化字符串。
/// </summary>
public static string TbSettingsEnableUpdateViaProxy {
get {
return ResourceManager.GetString("TbSettingsEnableUpdateViaProxy", resourceCulture);
}
}
/// <summary>
/// 查找类似 Enable sorting Configurations by drag-n-drop (requires restart) 的本地化字符串。

View File

@@ -693,6 +693,9 @@
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>به روز رسانی های پیش از انتشار را بررسی شود</value>
</data>
<data name="TbSettingsEnableUpdateViaProxy" xml:space="preserve">
<value>به‌روزرسانی از طریق پروکسی</value>
</data>
<data name="TbSettingsException" xml:space="preserve">
<value>استثنا</value>
</data>

View File

@@ -693,6 +693,9 @@
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>Check for pre-release</value>
</data>
<data name="TbSettingsEnableUpdateViaProxy" xml:space="preserve">
<value>Update via proxy</value>
</data>
<data name="TbSettingsException" xml:space="preserve">
<value>Exception</value>
</data>

View File

@@ -693,6 +693,9 @@
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>检查 Pre-Release</value>
</data>
<data name="TbSettingsEnableUpdateViaProxy" xml:space="preserve">
<value>通过代理更新</value>
</data>
<data name="TbSettingsException" xml:space="preserve">
<value>例外</value>
</data>

View File

@@ -693,6 +693,9 @@
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>檢查 Pre-Release</value>
</data>
<data name="TbSettingsEnableUpdateViaProxy" xml:space="preserve">
<value>透過代理更新</value>
</data>
<data name="TbSettingsException" xml:space="preserve">
<value>例外</value>
</data>

View File

@@ -7,7 +7,7 @@ public partial class UpdateService(Config config, Func<bool, string, Task> 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<bool, string, Task> 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<bool, string, Task> 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<bool, string, Task> 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<bool, string, Task> 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<bool, string, Task> 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<bool, string, Task> updat
}
}
public async Task<UpdateResult> CheckHasUpdateOnly(ECoreType type, bool preRelease)
public async Task<UpdateResult> CheckHasUpdateOnly(ECoreType type, bool preRelease, bool blProxy = true)
{
if (!CoreInfoManager.Instance.IsCheckUpdateSupported(type))
{
@@ -109,10 +109,10 @@ public partial class UpdateService(Config config, Func<bool, string, Task> 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<List<string>> CheckHasUpdateOnlyAll(bool preRelease)
public async Task<List<string>> CheckHasUpdateOnlyAll(bool preRelease, bool blProxy = true)
{
var msgs = new List<string>();
foreach (var type in CoreInfoManager.Instance.GetCheckUpdateCoreTypes())
@@ -122,7 +122,7 @@ public partial class UpdateService(Config config, Func<bool, string, Task> 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<bool, string, Task> updat
return msgs;
}
public async Task UpdateGeoFileAll()
public async Task UpdateGeoFileAll(bool blProxy = true)
{
var requests = new List<FileDownloadRequest>();
requests.AddRange(GetGeoFilesRequest());
@@ -145,17 +145,17 @@ public partial class UpdateService(Config config, Func<bool, string, Task> 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<UpdateResult> CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease)
private async Task<UpdateResult> 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<bool, string, Task> updat
}
}
private async Task<UpdateResult> GetRemoteVersion(DownloadService downloadHandle, ECoreType type, bool preRelease)
private async Task<UpdateResult> 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<bool, string, Task> 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<bool, string, Task> updat
};
}
private async Task DownloadGeoFiles(List<FileDownloadRequest> requests)
private async Task DownloadGeoFiles(List<FileDownloadRequest> requests, bool blProxy)
{
var tmpFilePathDict = new Dictionary<string, string>();
var tmpFileRequests = new List<FileDownloadRequest>();
@@ -586,7 +586,7 @@ public partial class UpdateService(Config config, Func<bool, string, Task> 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

View File

@@ -13,6 +13,7 @@ public partial class CheckUpdateViewModel : MyReactiveObject
public ReactiveCommand<RxVoid, RxVoid> CheckUpdateCmd { get; }
public ReactiveCommand<RxVoid, RxVoid> 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, ""));
}
}

View File

@@ -25,6 +25,16 @@
HorizontalAlignment="Center"
Orientation="Horizontal">
<TextBlock
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsEnableUpdateViaProxy}" />
<ToggleSwitch
x:Name="togEnableUpdateViaProxy"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"

View File

@@ -11,6 +11,7 @@ public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
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);
});

View File

@@ -22,6 +22,16 @@
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<TextBlock
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsEnableUpdateViaProxy}" />
<ToggleButton
x:Name="togEnableUpdateViaProxy"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"

View File

@@ -11,6 +11,7 @@ public partial class CheckUpdateView
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);
});