mirror of
https://github.com/2dust/v2rayN.git
synced 2025-10-29 03:22:52 +00:00
Compare commits
7 commits
5c2370edb8
...
cf503da469
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf503da469 | ||
|
|
9ec6d3f955 | ||
|
|
e357e2a0b6 | ||
|
|
237eba22e4 | ||
|
|
856d155937 | ||
|
|
ff1df89038 | ||
|
|
6349553fe0 |
23 changed files with 1153 additions and 5 deletions
|
|
@ -29,6 +29,7 @@ public enum EViewAction
|
|||
DNSSettingWindow,
|
||||
RoutingSettingWindow,
|
||||
OptionSettingWindow,
|
||||
CustomConfigWindow,
|
||||
GlobalHotkeySettingWindow,
|
||||
SubSettingWindow,
|
||||
DispatcherSpeedTest,
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ public sealed class AppHandler
|
|||
SQLiteHelper.Instance.CreateTable<RoutingItem>();
|
||||
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
|
||||
SQLiteHelper.Instance.CreateTable<DNSItem>();
|
||||
SQLiteHelper.Instance.CreateTable<CustomConfigItem>();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -203,6 +204,16 @@ public sealed class AppHandler
|
|||
return await SQLiteHelper.Instance.TableAsync<DNSItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
|
||||
}
|
||||
|
||||
public async Task<List<CustomConfigItem>?> CustomConfigItem()
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<CustomConfigItem>().ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<CustomConfigItem?> GetCustomConfigItem(ECoreType eCoreType)
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<CustomConfigItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
|
||||
}
|
||||
|
||||
#endregion SqliteHelper
|
||||
|
||||
#region Core Type
|
||||
|
|
|
|||
|
|
@ -2184,6 +2184,54 @@ public class ConfigHandler
|
|||
|
||||
#endregion DNS
|
||||
|
||||
#region Custom Config
|
||||
|
||||
public static async Task<int> InitBuiltinCustomConfig(Config config)
|
||||
{
|
||||
var items = await AppHandler.Instance.CustomConfigItem();
|
||||
if (items.Count <= 0)
|
||||
{
|
||||
var item = new CustomConfigItem()
|
||||
{
|
||||
Remarks = "V2ray",
|
||||
CoreType = ECoreType.Xray,
|
||||
};
|
||||
await SaveCustomConfigItem(config, item);
|
||||
|
||||
var item2 = new CustomConfigItem()
|
||||
{
|
||||
Remarks = "sing-box",
|
||||
CoreType = ECoreType.sing_box,
|
||||
};
|
||||
await SaveCustomConfigItem(config, item2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
public static async Task<int> SaveCustomConfigItem(Config config, CustomConfigItem item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (item.Id.IsNullOrEmpty())
|
||||
{
|
||||
item.Id = Utils.GetGuid(false);
|
||||
}
|
||||
|
||||
if (await SQLiteHelper.Instance.ReplaceAsync(item) > 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Custom Config
|
||||
|
||||
#region Regional Presets
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
18
v2rayN/ServiceLib/Models/CustomConfigItem.cs
Normal file
18
v2rayN/ServiceLib/Models/CustomConfigItem.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
public class CustomConfigItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Remarks { get; set; }
|
||||
public bool Enabled { get; set; } = false;
|
||||
public ECoreType CoreType { get; set; }
|
||||
public string? Config { get; set; }
|
||||
public string? TunConfig { get; set; }
|
||||
public bool? AddProxyOnly { get; set; } = false;
|
||||
public string? ProxyDetour { get; set; }
|
||||
}
|
||||
164
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
164
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -1,4 +1,4 @@
|
|||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
|
|
@ -186,6 +186,15 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Please fill in the correct custom config 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string FillCorrectConfigText {
|
||||
get {
|
||||
return ResourceManager.GetString("FillCorrectConfigText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Please fill in the correct custom DNS 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -852,6 +861,15 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Custom Config 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuCustomConfig {
|
||||
get {
|
||||
return ResourceManager.GetString("menuCustomConfig", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 DNS Settings 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -2220,6 +2238,15 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Do Not Add Non-Proxy Protocol Outbound 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbAddProxyProtocolOutboundOnly {
|
||||
get {
|
||||
return ResourceManager.GetString("TbAddProxyProtocolOutboundOnly", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Address 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -2337,6 +2364,42 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enable Custom Config 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbCustomConfigEnable {
|
||||
get {
|
||||
return ResourceManager.GetString("TbCustomConfigEnable", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 sing-box Custom Config 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbCustomConfigSingbox {
|
||||
get {
|
||||
return ResourceManager.GetString("TbCustomConfigSingbox", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enable Custom DNS 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbCustomDNSEnable {
|
||||
get {
|
||||
return ResourceManager.GetString("TbCustomDNSEnable", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Custom DNS Enabled, This Page's Settings Invalid 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbCustomDNSEnabledPageInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("TbCustomDNSEnabledPageInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Display GUI 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -2643,6 +2706,24 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 v2ray Custom Config 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbRayCustomConfig {
|
||||
get {
|
||||
return ResourceManager.GetString("TbRayCustomConfig", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add Outbound Config Only, routing.balancers and routing.rules.outboundTag 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbRayCustomConfigDesc {
|
||||
get {
|
||||
return ResourceManager.GetString("TbRayCustomConfigDesc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Alias (remarks) 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -2760,6 +2841,78 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add Outbound and Endpoint Config Only 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBCustomConfigDesc {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBCustomConfigDesc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBDirectResolveStrategy {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBDirectResolveStrategy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 The sing-box DoH resolution server can be overwritten 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBDoHOverride {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBDoHOverride", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 sing-box DoH Resolver Server 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBDoHResolverServer {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBDoHResolverServer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Fallback DNS Resolution, Suggest IP 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBFallbackDNSResolve {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBFallbackDNSResolve", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Resolve Outbound Domains 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBOutboundDomainResolve {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBOutboundDomainResolve", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Outbound DNS Resolution (sing-box) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBOutboundsResolverDNS {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBOutboundsResolverDNS", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBRemoteResolveStrategy {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBRemoteResolveStrategy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Encryption method (security) 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
@ -3543,6 +3696,15 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Set Upstream Proxy Tag 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSetUpstreamProxyDetour {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSetUpstreamProxyDetour", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Short Id 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1404,4 +1404,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>Add [Anytls] Configuration</value>
|
||||
</data>
|
||||
<data name="menuCustomConfig" xml:space="preserve">
|
||||
<value>Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigEnable" xml:space="preserve">
|
||||
<value>Enable Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfig" xml:space="preserve">
|
||||
<value>v2ray Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigSingbox" xml:space="preserve">
|
||||
<value>sing-box Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value>
|
||||
</data>
|
||||
<data name="TbSBCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound and Endpoint Config Only</value>
|
||||
</data>
|
||||
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
|
||||
<value>Do Not Add Non-Proxy Protocol Outbound</value>
|
||||
</data>
|
||||
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
|
||||
<value>Set Upstream Proxy Tag</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1404,4 +1404,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>[Anytls] konfiguráció hozzáadása</value>
|
||||
</data>
|
||||
<data name="menuCustomConfig" xml:space="preserve">
|
||||
<value>Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigEnable" xml:space="preserve">
|
||||
<value>Enable Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfig" xml:space="preserve">
|
||||
<value>v2ray Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigSingbox" xml:space="preserve">
|
||||
<value>sing-box Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value>
|
||||
</data>
|
||||
<data name="TbSBCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound and Endpoint Config Only</value>
|
||||
</data>
|
||||
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
|
||||
<value>Do Not Add Non-Proxy Protocol Outbound</value>
|
||||
</data>
|
||||
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
|
||||
<value>Set Upstream Proxy Tag</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1404,4 +1404,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>Add [Anytls] Configuration</value>
|
||||
</data>
|
||||
<data name="menuCustomConfig" xml:space="preserve">
|
||||
<value>Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigEnable" xml:space="preserve">
|
||||
<value>Enable Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfig" xml:space="preserve">
|
||||
<value>v2ray Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigSingbox" xml:space="preserve">
|
||||
<value>sing-box Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value>
|
||||
</data>
|
||||
<data name="TbSBCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound and Endpoint Config Only</value>
|
||||
</data>
|
||||
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
|
||||
<value>Do Not Add Non-Proxy Protocol Outbound</value>
|
||||
</data>
|
||||
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
|
||||
<value>Set Upstream Proxy Tag</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1404,4 +1404,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>Добавить сервер [Anytls]</value>
|
||||
</data>
|
||||
<data name="menuCustomConfig" xml:space="preserve">
|
||||
<value>Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigEnable" xml:space="preserve">
|
||||
<value>Enable Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfig" xml:space="preserve">
|
||||
<value>v2ray Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigSingbox" xml:space="preserve">
|
||||
<value>sing-box Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value>
|
||||
</data>
|
||||
<data name="TbSBCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound and Endpoint Config Only</value>
|
||||
</data>
|
||||
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
|
||||
<value>Do Not Add Non-Proxy Protocol Outbound</value>
|
||||
</data>
|
||||
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
|
||||
<value>Set Upstream Proxy Tag</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1401,4 +1401,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>添加 [Anytls] 配置文件</value>
|
||||
</data>
|
||||
<data name="menuCustomConfig" xml:space="preserve">
|
||||
<value>自定义配置</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigEnable" xml:space="preserve">
|
||||
<value>启用自定义配置</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfig" xml:space="preserve">
|
||||
<value>v2ray 自定义配置</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigSingbox" xml:space="preserve">
|
||||
<value>sing-box 自定义配置</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfigDesc" xml:space="preserve">
|
||||
<value>仅添加出站配置,routing.balancers 和 routing.rules.outboundTag</value>
|
||||
</data>
|
||||
<data name="TbSBCustomConfigDesc" xml:space="preserve">
|
||||
<value>仅添加出站和端点配置</value>
|
||||
</data>
|
||||
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
|
||||
<value>不添加非代理协议出站</value>
|
||||
</data>
|
||||
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
|
||||
<value>设置上游代理 tag</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1401,4 +1401,28 @@
|
|||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>新增 [Anytls] 設定檔</value>
|
||||
</data>
|
||||
<data name="menuCustomConfig" xml:space="preserve">
|
||||
<value>Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigEnable" xml:space="preserve">
|
||||
<value>Enable Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfig" xml:space="preserve">
|
||||
<value>v2ray Custom Config</value>
|
||||
</data>
|
||||
<data name="TbCustomConfigSingbox" xml:space="preserve">
|
||||
<value>sing-box Custom Config</value>
|
||||
</data>
|
||||
<data name="TbRayCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag</value>
|
||||
</data>
|
||||
<data name="TbSBCustomConfigDesc" xml:space="preserve">
|
||||
<value>Add Outbound and Endpoint Config Only</value>
|
||||
</data>
|
||||
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
|
||||
<value>Do Not Add Non-Proxy Protocol Outbound</value>
|
||||
</data>
|
||||
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
|
||||
<value>Set Upstream Proxy Tag</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -2,6 +2,8 @@ using System.Data;
|
|||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Reactive;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using DynamicData;
|
||||
using ServiceLib.Models;
|
||||
|
||||
|
|
@ -81,7 +83,9 @@ public class CoreConfigSingboxService
|
|||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(singboxConfig);
|
||||
|
||||
var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box);
|
||||
ret.Data = await ApplyCustomConfig(customConfig, singboxConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -421,7 +425,9 @@ public class CoreConfigSingboxService
|
|||
await ConvertGeo2Ruleset(singboxConfig);
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(singboxConfig);
|
||||
|
||||
var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box);
|
||||
ret.Data = await ApplyCustomConfig(customConfig, singboxConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -1970,5 +1976,61 @@ public class CoreConfigSingboxService
|
|||
return 0;
|
||||
}
|
||||
|
||||
private async Task<string> ApplyCustomConfig(CustomConfigItem customConfig, SingboxConfig singboxConfig)
|
||||
{
|
||||
var customConfigItem = customConfig.Config;
|
||||
if (_config.TunModeItem.EnableTun)
|
||||
{
|
||||
customConfigItem = customConfig.TunConfig;
|
||||
}
|
||||
|
||||
if (!customConfig.Enabled || customConfigItem.IsNullOrEmpty())
|
||||
{
|
||||
return JsonUtils.Serialize(singboxConfig);
|
||||
}
|
||||
|
||||
var customConfigNode = JsonNode.Parse(customConfigItem);
|
||||
if (customConfigNode == null)
|
||||
{
|
||||
return JsonUtils.Serialize(singboxConfig);
|
||||
}
|
||||
|
||||
// Process outbounds
|
||||
var customOutboundsNode = customConfigNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
|
||||
foreach (var outbound in singboxConfig.outbounds)
|
||||
{
|
||||
if (outbound.type.ToLower() is "direct" or "block")
|
||||
{
|
||||
if (customConfig.AddProxyOnly == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (outbound.detour.IsNullOrEmpty() && (!customConfig.ProxyDetour.IsNullOrEmpty()) && !Utils.IsPrivateNetwork(outbound.server ?? string.Empty))
|
||||
{
|
||||
outbound.detour = customConfig.ProxyDetour;
|
||||
}
|
||||
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
|
||||
}
|
||||
customConfigNode["outbounds"] = customOutboundsNode;
|
||||
|
||||
// Process endpoints
|
||||
if (singboxConfig.endpoints != null && singboxConfig.endpoints.Count > 0)
|
||||
{
|
||||
var customEndpointsNode = customConfigNode["endpoints"] is JsonArray endpoints ? endpoints : new JsonArray();
|
||||
foreach (var endpoint in singboxConfig.endpoints)
|
||||
{
|
||||
if (endpoint.detour.IsNullOrEmpty() && (!customConfig.ProxyDetour.IsNullOrEmpty()))
|
||||
{
|
||||
endpoint.detour = customConfig.ProxyDetour;
|
||||
}
|
||||
customEndpointsNode.Add(JsonUtils.DeepCopy(endpoint));
|
||||
}
|
||||
customConfigNode["endpoints"] = customEndpointsNode;
|
||||
}
|
||||
|
||||
return await Task.FromResult(JsonUtils.Serialize(customConfigNode));
|
||||
}
|
||||
|
||||
#endregion private gen function
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using ServiceLib.Models;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
|
|
@ -66,7 +68,9 @@ public class CoreConfigV2rayService
|
|||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(v2rayConfig);
|
||||
|
||||
var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray);
|
||||
ret.Data = await ApplyCustomConfig(customConfig, v2rayConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -195,7 +199,9 @@ public class CoreConfigV2rayService
|
|||
}
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(v2rayConfig);
|
||||
|
||||
var customConfig = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray);
|
||||
ret.Data = await ApplyCustomConfig(customConfig, v2rayConfig, true);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -1570,5 +1576,83 @@ public class CoreConfigV2rayService
|
|||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<string> ApplyCustomConfig(CustomConfigItem customConfig, V2rayConfig v2rayConfig, bool handleBalancerAndRules = false)
|
||||
{
|
||||
if (!customConfig.Enabled || customConfig.Config.IsNullOrEmpty())
|
||||
{
|
||||
return JsonUtils.Serialize(v2rayConfig);
|
||||
}
|
||||
|
||||
var customConfigNode = JsonNode.Parse(customConfig.Config);
|
||||
if (customConfigNode == null)
|
||||
{
|
||||
return JsonUtils.Serialize(v2rayConfig);
|
||||
}
|
||||
|
||||
// Handle balancer and rules modifications (for multiple load scenarios)
|
||||
if (handleBalancerAndRules && v2rayConfig.routing?.balancers?.Count > 0)
|
||||
{
|
||||
var balancer = v2rayConfig.routing.balancers.First();
|
||||
|
||||
// Modify existing rules in custom config
|
||||
var rulesNode = customConfigNode["routing"]?["rules"];
|
||||
if (rulesNode != null)
|
||||
{
|
||||
foreach (var rule in rulesNode.AsArray())
|
||||
{
|
||||
if (rule["outboundTag"]?.GetValue<string>() == Global.ProxyTag)
|
||||
{
|
||||
rule.AsObject().Remove("outboundTag");
|
||||
rule["balancerTag"] = balancer.tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure routing node exists
|
||||
if (customConfigNode["routing"] == null)
|
||||
{
|
||||
customConfigNode["routing"] = new JsonObject();
|
||||
}
|
||||
|
||||
// Handle balancers - append instead of override
|
||||
if (customConfigNode["routing"]["balancers"] is JsonArray customBalancersNode)
|
||||
{
|
||||
if (JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers)) is JsonArray newBalancers)
|
||||
{
|
||||
foreach (var balancerNode in newBalancers)
|
||||
{
|
||||
customBalancersNode.Add(balancerNode?.DeepClone());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
customConfigNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle outbounds - append instead of override
|
||||
var customOutboundsNode = customConfigNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
|
||||
foreach (var outbound in v2rayConfig.outbounds)
|
||||
{
|
||||
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
|
||||
{
|
||||
if (customConfig.AddProxyOnly == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!customConfig.ProxyDetour.IsNullOrEmpty()) && !(Utils.IsPrivateNetwork(outbound.settings?.servers?.FirstOrDefault()?.address ?? string.Empty) || Utils.IsPrivateNetwork(outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty)))
|
||||
{
|
||||
outbound.streamSettings ??= new StreamSettings4Ray();
|
||||
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
|
||||
outbound.streamSettings.sockopt.dialerProxy = customConfig.ProxyDetour;
|
||||
}
|
||||
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
|
||||
}
|
||||
|
||||
return await Task.FromResult(JsonUtils.Serialize(customConfigNode));
|
||||
}
|
||||
|
||||
#endregion private gen function
|
||||
}
|
||||
|
|
|
|||
110
v2rayN/ServiceLib/ViewModels/CustomConfigViewModel.cs
Normal file
110
v2rayN/ServiceLib/ViewModels/CustomConfigViewModel.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
using System.Reactive;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
public class CustomConfigViewModel : MyReactiveObject
|
||||
{
|
||||
#region Reactive
|
||||
[Reactive]
|
||||
public bool EnableCustomConfig4Ray { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool EnableCustomConfig4Singbox { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string CustomConfig4Ray { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string CustomConfig4Singbox { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string CustomTunConfig4Singbox { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AddProxyOnly4Ray { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AddProxyOnly4Singbox { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string ProxyDetour4Ray { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string ProxyDetour4Singbox { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
#endregion Reactive
|
||||
|
||||
public CustomConfigViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveSettingAsync();
|
||||
});
|
||||
|
||||
_ = Init();
|
||||
}
|
||||
private async Task Init()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray);
|
||||
EnableCustomConfig4Ray = item?.Enabled ?? false;
|
||||
CustomConfig4Ray = item?.Config ?? string.Empty;
|
||||
AddProxyOnly4Ray = item?.AddProxyOnly ?? false;
|
||||
ProxyDetour4Ray = item?.ProxyDetour ?? string.Empty;
|
||||
|
||||
var item2 = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box);
|
||||
EnableCustomConfig4Singbox = item2?.Enabled ?? false;
|
||||
CustomConfig4Singbox = item2?.Config ?? string.Empty;
|
||||
CustomTunConfig4Singbox = item2?.TunConfig ?? string.Empty;
|
||||
AddProxyOnly4Singbox = item2?.AddProxyOnly ?? false;
|
||||
ProxyDetour4Singbox = item2?.ProxyDetour ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
if (!await SaveXrayConfigAsync())
|
||||
return;
|
||||
|
||||
if (!await SaveSingboxConfigAsync())
|
||||
return;
|
||||
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_ = _updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
|
||||
private async Task<bool> SaveXrayConfigAsync()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetCustomConfigItem(ECoreType.Xray);
|
||||
item.Enabled = EnableCustomConfig4Ray;
|
||||
item.Config = null;
|
||||
|
||||
item.Config = CustomConfig4Ray;
|
||||
|
||||
item.AddProxyOnly = AddProxyOnly4Ray;
|
||||
item.ProxyDetour = ProxyDetour4Ray;
|
||||
|
||||
await ConfigHandler.SaveCustomConfigItem(_config, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> SaveSingboxConfigAsync()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetCustomConfigItem(ECoreType.sing_box);
|
||||
item.Enabled = EnableCustomConfig4Singbox;
|
||||
item.Config = null;
|
||||
item.TunConfig = null;
|
||||
|
||||
item.Config = CustomConfig4Singbox;
|
||||
item.TunConfig = CustomTunConfig4Singbox;
|
||||
|
||||
item.AddProxyOnly = AddProxyOnly4Singbox;
|
||||
item.ProxyDetour = ProxyDetour4Singbox;
|
||||
|
||||
await ConfigHandler.SaveCustomConfigItem(_config, item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
|
||||
public ReactiveCommand<Unit, Unit> RoutingSettingCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> DNSSettingCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CustomConfigCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> GlobalHotkeySettingCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RebootAsAdminCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ClearServerStatisticsCmd { get; }
|
||||
|
|
@ -169,6 +170,10 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
await DNSSettingAsync();
|
||||
});
|
||||
CustomConfigCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await CustomConfigAsync();
|
||||
});
|
||||
GlobalHotkeySettingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
if (await _updateView?.Invoke(EViewAction.GlobalHotkeySettingWindow, null) == true)
|
||||
|
|
@ -220,6 +225,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
|
||||
await ConfigHandler.InitBuiltinRouting(_config);
|
||||
await ConfigHandler.InitBuiltinDNS(_config);
|
||||
await ConfigHandler.InitBuiltinCustomConfig(_config);
|
||||
await ProfileExHandler.Instance.Init();
|
||||
await CoreHandler.Instance.Init(_config, UpdateHandler);
|
||||
TaskHandler.Instance.RegUpdateTask(_config, UpdateTaskHandler);
|
||||
|
|
@ -508,6 +514,15 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
}
|
||||
}
|
||||
|
||||
private async Task CustomConfigAsync()
|
||||
{
|
||||
var ret = await _updateView?.Invoke(EViewAction.CustomConfigWindow, null);
|
||||
if (ret == true)
|
||||
{
|
||||
await Reload();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RebootAsAdmin()
|
||||
{
|
||||
ProcUtils.RebootAsAdmin();
|
||||
|
|
|
|||
182
v2rayN/v2rayN.Desktop/Views/CustomConfigWindow.axaml
Normal file
182
v2rayN/v2rayN.Desktop/Views/CustomConfigWindow.axaml
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<Window
|
||||
x:Class="v2rayN.Desktop.Views.CustomConfigWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuCustomConfig}"
|
||||
Width="900"
|
||||
Height="600"
|
||||
x:DataType="vms:CustomConfigViewModel"
|
||||
ShowInTaskbar="False"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d">
|
||||
<DockPanel Margin="{StaticResource Margin8}">
|
||||
<StackPanel
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Center"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnSave"
|
||||
Width="100"
|
||||
Content="{x:Static resx:ResUI.TbConfirm}"
|
||||
Cursor="Hand"
|
||||
IsDefault="True" />
|
||||
<Button
|
||||
x:Name="btnCancel"
|
||||
Width="100"
|
||||
Margin="{StaticResource MarginLr8}"
|
||||
Content="{x:Static resx:ResUI.TbCancel}"
|
||||
Cursor="Hand"
|
||||
IsCancel="True" />
|
||||
</StackPanel>
|
||||
|
||||
<TabControl HorizontalContentAlignment="Stretch">
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbRayCustomConfig}">
|
||||
<DockPanel Margin="{StaticResource Margin4}">
|
||||
<Grid DockPanel.Dock="Top" RowDefinitions="Auto,Auto">
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbRayCustomConfigDesc}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbCustomConfigEnable}" />
|
||||
<ToggleSwitch
|
||||
x:Name="rayCustomConfigEnable"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbAddProxyProtocolOutboundOnly}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togAddProxyProtocolOutboundOnly4Ray"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSetUpstreamProxyDetour}" />
|
||||
<TextBox
|
||||
x:Name="txtProxyDetour4Ray"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
|
||||
<HeaderedContentControl
|
||||
Margin="{StaticResource Margin4}"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="xray json config">
|
||||
<TextBox
|
||||
x:Name="rayCustomConfig"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
</HeaderedContentControl>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbCustomConfigSingbox}">
|
||||
<DockPanel Margin="{StaticResource Margin4}">
|
||||
<Grid DockPanel.Dock="Top" RowDefinitions="Auto,Auto">
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSBCustomConfigDesc}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbCustomConfigEnable}" />
|
||||
<ToggleSwitch
|
||||
x:Name="sbCustomConfigEnable"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbAddProxyProtocolOutboundOnly}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togAddProxyProtocolOutboundOnly4Singbox"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSetUpstreamProxyDetour}" />
|
||||
<TextBox
|
||||
x:Name="txtProxyDetour4Singbox"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
|
||||
<Grid Margin="{StaticResource Margin4}" ColumnDefinitions="*,10,*">
|
||||
|
||||
<HeaderedContentControl
|
||||
Grid.Column="0"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="sing-box json config">
|
||||
<TextBox
|
||||
x:Name="sbCustomConfig"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
</HeaderedContentControl>
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
|
||||
|
||||
<HeaderedContentControl
|
||||
Grid.Column="2"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="sing-box json tun config">
|
||||
<TextBox
|
||||
x:Name="sbCustomTunConfig"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
</HeaderedContentControl>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
46
v2rayN/v2rayN.Desktop/Views/CustomConfigWindow.axaml.cs
Normal file
46
v2rayN/v2rayN.Desktop/Views/CustomConfigWindow.axaml.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Interactivity;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class CustomConfigWindow : WindowBase<CustomConfigViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
|
||||
public CustomConfigWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_config = AppHandler.Instance.Config;
|
||||
btnCancel.Click += (s, e) => this.Close();
|
||||
ViewModel = new CustomConfigViewModel(UpdateViewHandler);
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.Bind(ViewModel, vm => vm.EnableCustomConfig4Ray, v => v.rayCustomConfigEnable.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CustomConfig4Ray, v => v.rayCustomConfig.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AddProxyOnly4Ray, v => v.togAddProxyProtocolOutboundOnly4Ray.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.ProxyDetour4Ray, v => v.txtProxyDetour4Ray.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableCustomConfig4Singbox, v => v.sbCustomConfigEnable.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CustomConfig4Singbox, v => v.sbCustomConfig.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CustomTunConfig4Singbox, v => v.sbCustomTunConfig.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AddProxyOnly4Singbox, v => v.togAddProxyProtocolOutboundOnly4Singbox.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.ProxyDetour4Singbox, v => v.txtProxyDetour4Singbox.Text).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case EViewAction.CloseWindow:
|
||||
this.Close(true);
|
||||
break;
|
||||
}
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +72,7 @@
|
|||
<MenuItem x:Name="menuOptionSetting" Header="{x:Static resx:ResUI.menuOptionSetting}" />
|
||||
<MenuItem x:Name="menuRoutingSetting" Header="{x:Static resx:ResUI.menuRoutingSetting}" />
|
||||
<MenuItem x:Name="menuDNSSetting" Header="{x:Static resx:ResUI.menuDNSSetting}" />
|
||||
<MenuItem x:Name="menuCustomConfig" Header="{x:Static resx:ResUI.menuCustomConfig}" />
|
||||
<MenuItem x:Name="menuGlobalHotkeySetting" Header="{x:Static resx:ResUI.menuGlobalHotkeySetting}" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuRebootAsAdmin" Header="{x:Static resx:ResUI.menuRebootAsAdmin}" />
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
this.BindCommand(ViewModel, vm => vm.OptionSettingCmd, v => v.menuOptionSetting).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.RoutingSettingCmd, v => v.menuRoutingSetting).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.DNSSettingCmd, v => v.menuDNSSetting).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CustomConfigCmd, v => v.menuCustomConfig).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GlobalHotkeySettingCmd, v => v.menuGlobalHotkeySetting).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.RebootAsAdminCmd, v => v.menuRebootAsAdmin).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ClearServerStatisticsCmd, v => v.menuClearServerStatistics).DisposeWith(disposables);
|
||||
|
|
@ -190,6 +191,9 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
case EViewAction.DNSSettingWindow:
|
||||
return await new DNSSettingWindow().ShowDialog<bool>(this);
|
||||
|
||||
case EViewAction.CustomConfigWindow:
|
||||
return await new CustomConfigWindow().ShowDialog<bool>(this);
|
||||
|
||||
case EViewAction.RoutingSettingWindow:
|
||||
return await new RoutingSettingWindow().ShowDialog<bool>(this);
|
||||
|
||||
|
|
|
|||
205
v2rayN/v2rayN/Views/CustomConfigWindow.xaml
Normal file
205
v2rayN/v2rayN/Views/CustomConfigWindow.xaml
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
<base:WindowBase
|
||||
x:Class="v2rayN.Views.CustomConfigWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Title="{x:Static resx:ResUI.menuCustomConfig}"
|
||||
Width="1000"
|
||||
Height="700"
|
||||
x:TypeArguments="vms:CustomConfigViewModel"
|
||||
ShowInTaskbar="False"
|
||||
Style="{StaticResource WindowGlobal}"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d">
|
||||
<DockPanel Margin="{StaticResource Margin8}">
|
||||
<StackPanel
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Center"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnSave"
|
||||
Width="100"
|
||||
Content="{x:Static resx:ResUI.TbConfirm}"
|
||||
Cursor="Hand"
|
||||
IsDefault="True"
|
||||
Style="{StaticResource DefButton}" />
|
||||
<Button
|
||||
x:Name="btnCancel"
|
||||
Width="100"
|
||||
Margin="{StaticResource MarginLeftRight8}"
|
||||
Content="{x:Static resx:ResUI.TbCancel}"
|
||||
Cursor="Hand"
|
||||
IsCancel="true"
|
||||
Style="{StaticResource DefButton}" />
|
||||
</StackPanel>
|
||||
|
||||
<TabControl HorizontalContentAlignment="Left">
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbRayCustomConfig}">
|
||||
<DockPanel Margin="{StaticResource Margin8}">
|
||||
<Grid DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbRayCustomConfigDesc}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbCustomConfigEnable}" />
|
||||
<ToggleButton
|
||||
x:Name="rayCustomConfigEnable"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbAddProxyProtocolOutboundOnly}" />
|
||||
<ToggleButton
|
||||
x:Name="togAddProxyProtocolOutboundOnly4Ray"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSetUpstreamProxyDetour}" />
|
||||
<TextBox
|
||||
x:Name="txtProxyDetour4Ray"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
|
||||
<TextBox
|
||||
x:Name="rayCustomConfig"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Stretch"
|
||||
materialDesign:HintAssist.Hint="xray json config"
|
||||
AcceptsReturn="True"
|
||||
BorderThickness="1"
|
||||
Style="{StaticResource MaterialDesignOutlinedTextBox}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto" />
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbCustomConfigSingbox}">
|
||||
<DockPanel Margin="{StaticResource Margin8}">
|
||||
<Grid DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSBCustomConfigDesc}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbCustomConfigEnable}" />
|
||||
<ToggleButton
|
||||
x:Name="sbCustomConfigEnable"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbAddProxyProtocolOutboundOnly}" />
|
||||
<ToggleButton
|
||||
x:Name="togAddProxyProtocolOutboundOnly4Singbox"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSetUpstreamProxyDetour}" />
|
||||
<TextBox
|
||||
x:Name="txtProxyDetour4Singbox"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
|
||||
<Grid Margin="{StaticResource Margin8}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
x:Name="sbCustomConfig"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Stretch"
|
||||
materialDesign:HintAssist.Hint="sing-box json config"
|
||||
AcceptsReturn="True"
|
||||
BorderThickness="1"
|
||||
Style="{StaticResource MaterialDesignOutlinedTextBox}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto" />
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
|
||||
|
||||
<TextBox
|
||||
x:Name="sbCustomTunConfig"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Stretch"
|
||||
materialDesign:HintAssist.Hint="sing-box json tun config"
|
||||
AcceptsReturn="True"
|
||||
BorderThickness="1"
|
||||
Style="{StaticResource MaterialDesignOutlinedTextBox}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
47
v2rayN/v2rayN/Views/CustomConfigWindow.xaml.cs
Normal file
47
v2rayN/v2rayN/Views/CustomConfigWindow.xaml.cs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
public partial class CustomConfigWindow
|
||||
{
|
||||
private static Config _config;
|
||||
|
||||
public CustomConfigWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.Owner = Application.Current.MainWindow;
|
||||
_config = AppHandler.Instance.Config;
|
||||
|
||||
ViewModel = new CustomConfigViewModel(UpdateViewHandler);
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.Bind(ViewModel, vm => vm.EnableCustomConfig4Ray, v => v.rayCustomConfigEnable.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CustomConfig4Ray, v => v.rayCustomConfig.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AddProxyOnly4Ray, v => v.togAddProxyProtocolOutboundOnly4Ray.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.ProxyDetour4Ray, v => v.txtProxyDetour4Ray.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableCustomConfig4Singbox, v => v.sbCustomConfigEnable.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CustomConfig4Singbox, v => v.sbCustomConfig.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CustomTunConfig4Singbox, v => v.sbCustomTunConfig.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AddProxyOnly4Singbox, v => v.togAddProxyProtocolOutboundOnly4Singbox.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.ProxyDetour4Singbox, v => v.txtProxyDetour4Singbox.Text).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case EViewAction.CloseWindow:
|
||||
this.DialogResult = true;
|
||||
break;
|
||||
}
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -173,6 +173,10 @@
|
|||
x:Name="menuDNSSetting"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuDNSSetting}" />
|
||||
<MenuItem
|
||||
x:Name="menuCustomConfig"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuCustomConfig}" />
|
||||
<MenuItem
|
||||
x:Name="menuGlobalHotkeySetting"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ public partial class MainWindow
|
|||
this.BindCommand(ViewModel, vm => vm.OptionSettingCmd, v => v.menuOptionSetting).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.RoutingSettingCmd, v => v.menuRoutingSetting).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.DNSSettingCmd, v => v.menuDNSSetting).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CustomConfigCmd, v => v.menuCustomConfig).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.GlobalHotkeySettingCmd, v => v.menuGlobalHotkeySetting).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.RebootAsAdminCmd, v => v.menuRebootAsAdmin).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ClearServerStatisticsCmd, v => v.menuClearServerStatistics).DisposeWith(disposables);
|
||||
|
|
@ -186,6 +187,9 @@ public partial class MainWindow
|
|||
case EViewAction.OptionSettingWindow:
|
||||
return (new OptionSettingWindow().ShowDialog() ?? false);
|
||||
|
||||
case EViewAction.CustomConfigWindow:
|
||||
return (new CustomConfigWindow().ShowDialog() ?? false);
|
||||
|
||||
case EViewAction.GlobalHotkeySettingWindow:
|
||||
return (new GlobalHotkeySettingWindow().ShowDialog() ?? false);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue