mirror of
https://github.com/2dust/v2rayN.git
synced 2025-08-23 19:36:55 +00:00
Compare commits
7 commits
c27651b7b7
...
7b7fe0ef46
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7b7fe0ef46 | ||
![]() |
f66226c103 | ||
![]() |
d5c50ef27c | ||
![]() |
2060ac18fd | ||
![]() |
c9c1cd8cbb | ||
![]() |
5201dd5ad0 | ||
![]() |
4c3c1e0b5f |
104 changed files with 4769 additions and 4685 deletions
|
@ -79,15 +79,7 @@ internal class UpgradeApp
|
|||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
entry.ExtractToFile(entryOutputPath, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
entry.ExtractToFile(entryOutputPath, true);
|
||||
}
|
||||
TryExtractToFile(entry, entryOutputPath);
|
||||
|
||||
Console.WriteLine(entryOutputPath);
|
||||
}
|
||||
|
@ -113,4 +105,24 @@ internal class UpgradeApp
|
|||
|
||||
Utils.StartV2RayN();
|
||||
}
|
||||
|
||||
private static bool TryExtractToFile(ZipArchiveEntry entry, string outputPath)
|
||||
{
|
||||
var retryCount = 5;
|
||||
var delayMs = 1000;
|
||||
|
||||
for (var i = 1; i <= retryCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
entry.ExtractToFile(outputPath, true);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Thread.Sleep(delayMs * i);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.14.0</Version>
|
||||
<Version>7.14.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -4,7 +4,7 @@ using ZXing.SkiaSharp;
|
|||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
public class QRCodeHelper
|
||||
public class QRCodeUtils
|
||||
{
|
||||
public static byte[]? GenQRCode(string? url)
|
||||
{
|
|
@ -1,7 +1,9 @@
|
|||
global using ServiceLib.Base;
|
||||
global using ServiceLib.Base;
|
||||
global using ServiceLib.Common;
|
||||
global using ServiceLib.Enums;
|
||||
global using ServiceLib.Handler;
|
||||
global using ServiceLib.Helper;
|
||||
global using ServiceLib.Manager;
|
||||
global using ServiceLib.Handler.Fmt;
|
||||
global using ServiceLib.Services;
|
||||
global using ServiceLib.Services.Statistics;
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Text.RegularExpressions;
|
|||
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
public class ConfigHandler
|
||||
public static class ConfigHandler
|
||||
{
|
||||
private static readonly string _configRes = Global.ConfigFileName;
|
||||
private static readonly string _tag = "ConfigHandler";
|
||||
|
@ -216,7 +216,7 @@ public class ConfigHandler
|
|||
/// <returns>Result of the operation (0 if successful, -1 if failed)</returns>
|
||||
public static async Task<int> AddServer(Config config, ProfileItem profileItem)
|
||||
{
|
||||
var item = await AppHandler.Instance.GetProfileItem(profileItem.IndexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(profileItem.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
item = profileItem;
|
||||
|
@ -336,7 +336,7 @@ public class ConfigHandler
|
|||
{
|
||||
foreach (var it in indexes)
|
||||
{
|
||||
var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
continue;
|
||||
|
@ -418,7 +418,7 @@ public class ConfigHandler
|
|||
/// <returns>The default profile item or null if none exists</returns>
|
||||
public static async Task<ProfileItem?> GetDefaultServer(Config config)
|
||||
{
|
||||
var item = await AppHandler.Instance.GetProfileItem(config.IndexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(config.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
var item2 = await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync();
|
||||
|
@ -449,7 +449,7 @@ public class ConfigHandler
|
|||
|
||||
for (int i = 0; i < lstProfile.Count; i++)
|
||||
{
|
||||
ProfileExHandler.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
|
||||
ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
|
||||
}
|
||||
|
||||
var sort = 0;
|
||||
|
@ -461,7 +461,7 @@ public class ConfigHandler
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
sort = ProfileExHandler.Instance.GetSort(lstProfile.First().IndexId) - 1;
|
||||
sort = ProfileExManager.Instance.GetSort(lstProfile.First().IndexId) - 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -471,7 +471,7 @@ public class ConfigHandler
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
sort = ProfileExHandler.Instance.GetSort(lstProfile[index - 1].IndexId) - 1;
|
||||
sort = ProfileExManager.Instance.GetSort(lstProfile[index - 1].IndexId) - 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -482,7 +482,7 @@ public class ConfigHandler
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
sort = ProfileExHandler.Instance.GetSort(lstProfile[index + 1].IndexId) + 1;
|
||||
sort = ProfileExManager.Instance.GetSort(lstProfile[index + 1].IndexId) + 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -492,7 +492,7 @@ public class ConfigHandler
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
sort = ProfileExHandler.Instance.GetSort(lstProfile[^1].IndexId) + 1;
|
||||
sort = ProfileExManager.Instance.GetSort(lstProfile[^1].IndexId) + 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -501,7 +501,7 @@ public class ConfigHandler
|
|||
break;
|
||||
}
|
||||
|
||||
ProfileExHandler.Instance.SetSort(lstProfile[index].IndexId, sort);
|
||||
ProfileExManager.Instance.SetSort(lstProfile[index].IndexId, sort);
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
|
@ -559,7 +559,7 @@ public class ConfigHandler
|
|||
/// <returns>0 if successful, -1 if failed</returns>
|
||||
public static async Task<int> EditCustomServer(Config config, ProfileItem profileItem)
|
||||
{
|
||||
var item = await AppHandler.Instance.GetProfileItem(profileItem.IndexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(profileItem.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
item = profileItem;
|
||||
|
@ -601,7 +601,7 @@ public class ConfigHandler
|
|||
profileItem.Id = profileItem.Id.TrimEx();
|
||||
profileItem.Security = profileItem.Security.TrimEx();
|
||||
|
||||
if (!AppHandler.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.Security))
|
||||
if (!AppManager.Instance.GetShadowsocksSecurities(profileItem).Contains(profileItem.Security))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -829,13 +829,13 @@ public class ConfigHandler
|
|||
/// <returns>0 if successful, -1 if failed</returns>
|
||||
public static async Task<int> SortServers(Config config, string subId, string colName, bool asc)
|
||||
{
|
||||
var lstModel = await AppHandler.Instance.ProfileItems(subId, "");
|
||||
var lstModel = await AppManager.Instance.ProfileItems(subId, "");
|
||||
if (lstModel.Count <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var lstServerStat = (config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
|
||||
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
|
||||
var lstServerStat = (config.GuiItem.EnableStatistics ? StatisticsManager.Instance.ServerStat : null) ?? [];
|
||||
var lstProfileExs = await ProfileExManager.Instance.GetProfileExs();
|
||||
var lstProfile = (from t in lstModel
|
||||
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
|
||||
from t22 in t2b.DefaultIfEmpty()
|
||||
|
@ -905,7 +905,7 @@ public class ConfigHandler
|
|||
|
||||
for (var i = 0; i < lstProfile.Count; i++)
|
||||
{
|
||||
ProfileExHandler.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
|
||||
ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
|
||||
}
|
||||
switch (name)
|
||||
{
|
||||
|
@ -914,7 +914,7 @@ public class ConfigHandler
|
|||
var maxSort = lstProfile.Max(t => t.Sort) + 10;
|
||||
foreach (var item in lstProfile.Where(item => item.Delay <= 0))
|
||||
{
|
||||
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort);
|
||||
ProfileExManager.Instance.SetSort(item.IndexId, maxSort);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -924,7 +924,7 @@ public class ConfigHandler
|
|||
var maxSort = lstProfile.Max(t => t.Sort) + 10;
|
||||
foreach (var item in lstProfile.Where(item => item.Speed <= 0))
|
||||
{
|
||||
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort);
|
||||
ProfileExManager.Instance.SetSort(item.IndexId, maxSort);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -982,7 +982,7 @@ public class ConfigHandler
|
|||
/// <returns>Tuple with total count and remaining count after deduplication</returns>
|
||||
public static async Task<Tuple<int, int>> DedupServerList(Config config, string subId)
|
||||
{
|
||||
var lstProfile = await AppHandler.Instance.ProfileItems(subId);
|
||||
var lstProfile = await AppManager.Instance.ProfileItems(subId);
|
||||
if (lstProfile == null)
|
||||
{
|
||||
return new Tuple<int, int>(0, 0);
|
||||
|
@ -1052,15 +1052,15 @@ public class ConfigHandler
|
|||
if (profileItem.IndexId.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.IndexId = Utils.GetGuid(false);
|
||||
maxSort = ProfileExHandler.Instance.GetMaxSort();
|
||||
maxSort = ProfileExManager.Instance.GetMaxSort();
|
||||
}
|
||||
if (!toFile && maxSort < 0)
|
||||
{
|
||||
maxSort = ProfileExHandler.Instance.GetMaxSort();
|
||||
maxSort = ProfileExManager.Instance.GetMaxSort();
|
||||
}
|
||||
if (maxSort > 0)
|
||||
{
|
||||
ProfileExHandler.Instance.SetSort(profileItem.IndexId, maxSort + 1);
|
||||
ProfileExManager.Instance.SetSort(profileItem.IndexId, maxSort + 1);
|
||||
}
|
||||
|
||||
if (toFile)
|
||||
|
@ -1120,7 +1120,7 @@ public class ConfigHandler
|
|||
{
|
||||
try
|
||||
{
|
||||
var item = await AppHandler.Instance.GetProfileItem(indexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(indexId);
|
||||
if (item == null)
|
||||
{
|
||||
return 0;
|
||||
|
@ -1165,7 +1165,7 @@ public class ConfigHandler
|
|||
return result;
|
||||
}
|
||||
|
||||
var profileItem = await AppHandler.Instance.GetProfileItem(indexId) ?? new();
|
||||
var profileItem = await AppManager.Instance.GetProfileItem(indexId) ?? new();
|
||||
profileItem.IndexId = indexId;
|
||||
if (coreType == ECoreType.Xray)
|
||||
{
|
||||
|
@ -1211,7 +1211,7 @@ public class ConfigHandler
|
|||
ConfigType = EConfigType.SOCKS,
|
||||
Address = Global.Loopback,
|
||||
Sni = node.Address, //Tun2SocksAddress
|
||||
Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||
};
|
||||
}
|
||||
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
|
||||
|
@ -1238,12 +1238,12 @@ public class ConfigHandler
|
|||
/// <returns>Number of removed servers or -1 if failed</returns>
|
||||
public static async Task<int> RemoveInvalidServerResult(Config config, string subid)
|
||||
{
|
||||
var lstModel = await AppHandler.Instance.ProfileItems(subid, "");
|
||||
var lstModel = await AppManager.Instance.ProfileItems(subid, "");
|
||||
if (lstModel is { Count: <= 0 })
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
|
||||
var lstProfileExs = await ProfileExManager.Instance.GetProfileExs();
|
||||
var lstProfile = (from t in lstModel
|
||||
join t2 in lstProfileExs on t.IndexId equals t2.IndexId
|
||||
where t2.Delay == -1
|
||||
|
@ -1279,7 +1279,7 @@ public class ConfigHandler
|
|||
if (isSub && subid.IsNotEmpty())
|
||||
{
|
||||
await RemoveServersViaSubid(config, subid, isSub);
|
||||
subFilter = (await AppHandler.Instance.GetSubItem(subid))?.Filter ?? "";
|
||||
subFilter = (await AppManager.Instance.GetSubItem(subid))?.Filter ?? "";
|
||||
}
|
||||
|
||||
var countServers = 0;
|
||||
|
@ -1363,7 +1363,7 @@ public class ConfigHandler
|
|||
return -1;
|
||||
}
|
||||
|
||||
var subItem = await AppHandler.Instance.GetSubItem(subid);
|
||||
var subItem = await AppManager.Instance.GetSubItem(subid);
|
||||
var subRemarks = subItem?.Remarks;
|
||||
var preSocksPort = subItem?.PreSocksPort;
|
||||
|
||||
|
@ -1519,7 +1519,7 @@ public class ConfigHandler
|
|||
ProfileItem? activeProfile = null;
|
||||
if (isSub && subid.IsNotEmpty())
|
||||
{
|
||||
lstOriSub = await AppHandler.Instance.ProfileItems(subid);
|
||||
lstOriSub = await AppManager.Instance.ProfileItems(subid);
|
||||
activeProfile = lstOriSub?.FirstOrDefault(t => t.IndexId == config.IndexId);
|
||||
}
|
||||
|
||||
|
@ -1551,7 +1551,7 @@ public class ConfigHandler
|
|||
//Select active node
|
||||
if (activeProfile != null)
|
||||
{
|
||||
var lstSub = await AppHandler.Instance.ProfileItems(subid);
|
||||
var lstSub = await AppManager.Instance.ProfileItems(subid);
|
||||
var existItem = lstSub?.FirstOrDefault(t => config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == activeProfile.Remarks : CompareProfileItem(t, activeProfile, true));
|
||||
if (existItem != null)
|
||||
{
|
||||
|
@ -1562,13 +1562,13 @@ public class ConfigHandler
|
|||
//Keep the last traffic statistics
|
||||
if (lstOriSub != null)
|
||||
{
|
||||
var lstSub = await AppHandler.Instance.ProfileItems(subid);
|
||||
var lstSub = await AppManager.Instance.ProfileItems(subid);
|
||||
foreach (var item in lstSub)
|
||||
{
|
||||
var existItem = lstOriSub?.FirstOrDefault(t => config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == item.Remarks : CompareProfileItem(t, item, true));
|
||||
if (existItem != null)
|
||||
{
|
||||
await StatisticsHandler.Instance.CloneServerStatItem(existItem.IndexId, item.IndexId);
|
||||
await StatisticsManager.Instance.CloneServerStatItem(existItem.IndexId, item.IndexId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1608,7 +1608,7 @@ public class ConfigHandler
|
|||
if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost))
|
||||
{
|
||||
//TODO Temporary reminder to be removed later
|
||||
NoticeHandler.Instance.Enqueue(ResUI.InsecureUrlProtocol);
|
||||
NoticeManager.Instance.Enqueue(ResUI.InsecureUrlProtocol);
|
||||
//return -1;
|
||||
}
|
||||
|
||||
|
@ -1626,7 +1626,7 @@ public class ConfigHandler
|
|||
/// <returns>0 if successful, -1 if failed</returns>
|
||||
public static async Task<int> AddSubItem(Config config, SubItem subItem)
|
||||
{
|
||||
var item = await AppHandler.Instance.GetSubItem(subItem.Id);
|
||||
var item = await AppManager.Instance.GetSubItem(subItem.Id);
|
||||
if (item is null)
|
||||
{
|
||||
item = subItem;
|
||||
|
@ -1658,7 +1658,7 @@ public class ConfigHandler
|
|||
var maxSort = 0;
|
||||
if (await SQLiteHelper.Instance.TableAsync<SubItem>().CountAsync() > 0)
|
||||
{
|
||||
var lstSubs = (await AppHandler.Instance.SubItems());
|
||||
var lstSubs = (await AppManager.Instance.SubItems());
|
||||
maxSort = lstSubs.LastOrDefault()?.Sort ?? 0;
|
||||
}
|
||||
item.Sort = maxSort + 1;
|
||||
|
@ -1712,7 +1712,7 @@ public class ConfigHandler
|
|||
/// <returns>0 if successful</returns>
|
||||
public static async Task<int> DeleteSubItem(Config config, string id)
|
||||
{
|
||||
var item = await AppHandler.Instance.GetSubItem(id);
|
||||
var item = await AppManager.Instance.GetSubItem(id);
|
||||
if (item is null)
|
||||
{
|
||||
return 0;
|
||||
|
@ -1896,7 +1896,7 @@ public class ConfigHandler
|
|||
/// <returns>0 if successful</returns>
|
||||
public static async Task<int> SetDefaultRouting(Config config, RoutingItem routingItem)
|
||||
{
|
||||
var items = await AppHandler.Instance.RoutingItems();
|
||||
var items = await AppManager.Instance.RoutingItems();
|
||||
if (items.Any(t => t.Id == routingItem.Id && t.IsActive == true))
|
||||
{
|
||||
return -1;
|
||||
|
@ -1976,7 +1976,7 @@ public class ConfigHandler
|
|||
if (template == null)
|
||||
return await InitBuiltinRouting(config, blImportAdvancedRules); // fallback
|
||||
|
||||
var items = await AppHandler.Instance.RoutingItems();
|
||||
var items = await AppManager.Instance.RoutingItems();
|
||||
var maxSort = items.Count;
|
||||
if (!blImportAdvancedRules && items.Where(t => t.Remarks.StartsWith(template.Version)).ToList().Count > 0)
|
||||
{
|
||||
|
@ -2023,14 +2023,14 @@ public class ConfigHandler
|
|||
public static async Task<int> InitBuiltinRouting(Config config, bool blImportAdvancedRules = false)
|
||||
{
|
||||
var ver = "V3-";
|
||||
var items = await AppHandler.Instance.RoutingItems();
|
||||
var items = await AppManager.Instance.RoutingItems();
|
||||
|
||||
//TODO Temporary code to be removed later
|
||||
var lockItem = items?.FirstOrDefault(t => t.Locked == true);
|
||||
if (lockItem != null)
|
||||
{
|
||||
await ConfigHandler.RemoveRoutingItem(lockItem);
|
||||
items = await AppHandler.Instance.RoutingItems();
|
||||
items = await AppManager.Instance.RoutingItems();
|
||||
}
|
||||
|
||||
if (!blImportAdvancedRules && items.Count > 0)
|
||||
|
@ -2107,7 +2107,7 @@ public class ConfigHandler
|
|||
/// <returns>0 if successful</returns>
|
||||
public static async Task<int> InitBuiltinDNS(Config config)
|
||||
{
|
||||
var items = await AppHandler.Instance.DNSItems();
|
||||
var items = await AppManager.Instance.DNSItems();
|
||||
|
||||
// Check existing DNS items and disable those with empty NormalDNS
|
||||
var needsUpdate = false;
|
||||
|
@ -2185,7 +2185,7 @@ public class ConfigHandler
|
|||
/// <returns>DNS item with configuration from the URL</returns>
|
||||
public static async Task<DNSItem> GetExternalDNSItem(ECoreType type, string url)
|
||||
{
|
||||
var currentItem = await AppHandler.Instance.GetDNSItem(type);
|
||||
var currentItem = await AppManager.Instance.GetDNSItem(type);
|
||||
|
||||
var downloadHandle = new DownloadService();
|
||||
var templateContent = await downloadHandle.TryDownloadString(url, true, "");
|
||||
|
@ -2247,7 +2247,7 @@ public class ConfigHandler
|
|||
|
||||
public static async Task<int> InitBuiltinFullConfigTemplate(Config config)
|
||||
{
|
||||
var items = await AppHandler.Instance.FullConfigTemplateItem();
|
||||
var items = await AppManager.Instance.FullConfigTemplateItem();
|
||||
if (items.Count <= 0)
|
||||
{
|
||||
var item = new FullConfigTemplateItem()
|
||||
|
|
|
@ -1,27 +1,28 @@
|
|||
using System.Net;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
public class ConnectionHandler
|
||||
public static class ConnectionHandler
|
||||
{
|
||||
private static readonly Lazy<ConnectionHandler> _instance = new(() => new());
|
||||
public static ConnectionHandler Instance => _instance.Value;
|
||||
private static readonly string _tag = "ConnectionHandler";
|
||||
|
||||
public async Task<string> RunAvailabilityCheck()
|
||||
public static async Task<string> RunAvailabilityCheck()
|
||||
{
|
||||
var downloadHandle = new DownloadService();
|
||||
var time = await downloadHandle.RunAvailabilityCheck(null);
|
||||
var ip = time > 0 ? await GetIPInfo(downloadHandle) ?? Global.None : Global.None;
|
||||
var time = await GetRealPingTime();
|
||||
var ip = time > 0 ? await GetIPInfo() ?? Global.None : Global.None;
|
||||
|
||||
return string.Format(ResUI.TestMeOutput, time, ip);
|
||||
}
|
||||
|
||||
private async Task<string?> GetIPInfo(DownloadService downloadHandle)
|
||||
private static async Task<string?> GetIPInfo()
|
||||
{
|
||||
var url = AppHandler.Instance.Config.SpeedTestItem.IPAPIUrl;
|
||||
var url = AppManager.Instance.Config.SpeedTestItem.IPAPIUrl;
|
||||
if (url.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var downloadHandle = new DownloadService();
|
||||
var result = await downloadHandle.TryDownloadString(url, true, "");
|
||||
if (result == null)
|
||||
{
|
||||
|
@ -39,4 +40,31 @@ public class ConnectionHandler
|
|||
|
||||
return $"({country ?? "unknown"}) {ip}";
|
||||
}
|
||||
|
||||
private static async Task<int> GetRealPingTime()
|
||||
{
|
||||
var responseTime = -1;
|
||||
try
|
||||
{
|
||||
var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{port}");
|
||||
var url = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
|
||||
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
responseTime = await HttpClientHelper.Instance.GetRealPingTime(url, webProxy, 10);
|
||||
if (responseTime > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
await Task.Delay(500);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return -1;
|
||||
}
|
||||
return responseTime;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@ namespace ServiceLib.Handler;
|
|||
/// <summary>
|
||||
/// Core configuration file processing class
|
||||
/// </summary>
|
||||
public class CoreConfigHandler
|
||||
public static class CoreConfigHandler
|
||||
{
|
||||
private static readonly string _tag = "CoreConfigHandler";
|
||||
|
||||
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var config = AppHandler.Instance.Config;
|
||||
var config = AppManager.Instance.Config;
|
||||
var result = new RetResult();
|
||||
|
||||
if (node.ConfigType == EConfigType.Custom)
|
||||
|
@ -21,7 +21,7 @@ public class CoreConfigHandler
|
|||
_ => await GenerateClientCustomConfig(node, fileName)
|
||||
};
|
||||
}
|
||||
else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||
else if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node);
|
||||
}
|
||||
|
@ -112,11 +112,11 @@ public class CoreConfigHandler
|
|||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName)
|
||||
{
|
||||
var result = new RetResult();
|
||||
var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
|
||||
testItem.Port = port;
|
||||
|
||||
if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||
if (AppManager.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
namespace ServiceLib.Handler;
|
||||
|
||||
public class SubscriptionHandler
|
||||
public static class SubscriptionHandler
|
||||
{
|
||||
public static async Task UpdateProcess(Config config, string subId, bool blProxy, Action<bool, string> updateFunc)
|
||||
{
|
||||
updateFunc?.Invoke(false, ResUI.MsgUpdateSubscriptionStart);
|
||||
var subItem = await AppHandler.Instance.SubItems();
|
||||
var subItem = await AppManager.Instance.SubItems();
|
||||
|
||||
if (subItem is not { Count: > 0 })
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace ServiceLib.Handler.SysProxy;
|
||||
|
||||
public class ProxySettingLinux
|
||||
public static class ProxySettingLinux
|
||||
{
|
||||
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace ServiceLib.Handler.SysProxy;
|
||||
|
||||
public class ProxySettingOSX
|
||||
public static class ProxySettingOSX
|
||||
{
|
||||
private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionO
|
|||
|
||||
namespace ServiceLib.Handler.SysProxy;
|
||||
|
||||
public class ProxySettingWindows
|
||||
public static class ProxySettingWindows
|
||||
{
|
||||
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ public static class SysProxyHandler
|
|||
|
||||
try
|
||||
{
|
||||
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
var exceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
|
||||
if (port <= 0)
|
||||
{
|
||||
|
@ -56,7 +56,7 @@ public static class SysProxyHandler
|
|||
|
||||
if (type != ESysProxyType.Pac && Utils.IsWindows())
|
||||
{
|
||||
PacHandler.Instance.Stop();
|
||||
PacManager.Instance.Stop();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -90,8 +90,8 @@ public static class SysProxyHandler
|
|||
|
||||
private static async Task SetWindowsProxyPac(int port)
|
||||
{
|
||||
var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac);
|
||||
await PacHandler.Instance.StartAsync(Utils.GetConfigPath(), port, portPac);
|
||||
var portPac = AppManager.Instance.GetLocalPort(EInboundProtocol.pac);
|
||||
await PacManager.Instance.StartAsync(Utils.GetConfigPath(), port, portPac);
|
||||
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
|
||||
ProxySettingWindows.SetProxy(strProxy, "", 4);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System.Net;
|
||||
using Downloader;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
namespace ServiceLib.Helper;
|
||||
|
||||
public class DownloaderHelper
|
||||
{
|
|
@ -1,8 +1,10 @@
|
|||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
namespace ServiceLib.Helper;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
|
@ -202,4 +204,35 @@ public class HttpClientHelper
|
|||
}
|
||||
} while (isMoreToRead);
|
||||
}
|
||||
|
||||
public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
|
||||
{
|
||||
var responseTime = -1;
|
||||
try
|
||||
{
|
||||
using var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
|
||||
using var client = new HttpClient(new SocketsHttpHandler()
|
||||
{
|
||||
Proxy = webProxy,
|
||||
UseProxy = webProxy != null
|
||||
});
|
||||
|
||||
List<int> oneTime = new();
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
await client.GetAsync(url, cts.Token).ConfigureAwait(false);
|
||||
timer.Stop();
|
||||
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault();
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
//Utile.SaveLog(ex.Message, ex);
|
||||
}
|
||||
return responseTime;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections;
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
namespace ServiceLib.Helper;
|
||||
|
||||
public sealed class SQLiteHelper
|
||||
{
|
|
@ -1,15 +1,15 @@
|
|||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public sealed class AppHandler
|
||||
public sealed class AppManager
|
||||
{
|
||||
#region Property
|
||||
|
||||
private static readonly Lazy<AppHandler> _instance = new(() => new());
|
||||
private static readonly Lazy<AppManager> _instance = new(() => new());
|
||||
private Config _config;
|
||||
private int? _statePort;
|
||||
private int? _statePort2;
|
||||
private Job? _processJob;
|
||||
public static AppHandler Instance => _instance.Value;
|
||||
public static AppManager Instance => _instance.Value;
|
||||
public Config Config => _config;
|
||||
|
||||
public int StatePort
|
||||
|
@ -97,7 +97,7 @@ public sealed class AppHandler
|
|||
return localPort + (int)protocol;
|
||||
}
|
||||
|
||||
public void AddProcess(IntPtr processHandle)
|
||||
public void AddProcess(nint processHandle)
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
|
@ -1,11 +1,11 @@
|
|||
using static ServiceLib.Models.ClashProxies;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public sealed class ClashApiHandler
|
||||
public sealed class ClashApiManager
|
||||
{
|
||||
private static readonly Lazy<ClashApiHandler> instance = new(() => new());
|
||||
public static ClashApiHandler Instance => instance.Value;
|
||||
private static readonly Lazy<ClashApiManager> instance = new(() => new());
|
||||
public static ClashApiManager Instance => instance.Value;
|
||||
|
||||
private static readonly string _tag = "ClashApiHandler";
|
||||
private Dictionary<string, ProxiesItem>? _proxies;
|
||||
|
@ -65,7 +65,7 @@ public sealed class ClashApiHandler
|
|||
return;
|
||||
}
|
||||
var urlBase = $"{GetApiUrl()}/proxies";
|
||||
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
|
||||
urlBase += @"/{0}/delay?timeout=10000&url=" + AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
|
||||
|
||||
var tasks = new List<Task>();
|
||||
foreach (var it in lstProxy)
|
||||
|
@ -182,6 +182,6 @@ public sealed class ClashApiHandler
|
|||
|
||||
private string GetApiUrl()
|
||||
{
|
||||
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
|
||||
return $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort2}";
|
||||
}
|
||||
}
|
|
@ -3,12 +3,12 @@ using System.Text;
|
|||
using CliWrap;
|
||||
using CliWrap.Buffered;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public class CoreAdminHandler
|
||||
public class CoreAdminManager
|
||||
{
|
||||
private static readonly Lazy<CoreAdminHandler> _instance = new(() => new());
|
||||
public static CoreAdminHandler Instance => _instance.Value;
|
||||
private static readonly Lazy<CoreAdminManager> _instance = new(() => new());
|
||||
public static CoreAdminManager Instance => _instance.Value;
|
||||
private Config _config;
|
||||
private Action<bool, string>? _updateFunc;
|
||||
private int _linuxSudoPid = -1;
|
||||
|
@ -72,7 +72,7 @@ public class CoreAdminHandler
|
|||
proc.BeginErrorReadLine();
|
||||
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
|
||||
await proc.StandardInput.WriteLineAsync(AppManager.Instance.LinuxSudoPwd);
|
||||
|
||||
await Task.Delay(100);
|
||||
if (proc is null or { HasExited: true })
|
||||
|
@ -103,7 +103,7 @@ public class CoreAdminHandler
|
|||
var arg = new List<string>() { "-c", $"sudo -S {shFilePath} {_linuxSudoPid}" };
|
||||
var result = await Cli.Wrap(Global.LinuxBash)
|
||||
.WithArguments(arg)
|
||||
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
|
||||
.WithStandardInputPipe(PipeSource.FromString(AppManager.Instance.LinuxSudoPwd))
|
||||
.ExecuteBufferedAsync();
|
||||
|
||||
UpdateFunc(false, result.StandardOutput.ToString());
|
|
@ -1,12 +1,12 @@
|
|||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public sealed class CoreInfoHandler
|
||||
public sealed class CoreInfoManager
|
||||
{
|
||||
private static readonly Lazy<CoreInfoHandler> _instance = new(() => new());
|
||||
private static readonly Lazy<CoreInfoManager> _instance = new(() => new());
|
||||
private List<CoreInfo>? _coreInfo;
|
||||
public static CoreInfoHandler Instance => _instance.Value;
|
||||
public static CoreInfoManager Instance => _instance.Value;
|
||||
|
||||
public CoreInfoHandler()
|
||||
public CoreInfoManager()
|
||||
{
|
||||
InitCoreInfo();
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
/// <summary>
|
||||
/// Core process processing class
|
||||
/// </summary>
|
||||
public class CoreHandler
|
||||
public class CoreManager
|
||||
{
|
||||
private static readonly Lazy<CoreHandler> _instance = new(() => new());
|
||||
public static CoreHandler Instance => _instance.Value;
|
||||
private static readonly Lazy<CoreManager> _instance = new(() => new());
|
||||
public static CoreManager Instance => _instance.Value;
|
||||
private Config _config;
|
||||
private Process? _process;
|
||||
private Process? _processPre;
|
||||
|
@ -39,7 +39,7 @@ public class CoreHandler
|
|||
|
||||
if (Utils.IsNonWindows())
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo();
|
||||
foreach (var it in coreInfo)
|
||||
{
|
||||
if (it.CoreType == ECoreType.v2rayN)
|
||||
|
@ -114,7 +114,7 @@ public class CoreHandler
|
|||
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||
UpdateFunc(false, configPath);
|
||||
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
||||
var proc = await RunProcess(coreInfo, fileName, true, false);
|
||||
if (proc is null)
|
||||
{
|
||||
|
@ -126,7 +126,7 @@ public class CoreHandler
|
|||
|
||||
public async Task<int> LoadCoreConfigSpeedtest(ServerTestItem testItem)
|
||||
{
|
||||
var node = await AppHandler.Instance.GetProfileItem(testItem.IndexId);
|
||||
var node = await AppManager.Instance.GetProfileItem(testItem.IndexId);
|
||||
if (node is null)
|
||||
{
|
||||
return -1;
|
||||
|
@ -140,8 +140,8 @@ public class CoreHandler
|
|||
return -1;
|
||||
}
|
||||
|
||||
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
||||
var proc = await RunProcess(coreInfo, fileName, true, false);
|
||||
if (proc is null)
|
||||
{
|
||||
|
@ -157,7 +157,7 @@ public class CoreHandler
|
|||
{
|
||||
if (_linuxSudo)
|
||||
{
|
||||
await CoreAdminHandler.Instance.KillProcessAsLinuxSudo();
|
||||
await CoreAdminManager.Instance.KillProcessAsLinuxSudo();
|
||||
_linuxSudo = false;
|
||||
}
|
||||
|
||||
|
@ -183,8 +183,8 @@ public class CoreHandler
|
|||
|
||||
private async Task CoreStart(ProfileItem node)
|
||||
{
|
||||
var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
var coreType = _config.RunningCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
||||
|
||||
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
|
||||
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
|
||||
|
@ -199,7 +199,7 @@ public class CoreHandler
|
|||
{
|
||||
if (_process != null && !_process.HasExited)
|
||||
{
|
||||
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
||||
if (itemSocks != null)
|
||||
{
|
||||
|
@ -208,7 +208,7 @@ public class CoreHandler
|
|||
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
|
||||
if (result.Success)
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(preCoreType);
|
||||
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
|
||||
if (proc is null)
|
||||
{
|
||||
|
@ -231,7 +231,7 @@ public class CoreHandler
|
|||
|
||||
private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
|
||||
{
|
||||
var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg);
|
||||
var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg);
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
UpdateFunc(false, msg);
|
||||
|
@ -246,8 +246,8 @@ public class CoreHandler
|
|||
&& Utils.IsNonWindows())
|
||||
{
|
||||
_linuxSudo = true;
|
||||
await CoreAdminHandler.Instance.Init(_config, _updateFunc);
|
||||
return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath);
|
||||
await CoreAdminManager.Instance.Init(_config, _updateFunc);
|
||||
return await CoreAdminManager.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath);
|
||||
}
|
||||
|
||||
return await RunProcessNormal(fileName, coreInfo, configPath, displayLog);
|
||||
|
@ -299,7 +299,7 @@ public class CoreHandler
|
|||
}
|
||||
|
||||
await Task.Delay(100);
|
||||
AppHandler.Instance.AddProcess(proc.Handle);
|
||||
AppManager.Instance.AddProcess(proc.Handle);
|
||||
if (proc is null or { HasExited: true })
|
||||
{
|
||||
throw new Exception(ResUI.FailedToRunCore);
|
|
@ -1,11 +1,11 @@
|
|||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public class NoticeHandler
|
||||
public class NoticeManager
|
||||
{
|
||||
private static readonly Lazy<NoticeHandler> _instance = new(() => new());
|
||||
public static NoticeHandler Instance => _instance.Value;
|
||||
private static readonly Lazy<NoticeManager> _instance = new(() => new());
|
||||
public static NoticeManager Instance => _instance.Value;
|
||||
|
||||
public void Enqueue(string? content)
|
||||
{
|
|
@ -1,12 +1,12 @@
|
|||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public class PacHandler
|
||||
public class PacManager
|
||||
{
|
||||
private static readonly Lazy<PacHandler> _instance = new(() => new PacHandler());
|
||||
public static PacHandler Instance => _instance.Value;
|
||||
private static readonly Lazy<PacManager> _instance = new(() => new PacManager());
|
||||
public static PacManager Instance => _instance.Value;
|
||||
|
||||
private string _configPath;
|
||||
private int _httpPort;
|
|
@ -2,17 +2,17 @@ using System.Collections.Concurrent;
|
|||
|
||||
//using System.Reactive.Linq;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public class ProfileExHandler
|
||||
public class ProfileExManager
|
||||
{
|
||||
private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
|
||||
private static readonly Lazy<ProfileExManager> _instance = new(() => new());
|
||||
private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
|
||||
private readonly Queue<string> _queIndexIds = new();
|
||||
public static ProfileExHandler Instance => _instance.Value;
|
||||
public static ProfileExManager Instance => _instance.Value;
|
||||
private static readonly string _tag = "ProfileExHandler";
|
||||
|
||||
public ProfileExHandler()
|
||||
public ProfileExManager()
|
||||
{
|
||||
//Init();
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public class StatisticsHandler
|
||||
public class StatisticsManager
|
||||
{
|
||||
private static readonly Lazy<StatisticsHandler> instance = new(() => new());
|
||||
public static StatisticsHandler Instance => instance.Value;
|
||||
private static readonly Lazy<StatisticsManager> instance = new(() => new());
|
||||
public static StatisticsManager Instance => instance.Value;
|
||||
|
||||
private Config _config;
|
||||
private ServerStatItem? _serverStatItem;
|
||||
|
@ -91,7 +91,7 @@ public class StatisticsHandler
|
|||
{
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
|
||||
|
||||
long ticks = DateTime.Now.Date.Ticks;
|
||||
var ticks = DateTime.Now.Date.Ticks;
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}");
|
||||
|
||||
_lstServerStat = await SQLiteHelper.Instance.TableAsync<ServerStatItem>().ToListAsync();
|
||||
|
@ -128,7 +128,7 @@ public class StatisticsHandler
|
|||
|
||||
private async Task GetServerStatItem(string indexId)
|
||||
{
|
||||
long ticks = DateTime.Now.Date.Ticks;
|
||||
var ticks = DateTime.Now.Date.Ticks;
|
||||
if (_serverStatItem != null && _serverStatItem.IndexId != indexId)
|
||||
{
|
||||
_serverStatItem = null;
|
|
@ -1,9 +1,9 @@
|
|||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public class TaskHandler
|
||||
public class TaskManager
|
||||
{
|
||||
private static readonly Lazy<TaskHandler> _instance = new(() => new());
|
||||
public static TaskHandler Instance => _instance.Value;
|
||||
private static readonly Lazy<TaskManager> _instance = new(() => new());
|
||||
public static TaskManager Instance => _instance.Value;
|
||||
|
||||
public void RegUpdateTask(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
|
@ -29,7 +29,7 @@ public class TaskHandler
|
|||
//Logging.SaveLog("Execute save config");
|
||||
|
||||
await ConfigHandler.SaveConfig(config);
|
||||
await ProfileExHandler.Instance.SaveTo();
|
||||
await ProfileExManager.Instance.SaveTo();
|
||||
}
|
||||
|
||||
//Execute once 1 hour
|
||||
|
@ -52,7 +52,7 @@ public class TaskHandler
|
|||
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
|
||||
var lstSubs = (await AppHandler.Instance.SubItems())?
|
||||
var lstSubs = (await AppManager.Instance.SubItems())?
|
||||
.Where(t => t.AutoUpdateInterval > 0)
|
||||
.Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60)
|
||||
.ToList();
|
||||
|
@ -66,7 +66,7 @@ public class TaskHandler
|
|||
|
||||
foreach (var item in lstSubs)
|
||||
{
|
||||
await SubscriptionHandler.UpdateProcess(config, item.Id, true, (bool success, string msg) =>
|
||||
await SubscriptionHandler.UpdateProcess(config, item.Id, true, (success, msg) =>
|
||||
{
|
||||
updateFunc?.Invoke(success, msg);
|
||||
if (success)
|
||||
|
@ -87,7 +87,7 @@ public class TaskHandler
|
|||
Logging.SaveLog("Execute update geo files");
|
||||
|
||||
var updateHandle = new UpdateService();
|
||||
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
|
||||
await updateHandle.UpdateGeoFileAll(config, (success, msg) =>
|
||||
{
|
||||
updateFunc?.Invoke(false, msg);
|
||||
});
|
|
@ -1,12 +1,12 @@
|
|||
using System.Net;
|
||||
using WebDav;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public sealed class WebDavHandler
|
||||
public sealed class WebDavManager
|
||||
{
|
||||
private static readonly Lazy<WebDavHandler> _instance = new(() => new());
|
||||
public static WebDavHandler Instance => _instance.Value;
|
||||
private static readonly Lazy<WebDavManager> _instance = new(() => new());
|
||||
public static WebDavManager Instance => _instance.Value;
|
||||
|
||||
private readonly Config? _config;
|
||||
private WebDavClient? _client;
|
||||
|
@ -15,9 +15,9 @@ public sealed class WebDavHandler
|
|||
private readonly string _webFileName = "backup.zip";
|
||||
private readonly string _tag = "WebDav--";
|
||||
|
||||
public WebDavHandler()
|
||||
public WebDavManager()
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
}
|
||||
|
||||
private async Task<bool> GetClient()
|
|
@ -73,12 +73,12 @@ public class CoreConfigClashService
|
|||
}
|
||||
|
||||
//mixed-port
|
||||
fileContent["mixed-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
fileContent["mixed-port"] = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
//log-level
|
||||
fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel);
|
||||
|
||||
//external-controller
|
||||
fileContent["external-controller"] = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}";
|
||||
fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}";
|
||||
//allow-lan
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
|
@ -139,7 +139,7 @@ public class CoreConfigClashService
|
|||
return ret;
|
||||
}
|
||||
|
||||
ClashApiHandler.Instance.ProfileContent = fileContent;
|
||||
ClashApiManager.Instance.ProfileContent = fileContent;
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, $"{node.GetSummary()}");
|
||||
ret.Success = true;
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,522 @@
|
|||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService(Config config)
|
||||
{
|
||||
private readonly Config _config = config;
|
||||
private static readonly string _tag = "CoreConfigSingboxService";
|
||||
|
||||
#region public gen function
|
||||
|
||||
public async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||
if (singboxConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
await GenLog(singboxConfig);
|
||||
|
||||
await GenInbounds(singboxConfig);
|
||||
|
||||
if (node.ConfigType == EConfigType.WireGuard)
|
||||
{
|
||||
singboxConfig.outbounds.RemoveAt(0);
|
||||
var endpoints = new Endpoints4Sbox();
|
||||
await GenEndpoint(node, endpoints);
|
||||
endpoints.tag = Global.ProxyTag;
|
||||
singboxConfig.endpoints = new() { endpoints };
|
||||
}
|
||||
else
|
||||
{
|
||||
await GenOutbound(node, singboxConfig.outbounds.First());
|
||||
}
|
||||
|
||||
await GenMoreOutbounds(node, singboxConfig);
|
||||
|
||||
await GenRouting(singboxConfig);
|
||||
|
||||
await GenDns(singboxConfig);
|
||||
|
||||
await GenExperimental(singboxConfig);
|
||||
|
||||
await ConvertGeo2Ruleset(singboxConfig);
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
|
||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||
if (singboxConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
List<IPEndPoint> lstIpEndPoints = new();
|
||||
List<TcpConnectionInformation> lstTcpConns = new();
|
||||
try
|
||||
{
|
||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
|
||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners());
|
||||
lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
await GenLog(singboxConfig);
|
||||
//GenDns(new(), singboxConfig);
|
||||
singboxConfig.inbounds.Clear();
|
||||
singboxConfig.outbounds.RemoveAt(0);
|
||||
|
||||
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.Port <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
||||
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
|
||||
{
|
||||
if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//find unused port
|
||||
var port = initPort;
|
||||
for (var k = initPort; k < Global.MaxPort; k++)
|
||||
{
|
||||
if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//found
|
||||
port = k;
|
||||
initPort = port + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
//Port In Used
|
||||
if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
it.Port = port;
|
||||
it.AllowTest = true;
|
||||
|
||||
//inbound
|
||||
Inbound4Sbox inbound = new()
|
||||
{
|
||||
listen = Global.Loopback,
|
||||
listen_port = port,
|
||||
type = EInboundProtocol.mixed.ToString(),
|
||||
};
|
||||
inbound.tag = inbound.type + inbound.listen_port.ToString();
|
||||
singboxConfig.inbounds.Add(inbound);
|
||||
|
||||
//outbound
|
||||
if (item is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.ConfigType == EConfigType.Shadowsocks
|
||||
&& !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.ConfigType == EConfigType.VLESS
|
||||
&& !Global.Flows.Contains(item.Flow))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan
|
||||
&& item.StreamSecurity == Global.StreamSecurityReality
|
||||
&& item.PublicKey.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var server = await GenServer(item);
|
||||
if (server is null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
var tag = Global.ProxyTag + inbound.listen_port.ToString();
|
||||
server.tag = tag;
|
||||
if (server is Endpoints4Sbox endpoint)
|
||||
{
|
||||
singboxConfig.endpoints ??= new();
|
||||
singboxConfig.endpoints.Add(endpoint);
|
||||
}
|
||||
else if (server is Outbound4Sbox outbound)
|
||||
{
|
||||
singboxConfig.outbounds.Add(outbound);
|
||||
}
|
||||
|
||||
//rule
|
||||
Rule4Sbox rule = new()
|
||||
{
|
||||
inbound = new List<string> { inbound.tag },
|
||||
outbound = tag
|
||||
};
|
||||
singboxConfig.route.rules.Add(rule);
|
||||
}
|
||||
|
||||
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
if (rawDNSItem != null && rawDNSItem.Enabled == true)
|
||||
{
|
||||
await GenDnsDomainsCompatible(singboxConfig, rawDNSItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
await GenDnsDomains(singboxConfig, _config.SimpleDNSItem);
|
||||
}
|
||||
singboxConfig.route.default_domain_resolver = new()
|
||||
{
|
||||
server = Global.SingboxFinalResolverTag
|
||||
};
|
||||
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(singboxConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node is not { Port: > 0 })
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||
if (singboxConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
await GenLog(singboxConfig);
|
||||
if (node.ConfigType == EConfigType.WireGuard)
|
||||
{
|
||||
singboxConfig.outbounds.RemoveAt(0);
|
||||
var endpoints = new Endpoints4Sbox();
|
||||
await GenEndpoint(node, endpoints);
|
||||
endpoints.tag = Global.ProxyTag;
|
||||
singboxConfig.endpoints = new() { endpoints };
|
||||
}
|
||||
else
|
||||
{
|
||||
await GenOutbound(node, singboxConfig.outbounds.First());
|
||||
}
|
||||
await GenMoreOutbounds(node, singboxConfig);
|
||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
if (item != null && item.Enabled == true)
|
||||
{
|
||||
await GenDnsDomainsCompatible(singboxConfig, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
await GenDnsDomains(singboxConfig, _config.SimpleDNSItem);
|
||||
}
|
||||
singboxConfig.route.default_domain_resolver = new()
|
||||
{
|
||||
server = Global.SingboxFinalResolverTag
|
||||
};
|
||||
|
||||
singboxConfig.route.rules.Clear();
|
||||
singboxConfig.inbounds.Clear();
|
||||
singboxConfig.inbounds.Add(new()
|
||||
{
|
||||
tag = $"{EInboundProtocol.mixed}{port}",
|
||||
listen = Global.Loopback,
|
||||
listen_port = port,
|
||||
type = EInboundProtocol.mixed.ToString(),
|
||||
});
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(singboxConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient);
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||
if (singboxConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
await GenLog(singboxConfig);
|
||||
await GenInbounds(singboxConfig);
|
||||
await GenRouting(singboxConfig);
|
||||
await GenExperimental(singboxConfig);
|
||||
singboxConfig.outbounds.RemoveAt(0);
|
||||
|
||||
var proxyProfiles = new List<ProfileItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.Port <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
|
||||
{
|
||||
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (item.ConfigType == EConfigType.Shadowsocks
|
||||
&& !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//outbound
|
||||
proxyProfiles.Add(item);
|
||||
}
|
||||
if (proxyProfiles.Count <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
await GenOutboundsList(proxyProfiles, singboxConfig);
|
||||
|
||||
await GenDns(singboxConfig);
|
||||
await ConvertGeo2Ruleset(singboxConfig);
|
||||
|
||||
ret.Success = true;
|
||||
|
||||
ret.Data = await ApplyFullConfigTemplate(singboxConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
if (node == null || fileName is null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
try
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
var addressFileName = node.Address;
|
||||
if (addressFileName.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
addressFileName = Path.Combine(Utils.GetConfigPath(), addressFileName);
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedReadConfiguration + "1";
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.Address == Global.CoreMultipleLoadConfigFileName)
|
||||
{
|
||||
var txtFile = File.ReadAllText(addressFileName);
|
||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(txtFile);
|
||||
if (singboxConfig == null)
|
||||
{
|
||||
File.Copy(addressFileName, fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
await GenInbounds(singboxConfig);
|
||||
await GenExperimental(singboxConfig);
|
||||
|
||||
var content = JsonUtils.Serialize(singboxConfig, true);
|
||||
await File.WriteAllTextAsync(fileName, content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Copy(addressFileName, fileName);
|
||||
}
|
||||
|
||||
//check again
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedReadConfiguration + "2";
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion public gen function
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService
|
||||
{
|
||||
private async Task<string> ApplyFullConfigTemplate(SingboxConfig singboxConfig)
|
||||
{
|
||||
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
||||
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled)
|
||||
{
|
||||
return JsonUtils.Serialize(singboxConfig);
|
||||
}
|
||||
|
||||
var fullConfigTemplateItem = _config.TunModeItem.EnableTun ? fullConfigTemplate.TunConfig : fullConfigTemplate.Config;
|
||||
if (fullConfigTemplateItem.IsNullOrEmpty())
|
||||
{
|
||||
return JsonUtils.Serialize(singboxConfig);
|
||||
}
|
||||
|
||||
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplateItem);
|
||||
if (fullConfigTemplateNode == null)
|
||||
{
|
||||
return JsonUtils.Serialize(singboxConfig);
|
||||
}
|
||||
|
||||
// Process outbounds
|
||||
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
|
||||
foreach (var outbound in singboxConfig.outbounds)
|
||||
{
|
||||
if (outbound.type.ToLower() is "direct" or "block")
|
||||
{
|
||||
if (fullConfigTemplate.AddProxyOnly == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (outbound.detour.IsNullOrEmpty() && !fullConfigTemplate.ProxyDetour.IsNullOrEmpty() && !Utils.IsPrivateNetwork(outbound.server ?? string.Empty))
|
||||
{
|
||||
outbound.detour = fullConfigTemplate.ProxyDetour;
|
||||
}
|
||||
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
|
||||
}
|
||||
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
||||
|
||||
// Process endpoints
|
||||
if (singboxConfig.endpoints != null && singboxConfig.endpoints.Count > 0)
|
||||
{
|
||||
var customEndpointsNode = fullConfigTemplateNode["endpoints"] is JsonArray endpoints ? endpoints : new JsonArray();
|
||||
foreach (var endpoint in singboxConfig.endpoints)
|
||||
{
|
||||
if (endpoint.detour.IsNullOrEmpty() && !fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
|
||||
{
|
||||
endpoint.detour = fullConfigTemplate.ProxyDetour;
|
||||
}
|
||||
customEndpointsNode.Add(JsonUtils.DeepCopy(endpoint));
|
||||
}
|
||||
fullConfigTemplateNode["endpoints"] = customEndpointsNode;
|
||||
}
|
||||
|
||||
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,482 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService
|
||||
{
|
||||
private async Task<int> GenDns(SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
if (item != null && item.Enabled == true)
|
||||
{
|
||||
return await GenDnsCompatible(singboxConfig);
|
||||
}
|
||||
|
||||
var simpleDNSItem = _config.SimpleDNSItem;
|
||||
await GenDnsServers(singboxConfig, simpleDNSItem);
|
||||
await GenDnsRules(singboxConfig, simpleDNSItem);
|
||||
|
||||
singboxConfig.dns ??= new Dns4Sbox();
|
||||
singboxConfig.dns.independent_cache = true;
|
||||
|
||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||
var useDirectDns = false;
|
||||
if (routing != null)
|
||||
{
|
||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
||||
|
||||
useDirectDns = rules?.LastOrDefault() is { } lastRule &&
|
||||
lastRule.OutboundTag == Global.DirectTag &&
|
||||
(lastRule.Port == "0-65535" ||
|
||||
lastRule.Network == "tcp,udp" ||
|
||||
lastRule.Ip?.Contains("0.0.0.0/0") == true);
|
||||
}
|
||||
singboxConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsServers(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
||||
{
|
||||
var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem);
|
||||
|
||||
var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS);
|
||||
directDns.tag = Global.SingboxDirectDNSTag;
|
||||
directDns.domain_resolver = Global.SingboxFinalResolverTag;
|
||||
|
||||
var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS);
|
||||
remoteDns.tag = Global.SingboxRemoteDNSTag;
|
||||
remoteDns.detour = Global.ProxyTag;
|
||||
remoteDns.domain_resolver = Global.SingboxFinalResolverTag;
|
||||
|
||||
var resolverDns = ParseDnsAddress(simpleDNSItem.SingboxOutboundsResolveDNS);
|
||||
resolverDns.tag = Global.SingboxOutboundResolverTag;
|
||||
resolverDns.domain_resolver = Global.SingboxFinalResolverTag;
|
||||
|
||||
var hostsDns = new Server4Sbox
|
||||
{
|
||||
tag = Global.SingboxHostsDNSTag,
|
||||
type = "hosts",
|
||||
predefined = new(),
|
||||
};
|
||||
if (simpleDNSItem.AddCommonHosts == true)
|
||||
{
|
||||
hostsDns.predefined = Global.PredefinedHosts;
|
||||
}
|
||||
var userHostsMap = simpleDNSItem.Hosts?
|
||||
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(line => !string.IsNullOrWhiteSpace(line))
|
||||
.Where(line => line.Contains(' '))
|
||||
.ToDictionary(
|
||||
line =>
|
||||
{
|
||||
var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return parts[0];
|
||||
},
|
||||
line =>
|
||||
{
|
||||
var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var values = parts.Skip(1).ToList();
|
||||
return values;
|
||||
}
|
||||
);
|
||||
|
||||
if (userHostsMap != null)
|
||||
{
|
||||
foreach (var kvp in userHostsMap)
|
||||
{
|
||||
hostsDns.predefined[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (simpleDNSItem.UseSystemHosts == true)
|
||||
{
|
||||
var systemHosts = Utils.GetSystemHosts();
|
||||
if (systemHosts.Count > 0)
|
||||
{
|
||||
foreach (var host in systemHosts)
|
||||
{
|
||||
if (userHostsMap[host.Key] != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
userHostsMap[host.Key] = new List<string> { host.Value };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var host in hostsDns.predefined)
|
||||
{
|
||||
if (finalDns.server == host.Key)
|
||||
{
|
||||
finalDns.domain_resolver = Global.SingboxHostsDNSTag;
|
||||
}
|
||||
if (remoteDns.server == host.Key)
|
||||
{
|
||||
remoteDns.domain_resolver = Global.SingboxHostsDNSTag;
|
||||
}
|
||||
if (resolverDns.server == host.Key)
|
||||
{
|
||||
resolverDns.domain_resolver = Global.SingboxHostsDNSTag;
|
||||
}
|
||||
if (directDns.server == host.Key)
|
||||
{
|
||||
directDns.domain_resolver = Global.SingboxHostsDNSTag;
|
||||
}
|
||||
}
|
||||
|
||||
singboxConfig.dns ??= new Dns4Sbox();
|
||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
||||
singboxConfig.dns.servers.Add(remoteDns);
|
||||
singboxConfig.dns.servers.Add(directDns);
|
||||
singboxConfig.dns.servers.Add(resolverDns);
|
||||
singboxConfig.dns.servers.Add(hostsDns);
|
||||
|
||||
// fake ip
|
||||
if (simpleDNSItem.FakeIP == true)
|
||||
{
|
||||
var fakeip = new Server4Sbox
|
||||
{
|
||||
tag = Global.SingboxFakeDNSTag,
|
||||
type = "fakeip",
|
||||
inet4_range = "198.18.0.0/15",
|
||||
inet6_range = "fc00::/18",
|
||||
};
|
||||
singboxConfig.dns.servers.Add(fakeip);
|
||||
}
|
||||
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem)
|
||||
{
|
||||
var finalDns = ParseDnsAddress(simpleDNSItem.SingboxFinalResolveDNS);
|
||||
finalDns.tag = Global.SingboxFinalResolverTag;
|
||||
singboxConfig.dns ??= new Dns4Sbox();
|
||||
singboxConfig.dns.servers ??= new List<Server4Sbox>();
|
||||
singboxConfig.dns.servers.Add(finalDns);
|
||||
return await Task.FromResult(finalDns);
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsRules(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
||||
{
|
||||
singboxConfig.dns ??= new Dns4Sbox();
|
||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
||||
|
||||
singboxConfig.dns.rules.AddRange(new[]
|
||||
{
|
||||
new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag },
|
||||
new Rule4Sbox
|
||||
{
|
||||
server = Global.SingboxRemoteDNSTag,
|
||||
strategy = simpleDNSItem.SingboxStrategy4Proxy.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Proxy,
|
||||
clash_mode = ERuleMode.Global.ToString()
|
||||
},
|
||||
new Rule4Sbox
|
||||
{
|
||||
server = Global.SingboxDirectDNSTag,
|
||||
strategy = simpleDNSItem.SingboxStrategy4Direct.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Direct,
|
||||
clash_mode = ERuleMode.Direct.ToString()
|
||||
}
|
||||
});
|
||||
|
||||
if (simpleDNSItem.BlockBindingQuery == true)
|
||||
{
|
||||
singboxConfig.dns.rules.Add(new()
|
||||
{
|
||||
query_type = new List<int> { 64, 65 },
|
||||
action = "predefined",
|
||||
rcode = "NOTIMP"
|
||||
});
|
||||
}
|
||||
|
||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||
if (routing == null)
|
||||
return 0;
|
||||
|
||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
||||
var expectedIPCidr = new List<string>();
|
||||
var expectedIPsRegions = new List<string>();
|
||||
var regionNames = new HashSet<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
|
||||
{
|
||||
var ipItems = simpleDNSItem.DirectExpectedIPs
|
||||
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => s.Trim())
|
||||
.Where(s => !string.IsNullOrEmpty(s))
|
||||
.ToList();
|
||||
|
||||
foreach (var ip in ipItems)
|
||||
{
|
||||
if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var region = ip["geoip:".Length..];
|
||||
if (!string.IsNullOrEmpty(region))
|
||||
{
|
||||
expectedIPsRegions.Add(region);
|
||||
regionNames.Add(region);
|
||||
regionNames.Add($"geolocation-{region}");
|
||||
regionNames.Add($"tld-{region}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
expectedIPCidr.Add(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in rules)
|
||||
{
|
||||
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var rule = new Rule4Sbox();
|
||||
var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule));
|
||||
if (validDomains <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.OutboundTag == Global.DirectTag)
|
||||
{
|
||||
rule.server = Global.SingboxDirectDNSTag;
|
||||
rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Direct) ? null : simpleDNSItem.SingboxStrategy4Direct;
|
||||
|
||||
if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0)
|
||||
{
|
||||
var geositeSet = new HashSet<string>(rule.geosite);
|
||||
if (regionNames.Intersect(geositeSet).Any())
|
||||
{
|
||||
if (expectedIPsRegions.Count > 0)
|
||||
{
|
||||
rule.geoip = expectedIPsRegions;
|
||||
}
|
||||
if (expectedIPCidr.Count > 0)
|
||||
{
|
||||
rule.ip_cidr = expectedIPCidr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (item.OutboundTag == Global.BlockTag)
|
||||
{
|
||||
rule.action = "predefined";
|
||||
rule.rcode = "NXDOMAIN";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (simpleDNSItem.FakeIP == true)
|
||||
{
|
||||
var rule4Fake = JsonUtils.DeepCopy(rule);
|
||||
rule4Fake.server = Global.SingboxFakeDNSTag;
|
||||
singboxConfig.dns.rules.Add(rule4Fake);
|
||||
}
|
||||
rule.server = Global.SingboxRemoteDNSTag;
|
||||
rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Proxy) ? null : simpleDNSItem.SingboxStrategy4Proxy;
|
||||
}
|
||||
|
||||
singboxConfig.dns.rules.Add(rule);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsCompatible(SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
var strDNS = string.Empty;
|
||||
if (_config.TunModeItem.EnableTun)
|
||||
{
|
||||
strDNS = string.IsNullOrEmpty(item?.TunDNS) ? EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName) : item?.TunDNS;
|
||||
}
|
||||
else
|
||||
{
|
||||
strDNS = string.IsNullOrEmpty(item?.NormalDNS) ? EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName) : item?.NormalDNS;
|
||||
}
|
||||
|
||||
var dns4Sbox = JsonUtils.Deserialize<Dns4Sbox>(strDNS);
|
||||
if (dns4Sbox is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
singboxConfig.dns = dns4Sbox;
|
||||
|
||||
if (dns4Sbox.servers != null && dns4Sbox.servers.Count > 0 && dns4Sbox.servers.First().address.IsNullOrEmpty())
|
||||
{
|
||||
await GenDnsDomainsCompatible(singboxConfig, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
await GenDnsDomainsLegacyCompatible(singboxConfig, item);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem)
|
||||
{
|
||||
var dns4Sbox = singboxConfig.dns ?? new();
|
||||
dns4Sbox.servers ??= [];
|
||||
dns4Sbox.rules ??= [];
|
||||
|
||||
var tag = Global.SingboxFinalResolverTag;
|
||||
var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress;
|
||||
|
||||
var localDnsServer = ParseDnsAddress(localDnsAddress);
|
||||
localDnsServer.tag = tag;
|
||||
|
||||
dns4Sbox.servers.Add(localDnsServer);
|
||||
|
||||
singboxConfig.dns = dns4Sbox;
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem)
|
||||
{
|
||||
var dns4Sbox = singboxConfig.dns ?? new();
|
||||
dns4Sbox.servers ??= [];
|
||||
dns4Sbox.rules ??= [];
|
||||
|
||||
var tag = Global.SingboxFinalResolverTag;
|
||||
dns4Sbox.servers.Add(new()
|
||||
{
|
||||
tag = tag,
|
||||
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
|
||||
detour = Global.DirectTag,
|
||||
strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom,
|
||||
});
|
||||
dns4Sbox.rules.Insert(0, new()
|
||||
{
|
||||
server = tag,
|
||||
clash_mode = ERuleMode.Direct.ToString()
|
||||
});
|
||||
dns4Sbox.rules.Insert(0, new()
|
||||
{
|
||||
server = dns4Sbox.servers.Where(t => t.detour == Global.ProxyTag).Select(t => t.tag).FirstOrDefault() ?? "remote",
|
||||
clash_mode = ERuleMode.Global.ToString()
|
||||
});
|
||||
|
||||
var lstDomain = singboxConfig.outbounds
|
||||
.Where(t => t.server.IsNotEmpty() && Utils.IsDomain(t.server))
|
||||
.Select(t => t.server)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
if (lstDomain != null && lstDomain.Count > 0)
|
||||
{
|
||||
dns4Sbox.rules.Insert(0, new()
|
||||
{
|
||||
server = tag,
|
||||
domain = lstDomain
|
||||
});
|
||||
}
|
||||
|
||||
singboxConfig.dns = dns4Sbox;
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private static Server4Sbox? ParseDnsAddress(string address)
|
||||
{
|
||||
var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim();
|
||||
if (string.IsNullOrEmpty(addressFirst))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var server = new Server4Sbox();
|
||||
|
||||
if (addressFirst is "local" or "localhost")
|
||||
{
|
||||
server.type = "local";
|
||||
return server;
|
||||
}
|
||||
|
||||
if (addressFirst.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var interface_name = addressFirst.Substring(7);
|
||||
server.type = "dhcp";
|
||||
server.Interface = interface_name == "auto" ? null : interface_name;
|
||||
return server;
|
||||
}
|
||||
|
||||
if (!addressFirst.Contains("://"))
|
||||
{
|
||||
// udp dns
|
||||
server.type = "udp";
|
||||
server.server = addressFirst;
|
||||
return server;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal);
|
||||
server.type = addressFirst.Substring(0, protocolEndIndex).ToLower();
|
||||
|
||||
var uri = new Uri(addressFirst);
|
||||
server.server = uri.Host;
|
||||
|
||||
if (!uri.IsDefaultPort)
|
||||
{
|
||||
server.server_port = uri.Port;
|
||||
}
|
||||
|
||||
if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/")
|
||||
{
|
||||
server.path = uri.AbsolutePath;
|
||||
}
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal);
|
||||
if (protocolEndIndex > 0)
|
||||
{
|
||||
server.type = addressFirst.Substring(0, protocolEndIndex).ToLower();
|
||||
var remaining = addressFirst.Substring(protocolEndIndex + 3);
|
||||
|
||||
var portIndex = remaining.IndexOf(':');
|
||||
var pathIndex = remaining.IndexOf('/');
|
||||
|
||||
if (portIndex > 0)
|
||||
{
|
||||
server.server = remaining.Substring(0, portIndex);
|
||||
var portPart = pathIndex > portIndex
|
||||
? remaining.Substring(portIndex + 1, pathIndex - portIndex - 1)
|
||||
: remaining.Substring(portIndex + 1);
|
||||
|
||||
if (int.TryParse(portPart, out var parsedPort))
|
||||
{
|
||||
server.server_port = parsedPort;
|
||||
}
|
||||
}
|
||||
else if (pathIndex > 0)
|
||||
{
|
||||
server.server = remaining.Substring(0, pathIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
server.server = remaining;
|
||||
}
|
||||
|
||||
if (pathIndex > 0 && (server.type == "https" || server.type == "h3"))
|
||||
{
|
||||
server.path = remaining.Substring(pathIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService
|
||||
{
|
||||
private async Task<int> GenInbounds(SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var listen = "0.0.0.0";
|
||||
singboxConfig.inbounds = [];
|
||||
|
||||
if (!_config.TunModeItem.EnableTun
|
||||
|| _config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && _config.RunningCoreType == ECoreType.sing_box)
|
||||
{
|
||||
var inbound = new Inbound4Sbox()
|
||||
{
|
||||
type = EInboundProtocol.mixed.ToString(),
|
||||
tag = EInboundProtocol.socks.ToString(),
|
||||
listen = Global.Loopback,
|
||||
};
|
||||
singboxConfig.inbounds.Add(inbound);
|
||||
|
||||
inbound.listen_port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
|
||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
||||
{
|
||||
var inbound2 = GetInbound(inbound, EInboundProtocol.socks2, true);
|
||||
singboxConfig.inbounds.Add(inbound2);
|
||||
}
|
||||
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
if (_config.Inbound.First().NewPort4LAN)
|
||||
{
|
||||
var inbound3 = GetInbound(inbound, EInboundProtocol.socks3, true);
|
||||
inbound3.listen = listen;
|
||||
singboxConfig.inbounds.Add(inbound3);
|
||||
|
||||
//auth
|
||||
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
||||
{
|
||||
inbound3.users = new() { new() { username = _config.Inbound.First().User, password = _config.Inbound.First().Pass } };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inbound.listen = listen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_config.TunModeItem.EnableTun)
|
||||
{
|
||||
if (_config.TunModeItem.Mtu <= 0)
|
||||
{
|
||||
_config.TunModeItem.Mtu = Global.TunMtus.First();
|
||||
}
|
||||
if (_config.TunModeItem.Stack.IsNullOrEmpty())
|
||||
{
|
||||
_config.TunModeItem.Stack = Global.TunStacks.First();
|
||||
}
|
||||
|
||||
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(EmbedUtils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
|
||||
tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun";
|
||||
tunInbound.mtu = _config.TunModeItem.Mtu;
|
||||
tunInbound.auto_route = _config.TunModeItem.AutoRoute;
|
||||
tunInbound.strict_route = _config.TunModeItem.StrictRoute;
|
||||
tunInbound.stack = _config.TunModeItem.Stack;
|
||||
if (_config.TunModeItem.EnableIPv6Address == false)
|
||||
{
|
||||
tunInbound.address = ["172.18.0.1/30"];
|
||||
}
|
||||
|
||||
singboxConfig.inbounds.Add(tunInbound);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Inbound4Sbox GetInbound(Inbound4Sbox inItem, EInboundProtocol protocol, bool bSocks)
|
||||
{
|
||||
var inbound = JsonUtils.DeepCopy(inItem);
|
||||
inbound.tag = protocol.ToString();
|
||||
inbound.listen_port = inItem.listen_port + (int)protocol;
|
||||
inbound.type = EInboundProtocol.mixed.ToString();
|
||||
return inbound;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService
|
||||
{
|
||||
private async Task<int> GenLog(SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (_config.CoreBasicItem.Loglevel)
|
||||
{
|
||||
case "debug":
|
||||
case "info":
|
||||
case "error":
|
||||
singboxConfig.log.level = _config.CoreBasicItem.Loglevel;
|
||||
break;
|
||||
|
||||
case "warning":
|
||||
singboxConfig.log.level = "warn";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (_config.CoreBasicItem.Loglevel == Global.None)
|
||||
{
|
||||
singboxConfig.log.disabled = true;
|
||||
}
|
||||
if (_config.CoreBasicItem.LogEnabled)
|
||||
{
|
||||
var dtNow = DateTime.Now;
|
||||
singboxConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,577 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService
|
||||
{
|
||||
private async Task<int> GenOutbound(ProfileItem node, Outbound4Sbox outbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
outbound.server = node.Address;
|
||||
outbound.server_port = node.Port;
|
||||
outbound.type = Global.ProtocolTypes[node.ConfigType];
|
||||
|
||||
switch (node.ConfigType)
|
||||
{
|
||||
case EConfigType.VMess:
|
||||
{
|
||||
outbound.uuid = node.Id;
|
||||
outbound.alter_id = node.AlterId;
|
||||
if (Global.VmessSecurities.Contains(node.Security))
|
||||
{
|
||||
outbound.security = node.Security;
|
||||
}
|
||||
else
|
||||
{
|
||||
outbound.security = Global.DefaultSecurity;
|
||||
}
|
||||
|
||||
await GenOutboundMux(node, outbound);
|
||||
break;
|
||||
}
|
||||
case EConfigType.Shadowsocks:
|
||||
{
|
||||
outbound.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : Global.None;
|
||||
outbound.password = node.Id;
|
||||
|
||||
await GenOutboundMux(node, outbound);
|
||||
break;
|
||||
}
|
||||
case EConfigType.SOCKS:
|
||||
{
|
||||
outbound.version = "5";
|
||||
if (node.Security.IsNotEmpty()
|
||||
&& node.Id.IsNotEmpty())
|
||||
{
|
||||
outbound.username = node.Security;
|
||||
outbound.password = node.Id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EConfigType.HTTP:
|
||||
{
|
||||
if (node.Security.IsNotEmpty()
|
||||
&& node.Id.IsNotEmpty())
|
||||
{
|
||||
outbound.username = node.Security;
|
||||
outbound.password = node.Id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EConfigType.VLESS:
|
||||
{
|
||||
outbound.uuid = node.Id;
|
||||
|
||||
outbound.packet_encoding = "xudp";
|
||||
|
||||
if (node.Flow.IsNullOrEmpty())
|
||||
{
|
||||
await GenOutboundMux(node, outbound);
|
||||
}
|
||||
else
|
||||
{
|
||||
outbound.flow = node.Flow;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EConfigType.Trojan:
|
||||
{
|
||||
outbound.password = node.Id;
|
||||
|
||||
await GenOutboundMux(node, outbound);
|
||||
break;
|
||||
}
|
||||
case EConfigType.Hysteria2:
|
||||
{
|
||||
outbound.password = node.Id;
|
||||
|
||||
if (node.Path.IsNotEmpty())
|
||||
{
|
||||
outbound.obfs = new()
|
||||
{
|
||||
type = "salamander",
|
||||
password = node.Path.TrimEx(),
|
||||
};
|
||||
}
|
||||
|
||||
outbound.up_mbps = _config.HysteriaItem.UpMbps > 0 ? _config.HysteriaItem.UpMbps : null;
|
||||
outbound.down_mbps = _config.HysteriaItem.DownMbps > 0 ? _config.HysteriaItem.DownMbps : null;
|
||||
if (node.Ports.IsNotEmpty() && (node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
|
||||
{
|
||||
outbound.server_port = null;
|
||||
outbound.server_ports = node.Ports.Split(',')
|
||||
.Select(p => p.Trim())
|
||||
.Where(p => p.IsNotEmpty())
|
||||
.Select(p =>
|
||||
{
|
||||
var port = p.Replace('-', ':');
|
||||
return port.Contains(':') ? port : $"{port}:{port}";
|
||||
})
|
||||
.ToList();
|
||||
outbound.hop_interval = _config.HysteriaItem.HopInterval > 0 ? $"{_config.HysteriaItem.HopInterval}s" : null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EConfigType.TUIC:
|
||||
{
|
||||
outbound.uuid = node.Id;
|
||||
outbound.password = node.Security;
|
||||
outbound.congestion_control = node.HeaderType;
|
||||
break;
|
||||
}
|
||||
case EConfigType.Anytls:
|
||||
{
|
||||
outbound.password = node.Id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await GenOutboundTls(node, outbound);
|
||||
|
||||
await GenOutboundTransport(node, outbound);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenEndpoint(ProfileItem node, Endpoints4Sbox endpoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
endpoint.address = Utils.String2List(node.RequestHost);
|
||||
endpoint.type = Global.ProtocolTypes[node.ConfigType];
|
||||
|
||||
switch (node.ConfigType)
|
||||
{
|
||||
case EConfigType.WireGuard:
|
||||
{
|
||||
var peer = new Peer4Sbox
|
||||
{
|
||||
public_key = node.PublicKey,
|
||||
reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(),
|
||||
address = node.Address,
|
||||
port = node.Port,
|
||||
// TODO default ["0.0.0.0/0", "::/0"]
|
||||
allowed_ips = new() { "0.0.0.0/0", "::/0" },
|
||||
};
|
||||
endpoint.private_key = node.Id;
|
||||
endpoint.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt();
|
||||
endpoint.peers = new() { peer };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<BaseServer4Sbox?> GenServer(ProfileItem node)
|
||||
{
|
||||
try
|
||||
{
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
if (node.ConfigType == EConfigType.WireGuard)
|
||||
{
|
||||
var endpoint = JsonUtils.Deserialize<Endpoints4Sbox>(txtOutbound);
|
||||
await GenEndpoint(node, endpoint);
|
||||
return endpoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(node, outbound);
|
||||
return outbound;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult<BaseServer4Sbox?>(null);
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||
if (muxEnabled && _config.Mux4SboxItem.Protocol.IsNotEmpty())
|
||||
{
|
||||
var mux = new Multiplex4Sbox()
|
||||
{
|
||||
enabled = true,
|
||||
protocol = _config.Mux4SboxItem.Protocol,
|
||||
max_connections = _config.Mux4SboxItem.MaxConnections,
|
||||
padding = _config.Mux4SboxItem.Padding,
|
||||
};
|
||||
outbound.multiplex = mux;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundTls(ProfileItem node, Outbound4Sbox outbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (node.StreamSecurity == Global.StreamSecurityReality || node.StreamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
var server_name = string.Empty;
|
||||
if (node.Sni.IsNotEmpty())
|
||||
{
|
||||
server_name = node.Sni;
|
||||
}
|
||||
else if (node.RequestHost.IsNotEmpty())
|
||||
{
|
||||
server_name = Utils.String2List(node.RequestHost)?.First();
|
||||
}
|
||||
var tls = new Tls4Sbox()
|
||||
{
|
||||
enabled = true,
|
||||
record_fragment = _config.CoreBasicItem.EnableFragment,
|
||||
server_name = server_name,
|
||||
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
||||
alpn = node.GetAlpn(),
|
||||
};
|
||||
if (node.Fingerprint.IsNotEmpty())
|
||||
{
|
||||
tls.utls = new Utls4Sbox()
|
||||
{
|
||||
enabled = true,
|
||||
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
|
||||
};
|
||||
}
|
||||
if (node.StreamSecurity == Global.StreamSecurityReality)
|
||||
{
|
||||
tls.reality = new Reality4Sbox()
|
||||
{
|
||||
enabled = true,
|
||||
public_key = node.PublicKey,
|
||||
short_id = node.ShortId
|
||||
};
|
||||
tls.insecure = false;
|
||||
}
|
||||
outbound.tls = tls;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundTransport(ProfileItem node, Outbound4Sbox outbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transport = new Transport4Sbox();
|
||||
|
||||
switch (node.GetNetwork())
|
||||
{
|
||||
case nameof(ETransport.h2):
|
||||
transport.type = nameof(ETransport.http);
|
||||
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
||||
break;
|
||||
|
||||
case nameof(ETransport.tcp): //http
|
||||
if (node.HeaderType == Global.TcpHeaderHttp)
|
||||
{
|
||||
if (node.ConfigType == EConfigType.Shadowsocks)
|
||||
{
|
||||
outbound.plugin = "obfs-local";
|
||||
outbound.plugin_opts = $"obfs=http;obfs-host={node.RequestHost};";
|
||||
}
|
||||
else
|
||||
{
|
||||
transport.type = nameof(ETransport.http);
|
||||
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
transport.type = nameof(ETransport.ws);
|
||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
||||
if (node.RequestHost.IsNotEmpty())
|
||||
{
|
||||
transport.headers = new()
|
||||
{
|
||||
Host = node.RequestHost
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.httpupgrade):
|
||||
transport.type = nameof(ETransport.httpupgrade);
|
||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
||||
transport.host = node.RequestHost.IsNullOrEmpty() ? null : node.RequestHost;
|
||||
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
transport.type = nameof(ETransport.quic);
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
transport.type = nameof(ETransport.grpc);
|
||||
transport.service_name = node.Path;
|
||||
transport.idle_timeout = _config.GrpcItem.IdleTimeout?.ToString("##s");
|
||||
transport.ping_timeout = _config.GrpcItem.HealthCheckTimeout?.ToString("##s");
|
||||
transport.permit_without_stream = _config.GrpcItem.PermitWithoutStream;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (transport.type != null)
|
||||
{
|
||||
outbound.transport = transport;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<int> GenMoreOutbounds(ProfileItem node, SingboxConfig singboxConfig)
|
||||
{
|
||||
if (node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
try
|
||||
{
|
||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
||||
if (subItem is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//current proxy
|
||||
BaseServer4Sbox? outbound = singboxConfig.endpoints?.FirstOrDefault(t => t.tag == Global.ProxyTag, null);
|
||||
outbound ??= singboxConfig.outbounds.First();
|
||||
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
|
||||
//Previous proxy
|
||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
{
|
||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||
var prevServer = await GenServer(prevNode);
|
||||
prevServer.tag = prevOutboundTag;
|
||||
if (prevServer is Endpoints4Sbox endpoint)
|
||||
{
|
||||
singboxConfig.endpoints ??= new();
|
||||
singboxConfig.endpoints.Add(endpoint);
|
||||
}
|
||||
else if (prevServer is Outbound4Sbox outboundPrev)
|
||||
{
|
||||
singboxConfig.outbounds.Add(outboundPrev);
|
||||
}
|
||||
}
|
||||
var nextServer = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
||||
|
||||
if (nextServer is not null)
|
||||
{
|
||||
if (nextServer is Endpoints4Sbox endpoint)
|
||||
{
|
||||
singboxConfig.endpoints ??= new();
|
||||
singboxConfig.endpoints.Insert(0, endpoint);
|
||||
}
|
||||
else if (nextServer is Outbound4Sbox outboundNext)
|
||||
{
|
||||
singboxConfig.outbounds.Insert(0, outboundNext);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get outbound template and initialize lists
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
if (txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var resultOutbounds = new List<Outbound4Sbox>();
|
||||
var resultEndpoints = new List<Endpoints4Sbox>(); // For endpoints
|
||||
var prevOutbounds = new List<Outbound4Sbox>(); // Separate list for prev outbounds
|
||||
var prevEndpoints = new List<Endpoints4Sbox>(); // Separate list for prev endpoints
|
||||
var proxyTags = new List<string>(); // For selector and urltest outbounds
|
||||
|
||||
// Cache for chain proxies to avoid duplicate generation
|
||||
var nextProxyCache = new Dictionary<string, BaseServer4Sbox?>();
|
||||
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||
var prevIndex = 0; // Index for prev outbounds
|
||||
|
||||
// Process each node
|
||||
var index = 0;
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
index++;
|
||||
|
||||
// Handle proxy chain
|
||||
string? prevTag = null;
|
||||
var currentServer = await GenServer(node);
|
||||
var nextServer = nextProxyCache.GetValueOrDefault(node.Subid, null);
|
||||
if (nextServer != null)
|
||||
{
|
||||
nextServer = JsonUtils.DeepCopy(nextServer);
|
||||
}
|
||||
|
||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
||||
|
||||
// current proxy
|
||||
currentServer.tag = $"{Global.ProxyTag}-{index}";
|
||||
proxyTags.Add(currentServer.tag);
|
||||
|
||||
if (!node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
||||
{
|
||||
prevTag = value; // maybe null
|
||||
}
|
||||
else
|
||||
{
|
||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||
prevOutbound.tag = prevTag;
|
||||
prevOutbounds.Add(prevOutbound);
|
||||
}
|
||||
prevProxyTags[node.Subid] = prevTag;
|
||||
}
|
||||
|
||||
nextServer = await GenChainOutbounds(subItem, currentServer, prevTag, nextServer);
|
||||
if (!nextProxyCache.ContainsKey(node.Subid))
|
||||
{
|
||||
nextProxyCache[node.Subid] = nextServer;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextServer is not null)
|
||||
{
|
||||
if (nextServer is Endpoints4Sbox nextEndpoint)
|
||||
{
|
||||
resultEndpoints.Add(nextEndpoint);
|
||||
}
|
||||
else if (nextServer is Outbound4Sbox nextOutbound)
|
||||
{
|
||||
resultOutbounds.Add(nextOutbound);
|
||||
}
|
||||
}
|
||||
if (currentServer is Endpoints4Sbox currentEndpoint)
|
||||
{
|
||||
resultEndpoints.Add(currentEndpoint);
|
||||
}
|
||||
else if (currentServer is Outbound4Sbox currentOutbound)
|
||||
{
|
||||
resultOutbounds.Add(currentOutbound);
|
||||
}
|
||||
}
|
||||
|
||||
// Add urltest outbound (auto selection based on latency)
|
||||
if (proxyTags.Count > 0)
|
||||
{
|
||||
var outUrltest = new Outbound4Sbox
|
||||
{
|
||||
type = "urltest",
|
||||
tag = $"{Global.ProxyTag}-auto",
|
||||
outbounds = proxyTags,
|
||||
interrupt_exist_connections = false,
|
||||
};
|
||||
|
||||
// Add selector outbound (manual selection)
|
||||
var outSelector = new Outbound4Sbox
|
||||
{
|
||||
type = "selector",
|
||||
tag = Global.ProxyTag,
|
||||
outbounds = JsonUtils.DeepCopy(proxyTags),
|
||||
interrupt_exist_connections = false,
|
||||
};
|
||||
outSelector.outbounds.Insert(0, outUrltest.tag);
|
||||
|
||||
// Insert these at the beginning
|
||||
resultOutbounds.Insert(0, outUrltest);
|
||||
resultOutbounds.Insert(0, outSelector);
|
||||
}
|
||||
|
||||
// Merge results: first the selector/urltest/proxies, then other outbounds, and finally prev outbounds
|
||||
resultOutbounds.AddRange(prevOutbounds);
|
||||
resultOutbounds.AddRange(singboxConfig.outbounds);
|
||||
singboxConfig.outbounds = resultOutbounds;
|
||||
singboxConfig.endpoints ??= new List<Endpoints4Sbox>();
|
||||
resultEndpoints.AddRange(singboxConfig.endpoints);
|
||||
singboxConfig.endpoints = resultEndpoints;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<BaseServer4Sbox?> GenChainOutbounds(SubItem subItem, BaseServer4Sbox outbound, string? prevOutboundTag, BaseServer4Sbox? nextOutbound = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||
|
||||
if (!prevOutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
outbound.detour = prevOutboundTag;
|
||||
}
|
||||
|
||||
// Next proxy
|
||||
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom)
|
||||
{
|
||||
nextOutbound ??= await GenServer(nextNode);
|
||||
nextOutbound.tag = outbound.tag;
|
||||
|
||||
outbound.tag = $"mid-{outbound.tag}";
|
||||
nextOutbound.detour = outbound.tag;
|
||||
}
|
||||
return nextOutbound;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,365 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService
|
||||
{
|
||||
private async Task<int> GenRouting(SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
singboxConfig.route.final = Global.ProxyTag;
|
||||
var item = _config.SimpleDNSItem;
|
||||
|
||||
var defaultDomainResolverTag = Global.SingboxOutboundResolverTag;
|
||||
var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct;
|
||||
|
||||
var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
if (rawDNSItem != null && rawDNSItem.Enabled == true)
|
||||
{
|
||||
defaultDomainResolverTag = Global.SingboxFinalResolverTag;
|
||||
directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom;
|
||||
}
|
||||
singboxConfig.route.default_domain_resolver = new()
|
||||
{
|
||||
server = defaultDomainResolverTag,
|
||||
strategy = directDNSStrategy
|
||||
};
|
||||
|
||||
if (_config.TunModeItem.EnableTun)
|
||||
{
|
||||
singboxConfig.route.auto_detect_interface = true;
|
||||
|
||||
var tunRules = JsonUtils.Deserialize<List<Rule4Sbox>>(EmbedUtils.GetEmbedText(Global.TunSingboxRulesFileName));
|
||||
if (tunRules != null)
|
||||
{
|
||||
singboxConfig.route.rules.AddRange(tunRules);
|
||||
}
|
||||
|
||||
GenRoutingDirectExe(out var lstDnsExe, out var lstDirectExe);
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
port = new() { 53 },
|
||||
action = "hijack-dns",
|
||||
process_name = lstDnsExe
|
||||
});
|
||||
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
outbound = Global.DirectTag,
|
||||
process_name = lstDirectExe
|
||||
});
|
||||
}
|
||||
|
||||
if (_config.Inbound.First().SniffingEnabled)
|
||||
{
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
action = "sniff"
|
||||
});
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
protocol = new() { "dns" },
|
||||
action = "hijack-dns"
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
port = new() { 53 },
|
||||
network = new() { "udp" },
|
||||
action = "hijack-dns"
|
||||
});
|
||||
}
|
||||
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
outbound = Global.DirectTag,
|
||||
clash_mode = ERuleMode.Direct.ToString()
|
||||
});
|
||||
singboxConfig.route.rules.Add(new()
|
||||
{
|
||||
outbound = Global.ProxyTag,
|
||||
clash_mode = ERuleMode.Global.ToString()
|
||||
});
|
||||
|
||||
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
|
||||
var defaultRouting = await ConfigHandler.GetDefaultRouting(_config);
|
||||
if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty())
|
||||
{
|
||||
domainStrategy = defaultRouting.DomainStrategy4Singbox;
|
||||
}
|
||||
var resolveRule = new Rule4Sbox
|
||||
{
|
||||
action = "resolve",
|
||||
strategy = domainStrategy
|
||||
};
|
||||
if (_config.RoutingBasicItem.DomainStrategy == Global.IPOnDemand)
|
||||
{
|
||||
singboxConfig.route.rules.Add(resolveRule);
|
||||
}
|
||||
|
||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||
var ipRules = new List<RulesItem>();
|
||||
if (routing != null)
|
||||
{
|
||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||
foreach (var item1 in rules ?? [])
|
||||
{
|
||||
if (item1.Enabled)
|
||||
{
|
||||
await GenRoutingUserRule(item1, singboxConfig);
|
||||
if (item1.Ip != null && item1.Ip.Count > 0)
|
||||
{
|
||||
ipRules.Add(item1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_config.RoutingBasicItem.DomainStrategy == Global.IPIfNonMatch)
|
||||
{
|
||||
singboxConfig.route.rules.Add(resolveRule);
|
||||
foreach (var item2 in ipRules)
|
||||
{
|
||||
await GenRoutingUserRule(item2, singboxConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void GenRoutingDirectExe(out List<string> lstDnsExe, out List<string> lstDirectExe)
|
||||
{
|
||||
var dnsExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
var directExeSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var coreInfoResult = CoreInfoManager.Instance.GetCoreInfo();
|
||||
|
||||
foreach (var coreConfig in coreInfoResult)
|
||||
{
|
||||
if (coreConfig.CoreType == ECoreType.v2rayN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var baseExeName in coreConfig.CoreExes)
|
||||
{
|
||||
if (coreConfig.CoreType != ECoreType.sing_box)
|
||||
{
|
||||
dnsExeSet.Add(Utils.GetExeName(baseExeName));
|
||||
}
|
||||
directExeSet.Add(Utils.GetExeName(baseExeName));
|
||||
}
|
||||
}
|
||||
|
||||
lstDnsExe = new List<string>(dnsExeSet);
|
||||
lstDirectExe = new List<string>(directExeSet);
|
||||
}
|
||||
|
||||
private async Task<int> GenRoutingUserRule(RulesItem item, SingboxConfig singboxConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
item.OutboundTag = await GenRoutingUserRuleOutbound(item.OutboundTag, singboxConfig);
|
||||
var rules = singboxConfig.route.rules;
|
||||
|
||||
var rule = new Rule4Sbox();
|
||||
if (item.OutboundTag == "block")
|
||||
{
|
||||
rule.action = "reject";
|
||||
}
|
||||
else
|
||||
{
|
||||
rule.outbound = item.OutboundTag;
|
||||
}
|
||||
|
||||
if (item.Port.IsNotEmpty())
|
||||
{
|
||||
var portRanges = item.Port.Split(',').Where(it => it.Contains('-')).Select(it => it.Replace("-", ":")).ToList();
|
||||
var ports = item.Port.Split(',').Where(it => !it.Contains('-')).Select(it => it.ToInt()).ToList();
|
||||
|
||||
rule.port_range = portRanges.Count > 0 ? portRanges : null;
|
||||
rule.port = ports.Count > 0 ? ports : null;
|
||||
}
|
||||
if (item.Network.IsNotEmpty())
|
||||
{
|
||||
rule.network = Utils.String2List(item.Network);
|
||||
}
|
||||
if (item.Protocol?.Count > 0)
|
||||
{
|
||||
rule.protocol = item.Protocol;
|
||||
}
|
||||
if (item.InboundTag?.Count >= 0)
|
||||
{
|
||||
rule.inbound = item.InboundTag;
|
||||
}
|
||||
var rule1 = JsonUtils.DeepCopy(rule);
|
||||
var rule2 = JsonUtils.DeepCopy(rule);
|
||||
var rule3 = JsonUtils.DeepCopy(rule);
|
||||
|
||||
var hasDomainIp = false;
|
||||
if (item.Domain?.Count > 0)
|
||||
{
|
||||
var countDomain = 0;
|
||||
foreach (var it in item.Domain)
|
||||
{
|
||||
if (ParseV2Domain(it, rule1))
|
||||
countDomain++;
|
||||
}
|
||||
if (countDomain > 0)
|
||||
{
|
||||
rules.Add(rule1);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Ip?.Count > 0)
|
||||
{
|
||||
var countIp = 0;
|
||||
foreach (var it in item.Ip)
|
||||
{
|
||||
if (ParseV2Address(it, rule2))
|
||||
countIp++;
|
||||
}
|
||||
if (countIp > 0)
|
||||
{
|
||||
rules.Add(rule2);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_config.TunModeItem.EnableTun && item.Process?.Count > 0)
|
||||
{
|
||||
rule3.process_name = item.Process;
|
||||
rules.Add(rule3);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
|
||||
if (!hasDomainIp
|
||||
&& (rule.port != null || rule.port_range != null || rule.protocol != null || rule.inbound != null || rule.network != null))
|
||||
{
|
||||
rules.Add(rule);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private bool ParseV2Domain(string domain, Rule4Sbox rule)
|
||||
{
|
||||
if (domain.StartsWith("#") || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (domain.StartsWith("geosite:"))
|
||||
{
|
||||
rule.geosite ??= [];
|
||||
rule.geosite?.Add(domain.Substring(8));
|
||||
}
|
||||
else if (domain.StartsWith("regexp:"))
|
||||
{
|
||||
rule.domain_regex ??= [];
|
||||
rule.domain_regex?.Add(domain.Replace(Global.RoutingRuleComma, ",").Substring(7));
|
||||
}
|
||||
else if (domain.StartsWith("domain:"))
|
||||
{
|
||||
rule.domain ??= [];
|
||||
rule.domain_suffix ??= [];
|
||||
rule.domain?.Add(domain.Substring(7));
|
||||
rule.domain_suffix?.Add("." + domain.Substring(7));
|
||||
}
|
||||
else if (domain.StartsWith("full:"))
|
||||
{
|
||||
rule.domain ??= [];
|
||||
rule.domain?.Add(domain.Substring(5));
|
||||
}
|
||||
else if (domain.StartsWith("keyword:"))
|
||||
{
|
||||
rule.domain_keyword ??= [];
|
||||
rule.domain_keyword?.Add(domain.Substring(8));
|
||||
}
|
||||
else
|
||||
{
|
||||
rule.domain_keyword ??= [];
|
||||
rule.domain_keyword?.Add(domain);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ParseV2Address(string address, Rule4Sbox rule)
|
||||
{
|
||||
if (address.StartsWith("ext:") || address.StartsWith("ext-ip:"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (address.Equals("geoip:private"))
|
||||
{
|
||||
rule.ip_is_private = true;
|
||||
}
|
||||
else if (address.StartsWith("geoip:"))
|
||||
{
|
||||
rule.geoip ??= new();
|
||||
rule.geoip?.Add(address.Substring(6));
|
||||
}
|
||||
else if (address.Equals("geoip:!private"))
|
||||
{
|
||||
rule.ip_is_private = false;
|
||||
}
|
||||
else if (address.StartsWith("geoip:!"))
|
||||
{
|
||||
rule.geoip ??= new();
|
||||
rule.geoip?.Add(address.Substring(6));
|
||||
rule.invert = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
rule.ip_cidr ??= new();
|
||||
rule.ip_cidr?.Add(address);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<string?> GenRoutingUserRuleOutbound(string outboundTag, SingboxConfig singboxConfig)
|
||||
{
|
||||
if (Global.OutboundTags.Contains(outboundTag))
|
||||
{
|
||||
return outboundTag;
|
||||
}
|
||||
|
||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||
if (node == null
|
||||
|| node.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
return Global.ProxyTag;
|
||||
}
|
||||
|
||||
var server = await GenServer(node);
|
||||
if (server is null)
|
||||
{
|
||||
return Global.ProxyTag;
|
||||
}
|
||||
|
||||
server.tag = Global.ProxyTag + node.IndexId.ToString();
|
||||
if (server is Endpoints4Sbox endpoint)
|
||||
{
|
||||
singboxConfig.endpoints ??= new();
|
||||
singboxConfig.endpoints.Add(endpoint);
|
||||
}
|
||||
else if (server is Outbound4Sbox outbound)
|
||||
{
|
||||
singboxConfig.outbounds.Add(outbound);
|
||||
}
|
||||
|
||||
return server.tag;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService
|
||||
{
|
||||
private async Task<int> ConvertGeo2Ruleset(SingboxConfig singboxConfig)
|
||||
{
|
||||
static void AddRuleSets(List<string> ruleSets, List<string>? rule_set)
|
||||
{
|
||||
if (rule_set != null)
|
||||
ruleSets.AddRange(rule_set);
|
||||
}
|
||||
var geosite = "geosite";
|
||||
var geoip = "geoip";
|
||||
var ruleSets = new List<string>();
|
||||
|
||||
//convert route geosite & geoip to ruleset
|
||||
foreach (var rule in singboxConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set ??= new List<string>();
|
||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
||||
rule.geosite = null;
|
||||
AddRuleSets(ruleSets, rule.rule_set);
|
||||
}
|
||||
foreach (var rule in singboxConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set ??= new List<string>();
|
||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
||||
rule.geoip = null;
|
||||
AddRuleSets(ruleSets, rule.rule_set);
|
||||
}
|
||||
|
||||
//convert dns geosite & geoip to ruleset
|
||||
foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set ??= new List<string>();
|
||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
||||
rule.geosite = null;
|
||||
}
|
||||
foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set ??= new List<string>();
|
||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
||||
rule.geoip = null;
|
||||
}
|
||||
foreach (var dnsRule in singboxConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
AddRuleSets(ruleSets, dnsRule.rule_set);
|
||||
}
|
||||
//rules in rules
|
||||
foreach (var item in singboxConfig.dns?.rules.Where(t => t.rules?.Count > 0).Select(t => t.rules).ToList() ?? [])
|
||||
{
|
||||
foreach (var item2 in item ?? [])
|
||||
{
|
||||
AddRuleSets(ruleSets, item2.rule_set);
|
||||
}
|
||||
}
|
||||
|
||||
//load custom ruleset file
|
||||
List<Ruleset4Sbox> customRulesets = [];
|
||||
|
||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||
if (routing.CustomRulesetPath4Singbox.IsNotEmpty())
|
||||
{
|
||||
var result = EmbedUtils.LoadResource(routing.CustomRulesetPath4Singbox);
|
||||
if (result.IsNotEmpty())
|
||||
{
|
||||
customRulesets = (JsonUtils.Deserialize<List<Ruleset4Sbox>>(result) ?? [])
|
||||
.Where(t => t.tag != null)
|
||||
.Where(t => t.type != null)
|
||||
.Where(t => t.format != null)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
//Local srs files address
|
||||
var localSrss = Utils.GetBinPath("srss");
|
||||
|
||||
//Add ruleset srs
|
||||
singboxConfig.route.rule_set = [];
|
||||
foreach (var item in new HashSet<string>(ruleSets))
|
||||
{
|
||||
if (item.IsNullOrEmpty())
|
||||
{ continue; }
|
||||
var customRuleset = customRulesets.FirstOrDefault(t => t.tag != null && t.tag.Equals(item));
|
||||
if (customRuleset is null)
|
||||
{
|
||||
var pathSrs = Path.Combine(localSrss, $"{item}.srs");
|
||||
if (File.Exists(pathSrs))
|
||||
{
|
||||
customRuleset = new()
|
||||
{
|
||||
type = "local",
|
||||
format = "binary",
|
||||
tag = item,
|
||||
path = pathSrs
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var srsUrl = string.IsNullOrEmpty(_config.ConstItem.SrsSourceUrl)
|
||||
? Global.SingboxRulesetUrl
|
||||
: _config.ConstItem.SrsSourceUrl;
|
||||
|
||||
customRuleset = new()
|
||||
{
|
||||
type = "remote",
|
||||
format = "binary",
|
||||
tag = item,
|
||||
url = string.Format(srsUrl, item.StartsWith(geosite) ? geosite : geoip, item),
|
||||
download_detour = Global.ProxyTag
|
||||
};
|
||||
}
|
||||
}
|
||||
singboxConfig.route.rule_set.Add(customRuleset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService
|
||||
{
|
||||
private async Task<int> GenExperimental(SingboxConfig singboxConfig)
|
||||
{
|
||||
//if (_config.guiItem.enableStatistics)
|
||||
{
|
||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
||||
singboxConfig.experimental.clash_api = new Clash_Api4Sbox()
|
||||
{
|
||||
external_controller = $"{Global.Loopback}:{AppManager.Instance.StatePort2}",
|
||||
};
|
||||
}
|
||||
|
||||
if (_config.CoreBasicItem.EnableCacheFile4Sbox)
|
||||
{
|
||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
||||
singboxConfig.experimental.cache_file = new CacheFile4Sbox()
|
||||
{
|
||||
enabled = true,
|
||||
path = Utils.GetBinPath("cache.db"),
|
||||
store_fakeip = _config.SimpleDNSItem.FakeIP == true
|
||||
};
|
||||
}
|
||||
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,415 @@
|
|||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService(Config config)
|
||||
{
|
||||
private readonly Config _config = config;
|
||||
private static readonly string _tag = "CoreConfigV2rayService";
|
||||
|
||||
#region public gen function
|
||||
|
||||
public async Task<RetResult> GenerateClientConfigContent(ProfileItem node)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null
|
||||
|| node.Port <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.GetNetwork() is nameof(ETransport.quic))
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||
if (v2rayConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
await GenLog(v2rayConfig);
|
||||
|
||||
await GenInbounds(v2rayConfig);
|
||||
|
||||
await GenOutbound(node, v2rayConfig.outbounds.First());
|
||||
|
||||
await GenMoreOutbounds(node, v2rayConfig);
|
||||
|
||||
await GenRouting(v2rayConfig);
|
||||
|
||||
await GenDns(node, v2rayConfig);
|
||||
|
||||
await GenStatistic(v2rayConfig);
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, EMultipleLoad multipleLoad)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
|
||||
try
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||
if (v2rayConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
await GenLog(v2rayConfig);
|
||||
await GenInbounds(v2rayConfig);
|
||||
await GenRouting(v2rayConfig);
|
||||
await GenDns(null, v2rayConfig);
|
||||
await GenStatistic(v2rayConfig);
|
||||
v2rayConfig.outbounds.RemoveAt(0);
|
||||
|
||||
var proxyProfiles = new List<ProfileItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.Port <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
|
||||
{
|
||||
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (item.ConfigType == EConfigType.Shadowsocks
|
||||
&& !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.ConfigType == EConfigType.VLESS && !Global.Flows.Contains(item.Flow))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//outbound
|
||||
proxyProfiles.Add(item);
|
||||
}
|
||||
if (proxyProfiles.Count <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
await GenOutboundsList(proxyProfiles, v2rayConfig);
|
||||
|
||||
//add balancers
|
||||
await GenBalancer(v2rayConfig, multipleLoad);
|
||||
|
||||
var balancer = v2rayConfig.routing.balancers.First();
|
||||
|
||||
//add rule
|
||||
var rules = v2rayConfig.routing.rules.Where(t => t.outboundTag == Global.ProxyTag).ToList();
|
||||
if (rules?.Count > 0)
|
||||
{
|
||||
foreach (var rule in rules)
|
||||
{
|
||||
rule.outboundTag = null;
|
||||
rule.balancerTag = balancer.tag;
|
||||
}
|
||||
}
|
||||
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
||||
{
|
||||
v2rayConfig.routing.rules.Add(new()
|
||||
{
|
||||
ip = ["0.0.0.0/0", "::/0"],
|
||||
balancerTag = balancer.tag,
|
||||
type = "field"
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
v2rayConfig.routing.rules.Add(new()
|
||||
{
|
||||
network = "tcp,udp",
|
||||
balancerTag = balancer.tag,
|
||||
type = "field"
|
||||
});
|
||||
}
|
||||
|
||||
ret.Success = true;
|
||||
|
||||
ret.Data = await ApplyFullConfigTemplate(v2rayConfig, true);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = ResUI.InitialConfiguration;
|
||||
|
||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||
if (v2rayConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
List<IPEndPoint> lstIpEndPoints = new();
|
||||
List<TcpConnectionInformation> lstTcpConns = new();
|
||||
try
|
||||
{
|
||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
|
||||
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners());
|
||||
lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
await GenLog(v2rayConfig);
|
||||
v2rayConfig.inbounds.Clear();
|
||||
v2rayConfig.outbounds.Clear();
|
||||
v2rayConfig.routing.rules.Clear();
|
||||
|
||||
var initPort = AppManager.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.Port <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = await AppManager.Instance.GetProfileItem(it.IndexId);
|
||||
if (it.ConfigType is EConfigType.VMess or EConfigType.VLESS)
|
||||
{
|
||||
if (item is null || item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//find unused port
|
||||
var port = initPort;
|
||||
for (var k = initPort; k < Global.MaxPort; k++)
|
||||
{
|
||||
if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//found
|
||||
port = k;
|
||||
initPort = port + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
//Port In Used
|
||||
if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
it.Port = port;
|
||||
it.AllowTest = true;
|
||||
|
||||
//outbound
|
||||
if (item is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.ConfigType == EConfigType.Shadowsocks
|
||||
&& !Global.SsSecuritiesInXray.Contains(item.Security))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.ConfigType == EConfigType.VLESS
|
||||
&& !Global.Flows.Contains(item.Flow))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType is EConfigType.VLESS or EConfigType.Trojan
|
||||
&& item.StreamSecurity == Global.StreamSecurityReality
|
||||
&& item.PublicKey.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//inbound
|
||||
Inbounds4Ray inbound = new()
|
||||
{
|
||||
listen = Global.Loopback,
|
||||
port = port,
|
||||
protocol = EInboundProtocol.mixed.ToString(),
|
||||
};
|
||||
inbound.tag = inbound.protocol + inbound.port.ToString();
|
||||
v2rayConfig.inbounds.Add(inbound);
|
||||
|
||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(item, outbound);
|
||||
outbound.tag = Global.ProxyTag + inbound.port.ToString();
|
||||
v2rayConfig.outbounds.Add(outbound);
|
||||
|
||||
//rule
|
||||
RulesItem4Ray rule = new()
|
||||
{
|
||||
inboundTag = new List<string> { inbound.tag },
|
||||
outboundTag = outbound.tag,
|
||||
type = "field"
|
||||
};
|
||||
v2rayConfig.routing.rules.Add(rule);
|
||||
}
|
||||
|
||||
//ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(v2rayConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientSpeedtestConfig(ProfileItem node, int port)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node is not { Port: > 0 })
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (node.GetNetwork() is nameof(ETransport.quic))
|
||||
{
|
||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
|
||||
if (v2rayConfig == null)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
await GenLog(v2rayConfig);
|
||||
await GenOutbound(node, v2rayConfig.outbounds.First());
|
||||
await GenMoreOutbounds(node, v2rayConfig);
|
||||
|
||||
v2rayConfig.routing.rules.Clear();
|
||||
v2rayConfig.inbounds.Clear();
|
||||
v2rayConfig.inbounds.Add(new()
|
||||
{
|
||||
tag = $"{EInboundProtocol.socks}{port}",
|
||||
listen = Global.Loopback,
|
||||
port = port,
|
||||
protocol = EInboundProtocol.mixed.ToString(),
|
||||
});
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
ret.Data = JsonUtils.Serialize(v2rayConfig);
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion public gen function
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService
|
||||
{
|
||||
private async Task<int> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad)
|
||||
{
|
||||
if (multipleLoad == EMultipleLoad.LeastPing)
|
||||
{
|
||||
var observatory = new Observatory4Ray
|
||||
{
|
||||
subjectSelector = [Global.ProxyTag],
|
||||
probeUrl = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
||||
probeInterval = "3m",
|
||||
enableConcurrency = true,
|
||||
};
|
||||
v2rayConfig.observatory = observatory;
|
||||
}
|
||||
else if (multipleLoad == EMultipleLoad.LeastLoad)
|
||||
{
|
||||
var burstObservatory = new BurstObservatory4Ray
|
||||
{
|
||||
subjectSelector = [Global.ProxyTag],
|
||||
pingConfig = new()
|
||||
{
|
||||
destination = AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
||||
interval = "5m",
|
||||
timeout = "30s",
|
||||
sampling = 2,
|
||||
}
|
||||
};
|
||||
v2rayConfig.burstObservatory = burstObservatory;
|
||||
}
|
||||
var strategyType = multipleLoad switch
|
||||
{
|
||||
EMultipleLoad.Random => "random",
|
||||
EMultipleLoad.RoundRobin => "roundRobin",
|
||||
EMultipleLoad.LeastPing => "leastPing",
|
||||
EMultipleLoad.LeastLoad => "leastLoad",
|
||||
_ => "roundRobin",
|
||||
};
|
||||
var balancer = new BalancersItem4Ray
|
||||
{
|
||||
selector = [Global.ProxyTag],
|
||||
strategy = new() { type = strategyType },
|
||||
tag = $"{Global.ProxyTag}-round",
|
||||
};
|
||||
v2rayConfig.routing.balancers = [balancer];
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService
|
||||
{
|
||||
private async Task<string> ApplyFullConfigTemplate(V2rayConfig v2rayConfig, bool handleBalancerAndRules = false)
|
||||
{
|
||||
var fullConfigTemplate = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
||||
if (fullConfigTemplate == null || !fullConfigTemplate.Enabled || fullConfigTemplate.Config.IsNullOrEmpty())
|
||||
{
|
||||
return JsonUtils.Serialize(v2rayConfig);
|
||||
}
|
||||
|
||||
var fullConfigTemplateNode = JsonNode.Parse(fullConfigTemplate.Config);
|
||||
if (fullConfigTemplateNode == 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 = fullConfigTemplateNode["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 (fullConfigTemplateNode["routing"] == null)
|
||||
{
|
||||
fullConfigTemplateNode["routing"] = new JsonObject();
|
||||
}
|
||||
|
||||
// Handle balancers - append instead of override
|
||||
if (fullConfigTemplateNode["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
|
||||
{
|
||||
fullConfigTemplateNode["routing"]["balancers"] = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig.routing.balancers));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle outbounds - append instead of override
|
||||
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
|
||||
foreach (var outbound in v2rayConfig.outbounds)
|
||||
{
|
||||
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
|
||||
{
|
||||
if (fullConfigTemplate.AddProxyOnly == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!fullConfigTemplate.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 = fullConfigTemplate.ProxyDetour;
|
||||
}
|
||||
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
|
||||
}
|
||||
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
||||
|
||||
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
|
||||
}
|
||||
}
|
426
v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs
Normal file
426
v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs
Normal file
|
@ -0,0 +1,426 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService
|
||||
{
|
||||
private async Task<int> GenDns(ProfileItem? node, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
||||
if (item != null && item.Enabled == true)
|
||||
{
|
||||
var result = await GenDnsCompatible(node, v2rayConfig);
|
||||
|
||||
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
||||
{
|
||||
// DNS routing
|
||||
v2rayConfig.dns.tag = Global.DnsTag;
|
||||
v2rayConfig.routing.rules.Add(new RulesItem4Ray
|
||||
{
|
||||
type = "field",
|
||||
inboundTag = new List<string> { Global.DnsTag },
|
||||
outboundTag = Global.ProxyTag,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
var simpleDNSItem = _config.SimpleDNSItem;
|
||||
var domainStrategy4Freedom = simpleDNSItem?.RayStrategy4Freedom;
|
||||
|
||||
//Outbound Freedom domainStrategy
|
||||
if (domainStrategy4Freedom.IsNotEmpty())
|
||||
{
|
||||
var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
||||
if (outbound != null)
|
||||
{
|
||||
outbound.settings = new()
|
||||
{
|
||||
domainStrategy = domainStrategy4Freedom,
|
||||
userLevel = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
await GenDnsServers(node, v2rayConfig, simpleDNSItem);
|
||||
await GenDnsHosts(v2rayConfig, simpleDNSItem);
|
||||
|
||||
if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch)
|
||||
{
|
||||
// DNS routing
|
||||
v2rayConfig.dns.tag = Global.DnsTag;
|
||||
v2rayConfig.routing.rules.Add(new RulesItem4Ray
|
||||
{
|
||||
type = "field",
|
||||
inboundTag = new List<string> { Global.DnsTag },
|
||||
outboundTag = Global.ProxyTag,
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem)
|
||||
{
|
||||
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
|
||||
{
|
||||
var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
|
||||
.Select(addr => addr.Trim())
|
||||
.Where(addr => !string.IsNullOrEmpty(addr))
|
||||
.Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
|
||||
.Distinct()
|
||||
.ToList() ?? new List<string> { defaultAddress };
|
||||
return addresses.Count > 0 ? addresses : new List<string> { defaultAddress };
|
||||
}
|
||||
|
||||
static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
|
||||
{
|
||||
var dnsServer = new DnsServer4Ray
|
||||
{
|
||||
address = dnsAddress,
|
||||
skipFallback = true,
|
||||
domains = domains.Count > 0 ? domains : null,
|
||||
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
|
||||
};
|
||||
return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
});
|
||||
}
|
||||
|
||||
var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.FirstOrDefault());
|
||||
var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.FirstOrDefault());
|
||||
|
||||
var directDomainList = new List<string>();
|
||||
var directGeositeList = new List<string>();
|
||||
var proxyDomainList = new List<string>();
|
||||
var proxyGeositeList = new List<string>();
|
||||
var expectedDomainList = new List<string>();
|
||||
var expectedIPs = new List<string>();
|
||||
var regionNames = new HashSet<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
|
||||
{
|
||||
expectedIPs = simpleDNSItem.DirectExpectedIPs
|
||||
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => s.Trim())
|
||||
.Where(s => !string.IsNullOrEmpty(s))
|
||||
.ToList();
|
||||
|
||||
foreach (var ip in expectedIPs)
|
||||
{
|
||||
if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var region = ip["geoip:".Length..];
|
||||
if (!string.IsNullOrEmpty(region))
|
||||
{
|
||||
regionNames.Add($"geosite:{region}");
|
||||
regionNames.Add($"geosite:geolocation-{region}");
|
||||
regionNames.Add($"geosite:tld-{region}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||
List<RulesItem>? rules = null;
|
||||
if (routing != null)
|
||||
{
|
||||
rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
||||
foreach (var item in rules)
|
||||
{
|
||||
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var domain in item.Domain)
|
||||
{
|
||||
if (domain.StartsWith('#'))
|
||||
continue;
|
||||
var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ",");
|
||||
|
||||
if (item.OutboundTag == Global.DirectTag)
|
||||
{
|
||||
if (normalizedDomain.StartsWith("geosite:"))
|
||||
{
|
||||
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
directDomainList.Add(normalizedDomain);
|
||||
}
|
||||
}
|
||||
else if (item.OutboundTag != Global.BlockTag)
|
||||
{
|
||||
if (normalizedDomain.StartsWith("geosite:"))
|
||||
{
|
||||
proxyGeositeList.Add(normalizedDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxyDomainList.Add(normalizedDomain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Utils.IsDomain(node?.Address))
|
||||
{
|
||||
directDomainList.Add(node.Address);
|
||||
}
|
||||
|
||||
if (node?.Subid is not null)
|
||||
{
|
||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
||||
if (subItem is not null)
|
||||
{
|
||||
foreach (var profile in new[] { subItem.PrevProfile, subItem.NextProfile })
|
||||
{
|
||||
var profileNode = await AppManager.Instance.GetProfileItemViaRemarks(profile);
|
||||
if (profileNode is not null &&
|
||||
profileNode.ConfigType is not (EConfigType.Custom or EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) &&
|
||||
Utils.IsDomain(profileNode.Address))
|
||||
{
|
||||
directDomainList.Add(profileNode.Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v2rayConfig.dns ??= new Dns4Ray();
|
||||
v2rayConfig.dns.servers ??= new List<object>();
|
||||
|
||||
void AddDnsServers(List<string> dnsAddresses, List<string> domains, List<string>? expectedIPs = null)
|
||||
{
|
||||
if (domains.Count > 0)
|
||||
{
|
||||
foreach (var dnsAddress in dnsAddresses)
|
||||
{
|
||||
v2rayConfig.dns.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddDnsServers(remoteDNSAddress, proxyDomainList);
|
||||
AddDnsServers(directDNSAddress, directDomainList);
|
||||
AddDnsServers(remoteDNSAddress, proxyGeositeList);
|
||||
AddDnsServers(directDNSAddress, directGeositeList);
|
||||
AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs);
|
||||
|
||||
var useDirectDns = rules?.LastOrDefault() is { } lastRule &&
|
||||
lastRule.OutboundTag == Global.DirectTag &&
|
||||
(lastRule.Port == "0-65535" ||
|
||||
lastRule.Network == "tcp,udp" ||
|
||||
lastRule.Ip?.Contains("0.0.0.0/0") == true);
|
||||
|
||||
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
|
||||
v2rayConfig.dns.servers.AddRange(defaultDnsServers);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsHosts(V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem)
|
||||
{
|
||||
if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty())
|
||||
{
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
v2rayConfig.dns ??= new Dns4Ray();
|
||||
v2rayConfig.dns.hosts ??= new Dictionary<string, object>();
|
||||
if (simpleDNSItem.AddCommonHosts == true)
|
||||
{
|
||||
v2rayConfig.dns.hosts = Global.PredefinedHosts.ToDictionary(
|
||||
kvp => kvp.Key,
|
||||
kvp => (object)kvp.Value
|
||||
);
|
||||
}
|
||||
|
||||
if (simpleDNSItem.UseSystemHosts == true)
|
||||
{
|
||||
var systemHosts = Utils.GetSystemHosts();
|
||||
if (systemHosts.Count > 0)
|
||||
{
|
||||
var normalHost = v2rayConfig.dns.hosts;
|
||||
if (normalHost != null)
|
||||
{
|
||||
foreach (var host in systemHosts)
|
||||
{
|
||||
if (normalHost[host.Key] != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
normalHost[host.Key] = new List<string> { host.Value };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var userHostsMap = simpleDNSItem.Hosts?
|
||||
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(line => !string.IsNullOrWhiteSpace(line))
|
||||
.Where(line => line.Contains(' '))
|
||||
.ToDictionary(
|
||||
line =>
|
||||
{
|
||||
var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return parts[0];
|
||||
},
|
||||
line =>
|
||||
{
|
||||
var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var values = parts.Skip(1).ToList();
|
||||
return values;
|
||||
}
|
||||
);
|
||||
|
||||
if (userHostsMap != null)
|
||||
{
|
||||
foreach (var kvp in userHostsMap)
|
||||
{
|
||||
v2rayConfig.dns.hosts[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsCompatible(ProfileItem? node, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
||||
var normalDNS = item?.NormalDNS;
|
||||
var domainStrategy4Freedom = item?.DomainStrategy4Freedom;
|
||||
if (normalDNS.IsNullOrEmpty())
|
||||
{
|
||||
normalDNS = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName);
|
||||
}
|
||||
|
||||
//Outbound Freedom domainStrategy
|
||||
if (domainStrategy4Freedom.IsNotEmpty())
|
||||
{
|
||||
var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
||||
if (outbound != null)
|
||||
{
|
||||
outbound.settings = new();
|
||||
outbound.settings.domainStrategy = domainStrategy4Freedom;
|
||||
outbound.settings.userLevel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var obj = JsonUtils.ParseJson(normalDNS);
|
||||
if (obj is null)
|
||||
{
|
||||
List<string> servers = [];
|
||||
string[] arrDNS = normalDNS.Split(',');
|
||||
foreach (string str in arrDNS)
|
||||
{
|
||||
servers.Add(str);
|
||||
}
|
||||
obj = JsonUtils.ParseJson("{}");
|
||||
obj["servers"] = JsonUtils.SerializeToNode(servers);
|
||||
}
|
||||
|
||||
// Append to dns settings
|
||||
if (item.UseSystemHosts)
|
||||
{
|
||||
var systemHosts = Utils.GetSystemHosts();
|
||||
if (systemHosts.Count > 0)
|
||||
{
|
||||
var normalHost1 = obj["hosts"];
|
||||
if (normalHost1 != null)
|
||||
{
|
||||
foreach (var host in systemHosts)
|
||||
{
|
||||
if (normalHost1[host.Key] != null)
|
||||
continue;
|
||||
normalHost1[host.Key] = host.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var normalHost = obj["hosts"];
|
||||
if (normalHost != null)
|
||||
{
|
||||
foreach (var hostProp in normalHost.AsObject().ToList())
|
||||
{
|
||||
if (hostProp.Value is JsonValue value && value.TryGetValue<string>(out var ip))
|
||||
{
|
||||
normalHost[hostProp.Key] = new JsonArray(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await GenDnsDomainsCompatible(node, obj, item);
|
||||
|
||||
v2rayConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(obj));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dNSItem)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var servers = dns["servers"];
|
||||
if (servers != null)
|
||||
{
|
||||
var domainList = new List<string>();
|
||||
if (Utils.IsDomain(node.Address))
|
||||
{
|
||||
domainList.Add(node.Address);
|
||||
}
|
||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
||||
if (subItem is not null)
|
||||
{
|
||||
// Previous proxy
|
||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& Utils.IsDomain(prevNode.Address))
|
||||
{
|
||||
domainList.Add(prevNode.Address);
|
||||
}
|
||||
|
||||
// Next proxy
|
||||
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC
|
||||
&& Utils.IsDomain(nextNode.Address))
|
||||
{
|
||||
domainList.Add(nextNode.Address);
|
||||
}
|
||||
}
|
||||
if (domainList.Count > 0)
|
||||
{
|
||||
var dnsServer = new DnsServer4Ray()
|
||||
{
|
||||
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
|
||||
skipFallback = true,
|
||||
domains = domainList
|
||||
};
|
||||
servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer));
|
||||
}
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService
|
||||
{
|
||||
private async Task<int> GenInbounds(V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var listen = "0.0.0.0";
|
||||
v2rayConfig.inbounds = [];
|
||||
|
||||
var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
|
||||
v2rayConfig.inbounds.Add(inbound);
|
||||
|
||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
||||
{
|
||||
var inbound2 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
|
||||
v2rayConfig.inbounds.Add(inbound2);
|
||||
}
|
||||
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
if (_config.Inbound.First().NewPort4LAN)
|
||||
{
|
||||
var inbound3 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks3, true);
|
||||
inbound3.listen = listen;
|
||||
v2rayConfig.inbounds.Add(inbound3);
|
||||
|
||||
//auth
|
||||
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
||||
{
|
||||
inbound3.settings.auth = "password";
|
||||
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inbound.listen = listen;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
|
||||
{
|
||||
string result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
return new();
|
||||
}
|
||||
|
||||
var inbound = JsonUtils.Deserialize<Inbounds4Ray>(result);
|
||||
if (inbound == null)
|
||||
{
|
||||
return new();
|
||||
}
|
||||
inbound.tag = protocol.ToString();
|
||||
inbound.port = inItem.LocalPort + (int)protocol;
|
||||
inbound.protocol = EInboundProtocol.mixed.ToString();
|
||||
inbound.settings.udp = inItem.UdpEnabled;
|
||||
inbound.sniffing.enabled = inItem.SniffingEnabled;
|
||||
inbound.sniffing.destOverride = inItem.DestOverride;
|
||||
inbound.sniffing.routeOnly = inItem.RouteOnly;
|
||||
|
||||
return inbound;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService
|
||||
{
|
||||
private async Task<int> GenLog(V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_config.CoreBasicItem.LogEnabled)
|
||||
{
|
||||
var dtNow = DateTime.Now;
|
||||
v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
||||
v2rayConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt");
|
||||
v2rayConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt");
|
||||
}
|
||||
else
|
||||
{
|
||||
v2rayConfig.log.loglevel = _config.CoreBasicItem.Loglevel;
|
||||
v2rayConfig.log.access = null;
|
||||
v2rayConfig.log.error = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,704 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService
|
||||
{
|
||||
private async Task<int> GenOutbound(ProfileItem node, Outbounds4Ray outbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||
switch (node.ConfigType)
|
||||
{
|
||||
case EConfigType.VMess:
|
||||
{
|
||||
VnextItem4Ray vnextItem;
|
||||
if (outbound.settings.vnext.Count <= 0)
|
||||
{
|
||||
vnextItem = new VnextItem4Ray();
|
||||
outbound.settings.vnext.Add(vnextItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
vnextItem = outbound.settings.vnext.First();
|
||||
}
|
||||
vnextItem.address = node.Address;
|
||||
vnextItem.port = node.Port;
|
||||
|
||||
UsersItem4Ray usersItem;
|
||||
if (vnextItem.users.Count <= 0)
|
||||
{
|
||||
usersItem = new UsersItem4Ray();
|
||||
vnextItem.users.Add(usersItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem = vnextItem.users.First();
|
||||
}
|
||||
|
||||
usersItem.id = node.Id;
|
||||
usersItem.alterId = node.AlterId;
|
||||
usersItem.email = Global.UserEMail;
|
||||
if (Global.VmessSecurities.Contains(node.Security))
|
||||
{
|
||||
usersItem.security = node.Security;
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem.security = Global.DefaultSecurity;
|
||||
}
|
||||
|
||||
await GenOutboundMux(node, outbound, muxEnabled, muxEnabled);
|
||||
|
||||
outbound.settings.servers = null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.Shadowsocks:
|
||||
{
|
||||
ServersItem4Ray serversItem;
|
||||
if (outbound.settings.servers.Count <= 0)
|
||||
{
|
||||
serversItem = new ServersItem4Ray();
|
||||
outbound.settings.servers.Add(serversItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
serversItem = outbound.settings.servers.First();
|
||||
}
|
||||
serversItem.address = node.Address;
|
||||
serversItem.port = node.Port;
|
||||
serversItem.password = node.Id;
|
||||
serversItem.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : "none";
|
||||
|
||||
serversItem.ota = false;
|
||||
serversItem.level = 1;
|
||||
|
||||
await GenOutboundMux(node, outbound);
|
||||
|
||||
outbound.settings.vnext = null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.SOCKS:
|
||||
case EConfigType.HTTP:
|
||||
{
|
||||
ServersItem4Ray serversItem;
|
||||
if (outbound.settings.servers.Count <= 0)
|
||||
{
|
||||
serversItem = new ServersItem4Ray();
|
||||
outbound.settings.servers.Add(serversItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
serversItem = outbound.settings.servers.First();
|
||||
}
|
||||
serversItem.address = node.Address;
|
||||
serversItem.port = node.Port;
|
||||
serversItem.method = null;
|
||||
serversItem.password = null;
|
||||
|
||||
if (node.Security.IsNotEmpty()
|
||||
&& node.Id.IsNotEmpty())
|
||||
{
|
||||
SocksUsersItem4Ray socksUsersItem = new()
|
||||
{
|
||||
user = node.Security,
|
||||
pass = node.Id,
|
||||
level = 1
|
||||
};
|
||||
|
||||
serversItem.users = new List<SocksUsersItem4Ray>() { socksUsersItem };
|
||||
}
|
||||
|
||||
await GenOutboundMux(node, outbound);
|
||||
|
||||
outbound.settings.vnext = null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.VLESS:
|
||||
{
|
||||
VnextItem4Ray vnextItem;
|
||||
if (outbound.settings.vnext?.Count <= 0)
|
||||
{
|
||||
vnextItem = new VnextItem4Ray();
|
||||
outbound.settings.vnext.Add(vnextItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
vnextItem = outbound.settings.vnext.First();
|
||||
}
|
||||
vnextItem.address = node.Address;
|
||||
vnextItem.port = node.Port;
|
||||
|
||||
UsersItem4Ray usersItem;
|
||||
if (vnextItem.users.Count <= 0)
|
||||
{
|
||||
usersItem = new UsersItem4Ray();
|
||||
vnextItem.users.Add(usersItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem = vnextItem.users.First();
|
||||
}
|
||||
usersItem.id = node.Id;
|
||||
usersItem.email = Global.UserEMail;
|
||||
usersItem.encryption = node.Security;
|
||||
|
||||
if (node.Flow.IsNullOrEmpty())
|
||||
{
|
||||
await GenOutboundMux(node, outbound, muxEnabled, muxEnabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
usersItem.flow = node.Flow;
|
||||
await GenOutboundMux(node, outbound, false, muxEnabled);
|
||||
}
|
||||
outbound.settings.servers = null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.Trojan:
|
||||
{
|
||||
ServersItem4Ray serversItem;
|
||||
if (outbound.settings.servers.Count <= 0)
|
||||
{
|
||||
serversItem = new ServersItem4Ray();
|
||||
outbound.settings.servers.Add(serversItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
serversItem = outbound.settings.servers.First();
|
||||
}
|
||||
serversItem.address = node.Address;
|
||||
serversItem.port = node.Port;
|
||||
serversItem.password = node.Id;
|
||||
|
||||
serversItem.ota = false;
|
||||
serversItem.level = 1;
|
||||
|
||||
await GenOutboundMux(node, outbound);
|
||||
|
||||
outbound.settings.vnext = null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.WireGuard:
|
||||
{
|
||||
var peer = new WireguardPeer4Ray
|
||||
{
|
||||
publicKey = node.PublicKey,
|
||||
endpoint = node.Address + ":" + node.Port.ToString()
|
||||
};
|
||||
var setting = new Outboundsettings4Ray
|
||||
{
|
||||
address = Utils.String2List(node.RequestHost),
|
||||
secretKey = node.Id,
|
||||
reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(),
|
||||
mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(),
|
||||
peers = new List<WireguardPeer4Ray> { peer }
|
||||
};
|
||||
outbound.settings = setting;
|
||||
outbound.settings.vnext = null;
|
||||
outbound.settings.servers = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outbound.protocol = Global.ProtocolTypes[node.ConfigType];
|
||||
await GenBoundStreamSettings(node, outbound);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabledTCP = false, bool enabledUDP = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
outbound.mux.enabled = false;
|
||||
outbound.mux.concurrency = -1;
|
||||
|
||||
if (enabledTCP)
|
||||
{
|
||||
outbound.mux.enabled = true;
|
||||
outbound.mux.concurrency = _config.Mux4RayItem.Concurrency;
|
||||
}
|
||||
else if (enabledUDP)
|
||||
{
|
||||
outbound.mux.enabled = true;
|
||||
outbound.mux.xudpConcurrency = _config.Mux4RayItem.XudpConcurrency;
|
||||
outbound.mux.xudpProxyUDP443 = _config.Mux4RayItem.XudpProxyUDP443;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<int> GenBoundStreamSettings(ProfileItem node, Outbounds4Ray outbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
var streamSettings = outbound.streamSettings;
|
||||
streamSettings.network = node.GetNetwork();
|
||||
var host = node.RequestHost.TrimEx();
|
||||
var path = node.Path.TrimEx();
|
||||
var sni = node.Sni.TrimEx();
|
||||
var useragent = "";
|
||||
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
useragent = Global.UserAgentTexts[_config.CoreBasicItem.DefUserAgent];
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
useragent = _config.CoreBasicItem.DefUserAgent;
|
||||
}
|
||||
}
|
||||
|
||||
//if tls
|
||||
if (node.StreamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
streamSettings.security = node.StreamSecurity;
|
||||
|
||||
TlsSettings4Ray tlsSettings = new()
|
||||
{
|
||||
allowInsecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
||||
alpn = node.GetAlpn(),
|
||||
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
|
||||
};
|
||||
if (sni.IsNotEmpty())
|
||||
{
|
||||
tlsSettings.serverName = sni;
|
||||
}
|
||||
else if (host.IsNotEmpty())
|
||||
{
|
||||
tlsSettings.serverName = Utils.String2List(host)?.First();
|
||||
}
|
||||
streamSettings.tlsSettings = tlsSettings;
|
||||
}
|
||||
|
||||
//if Reality
|
||||
if (node.StreamSecurity == Global.StreamSecurityReality)
|
||||
{
|
||||
streamSettings.security = node.StreamSecurity;
|
||||
|
||||
TlsSettings4Ray realitySettings = new()
|
||||
{
|
||||
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint,
|
||||
serverName = sni,
|
||||
publicKey = node.PublicKey,
|
||||
shortId = node.ShortId,
|
||||
spiderX = node.SpiderX,
|
||||
mldsa65Verify = node.Mldsa65Verify,
|
||||
show = false,
|
||||
};
|
||||
|
||||
streamSettings.realitySettings = realitySettings;
|
||||
}
|
||||
|
||||
//streamSettings
|
||||
switch (node.GetNetwork())
|
||||
{
|
||||
case nameof(ETransport.kcp):
|
||||
KcpSettings4Ray kcpSettings = new()
|
||||
{
|
||||
mtu = _config.KcpItem.Mtu,
|
||||
tti = _config.KcpItem.Tti
|
||||
};
|
||||
|
||||
kcpSettings.uplinkCapacity = _config.KcpItem.UplinkCapacity;
|
||||
kcpSettings.downlinkCapacity = _config.KcpItem.DownlinkCapacity;
|
||||
|
||||
kcpSettings.congestion = _config.KcpItem.Congestion;
|
||||
kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize;
|
||||
kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize;
|
||||
kcpSettings.header = new Header4Ray
|
||||
{
|
||||
type = node.HeaderType,
|
||||
domain = host.IsNullOrEmpty() ? null : host
|
||||
};
|
||||
if (path.IsNotEmpty())
|
||||
{
|
||||
kcpSettings.seed = path;
|
||||
}
|
||||
streamSettings.kcpSettings = kcpSettings;
|
||||
break;
|
||||
//ws
|
||||
case nameof(ETransport.ws):
|
||||
WsSettings4Ray wsSettings = new();
|
||||
wsSettings.headers = new Headers4Ray();
|
||||
|
||||
if (host.IsNotEmpty())
|
||||
{
|
||||
wsSettings.host = host;
|
||||
wsSettings.headers.Host = host;
|
||||
}
|
||||
if (path.IsNotEmpty())
|
||||
{
|
||||
wsSettings.path = path;
|
||||
}
|
||||
if (useragent.IsNotEmpty())
|
||||
{
|
||||
wsSettings.headers.UserAgent = useragent;
|
||||
}
|
||||
streamSettings.wsSettings = wsSettings;
|
||||
|
||||
break;
|
||||
//httpupgrade
|
||||
case nameof(ETransport.httpupgrade):
|
||||
HttpupgradeSettings4Ray httpupgradeSettings = new();
|
||||
|
||||
if (path.IsNotEmpty())
|
||||
{
|
||||
httpupgradeSettings.path = path;
|
||||
}
|
||||
if (host.IsNotEmpty())
|
||||
{
|
||||
httpupgradeSettings.host = host;
|
||||
}
|
||||
streamSettings.httpupgradeSettings = httpupgradeSettings;
|
||||
|
||||
break;
|
||||
//xhttp
|
||||
case nameof(ETransport.xhttp):
|
||||
streamSettings.network = ETransport.xhttp.ToString();
|
||||
XhttpSettings4Ray xhttpSettings = new();
|
||||
|
||||
if (path.IsNotEmpty())
|
||||
{
|
||||
xhttpSettings.path = path;
|
||||
}
|
||||
if (host.IsNotEmpty())
|
||||
{
|
||||
xhttpSettings.host = host;
|
||||
}
|
||||
if (node.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(node.HeaderType))
|
||||
{
|
||||
xhttpSettings.mode = node.HeaderType;
|
||||
}
|
||||
if (node.Extra.IsNotEmpty())
|
||||
{
|
||||
xhttpSettings.extra = JsonUtils.ParseJson(node.Extra);
|
||||
}
|
||||
|
||||
streamSettings.xhttpSettings = xhttpSettings;
|
||||
await GenOutboundMux(node, outbound);
|
||||
|
||||
break;
|
||||
//h2
|
||||
case nameof(ETransport.h2):
|
||||
HttpSettings4Ray httpSettings = new();
|
||||
|
||||
if (host.IsNotEmpty())
|
||||
{
|
||||
httpSettings.host = Utils.String2List(host);
|
||||
}
|
||||
httpSettings.path = path;
|
||||
|
||||
streamSettings.httpSettings = httpSettings;
|
||||
|
||||
break;
|
||||
//quic
|
||||
case nameof(ETransport.quic):
|
||||
QuicSettings4Ray quicsettings = new()
|
||||
{
|
||||
security = host,
|
||||
key = path,
|
||||
header = new Header4Ray
|
||||
{
|
||||
type = node.HeaderType
|
||||
}
|
||||
};
|
||||
streamSettings.quicSettings = quicsettings;
|
||||
if (node.StreamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
if (sni.IsNotEmpty())
|
||||
{
|
||||
streamSettings.tlsSettings.serverName = sni;
|
||||
}
|
||||
else
|
||||
{
|
||||
streamSettings.tlsSettings.serverName = node.Address;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
GrpcSettings4Ray grpcSettings = new()
|
||||
{
|
||||
authority = host.IsNullOrEmpty() ? null : host,
|
||||
serviceName = path,
|
||||
multiMode = node.HeaderType == Global.GrpcMultiMode,
|
||||
idle_timeout = _config.GrpcItem.IdleTimeout,
|
||||
health_check_timeout = _config.GrpcItem.HealthCheckTimeout,
|
||||
permit_without_stream = _config.GrpcItem.PermitWithoutStream,
|
||||
initial_windows_size = _config.GrpcItem.InitialWindowsSize,
|
||||
};
|
||||
streamSettings.grpcSettings = grpcSettings;
|
||||
break;
|
||||
|
||||
default:
|
||||
//tcp
|
||||
if (node.HeaderType == Global.TcpHeaderHttp)
|
||||
{
|
||||
TcpSettings4Ray tcpSettings = new()
|
||||
{
|
||||
header = new Header4Ray
|
||||
{
|
||||
type = node.HeaderType
|
||||
}
|
||||
};
|
||||
|
||||
//request Host
|
||||
string request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
|
||||
string[] arrHost = host.Split(',');
|
||||
string host2 = string.Join(",".AppendQuotes(), arrHost);
|
||||
request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
|
||||
request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
|
||||
//Path
|
||||
string pathHttp = @"/";
|
||||
if (path.IsNotEmpty())
|
||||
{
|
||||
string[] arrPath = path.Split(',');
|
||||
pathHttp = string.Join(",".AppendQuotes(), arrPath);
|
||||
}
|
||||
request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
|
||||
tcpSettings.header.request = JsonUtils.Deserialize<object>(request);
|
||||
|
||||
streamSettings.tcpSettings = tcpSettings;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenMoreOutbounds(ProfileItem node, V2rayConfig v2rayConfig)
|
||||
{
|
||||
//fragment proxy
|
||||
if (_config.CoreBasicItem.EnableFragment
|
||||
&& v2rayConfig.outbounds.First().streamSettings?.security.IsNullOrEmpty() == false)
|
||||
{
|
||||
var fragmentOutbound = new Outbounds4Ray
|
||||
{
|
||||
protocol = "freedom",
|
||||
tag = $"{Global.ProxyTag}3",
|
||||
settings = new()
|
||||
{
|
||||
fragment = new()
|
||||
{
|
||||
packets = _config.Fragment4RayItem?.Packets,
|
||||
length = _config.Fragment4RayItem?.Length,
|
||||
interval = _config.Fragment4RayItem?.Interval
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
v2rayConfig.outbounds.Add(fragmentOutbound);
|
||||
v2rayConfig.outbounds.First().streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = fragmentOutbound.tag
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
try
|
||||
{
|
||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
||||
if (subItem is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//current proxy
|
||||
var outbound = v2rayConfig.outbounds.First();
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
|
||||
//Previous proxy
|
||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& prevNode.ConfigType != EConfigType.Anytls)
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||
prevOutbound.tag = prevOutboundTag;
|
||||
v2rayConfig.outbounds.Add(prevOutbound);
|
||||
}
|
||||
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
||||
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
v2rayConfig.outbounds.Insert(0, nextOutbound);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get template and initialize list
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
if (txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var resultOutbounds = new List<Outbounds4Ray>();
|
||||
var prevOutbounds = new List<Outbounds4Ray>(); // Separate list for prev outbounds and fragment
|
||||
|
||||
// Cache for chain proxies to avoid duplicate generation
|
||||
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
|
||||
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||
int prevIndex = 0; // Index for prev outbounds
|
||||
|
||||
// Process nodes
|
||||
int index = 0;
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
index++;
|
||||
|
||||
// Handle proxy chain
|
||||
string? prevTag = null;
|
||||
var currentOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null);
|
||||
if (nextOutbound != null)
|
||||
{
|
||||
nextOutbound = JsonUtils.DeepCopy(nextOutbound);
|
||||
}
|
||||
|
||||
var subItem = await AppManager.Instance.GetSubItem(node.Subid);
|
||||
|
||||
// current proxy
|
||||
await GenOutbound(node, currentOutbound);
|
||||
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
|
||||
|
||||
if (!node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
||||
{
|
||||
prevTag = value; // maybe null
|
||||
}
|
||||
else
|
||||
{
|
||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& prevNode.ConfigType != EConfigType.Anytls)
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||
prevOutbound.tag = prevTag;
|
||||
prevOutbounds.Add(prevOutbound);
|
||||
}
|
||||
prevProxyTags[node.Subid] = prevTag;
|
||||
}
|
||||
|
||||
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
|
||||
if (!nextProxyCache.ContainsKey(node.Subid))
|
||||
{
|
||||
nextProxyCache[node.Subid] = nextOutbound;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
resultOutbounds.Add(nextOutbound);
|
||||
}
|
||||
resultOutbounds.Add(currentOutbound);
|
||||
}
|
||||
|
||||
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
||||
resultOutbounds.AddRange(prevOutbounds);
|
||||
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||
v2rayConfig.outbounds = resultOutbounds;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a chained outbound configuration for the given subItem and outbound.
|
||||
/// The outbound's tag must be set before calling this method.
|
||||
/// Returns the next proxy's outbound configuration, which may be null if no next proxy exists.
|
||||
/// </summary>
|
||||
/// <param name="subItem">The subscription item containing proxy chain information.</param>
|
||||
/// <param name="outbound">The current outbound configuration. Its tag must be set before calling this method.</param>
|
||||
/// <param name="prevOutboundTag">The tag of the previous outbound in the chain, if any.</param>
|
||||
/// <param name="nextOutbound">The outbound for the next proxy in the chain, if already created. If null, will be created inside.</param>
|
||||
/// <returns>
|
||||
/// The outbound configuration for the next proxy in the chain, or null if no next proxy exists.
|
||||
/// </returns>
|
||||
private async Task<Outbounds4Ray?> GenChainOutbounds(SubItem subItem, Outbounds4Ray outbound, string? prevOutboundTag, Outbounds4Ray? nextOutbound = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
|
||||
if (!prevOutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
outbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = prevOutboundTag
|
||||
};
|
||||
}
|
||||
|
||||
// Next proxy
|
||||
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC
|
||||
&& nextNode.ConfigType != EConfigType.Anytls)
|
||||
{
|
||||
if (nextOutbound == null)
|
||||
{
|
||||
nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
}
|
||||
nextOutbound.tag = outbound.tag;
|
||||
|
||||
outbound.tag = $"mid-{outbound.tag}";
|
||||
nextOutbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = outbound.tag
|
||||
};
|
||||
}
|
||||
return nextOutbound;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService
|
||||
{
|
||||
private async Task<int> GenRouting(V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (v2rayConfig.routing?.rules != null)
|
||||
{
|
||||
v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
||||
|
||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||
if (routing != null)
|
||||
{
|
||||
if (routing.DomainStrategy.IsNotEmpty())
|
||||
{
|
||||
v2rayConfig.routing.domainStrategy = routing.DomainStrategy;
|
||||
}
|
||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||
foreach (var item in rules)
|
||||
{
|
||||
if (item.Enabled)
|
||||
{
|
||||
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
||||
await GenRoutingUserRule(item2, v2rayConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (rule == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
rule.outboundTag = await GenRoutingUserRuleOutbound(rule.outboundTag, v2rayConfig);
|
||||
|
||||
if (rule.port.IsNullOrEmpty())
|
||||
{
|
||||
rule.port = null;
|
||||
}
|
||||
if (rule.network.IsNullOrEmpty())
|
||||
{
|
||||
rule.network = null;
|
||||
}
|
||||
if (rule.domain?.Count == 0)
|
||||
{
|
||||
rule.domain = null;
|
||||
}
|
||||
if (rule.ip?.Count == 0)
|
||||
{
|
||||
rule.ip = null;
|
||||
}
|
||||
if (rule.protocol?.Count == 0)
|
||||
{
|
||||
rule.protocol = null;
|
||||
}
|
||||
if (rule.inboundTag?.Count == 0)
|
||||
{
|
||||
rule.inboundTag = null;
|
||||
}
|
||||
|
||||
var hasDomainIp = false;
|
||||
if (rule.domain?.Count > 0)
|
||||
{
|
||||
var it = JsonUtils.DeepCopy(rule);
|
||||
it.ip = null;
|
||||
it.type = "field";
|
||||
for (var k = it.domain.Count - 1; k >= 0; k--)
|
||||
{
|
||||
if (it.domain[k].StartsWith("#"))
|
||||
{
|
||||
it.domain.RemoveAt(k);
|
||||
}
|
||||
it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ",");
|
||||
}
|
||||
v2rayConfig.routing.rules.Add(it);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
if (rule.ip?.Count > 0)
|
||||
{
|
||||
var it = JsonUtils.DeepCopy(rule);
|
||||
it.domain = null;
|
||||
it.type = "field";
|
||||
v2rayConfig.routing.rules.Add(it);
|
||||
hasDomainIp = true;
|
||||
}
|
||||
if (!hasDomainIp)
|
||||
{
|
||||
if (rule.port.IsNotEmpty()
|
||||
|| rule.protocol?.Count > 0
|
||||
|| rule.inboundTag?.Count > 0
|
||||
|| rule.network != null
|
||||
)
|
||||
{
|
||||
var it = JsonUtils.DeepCopy(rule);
|
||||
it.type = "field";
|
||||
v2rayConfig.routing.rules.Add(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<string?> GenRoutingUserRuleOutbound(string outboundTag, V2rayConfig v2rayConfig)
|
||||
{
|
||||
if (Global.OutboundTags.Contains(outboundTag))
|
||||
{
|
||||
return outboundTag;
|
||||
}
|
||||
|
||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||
if (node == null
|
||||
|| node.ConfigType == EConfigType.Custom
|
||||
|| node.ConfigType == EConfigType.Hysteria2
|
||||
|| node.ConfigType == EConfigType.TUIC
|
||||
|| node.ConfigType == EConfigType.Anytls)
|
||||
{
|
||||
return Global.ProxyTag;
|
||||
}
|
||||
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(node, outbound);
|
||||
outbound.tag = Global.ProxyTag + node.IndexId.ToString();
|
||||
v2rayConfig.outbounds.Add(outbound);
|
||||
|
||||
return outbound.tag;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService
|
||||
{
|
||||
private async Task<int> GenStatistic(V2rayConfig v2rayConfig)
|
||||
{
|
||||
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
|
||||
{
|
||||
string tag = EInboundProtocol.api.ToString();
|
||||
Metrics4Ray apiObj = new();
|
||||
Policy4Ray policyObj = new();
|
||||
SystemPolicy4Ray policySystemSetting = new();
|
||||
|
||||
v2rayConfig.stats = new Stats4Ray();
|
||||
|
||||
apiObj.tag = tag;
|
||||
v2rayConfig.metrics = apiObj;
|
||||
|
||||
policySystemSetting.statsOutboundDownlink = true;
|
||||
policySystemSetting.statsOutboundUplink = true;
|
||||
policyObj.system = policySystemSetting;
|
||||
v2rayConfig.policy = policyObj;
|
||||
|
||||
if (!v2rayConfig.inbounds.Exists(item => item.tag == tag))
|
||||
{
|
||||
Inbounds4Ray apiInbound = new();
|
||||
Inboundsettings4Ray apiInboundSettings = new();
|
||||
apiInbound.tag = tag;
|
||||
apiInbound.listen = Global.Loopback;
|
||||
apiInbound.port = AppManager.Instance.StatePort;
|
||||
apiInbound.protocol = Global.InboundAPIProtocol;
|
||||
apiInboundSettings.address = Global.Loopback;
|
||||
apiInbound.settings = apiInboundSettings;
|
||||
v2rayConfig.inbounds.Add(apiInbound);
|
||||
}
|
||||
|
||||
if (!v2rayConfig.routing.rules.Exists(item => item.outboundTag == tag))
|
||||
{
|
||||
RulesItem4Ray apiRoutingRule = new()
|
||||
{
|
||||
inboundTag = new List<string> { tag },
|
||||
outboundTag = tag,
|
||||
type = "field"
|
||||
};
|
||||
|
||||
v2rayConfig.routing.rules.Add(apiRoutingRule);
|
||||
}
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ public class DownloadService
|
|||
{
|
||||
try
|
||||
{
|
||||
SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
|
||||
var progress = new Progress<string>();
|
||||
progress.ProgressChanged += (sender, value) => updateFunc?.Invoke(false, $"{value}");
|
||||
|
@ -45,7 +45,7 @@ public class DownloadService
|
|||
{
|
||||
try
|
||||
{
|
||||
SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}"));
|
||||
|
||||
var progress = new Progress<double>();
|
||||
|
@ -72,7 +72,7 @@ public class DownloadService
|
|||
|
||||
public async Task<string?> UrlRedirectAsync(string url, bool blProxy)
|
||||
{
|
||||
SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
var webRequestHandler = new SocketsHttpHandler
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
|
@ -142,7 +142,7 @@ public class DownloadService
|
|||
{
|
||||
try
|
||||
{
|
||||
SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
var webProxy = await GetWebProxy(blProxy);
|
||||
var client = new HttpClient(new SocketsHttpHandler()
|
||||
{
|
||||
|
@ -187,7 +187,7 @@ public class DownloadService
|
|||
{
|
||||
try
|
||||
{
|
||||
SetSecurityProtocol(AppHandler.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
|
||||
|
||||
var webProxy = await GetWebProxy(blProxy);
|
||||
|
||||
|
@ -210,70 +210,13 @@ public class DownloadService
|
|||
return null;
|
||||
}
|
||||
|
||||
public async Task<int> RunAvailabilityCheck(IWebProxy? webProxy)
|
||||
{
|
||||
var responseTime = -1;
|
||||
try
|
||||
{
|
||||
webProxy ??= await GetWebProxy(true);
|
||||
var config = AppHandler.Instance.Config;
|
||||
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
responseTime = await GetRealPingTime(config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
||||
if (responseTime > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
await Task.Delay(500);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return -1;
|
||||
}
|
||||
return responseTime;
|
||||
}
|
||||
|
||||
public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
|
||||
{
|
||||
var responseTime = -1;
|
||||
try
|
||||
{
|
||||
using var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
|
||||
using var client = new HttpClient(new SocketsHttpHandler()
|
||||
{
|
||||
Proxy = webProxy,
|
||||
UseProxy = webProxy != null
|
||||
});
|
||||
|
||||
List<int> oneTime = new();
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
await client.GetAsync(url, cts.Token).ConfigureAwait(false);
|
||||
timer.Stop();
|
||||
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault();
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
//Utile.SaveLog(ex.Message, ex);
|
||||
}
|
||||
return responseTime;
|
||||
}
|
||||
|
||||
private async Task<WebProxy?> GetWebProxy(bool blProxy)
|
||||
{
|
||||
if (!blProxy)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
var port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
if (await SocketCheck(Global.Loopback, port) == false)
|
||||
{
|
||||
return null;
|
||||
|
|
|
@ -23,7 +23,7 @@ public class SpeedtestService
|
|||
Task.Run(async () =>
|
||||
{
|
||||
await RunAsync(actionType, selecteds);
|
||||
await ProfileExHandler.Instance.SaveTo();
|
||||
await ProfileExManager.Instance.SaveTo();
|
||||
UpdateFunc("", ResUI.SpeedtestingCompleted);
|
||||
});
|
||||
}
|
||||
|
@ -98,18 +98,18 @@ public class SpeedtestService
|
|||
case ESpeedActionType.Tcping:
|
||||
case ESpeedActionType.Realping:
|
||||
UpdateFunc(it.IndexId, ResUI.Speedtesting, "");
|
||||
ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0);
|
||||
ProfileExManager.Instance.SetTestDelay(it.IndexId, 0);
|
||||
break;
|
||||
|
||||
case ESpeedActionType.Speedtest:
|
||||
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingWait);
|
||||
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0);
|
||||
ProfileExManager.Instance.SetTestSpeed(it.IndexId, 0);
|
||||
break;
|
||||
|
||||
case ESpeedActionType.Mixedtest:
|
||||
UpdateFunc(it.IndexId, ResUI.Speedtesting, ResUI.SpeedtestingWait);
|
||||
ProfileExHandler.Instance.SetTestDelay(it.IndexId, 0);
|
||||
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, 0);
|
||||
ProfileExManager.Instance.SetTestDelay(it.IndexId, 0);
|
||||
ProfileExManager.Instance.SetTestSpeed(it.IndexId, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ public class SpeedtestService
|
|||
{
|
||||
var responseTime = await GetTcpingTime(it.Address, it.Port);
|
||||
|
||||
ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime);
|
||||
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
|
||||
UpdateFunc(it.IndexId, responseTime.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -191,15 +191,13 @@ public class SpeedtestService
|
|||
var pid = -1;
|
||||
try
|
||||
{
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
|
||||
pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(selecteds);
|
||||
if (pid < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
|
||||
var downloadHandle = new DownloadService();
|
||||
|
||||
List<Task> tasks = new();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
|
@ -213,7 +211,7 @@ public class SpeedtestService
|
|||
}
|
||||
tasks.Add(Task.Run(async () =>
|
||||
{
|
||||
await DoRealPing(downloadHandle, it);
|
||||
await DoRealPing(it);
|
||||
}));
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
|
@ -255,7 +253,7 @@ public class SpeedtestService
|
|||
var pid = -1;
|
||||
try
|
||||
{
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(it);
|
||||
pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(it);
|
||||
if (pid < 0)
|
||||
{
|
||||
UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
|
||||
|
@ -263,7 +261,7 @@ public class SpeedtestService
|
|||
else
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
var delay = await DoRealPing(downloadHandle, it);
|
||||
var delay = await DoRealPing(it);
|
||||
if (blSpeedTest)
|
||||
{
|
||||
if (delay > 0)
|
||||
|
@ -294,12 +292,12 @@ public class SpeedtestService
|
|||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
private async Task<int> DoRealPing(DownloadService downloadHandle, ServerTestItem it)
|
||||
private async Task<int> DoRealPing(ServerTestItem it)
|
||||
{
|
||||
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
|
||||
var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
||||
var responseTime = await HttpClientHelper.Instance.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
||||
|
||||
ProfileExHandler.Instance.SetTestDelay(it.IndexId, responseTime);
|
||||
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
|
||||
UpdateFunc(it.IndexId, responseTime.ToString());
|
||||
return responseTime;
|
||||
}
|
||||
|
@ -316,7 +314,7 @@ public class SpeedtestService
|
|||
decimal.TryParse(msg, out var dec);
|
||||
if (dec > 0)
|
||||
{
|
||||
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, dec);
|
||||
ProfileExManager.Instance.SetTestSpeed(it.IndexId, dec);
|
||||
}
|
||||
UpdateFunc(it.IndexId, "", msg);
|
||||
});
|
||||
|
@ -378,7 +376,7 @@ public class SpeedtestService
|
|||
_updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed });
|
||||
if (indexId.IsNotEmpty() && speed.IsNotEmpty())
|
||||
{
|
||||
ProfileExHandler.Instance.SetTestMessage(indexId, speed);
|
||||
ProfileExManager.Instance.SetTestMessage(indexId, speed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ public class StatisticsSingboxService
|
|||
private bool _exitFlag;
|
||||
private ClientWebSocket? webSocket;
|
||||
private Action<ServerSpeedItem>? _updateFunc;
|
||||
private string Url => $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic";
|
||||
private string Url => $"ws://{Global.Loopback}:{AppManager.Instance.StatePort2}/traffic";
|
||||
private static readonly string _tag = "StatisticsSingboxService";
|
||||
|
||||
public StatisticsSingboxService(Config config, Action<ServerSpeedItem> updateFunc)
|
||||
|
|
|
@ -7,7 +7,7 @@ public class StatisticsXrayService
|
|||
private readonly Config _config;
|
||||
private bool _exitFlag;
|
||||
private Action<ServerSpeedItem>? _updateFunc;
|
||||
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
|
||||
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort}/debug/vars";
|
||||
|
||||
public StatisticsXrayService(Config config, Action<ServerSpeedItem> updateFunc)
|
||||
{
|
||||
|
|
|
@ -135,7 +135,7 @@ public class UpdateService
|
|||
|
||||
private async Task<RetResult> GetRemoteVersion(DownloadService downloadHandle, ECoreType type, bool preRelease)
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type);
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
|
||||
var tagName = string.Empty;
|
||||
if (preRelease)
|
||||
{
|
||||
|
@ -169,7 +169,7 @@ public class UpdateService
|
|||
{
|
||||
try
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type);
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
|
||||
string filePath = string.Empty;
|
||||
foreach (var name in coreInfo.CoreExes)
|
||||
{
|
||||
|
@ -221,7 +221,7 @@ public class UpdateService
|
|||
{
|
||||
try
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type);
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
|
||||
var coreUrl = await GetUrlFromCore(coreInfo) ?? string.Empty;
|
||||
SemanticVersion curVersion;
|
||||
string message;
|
||||
|
@ -379,7 +379,7 @@ public class UpdateService
|
|||
var geoSiteFiles = new List<string>();
|
||||
|
||||
//Collect used files list
|
||||
var routingItems = await AppHandler.Instance.RoutingItems();
|
||||
var routingItems = await AppManager.Instance.RoutingItems();
|
||||
foreach (var routing in routingItems)
|
||||
{
|
||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||
|
|
|
@ -19,7 +19,7 @@ public class AddServer2ViewModel : MyReactiveObject
|
|||
|
||||
public AddServer2ViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
BrowseServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
|
@ -45,25 +45,25 @@ public class AddServer2ViewModel : MyReactiveObject
|
|||
var remarks = SelectedSource.Remarks;
|
||||
if (remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedSource.Address.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillServerAddressCustom);
|
||||
return;
|
||||
}
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||
|
||||
if (await ConfigHandler.EditCustomServer(_config, SelectedSource) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,12 +74,12 @@ public class AddServer2ViewModel : MyReactiveObject
|
|||
return;
|
||||
}
|
||||
|
||||
var item = await AppHandler.Instance.GetProfileItem(SelectedSource.IndexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(SelectedSource.IndexId);
|
||||
item ??= SelectedSource;
|
||||
item.Address = fileName;
|
||||
if (await ConfigHandler.AddCustomServer(_config, item, false) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.SuccessfullyImportedCustomServer);
|
||||
NoticeManager.Instance.Enqueue(ResUI.SuccessfullyImportedCustomServer);
|
||||
if (item.IndexId.IsNotEmpty())
|
||||
{
|
||||
SelectedSource = JsonUtils.DeepCopy(item);
|
||||
|
@ -88,7 +88,7 @@ public class AddServer2ViewModel : MyReactiveObject
|
|||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FailedImportedCustomServer);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FailedImportedCustomServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ public class AddServer2ViewModel : MyReactiveObject
|
|||
var address = SelectedSource.Address;
|
||||
if (address.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillServerAddressCustom);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ public class AddServer2ViewModel : MyReactiveObject
|
|||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FailedReadConfiguration);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FailedReadConfiguration);
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ public class AddServerViewModel : MyReactiveObject
|
|||
|
||||
public AddServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
|
@ -43,32 +43,32 @@ public class AddServerViewModel : MyReactiveObject
|
|||
{
|
||||
if (SelectedSource.Remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedSource.Address.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddress);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillServerAddress);
|
||||
return;
|
||||
}
|
||||
var port = SelectedSource.Port.ToString();
|
||||
if (port.IsNullOrEmpty() || !Utils.IsNumeric(port)
|
||||
|| SelectedSource.Port <= 0 || SelectedSource.Port >= Global.MaxPort)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectServerPort);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillCorrectServerPort);
|
||||
return;
|
||||
}
|
||||
if (SelectedSource.ConfigType == EConfigType.Shadowsocks)
|
||||
{
|
||||
if (SelectedSource.Id.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillPassword);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillPassword);
|
||||
return;
|
||||
}
|
||||
if (SelectedSource.Security.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectEncryption);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectEncryption);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ public class AddServerViewModel : MyReactiveObject
|
|||
{
|
||||
if (SelectedSource.Id.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillUUID);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillUUID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -84,12 +84,12 @@ public class AddServerViewModel : MyReactiveObject
|
|||
|
||||
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject
|
|||
|
||||
public BackupAndRestoreViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
WebDavCheckCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
|
@ -52,14 +52,14 @@ public class BackupAndRestoreViewModel : MyReactiveObject
|
|||
_config.WebDavItem = SelectedSource;
|
||||
_ = await ConfigHandler.SaveConfig(_config);
|
||||
|
||||
var result = await WebDavHandler.Instance.CheckConnection();
|
||||
var result = await WebDavManager.Instance.CheckConnection();
|
||||
if (result)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
DisplayOperationMsg(WebDavManager.Instance.GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject
|
|||
var result = await CreateZipFileFromDirectory(fileName);
|
||||
if (result)
|
||||
{
|
||||
var result2 = await WebDavHandler.Instance.PutFile(fileName);
|
||||
var result2 = await WebDavManager.Instance.PutFile(fileName);
|
||||
if (result2)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
|
@ -78,21 +78,21 @@ public class BackupAndRestoreViewModel : MyReactiveObject
|
|||
}
|
||||
}
|
||||
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
DisplayOperationMsg(WebDavManager.Instance.GetLastError());
|
||||
}
|
||||
|
||||
private async Task RemoteRestore()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var fileName = Utils.GetTempPath(Utils.GetGuid());
|
||||
var result = await WebDavHandler.Instance.GetRawFile(fileName);
|
||||
var result = await WebDavManager.Instance.GetRawFile(fileName);
|
||||
if (result)
|
||||
{
|
||||
await LocalRestore(fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
DisplayOperationMsg(WebDavManager.Instance.GetLastError());
|
||||
}
|
||||
|
||||
public async Task<bool> LocalBackup(string fileName)
|
||||
|
@ -105,7 +105,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject
|
|||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
DisplayOperationMsg(WebDavManager.Instance.GetLastError());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -158,7 +158,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject
|
|||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
DisplayOperationMsg(WebDavManager.Instance.GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ public class CheckUpdateViewModel : MyReactiveObject
|
|||
|
||||
public CheckUpdateViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
CheckUpdateCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
|
|
|
@ -26,7 +26,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
|||
|
||||
public ClashConnectionsViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
|
||||
|
||||
|
@ -58,7 +58,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
|||
|
||||
private async Task GetClashConnections()
|
||||
{
|
||||
var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync();
|
||||
var ret = await ClashApiManager.Instance.GetClashConnectionsAsync();
|
||||
if (ret == null)
|
||||
{
|
||||
return;
|
||||
|
@ -118,7 +118,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
|||
{
|
||||
_connectionItems.Clear();
|
||||
}
|
||||
await ClashApiHandler.Instance.ClashConnectionClose(id);
|
||||
await ClashApiManager.Instance.ClashConnectionClose(id);
|
||||
await GetClashConnections();
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||
|
||||
public ClashProxiesViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
ProxiesReloadCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
|
@ -152,13 +152,13 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||
{
|
||||
{ "mode", mode.ToString().ToLower() }
|
||||
};
|
||||
await ClashApiHandler.Instance.ClashConfigUpdate(headers);
|
||||
await ClashApiManager.Instance.ClashConfigUpdate(headers);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetClashProxies(bool refreshUI)
|
||||
{
|
||||
var ret = await ClashApiHandler.Instance.GetClashProxiesAsync();
|
||||
var ret = await ClashApiManager.Instance.GetClashProxiesAsync();
|
||||
if (ret?.Item1 == null || ret.Item2 == null)
|
||||
{
|
||||
return;
|
||||
|
@ -182,7 +182,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||
var selectedName = SelectedGroup?.Name;
|
||||
_proxyGroups.Clear();
|
||||
|
||||
var proxyGroups = ClashApiHandler.Instance.GetClashProxyGroups();
|
||||
var proxyGroups = ClashApiManager.Instance.GetClashProxyGroups();
|
||||
if (proxyGroups != null && proxyGroups.Count > 0)
|
||||
{
|
||||
foreach (var it in proxyGroups)
|
||||
|
@ -352,11 +352,11 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||
var selectedProxy = TryGetProxy(name);
|
||||
if (selectedProxy == null || selectedProxy.type != "Selector")
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
await ClashApiHandler.Instance.ClashSetActiveProxy(name, nameNode);
|
||||
await ClashApiManager.Instance.ClashSetActiveProxy(name, nameNode);
|
||||
|
||||
selectedProxy.now = nameNode;
|
||||
var group = _proxyGroups.FirstOrDefault(it => it.Name == SelectedGroup.Name);
|
||||
|
@ -368,12 +368,12 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||
|
||||
SelectedGroup = group2;
|
||||
}
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
|
||||
private async Task ProxiesDelayTest(bool blAll = true)
|
||||
{
|
||||
ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) =>
|
||||
ClashApiManager.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) =>
|
||||
{
|
||||
if (item == null || result.IsNullOrEmpty())
|
||||
{
|
||||
|
|
|
@ -38,7 +38,7 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||
|
||||
public DNSSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(SaveSettingAsync);
|
||||
|
||||
|
@ -60,7 +60,7 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||
|
||||
private async Task Init()
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
var item = _config.SimpleDNSItem;
|
||||
UseSystemHosts = item.UseSystemHosts;
|
||||
AddCommonHosts = item.AddCommonHosts;
|
||||
|
@ -76,14 +76,14 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||
Hosts = item.Hosts;
|
||||
DirectExpectedIPs = item.DirectExpectedIPs;
|
||||
|
||||
var item1 = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
var item1 = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
||||
RayCustomDNSEnableCompatible = item1.Enabled;
|
||||
UseSystemHostsCompatible = item1.UseSystemHosts;
|
||||
DomainStrategy4FreedomCompatible = item1?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddressCompatible = item1?.DomainDNSAddress ?? string.Empty;
|
||||
NormalDNSCompatible = item1?.NormalDNS ?? string.Empty;
|
||||
|
||||
var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
var item2 = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
SBCustomDNSEnableCompatible = item2.Enabled;
|
||||
DomainStrategy4Freedom2Compatible = item2?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddress2Compatible = item2?.DomainDNSAddress ?? string.Empty;
|
||||
|
@ -117,7 +117,7 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||
{
|
||||
if (NormalDNSCompatible.Contains('{') || NormalDNSCompatible.Contains('}'))
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(NormalDNS2Compatible);
|
||||
if (obj2 == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -136,12 +136,12 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(TunDNS2Compatible);
|
||||
if (obj2 == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var item1 = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
var item1 = await AppManager.Instance.GetDNSItem(ECoreType.Xray);
|
||||
item1.Enabled = RayCustomDNSEnableCompatible;
|
||||
item1.DomainStrategy4Freedom = DomainStrategy4FreedomCompatible;
|
||||
item1.DomainDNSAddress = DomainDNSAddressCompatible;
|
||||
|
@ -149,7 +149,7 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||
item1.NormalDNS = NormalDNSCompatible;
|
||||
await ConfigHandler.SaveDNSItems(_config, item1);
|
||||
|
||||
var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
var item2 = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
item2.Enabled = SBCustomDNSEnableCompatible;
|
||||
item2.DomainStrategy4Freedom = DomainStrategy4Freedom2Compatible;
|
||||
item2.DomainDNSAddress = DomainDNSAddress2Compatible;
|
||||
|
|
|
@ -41,7 +41,7 @@ public class FullConfigTemplateViewModel : MyReactiveObject
|
|||
|
||||
public FullConfigTemplateViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
|
@ -53,13 +53,13 @@ public class FullConfigTemplateViewModel : MyReactiveObject
|
|||
|
||||
private async Task Init()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
||||
var item = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
||||
EnableFullConfigTemplate4Ray = item?.Enabled ?? false;
|
||||
FullConfigTemplate4Ray = item?.Config ?? string.Empty;
|
||||
AddProxyOnly4Ray = item?.AddProxyOnly ?? false;
|
||||
ProxyDetour4Ray = item?.ProxyDetour ?? string.Empty;
|
||||
|
||||
var item2 = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
||||
var item2 = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
||||
EnableFullConfigTemplate4Singbox = item2?.Enabled ?? false;
|
||||
FullConfigTemplate4Singbox = item2?.Config ?? string.Empty;
|
||||
FullTunConfigTemplate4Singbox = item2?.TunConfig ?? string.Empty;
|
||||
|
@ -75,13 +75,13 @@ public class FullConfigTemplateViewModel : MyReactiveObject
|
|||
if (!await SaveSingboxConfigAsync())
|
||||
return;
|
||||
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_ = _updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
|
||||
private async Task<bool> SaveXrayConfigAsync()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
||||
var item = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.Xray);
|
||||
item.Enabled = EnableFullConfigTemplate4Ray;
|
||||
item.Config = null;
|
||||
|
||||
|
@ -96,7 +96,7 @@ public class FullConfigTemplateViewModel : MyReactiveObject
|
|||
|
||||
private async Task<bool> SaveSingboxConfigAsync()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
||||
var item = await AppManager.Instance.GetFullConfigTemplateItem(ECoreType.sing_box);
|
||||
item.Enabled = EnableFullConfigTemplate4Singbox;
|
||||
item.Config = null;
|
||||
item.TunConfig = null;
|
||||
|
|
|
@ -11,7 +11,7 @@ public class GlobalHotkeySettingViewModel : MyReactiveObject
|
|||
|
||||
public GlobalHotkeySettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
_globalHotkeys = JsonUtils.DeepCopy(_config.GlobalHotkeys);
|
||||
|
@ -58,7 +58,7 @@ public class GlobalHotkeySettingViewModel : MyReactiveObject
|
|||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
|
||||
public MainWindowViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
#region WhenAnyValue && ReactiveCommand
|
||||
|
@ -178,7 +178,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
if (await _updateView?.Invoke(EViewAction.GlobalHotkeySettingWindow, null) == true)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
});
|
||||
RebootAsAdminCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
|
@ -226,13 +226,13 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
await ConfigHandler.InitBuiltinRouting(_config);
|
||||
await ConfigHandler.InitBuiltinDNS(_config);
|
||||
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
||||
await ProfileExHandler.Instance.Init();
|
||||
await CoreHandler.Instance.Init(_config, UpdateHandler);
|
||||
TaskHandler.Instance.RegUpdateTask(_config, UpdateTaskHandler);
|
||||
await ProfileExManager.Instance.Init();
|
||||
await CoreManager.Instance.Init(_config, UpdateHandler);
|
||||
TaskManager.Instance.RegUpdateTask(_config, UpdateTaskHandler);
|
||||
|
||||
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
|
||||
{
|
||||
await StatisticsHandler.Instance.Init(_config, UpdateStatisticsHandler);
|
||||
await StatisticsManager.Instance.Init(_config, UpdateStatisticsHandler);
|
||||
}
|
||||
|
||||
BlReloadEnabled = true;
|
||||
|
@ -247,16 +247,16 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
|
||||
private void UpdateHandler(bool notify, string msg)
|
||||
{
|
||||
NoticeHandler.Instance.SendMessage(msg);
|
||||
NoticeManager.Instance.SendMessage(msg);
|
||||
if (notify)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(msg);
|
||||
NoticeManager.Instance.Enqueue(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTaskHandler(bool success, string msg)
|
||||
{
|
||||
NoticeHandler.Instance.SendMessageEx(msg);
|
||||
NoticeManager.Instance.SendMessageEx(msg);
|
||||
if (success)
|
||||
{
|
||||
var indexIdOld = _config.IndexId;
|
||||
|
@ -303,10 +303,10 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
MessageBus.Current.SendMessage("", EMsgCommand.AppExit.ToString());
|
||||
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
await ProfileExHandler.Instance.SaveTo();
|
||||
await StatisticsHandler.Instance.SaveTo();
|
||||
await CoreHandler.Instance.CoreStop();
|
||||
StatisticsHandler.Instance.Close();
|
||||
await ProfileExManager.Instance.SaveTo();
|
||||
await StatisticsManager.Instance.SaveTo();
|
||||
await CoreManager.Instance.CoreStop();
|
||||
StatisticsManager.Instance.Close();
|
||||
|
||||
Logging.SaveLog("MyAppExitAsync End");
|
||||
}
|
||||
|
@ -324,7 +324,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
if (!Utils.UpgradeAppExists(out var upgradeFileName))
|
||||
{
|
||||
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip);
|
||||
NoticeManager.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip);
|
||||
Logging.SaveLog("UpgradeApp does not exist");
|
||||
return;
|
||||
}
|
||||
|
@ -404,11 +404,11 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
RefreshSubscriptions();
|
||||
RefreshServers();
|
||||
NoticeHandler.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret));
|
||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret));
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -420,7 +420,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
|
||||
public async Task ScanScreenResult(byte[]? bytes)
|
||||
{
|
||||
var result = QRCodeHelper.ParseBarcode(bytes);
|
||||
var result = QRCodeUtils.ParseBarcode(bytes);
|
||||
await AddScanResultAsync(result);
|
||||
}
|
||||
|
||||
|
@ -437,7 +437,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
return;
|
||||
}
|
||||
|
||||
var result = QRCodeHelper.ParseBarcode(fileName);
|
||||
var result = QRCodeUtils.ParseBarcode(fileName);
|
||||
await AddScanResultAsync(result);
|
||||
}
|
||||
|
||||
|
@ -445,7 +445,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.NoValidQRcodeFound);
|
||||
NoticeManager.Instance.Enqueue(ResUI.NoValidQRcodeFound);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -454,11 +454,11 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
RefreshSubscriptions();
|
||||
RefreshServers();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan);
|
||||
NoticeManager.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -531,7 +531,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
|
||||
private async Task ClearServerStatistics()
|
||||
{
|
||||
await StatisticsHandler.Instance.ClearAllServerStatistics();
|
||||
await StatisticsManager.Instance.ClearAllServerStatistics();
|
||||
RefreshServers();
|
||||
}
|
||||
|
||||
|
@ -602,13 +602,13 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
private async Task LoadCore()
|
||||
{
|
||||
var node = await ConfigHandler.GetDefaultServer(_config);
|
||||
await CoreHandler.Instance.LoadCore(node);
|
||||
await CoreManager.Instance.LoadCore(node);
|
||||
}
|
||||
|
||||
public async Task CloseCore()
|
||||
{
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
await CoreHandler.Instance.CoreStop();
|
||||
await CoreManager.Instance.CoreStop();
|
||||
}
|
||||
|
||||
private async Task AutoHideStartup()
|
||||
|
|
|
@ -20,7 +20,7 @@ public class MsgViewModel : MyReactiveObject
|
|||
|
||||
public MsgViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
MsgFilter = _config.MsgUIItem.MainMsgFilter ?? string.Empty;
|
||||
AutoRefresh = _config.MsgUIItem.AutoRefresh ?? true;
|
||||
|
|
|
@ -109,7 +109,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
|
||||
public OptionSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
|
@ -276,7 +276,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString())
|
||||
|| localPort <= 0 || localPort >= Global.MaxPort)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillLocalListeningPort);
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillLocalListeningPort);
|
||||
return;
|
||||
}
|
||||
var needReboot = (EnableStatistics != _config.GuiItem.EnableStatistics
|
||||
|
@ -369,14 +369,14 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
await AutoStartupHandler.UpdateTask(_config);
|
||||
AppHandler.Instance.Reset();
|
||||
AppManager.Instance.Reset();
|
||||
|
||||
NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
|
||||
public ProfilesViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
#region WhenAnyValue && ReactiveCommand
|
||||
|
@ -284,8 +284,8 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
{
|
||||
if (result.IndexId.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.SendMessageEx(result.Delay);
|
||||
NoticeHandler.Instance.Enqueue(result.Delay);
|
||||
NoticeManager.Instance.SendMessageEx(result.Delay);
|
||||
NoticeManager.Instance.Enqueue(result.Delay);
|
||||
return;
|
||||
}
|
||||
var item = _profileItems.FirstOrDefault(it => it.IndexId == result.IndexId);
|
||||
|
@ -403,7 +403,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
|
||||
_subItems.Add(new SubItem { Remarks = ResUI.AllGroupServers });
|
||||
|
||||
foreach (var item in await AppHandler.Instance.SubItems())
|
||||
foreach (var item in await AppManager.Instance.SubItems())
|
||||
{
|
||||
_subItems.Add(item);
|
||||
}
|
||||
|
@ -419,12 +419,12 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
|
||||
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
||||
{
|
||||
var lstModel = await AppHandler.Instance.ProfileItems(_config.SubIndexId, filter);
|
||||
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, filter);
|
||||
|
||||
await ConfigHandler.SetDefaultServer(_config, lstModel);
|
||||
|
||||
var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
|
||||
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
|
||||
var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsManager.Instance.ServerStat : null) ?? [];
|
||||
var lstProfileExs = await ProfileExManager.Instance.GetProfileExs();
|
||||
lstModel = (from t in lstModel
|
||||
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
|
||||
from t22 in t2b.DefaultIfEmpty()
|
||||
|
@ -474,7 +474,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
{
|
||||
foreach (var profile in orderProfiles)
|
||||
{
|
||||
var item = await AppHandler.Instance.GetProfileItem(profile.IndexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(profile.IndexId);
|
||||
if (item is not null)
|
||||
{
|
||||
lstSelected.Add(item);
|
||||
|
@ -495,10 +495,10 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
{
|
||||
return;
|
||||
}
|
||||
var item = await AppHandler.Instance.GetProfileItem(SelectedProfile.IndexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(SelectedProfile.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
return;
|
||||
}
|
||||
eConfigType = item.ConfigType;
|
||||
|
@ -536,7 +536,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
var exists = lstSelected.Exists(t => t.IndexId == _config.IndexId);
|
||||
|
||||
await ConfigHandler.RemoveServers(_config, lstSelected);
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
if (lstSelected.Count == _profileItems.Count)
|
||||
{
|
||||
_profileItems.Clear();
|
||||
|
@ -556,7 +556,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
RefreshServers();
|
||||
Reload();
|
||||
}
|
||||
NoticeHandler.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2));
|
||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2));
|
||||
}
|
||||
|
||||
private async Task CopyServer()
|
||||
|
@ -569,7 +569,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
if (await ConfigHandler.CopyServer(_config, lstSelected) == 0)
|
||||
{
|
||||
RefreshServers();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -592,10 +592,10 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
{
|
||||
return;
|
||||
}
|
||||
var item = await AppHandler.Instance.GetProfileItem(indexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(indexId);
|
||||
if (item is null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -621,10 +621,10 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
|
||||
public async Task ShareServerAsync()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetProfileItem(SelectedProfile.IndexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(SelectedProfile.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
return;
|
||||
}
|
||||
var url = FmtHandler.GetShareUri(item);
|
||||
|
@ -647,7 +647,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
var ret = await ConfigHandler.AddCustomServer4Multiple(_config, lstSelected, coreType, multipleLoad);
|
||||
if (ret.Success != true)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
return;
|
||||
}
|
||||
if (ret?.Data?.ToString() == _config.IndexId)
|
||||
|
@ -682,7 +682,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
{
|
||||
var count = await ConfigHandler.RemoveInvalidServerResult(_config, _config.SubIndexId);
|
||||
RefreshServers();
|
||||
NoticeHandler.Instance.Enqueue(string.Format(ResUI.RemoveInvalidServerResultTip, count));
|
||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveInvalidServerResultTip, count));
|
||||
}
|
||||
|
||||
//move server
|
||||
|
@ -700,7 +700,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
}
|
||||
|
||||
await ConfigHandler.MoveToGroup(_config, lstSelected, SelectedMoveToGroup.Id);
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
|
||||
RefreshServers();
|
||||
SelectedMoveToGroup = null;
|
||||
|
@ -712,7 +712,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
var item = _lstProfile.FirstOrDefault(t => t.IndexId == SelectedProfile.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -762,10 +762,10 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
|
||||
private async Task Export2ClientConfigAsync(bool blClipboard)
|
||||
{
|
||||
var item = await AppHandler.Instance.GetProfileItem(SelectedProfile.IndexId);
|
||||
var item = await AppManager.Instance.GetProfileItem(SelectedProfile.IndexId);
|
||||
if (item is null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||
return;
|
||||
}
|
||||
if (blClipboard)
|
||||
|
@ -773,12 +773,12 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
|
||||
if (result.Success != true)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(result.Msg);
|
||||
NoticeManager.Instance.Enqueue(result.Msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.SetClipboardData, result.Data);
|
||||
NoticeHandler.Instance.SendMessage(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.SendMessage(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -796,11 +796,11 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
var result = await CoreConfigHandler.GenerateClientConfig(item, fileName);
|
||||
if (result.Success != true)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(result.Msg);
|
||||
NoticeManager.Instance.Enqueue(result.Msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.SendMessageAndEnqueue(string.Format(ResUI.SaveClientConfigurationIn, fileName));
|
||||
NoticeManager.Instance.SendMessageAndEnqueue(string.Format(ResUI.SaveClientConfigurationIn, fileName));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -833,7 +833,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
{
|
||||
await _updateView?.Invoke(EViewAction.SetClipboardData, sb.ToString());
|
||||
}
|
||||
NoticeHandler.Instance.SendMessage(ResUI.BatchExportURLSuccessfully);
|
||||
NoticeManager.Instance.SendMessage(ResUI.BatchExportURLSuccessfully);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -850,7 +850,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
}
|
||||
else
|
||||
{
|
||||
item = await AppHandler.Instance.GetSubItem(_config.SubIndexId);
|
||||
item = await AppManager.Instance.GetSubItem(_config.SubIndexId);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
|
|
|
@ -28,7 +28,7 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
|
|||
|
||||
public RoutingRuleDetailsViewModel(RulesItem rulesItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
|
@ -83,7 +83,7 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
|
|||
|
||||
if (!hasRule)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Network/Port/Protocol/Domain/IP/Process"));
|
||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Network/Port/Protocol/Domain/IP/Process"));
|
||||
return;
|
||||
}
|
||||
//NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
|
|
|
@ -37,7 +37,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
|
||||
public RoutingRuleSettingViewModel(RoutingItem routingItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
|
@ -151,7 +151,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
{
|
||||
if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||
|
@ -174,7 +174,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
{
|
||||
if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
{
|
||||
if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
string remarks = SelectedRouting.Remarks;
|
||||
if (remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
var item = SelectedRouting;
|
||||
|
@ -239,12 +239,12 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
|
||||
if (await ConfigHandler.SaveRoutingItem(_config, item) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,7 +266,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
if (ret == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,7 +281,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
if (ret == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,7 +290,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
var url = SelectedRouting.Url;
|
||||
if (url.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.MsgNeedUrl);
|
||||
NoticeManager.Instance.Enqueue(ResUI.MsgNeedUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -300,7 +300,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
if (ret == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||
|
||||
public RoutingSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
|
@ -84,7 +84,7 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||
{
|
||||
_routingItems.Clear();
|
||||
|
||||
var routings = await AppHandler.Instance.RoutingItems();
|
||||
var routings = await AppManager.Instance.RoutingItems();
|
||||
foreach (var item in routings)
|
||||
{
|
||||
var it = new RoutingItemModel()
|
||||
|
@ -109,12 +109,12 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||
|
||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||
}
|
||||
else
|
||||
{
|
||||
item = await AppHandler.Instance.GetRoutingItem(SelectedSource?.Id);
|
||||
item = await AppManager.Instance.GetRoutingItem(SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
|
@ -146,7 +146,7 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||
{
|
||||
if (SelectedSource is null || SelectedSource.Remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||
|
@ -155,7 +155,7 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||
}
|
||||
foreach (var it in SelectedSources ?? [SelectedSource])
|
||||
{
|
||||
var item = await AppHandler.Instance.GetRoutingItem(it?.Id);
|
||||
var item = await AppManager.Instance.GetRoutingItem(it?.Id);
|
||||
if (item != null)
|
||||
{
|
||||
await ConfigHandler.RemoveRoutingItem(item);
|
||||
|
@ -168,10 +168,10 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||
|
||||
public async Task RoutingAdvancedSetDefault()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetRoutingItem(SelectedSource?.Id);
|
||||
var item = await AppManager.Instance.GetRoutingItem(SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
|
||||
public StatusBarViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
SelectedRouting = new();
|
||||
SelectedServer = new();
|
||||
RunningServerToolTipText = "-";
|
||||
|
@ -228,7 +228,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
private async Task CopyProxyCmdToClipboard()
|
||||
{
|
||||
var cmd = Utils.IsWindows() ? "set" : "export";
|
||||
var address = $"{Global.Loopback}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}";
|
||||
var address = $"{Global.Loopback}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"{cmd} http_proxy={Global.HttpProtocol}{address}");
|
||||
|
@ -283,7 +283,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
|
||||
private async Task RefreshServersMenu()
|
||||
{
|
||||
var lstModel = await AppHandler.Instance.ProfileItems(_config.SubIndexId, "");
|
||||
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, "");
|
||||
|
||||
_servers.Clear();
|
||||
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
|
||||
|
@ -334,9 +334,9 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
|
||||
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, ResUI.Speedtesting);
|
||||
|
||||
var msg = await Task.Run(ConnectionHandler.Instance.RunAvailabilityCheck);
|
||||
var msg = await Task.Run(ConnectionHandler.RunAvailabilityCheck);
|
||||
|
||||
NoticeHandler.Instance.SendMessageEx(msg);
|
||||
NoticeManager.Instance.SendMessageEx(msg);
|
||||
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, msg);
|
||||
}
|
||||
|
||||
|
@ -355,7 +355,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
}
|
||||
_config.SystemProxyItem.SysProxyType = type;
|
||||
await ChangeSystemProxyAsync(type, true);
|
||||
NoticeHandler.Instance.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.SystemProxyItem.SysProxyType.ToString()}");
|
||||
NoticeManager.Instance.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.SystemProxyItem.SysProxyType.ToString()}");
|
||||
|
||||
SystemProxySelected = (int)_config.SystemProxyItem.SysProxyType;
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
|
@ -381,7 +381,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
_routingItems.Clear();
|
||||
|
||||
BlRouting = true;
|
||||
var routings = await AppHandler.Instance.RoutingItems();
|
||||
var routings = await AppManager.Instance.RoutingItems();
|
||||
foreach (var item in routings)
|
||||
{
|
||||
_routingItems.Add(item);
|
||||
|
@ -404,7 +404,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
return;
|
||||
}
|
||||
|
||||
var item = await AppHandler.Instance.GetRoutingItem(SelectedRouting?.Id);
|
||||
var item = await AppManager.Instance.GetRoutingItem(SelectedRouting?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
|
@ -412,7 +412,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
|
||||
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.SendMessageEx(ResUI.TipChangeRouting);
|
||||
NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting);
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
||||
}
|
||||
|
@ -471,11 +471,11 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
}
|
||||
else if (Utils.IsLinux())
|
||||
{
|
||||
return AppHandler.Instance.LinuxSudoPwd.IsNotEmpty();
|
||||
return AppManager.Instance.LinuxSudoPwd.IsNotEmpty();
|
||||
}
|
||||
else if (Utils.IsOSX())
|
||||
{
|
||||
return AppHandler.Instance.LinuxSudoPwd.IsNotEmpty();
|
||||
return AppManager.Instance.LinuxSudoPwd.IsNotEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -487,10 +487,10 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
public async Task InboundDisplayStatus()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}");
|
||||
sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}");
|
||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
||||
{
|
||||
sb.Append($",{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}");
|
||||
sb.Append($",{AppManager.Instance.GetLocalPort(EInboundProtocol.socks2)}");
|
||||
}
|
||||
sb.Append(']');
|
||||
InboundDisplay = $"{ResUI.LabLocal}:{sb}";
|
||||
|
@ -498,8 +498,8 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
var lan = _config.Inbound.First().NewPort4LAN
|
||||
? $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks3)}]"
|
||||
: $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]";
|
||||
? $"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks3)}]"
|
||||
: $"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}]";
|
||||
InboundLanDisplay = $"{ResUI.LabLAN}:{lan}";
|
||||
}
|
||||
else
|
||||
|
|
|
@ -13,7 +13,7 @@ public class SubEditViewModel : MyReactiveObject
|
|||
|
||||
public SubEditViewModel(SubItem subItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
|
@ -29,7 +29,7 @@ public class SubEditViewModel : MyReactiveObject
|
|||
var remarks = SelectedSource.Remarks;
|
||||
if (remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -39,25 +39,25 @@ public class SubEditViewModel : MyReactiveObject
|
|||
var uri = Utils.TryUri(url);
|
||||
if (uri == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.InvalidUrlTip);
|
||||
NoticeManager.Instance.Enqueue(ResUI.InvalidUrlTip);
|
||||
return;
|
||||
}
|
||||
//Do not allow http protocol
|
||||
if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost))
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.InsecureUrlProtocol);
|
||||
NoticeManager.Instance.Enqueue(ResUI.InsecureUrlProtocol);
|
||||
//return;
|
||||
}
|
||||
}
|
||||
|
||||
if (await ConfigHandler.AddSubItem(_config, SelectedSource) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public class SubSettingViewModel : MyReactiveObject
|
|||
|
||||
public SubSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
|
@ -61,7 +61,7 @@ public class SubSettingViewModel : MyReactiveObject
|
|||
public async Task RefreshSubItems()
|
||||
{
|
||||
_subItems.Clear();
|
||||
_subItems.AddRange(await AppHandler.Instance.SubItems());
|
||||
_subItems.AddRange(await AppManager.Instance.SubItems());
|
||||
}
|
||||
|
||||
public async Task EditSubAsync(bool blNew)
|
||||
|
@ -73,7 +73,7 @@ public class SubSettingViewModel : MyReactiveObject
|
|||
}
|
||||
else
|
||||
{
|
||||
item = await AppHandler.Instance.GetSubItem(SelectedSource?.Id);
|
||||
item = await AppManager.Instance.GetSubItem(SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
|
@ -98,7 +98,7 @@ public class SubSettingViewModel : MyReactiveObject
|
|||
await ConfigHandler.DeleteSubItem(_config, it.Id);
|
||||
}
|
||||
await RefreshSubItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
IsModified = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using ServiceLib.Manager;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Common;
|
||||
using v2rayN.Desktop.Views;
|
||||
|
@ -25,7 +26,7 @@ public partial class App : Application
|
|||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
AppHandler.Instance.InitComponents();
|
||||
AppManager.Instance.InitComponents();
|
||||
|
||||
desktop.Exit += OnExit;
|
||||
desktop.MainWindow = new MainWindow();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Desktop.Base;
|
||||
|
||||
|
@ -20,7 +21,7 @@ public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewMode
|
|||
{
|
||||
try
|
||||
{
|
||||
var sizeItem = ConfigHandler.GetWindowSizeItem(AppHandler.Instance.Config, GetType().Name);
|
||||
var sizeItem = ConfigHandler.GetWindowSizeItem(AppManager.Instance.Config, GetType().Name);
|
||||
if (sizeItem == null)
|
||||
{
|
||||
return;
|
||||
|
@ -45,7 +46,7 @@ public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewMode
|
|||
base.OnClosed(e);
|
||||
try
|
||||
{
|
||||
ConfigHandler.SaveWindowSizeItem(AppHandler.Instance.Config, GetType().Name, Width, Height);
|
||||
ConfigHandler.SaveWindowSizeItem(AppManager.Instance.Config, GetType().Name, Width, Height);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop;
|
||||
|
@ -46,7 +47,7 @@ internal class Program
|
|||
}
|
||||
}
|
||||
|
||||
if (!AppHandler.Instance.InitApp())
|
||||
if (!AppManager.Instance.InitApp())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -62,6 +63,6 @@ internal class Program
|
|||
.WithFontByDefault()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI()
|
||||
.With(new MacOSPlatformOptions { ShowInDock = AppHandler.Instance.Config.UiItem.MacOSShowInDock });
|
||||
.With(new MacOSPlatformOptions { ShowInDock = AppManager.Instance.Config.UiItem.MacOSShowInDock });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ using Avalonia.Styling;
|
|||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Semi.Avalonia;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Desktop.ViewModels;
|
||||
|
||||
|
@ -21,7 +22,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
|||
|
||||
public ThemeSettingViewModel()
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
|
||||
BindingUI();
|
||||
RestoreUI();
|
||||
|
@ -74,7 +75,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
|||
_config.UiItem.CurrentLanguage = CurrentLanguage;
|
||||
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
NoticeHandler.Instance.Enqueue(ResUI.NeedRebootTips);
|
||||
NoticeManager.Instance.Enqueue(ResUI.NeedRebootTips);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ using System.Reactive.Disposables;
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
@ -50,7 +51,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||
|
||||
case EConfigType.Shadowsocks:
|
||||
gridSs.IsVisible = true;
|
||||
cmbSecurity3.ItemsSource = AppHandler.Instance.GetShadowsocksSecurities(profileItem);
|
||||
cmbSecurity3.ItemsSource = AppManager.Instance.GetShadowsocksSecurities(profileItem);
|
||||
break;
|
||||
|
||||
case EConfigType.SOCKS:
|
||||
|
|
|
@ -2,6 +2,7 @@ using System.Reactive.Disposables;
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
@ -14,7 +15,7 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
|||
{
|
||||
InitializeComponent();
|
||||
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
btnCancel.Click += (s, e) => this.Close();
|
||||
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Interactivity;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
@ -13,7 +14,7 @@ public partial class FullConfigTemplateWindow : WindowBase<FullConfigTemplateVie
|
|||
{
|
||||
InitializeComponent();
|
||||
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
btnCancel.Click += (s, e) => this.Close();
|
||||
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ using Avalonia.Threading;
|
|||
using DialogHostAvalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
@ -28,7 +29,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
{
|
||||
InitializeComponent();
|
||||
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.TopRight };
|
||||
|
||||
this.KeyDown += MainWindow_KeyDown;
|
||||
|
@ -351,7 +352,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
{
|
||||
//ShowHideWindow(false);
|
||||
|
||||
NoticeHandler.Instance.SendMessageAndEnqueue("Not yet implemented.(还未实现)");
|
||||
NoticeManager.Instance.SendMessageAndEnqueue("Not yet implemented.(还未实现)");
|
||||
await Task.CompletedTask;
|
||||
//if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
//{
|
||||
|
@ -477,7 +478,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
|
||||
private void AddHelpMenuItem()
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo();
|
||||
foreach (var it in coreInfo
|
||||
.Where(t => t.CoreType != ECoreType.v2fly
|
||||
&& t.CoreType != ECoreType.hysteria))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
@ -14,7 +15,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||
InitializeComponent();
|
||||
|
||||
btnCancel.Click += (s, e) => this.Close();
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
|
||||
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ using Avalonia.Threading;
|
|||
using DialogHostAvalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
|
@ -26,7 +27,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||
{
|
||||
InitializeComponent();
|
||||
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
_window = window;
|
||||
|
||||
menuSelectAll.Click += menuSelectAll_Click;
|
||||
|
|
|
@ -23,7 +23,7 @@ public partial class QrcodeView : UserControl
|
|||
|
||||
private Bitmap? GetQRCode(string? url)
|
||||
{
|
||||
var bytes = QRCodeHelper.GenQRCode(url);
|
||||
var bytes = QRCodeUtils.GenQRCode(url);
|
||||
return ByteToBitmap(bytes);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ using Avalonia.ReactiveUI;
|
|||
using Avalonia.Threading;
|
||||
using DialogHostAvalonia;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
|
@ -19,7 +20,7 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
|||
{
|
||||
InitializeComponent();
|
||||
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
//ViewModel = new StatusBarViewModel(UpdateViewHandler);
|
||||
//Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
|
||||
ViewModel = Locator.Current.GetService<StatusBarViewModel>();
|
||||
|
@ -112,7 +113,7 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
|||
return false;
|
||||
}
|
||||
|
||||
AppHandler.Instance.LinuxSudoPwd = password;
|
||||
AppManager.Instance.LinuxSudoPwd = password;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ using Avalonia.Controls;
|
|||
using Avalonia.Threading;
|
||||
using CliWrap.Buffered;
|
||||
using DialogHostAvalonia;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
|
@ -46,7 +47,7 @@ public partial class SudoPasswordInputView : UserControl
|
|||
else
|
||||
{
|
||||
// Password verification failed, display error and let user try again
|
||||
NoticeHandler.Instance.Enqueue(ResUI.SudoIncorrectPasswordTip);
|
||||
NoticeManager.Instance.Enqueue(ResUI.SudoIncorrectPasswordTip);
|
||||
txtPassword.Focus();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN;
|
||||
|
||||
|
@ -35,14 +36,14 @@ public partial class App : Application
|
|||
return;
|
||||
}
|
||||
|
||||
if (!AppHandler.Instance.InitApp())
|
||||
if (!AppManager.Instance.InitApp())
|
||||
{
|
||||
UI.Show($"Loading GUI configuration file is abnormal,please restart the application{Environment.NewLine}加载GUI配置文件异常,请重启应用");
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
AppHandler.Instance.InitComponents();
|
||||
AppManager.Instance.InitComponents();
|
||||
base.OnStartup(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Windows;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Base;
|
||||
|
||||
|
@ -14,7 +15,7 @@ public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewMode
|
|||
{
|
||||
try
|
||||
{
|
||||
var sizeItem = ConfigHandler.GetWindowSizeItem(AppHandler.Instance.Config, GetType().Name);
|
||||
var sizeItem = ConfigHandler.GetWindowSizeItem(AppManager.Instance.Config, GetType().Name);
|
||||
if (sizeItem == null)
|
||||
{
|
||||
return;
|
||||
|
@ -35,7 +36,7 @@ public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewMode
|
|||
|
||||
try
|
||||
{
|
||||
ConfigHandler.SaveWindowSizeItem(AppHandler.Instance.Config, GetType().Name, Width, Height);
|
||||
ConfigHandler.SaveWindowSizeItem(AppManager.Instance.Config, GetType().Name, Width, Height);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ public class QRCodeHelper
|
|||
}
|
||||
try
|
||||
{
|
||||
var qrCodeImage = ServiceLib.Common.QRCodeHelper.GenQRCode(strContent);
|
||||
var qrCodeImage = QRCodeUtils.GenQRCode(strContent);
|
||||
return qrCodeImage is null ? null : ByteToImage(qrCodeImage);
|
||||
}
|
||||
catch
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Windows.Media;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Converters;
|
||||
|
||||
|
@ -10,7 +11,7 @@ public class MaterialDesignFonts
|
|||
{
|
||||
try
|
||||
{
|
||||
var fontFamily = AppHandler.Instance.Config.UiItem.CurrentFontFamily;
|
||||
var fontFamily = AppManager.Instance.Config.UiItem.CurrentFontFamily;
|
||||
if (fontFamily.IsNotEmpty())
|
||||
{
|
||||
var fontPath = Utils.GetFontsPath();
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Text;
|
|||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Handler;
|
||||
|
||||
|
@ -29,7 +30,7 @@ public sealed class HotkeyHandler
|
|||
private void Init()
|
||||
{
|
||||
_hotkeyTriggerDic.Clear();
|
||||
foreach (var item in AppHandler.Instance.Config.GlobalHotkeys)
|
||||
foreach (var item in AppManager.Instance.Config.GlobalHotkeys)
|
||||
{
|
||||
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
|
||||
{
|
||||
|
|
|
@ -9,6 +9,7 @@ using MaterialDesignColors.ColorManipulation;
|
|||
using MaterialDesignThemes.Wpf;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.ViewModels;
|
||||
|
||||
|
@ -30,7 +31,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
|||
|
||||
public ThemeSettingViewModel()
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
|
||||
RegisterSystemColorSet(_config, Application.Current.MainWindow, ModifyTheme);
|
||||
|
||||
|
@ -121,7 +122,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
|||
_config.UiItem.CurrentLanguage = CurrentLanguage;
|
||||
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
NoticeHandler.Instance.Enqueue(ResUI.NeedRebootTips);
|
||||
NoticeManager.Instance.Enqueue(ResUI.NeedRebootTips);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -28,7 +29,7 @@ public partial class AddServer2Window
|
|||
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.btnEdit).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.SaveServerCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
|
|
|
@ -2,6 +2,7 @@ using System.Reactive.Disposables;
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -44,7 +45,7 @@ public partial class AddServerWindow
|
|||
|
||||
case EConfigType.Shadowsocks:
|
||||
gridSs.Visibility = Visibility.Visible;
|
||||
cmbSecurity3.ItemsSource = AppHandler.Instance.GetShadowsocksSecurities(profileItem);
|
||||
cmbSecurity3.ItemsSource = AppManager.Instance.GetShadowsocksSecurities(profileItem);
|
||||
break;
|
||||
|
||||
case EConfigType.SOCKS:
|
||||
|
@ -195,7 +196,7 @@ public partial class AddServerWindow
|
|||
});
|
||||
|
||||
this.Title = $"{profileItem.ConfigType}";
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -13,7 +14,7 @@ public partial class DNSSettingWindow
|
|||
InitializeComponent();
|
||||
|
||||
this.Owner = Application.Current.MainWindow;
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
|
||||
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
|
||||
|
||||
|
@ -78,7 +79,7 @@ public partial class DNSSettingWindow
|
|||
.BindTo(this, x => x.txtAdvancedDNSSettingsInvalid.Visibility)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -13,7 +14,7 @@ public partial class FullConfigTemplateWindow
|
|||
InitializeComponent();
|
||||
|
||||
this.Owner = Application.Current.MainWindow;
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
|
||||
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
|
||||
|
||||
|
@ -31,7 +32,7 @@ public partial class FullConfigTemplateWindow
|
|||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Windows;
|
|||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using v2rayN.Handler;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
@ -29,7 +30,7 @@ public partial class GlobalHotkeySettingWindow
|
|||
{
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||
|
||||
Init();
|
||||
BindingData();
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.Windows.Media;
|
|||
using System.Windows.Threading;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using Splat;
|
||||
using v2rayN.Handler;
|
||||
|
||||
|
@ -23,7 +24,7 @@ public partial class MainWindow
|
|||
{
|
||||
InitializeComponent();
|
||||
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false);
|
||||
|
||||
App.Current.SessionEnding += Current_SessionEnding;
|
||||
|
@ -438,7 +439,7 @@ public partial class MainWindow
|
|||
|
||||
private void AddHelpMenuItem()
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
|
||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo();
|
||||
foreach (var it in coreInfo
|
||||
.Where(t => t.CoreType != ECoreType.v2fly
|
||||
&& t.CoreType != ECoreType.hysteria))
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Reactive.Disposables;
|
|||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -16,7 +17,7 @@ public partial class OptionSettingWindow
|
|||
InitializeComponent();
|
||||
|
||||
this.Owner = Application.Current.MainWindow;
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
|
||||
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
|
||||
|
||||
|
@ -134,7 +135,7 @@ public partial class OptionSettingWindow
|
|||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Windows.Media;
|
|||
using System.Windows.Threading;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using Splat;
|
||||
using v2rayN.Base;
|
||||
using Point = System.Windows.Point;
|
||||
|
@ -22,7 +23,7 @@ public partial class ProfilesView
|
|||
InitializeComponent();
|
||||
lstGroup.MaxHeight = Math.Floor(SystemParameters.WorkArea.Height * 0.20 / 40) * 40;
|
||||
|
||||
_config = AppHandler.Instance.Config;
|
||||
_config = AppManager.Instance.Config;
|
||||
|
||||
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||
txtServerFilter.PreviewKeyDown += TxtServerFilter_PreviewKeyDown;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -48,7 +49,7 @@ public partial class RoutingRuleDetailsWindow
|
|||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
|
|
|
@ -2,6 +2,7 @@ using System.Reactive.Disposables;
|
|||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -54,7 +55,7 @@ public partial class RoutingRuleSettingWindow
|
|||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme);
|
||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue