Compare commits

...

3 Commits

Author SHA1 Message Date
2dust 460a674ebc Add 'Check Only' update action
Introduce a new "Check Only" feature: add CheckOnlyCmd to CheckUpdateViewModel with CheckOnlyTask that queries updates (via UpdateService.CheckHasUpdateOnly) for selected cores and reports results without performing updates. Wire up a new btnCheckOnly in both Desktop and Avalonia views and bind the command. Add localized menuCheckOnly entries to multiple .resx files and update ResUI.Designer. Also shorten the pre-release label to "Check for pre-release" in resource files.
2026-05-17 19:15:57 +08:00
DHR60 e2428f2500 Add tun inbound rule (#9327) 2026-05-17 18:54:12 +08:00
DHR60 bc3cbb4277 Fix (#9325)
* Fix

* Rename tun tag
2026-05-17 18:52:42 +08:00
18 changed files with 130 additions and 18 deletions
+1
View File
@@ -505,6 +505,7 @@ public class Global
public static readonly List<string> InboundTags = public static readonly List<string> InboundTags =
[ [
"tun",
"socks", "socks",
"socks2", "socks2",
"socks3" "socks3"
+11 -2
View File
@@ -870,6 +870,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Only Check 的本地化字符串。
/// </summary>
public static string menuCheckOnly {
get {
return ResourceManager.GetString("menuCheckOnly", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Check Update 的本地化字符串。 /// 查找类似 Check Update 的本地化字符串。
/// </summary> /// </summary>
@@ -3493,7 +3502,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。 /// 查找类似 tun: TUN inbound, socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。
/// </summary> /// </summary>
public static string TbRoutingInboundTagTips { public static string TbRoutingInboundTagTips {
get { get {
@@ -3934,7 +3943,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Check for pre-release updates 的本地化字符串。 /// 查找类似 Check for pre-release 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsEnableCheckPreReleaseUpdate { public static string TbSettingsEnableCheckPreReleaseUpdate {
get { get {
+3
View File
@@ -1734,4 +1734,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgCheckUpdateHasNewVersion" xml:space="preserve"> <data name="MsgCheckUpdateHasNewVersion" xml:space="preserve">
<value>{0} has a new version available: {1}</value> <value>{0} has a new version available: {1}</value>
</data> </data>
<data name="menuCheckOnly" xml:space="preserve">
<value>Only Check</value>
</data>
</root> </root>
+3
View File
@@ -1731,4 +1731,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgCheckUpdateHasNewVersion" xml:space="preserve"> <data name="MsgCheckUpdateHasNewVersion" xml:space="preserve">
<value>{0} has a new version available: {1}</value> <value>{0} has a new version available: {1}</value>
</data> </data>
<data name="menuCheckOnly" xml:space="preserve">
<value>Only Check</value>
</data>
</root> </root>
+3
View File
@@ -1734,4 +1734,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgCheckUpdateHasNewVersion" xml:space="preserve"> <data name="MsgCheckUpdateHasNewVersion" xml:space="preserve">
<value>{0} has a new version available: {1}</value> <value>{0} has a new version available: {1}</value>
</data> </data>
<data name="menuCheckOnly" xml:space="preserve">
<value>Only Check</value>
</data>
</root> </root>
+5 -2
View File
@@ -691,7 +691,7 @@
<value>Automatically adjust column width after subscription update</value> <value>Automatically adjust column width after subscription update</value>
</data> </data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve"> <data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>Check for pre-release updates</value> <value>Check for pre-release</value>
</data> </data>
<data name="TbSettingsException" xml:space="preserve"> <data name="TbSettingsException" xml:space="preserve">
<value>Exception</value> <value>Exception</value>
@@ -1336,7 +1336,7 @@
<value>Enable second mixed port</value> <value>Enable second mixed port</value>
</data> </data>
<data name="TbRoutingInboundTagTips" xml:space="preserve"> <data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks: local port, socks2: second local port, socks3: LAN port</value> <value>tun: TUN inbound, socks: local port, socks2: second local port, socks3: LAN port</value>
</data> </data>
<data name="TbSettingsTheme" xml:space="preserve"> <data name="TbSettingsTheme" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
@@ -1734,4 +1734,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgCheckUpdateHasNewVersion" xml:space="preserve"> <data name="MsgCheckUpdateHasNewVersion" xml:space="preserve">
<value>{0} has a new version available: {1}</value> <value>{0} has a new version available: {1}</value>
</data> </data>
<data name="menuCheckOnly" xml:space="preserve">
<value>Only Check</value>
</data>
</root> </root>
+3
View File
@@ -1734,4 +1734,7 @@
<data name="MsgCheckUpdateHasNewVersion" xml:space="preserve"> <data name="MsgCheckUpdateHasNewVersion" xml:space="preserve">
<value>{0} has a new version available: {1}</value> <value>{0} has a new version available: {1}</value>
</data> </data>
<data name="menuCheckOnly" xml:space="preserve">
<value>Only Check</value>
</data>
</root> </root>
+5 -2
View File
@@ -691,7 +691,7 @@
<value>自动调整配置列宽在更新订阅后</value> <value>自动调整配置列宽在更新订阅后</value>
</data> </data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve"> <data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>检查 Pre-Release 更新 (请谨慎启用)</value> <value>检查 Pre-Release</value>
</data> </data>
<data name="TbSettingsException" xml:space="preserve"> <data name="TbSettingsException" xml:space="preserve">
<value>例外</value> <value>例外</value>
@@ -1333,7 +1333,7 @@
<value>开启第二个本地监听端口</value> <value>开启第二个本地监听端口</value>
</data> </data>
<data name="TbRoutingInboundTagTips" xml:space="preserve"> <data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>Socks:本地端口,Socks2:第二个本地端口,Socks3:局域网端口</value> <value>TunTUN 入站,Socks:本地端口,Socks2:第二个本地端口,Socks3:局域网端口</value>
</data> </data>
<data name="TbSettingsTheme" xml:space="preserve"> <data name="TbSettingsTheme" xml:space="preserve">
<value>主题</value> <value>主题</value>
@@ -1731,4 +1731,7 @@
<data name="MsgCheckUpdateHasNewVersion" xml:space="preserve"> <data name="MsgCheckUpdateHasNewVersion" xml:space="preserve">
<value>{0} 有新版本可用:{1}</value> <value>{0} 有新版本可用:{1}</value>
</data> </data>
<data name="menuCheckOnly" xml:space="preserve">
<value>仅检查</value>
</data>
</root> </root>
+4 -1
View File
@@ -691,7 +691,7 @@
<value>在更新訂閱後自動調整列寬</value> <value>在更新訂閱後自動調整列寬</value>
</data> </data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve"> <data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>檢查 Pre-Release 更新 (請謹慎啟用)</value> <value>檢查 Pre-Release</value>
</data> </data>
<data name="TbSettingsException" xml:space="preserve"> <data name="TbSettingsException" xml:space="preserve">
<value>例外</value> <value>例外</value>
@@ -1731,4 +1731,7 @@
<data name="MsgCheckUpdateHasNewVersion" xml:space="preserve"> <data name="MsgCheckUpdateHasNewVersion" xml:space="preserve">
<value>{0} 有新版本可用:{1}</value> <value>{0} 有新版本可用:{1}</value>
</data> </data>
<data name="menuCheckOnly" xml:space="preserve">
<value>僅檢查</value>
</data>
</root> </root>
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"type": "tun", "type": "tun",
"tag": "tun-in", "tag": "tun",
"interface_name": "singbox_tun", "interface_name": "singbox_tun",
"address": [ "address": [
"172.18.0.1/30", "172.18.0.1/30",
@@ -87,8 +87,14 @@ public partial class CoreConfigSingboxService
}); });
_coreConfig.route.rules.Add(new() _coreConfig.route.rules.Add(new()
{ {
protocol = ["dns"], type = "logical",
action = "hijack-dns" mode = "or",
action = "hijack-dns",
rules =
[
new() { port = [53] },
new() { protocol = ["dns"] },
],
}); });
} }
else else
@@ -96,7 +102,7 @@ public partial class CoreConfigSingboxService
_coreConfig.route.rules.Add(new() _coreConfig.route.rules.Add(new()
{ {
port = [53], port = [53],
action = "hijack-dns" action = "hijack-dns",
}); });
} }
@@ -27,6 +27,7 @@ public partial class CoreConfigV2rayService
}); });
_coreConfig.routing.rules.Add(new() _coreConfig.routing.rules.Add(new()
{ {
inboundTag = ["tun"],
port = "53", port = "53",
outboundTag = Global.DnsOutboundTag, outboundTag = Global.DnsOutboundTag,
}); });
@@ -9,6 +9,7 @@ public class CheckUpdateViewModel : MyReactiveObject
public IObservableCollection<CheckUpdateModel> CheckUpdateModels { get; } = new ObservableCollectionExtended<CheckUpdateModel>(); public IObservableCollection<CheckUpdateModel> CheckUpdateModels { get; } = new ObservableCollectionExtended<CheckUpdateModel>();
public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; } public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; }
public ReactiveCommand<Unit, Unit> CheckOnlyCmd { get; }
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; } [Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
public CheckUpdateViewModel(Func<EViewAction, object?, Task<bool>>? updateView) public CheckUpdateViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
@@ -23,6 +24,13 @@ public class CheckUpdateViewModel : MyReactiveObject
_ = UpdateView(_v2rayN, ex.Message); _ = UpdateView(_v2rayN, ex.Message);
}); });
CheckOnlyCmd = ReactiveCommand.CreateFromTask(CheckOnly);
CheckOnlyCmd.ThrownExceptions.Subscribe(ex =>
{
Logging.SaveLog(_tag, ex);
_ = UpdateView(_v2rayN, ex.Message);
});
EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate; EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate;
this.WhenAnyValue( this.WhenAnyValue(
@@ -71,11 +79,54 @@ public class CheckUpdateViewModel : MyReactiveObject
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
} }
private async Task CheckOnly()
{
await Task.Run(CheckOnlyTask);
}
private async Task CheckUpdate() private async Task CheckUpdate()
{ {
await Task.Run(CheckUpdateTask); await Task.Run(CheckUpdateTask);
} }
private async Task CheckOnlyTask()
{
await SaveSelectedCoreTypes();
for (var k = CheckUpdateModels.Count - 1; k >= 0; k--)
{
var item = CheckUpdateModels[k];
if (item.IsSelected != true)
{
continue;
}
await UpdateView(item.CoreType, "...");
if (item.CoreType == _geo)
{
await UpdateView(item.CoreType, ResUI.menuCheckOnly + " (Not Support)");
continue;
}
if (!Enum.TryParse<ECoreType>(item.CoreType, out var type))
{
await UpdateView(item.CoreType, "Not Support");
continue;
}
var updateService = new UpdateService(_config, async (success, msg) => await Task.CompletedTask);
var result = await updateService.CheckHasUpdateOnly(type, EnableCheckPreReleaseUpdate);
if (result.Success && result.Version != null)
{
await UpdateView(item.CoreType, string.Format(ResUI.MsgCheckUpdateHasNewVersion, type, result.Version));
}
else
{
await UpdateView(item.CoreType, result.Msg);
}
}
}
private async Task CheckUpdateTask() private async Task CheckUpdateTask()
{ {
_lstUpdated.Clear(); _lstUpdated.Clear();
@@ -33,6 +33,12 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<Button
x:Name="btnCheckOnly"
Width="100"
Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.menuCheckOnly}" />
<Button <Button
x:Name="btnCheckUpdate" x:Name="btnCheckUpdate"
Width="100" Width="100"
@@ -13,6 +13,7 @@ public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables); 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.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.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); this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
}); });
} }
@@ -16,7 +16,7 @@
<DockPanel> <DockPanel>
<Grid <Grid
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto,Auto" ColumnDefinitions="Auto,Auto,*"
DockPanel.Dock="Top" DockPanel.Dock="Top"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto"> RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock <TextBlock
@@ -66,7 +66,8 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRuleTypeTips}" /> Text="{x:Static resx:ResUI.TbRuleTypeTips}"
TextWrapping="Wrap" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
@@ -93,7 +94,10 @@
Margin="0,0,8,0" Margin="0,0,8,0"
Click="BtnSelectProfile_Click" Click="BtnSelectProfile_Click"
Content="{x:Static resx:ResUI.TbSelectProfile}" /> Content="{x:Static resx:ResUI.TbSelectProfile}" />
<TextBlock VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" /> <TextBlock
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}"
TextWrapping="Wrap" />
</StackPanel> </StackPanel>
<TextBlock <TextBlock
@@ -115,7 +119,8 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" /> Text="{x:Static resx:ResUI.TbRuleMatchingTips}"
TextWrapping="Wrap" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
@@ -135,7 +140,8 @@
Grid.Row="4" Grid.Row="4"
Grid.Column="2" Grid.Column="2"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Center"> VerticalAlignment="Center"
TextWrapping="Wrap">
<HyperlinkButton Classes="WithIcon" Click="linkRuleobjectDoc_Click"> <HyperlinkButton Classes="WithIcon" Click="linkRuleobjectDoc_Click">
<TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" /> <TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" />
</HyperlinkButton> </HyperlinkButton>
@@ -160,7 +166,8 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" /> Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}"
TextWrapping="Wrap" />
<TextBlock <TextBlock
Grid.Row="6" Grid.Row="6"
@@ -181,7 +188,8 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRoutingTips}" /> Text="{x:Static resx:ResUI.TbRoutingTips}"
TextWrapping="Wrap" />
</Grid> </Grid>
<StackPanel <StackPanel
+7
View File
@@ -32,6 +32,13 @@
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<Button
x:Name="btnCheckOnly"
Width="100"
Margin="{StaticResource Margin8}"
Content="{x:Static resx:ResUI.menuCheckOnly}"
Style="{StaticResource DefButton}" />
<Button <Button
x:Name="btnCheckUpdate" x:Name="btnCheckUpdate"
Width="100" Width="100"
@@ -13,6 +13,7 @@ public partial class CheckUpdateView
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables); 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.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.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); this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
}); });
} }