Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7f3a7b1a7 | ||
|
|
71eb5f0813 | ||
|
|
b4f50258a7 | ||
|
|
346a9c5fcc | ||
|
|
61635db5b5 | ||
|
|
4c0a59a715 | ||
|
|
aa829a66ea | ||
|
|
885f193a00 | ||
|
|
0a9bbf526d | ||
|
|
ff6716b39d | ||
|
|
8505f2db96 | ||
|
|
233d605256 | ||
|
|
c4e01d20a0 | ||
|
|
07156cb55c | ||
|
|
8361b4e4a0 | ||
|
|
bb90671979 | ||
|
|
beddc71ed8 | ||
|
|
d5f1cc99ac | ||
|
|
d03a86292e | ||
|
|
c061a24948 | ||
|
|
9c7446f820 | ||
|
|
8be1730719 | ||
|
|
7ceaf5c071 | ||
|
|
7901a59aa4 | ||
|
|
8343a1002f | ||
|
|
c83dce3cc0 | ||
|
|
14afeab2bb |
@@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.28.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.28.1" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.65.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.66.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace ServiceLib.Common
|
||||
Uri uri = new(url);
|
||||
//Authorization Header
|
||||
var headers = new WebHeaderCollection();
|
||||
if (!Utils.IsNullOrEmpty(uri.UserInfo))
|
||||
if (Utils.IsNotEmpty(uri.UserInfo))
|
||||
{
|
||||
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace ServiceLib.Common
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(ignoredName) && entry.Name.Contains(ignoredName))
|
||||
if (Utils.IsNotEmpty(ignoredName) && entry.Name.Contains(ignoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -102,11 +102,34 @@ namespace ServiceLib.Common
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<string>? GetFilesFromZip(string fileName)
|
||||
{
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
using ZipArchive archive = ZipFile.OpenRead(fileName);
|
||||
return archive.Entries.Select(entry => entry.FullName).ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName);
|
||||
if (File.Exists(destinationArchiveFileName))
|
||||
{
|
||||
File.Delete(destinationArchiveFileName);
|
||||
}
|
||||
|
||||
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, CompressionLevel.SmallestSize, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -115,5 +138,46 @@ namespace ServiceLib.Common
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive, string ignoredName)
|
||||
{
|
||||
// Get information about the source directory
|
||||
var dir = new DirectoryInfo(sourceDir);
|
||||
|
||||
// Check if the source directory exists
|
||||
if (!dir.Exists)
|
||||
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
|
||||
|
||||
// Cache directories before we start copying
|
||||
DirectoryInfo[] dirs = dir.GetDirectories();
|
||||
|
||||
// Create the destination directory
|
||||
Directory.CreateDirectory(destinationDir);
|
||||
|
||||
// Get the files in the source directory and copy to the destination directory
|
||||
foreach (FileInfo file in dir.GetFiles())
|
||||
{
|
||||
if (Utils.IsNotEmpty(ignoredName) && file.Name.Contains(ignoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (file.Extension == file.Name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string targetFilePath = Path.Combine(destinationDir, file.Name);
|
||||
file.CopyTo(targetFilePath);
|
||||
}
|
||||
|
||||
// If recursive and copying subdirectories, recursively call this method
|
||||
if (recursive)
|
||||
{
|
||||
foreach (DirectoryInfo subDir in dirs)
|
||||
{
|
||||
string newDestinationDir = Path.Combine(destinationDir, subDir.Name);
|
||||
CopyDirectory(subDir.FullName, newDestinationDir, true, ignoredName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace ServiceLib.Common
|
||||
|
||||
public async Task<string?> TryGetAsync(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
return null;
|
||||
|
||||
try
|
||||
|
||||
@@ -14,6 +14,11 @@ namespace ServiceLib.Common
|
||||
return string.IsNullOrWhiteSpace(value);
|
||||
}
|
||||
|
||||
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
|
||||
{
|
||||
return !string.IsNullOrEmpty(value);
|
||||
}
|
||||
|
||||
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
||||
{
|
||||
if (s.IsNullOrEmpty()) return false;
|
||||
|
||||
@@ -417,6 +417,11 @@ namespace ServiceLib.Common
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsNotEmpty(string? text)
|
||||
{
|
||||
return !string.IsNullOrEmpty(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证IP地址是否合法
|
||||
/// </summary>
|
||||
@@ -567,13 +572,12 @@ namespace ServiceLib.Common
|
||||
{
|
||||
try
|
||||
{
|
||||
string location = GetExePath();
|
||||
if (blFull)
|
||||
{
|
||||
return string.Format("{0} - V{1} - {2}",
|
||||
Global.AppName,
|
||||
GetVersionInfo(),
|
||||
File.GetLastWriteTime(location).ToString("yyyy/MM/dd"));
|
||||
File.GetLastWriteTime(GetExePath()).ToString("yyyy/MM/dd"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -593,7 +597,7 @@ namespace ServiceLib.Common
|
||||
{
|
||||
try
|
||||
{
|
||||
return Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString() ?? "0.0";
|
||||
return Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString(3) ?? "0.0";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
@@ -35,13 +36,17 @@ namespace ServiceLib.Common
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToYaml(Object obj)
|
||||
public static string ToYaml(Object? obj)
|
||||
{
|
||||
string result = string.Empty;
|
||||
if (obj == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
var serializer = new SerializerBuilder()
|
||||
.WithNamingConvention(HyphenatedNamingConvention.Instance)
|
||||
.Build();
|
||||
|
||||
string result = string.Empty;
|
||||
try
|
||||
{
|
||||
result = serializer.Serialize(obj);
|
||||
@@ -53,6 +58,24 @@ namespace ServiceLib.Common
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string? PreprocessYaml(string str)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
var mergingParser = new MergingParser(new Parser(new StringReader(str)));
|
||||
var obj = new DeserializerBuilder().Build().Deserialize(mergingParser);
|
||||
return ToYaml(obj);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("PreprocessYaml", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion YAML
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
//载入配置文件
|
||||
var result = Utils.LoadResource(Utils.GetConfigPath(configRes));
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
if (Utils.IsNotEmpty(result))
|
||||
{
|
||||
//转成Json
|
||||
config = JsonUtils.Deserialize<Config>(result);
|
||||
@@ -204,6 +204,8 @@ namespace ServiceLib.Handler
|
||||
};
|
||||
}
|
||||
|
||||
config.webDavItem ??= new();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1005,7 +1007,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(profileItem.security) && profileItem.security != Global.None)
|
||||
if (Utils.IsNotEmpty(profileItem.security) && profileItem.security != Global.None)
|
||||
{
|
||||
profileItem.security = Global.None;
|
||||
}
|
||||
@@ -1043,7 +1045,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
profileItem.configVersion = 2;
|
||||
|
||||
if (!Utils.IsNullOrEmpty(profileItem.streamSecurity))
|
||||
if (Utils.IsNotEmpty(profileItem.streamSecurity))
|
||||
{
|
||||
if (profileItem.streamSecurity != Global.StreamSecurity
|
||||
&& profileItem.streamSecurity != Global.StreamSecurityReality)
|
||||
@@ -1063,7 +1065,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
}
|
||||
|
||||
if (!Utils.IsNullOrEmpty(profileItem.network) && !Global.Networks.Contains(profileItem.network))
|
||||
if (Utils.IsNotEmpty(profileItem.network) && !Global.Networks.Contains(profileItem.network))
|
||||
{
|
||||
profileItem.network = Global.DefaultNetwork;
|
||||
}
|
||||
@@ -1184,7 +1186,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
string subFilter = string.Empty;
|
||||
//remove sub items
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
subFilter = LazyConfig.Instance.GetSubItem(subid)?.filter ?? "";
|
||||
@@ -1217,7 +1219,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
|
||||
//exist sub items
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
var existItem = lstOriSub?.FirstOrDefault(t => t.isSub == isSub
|
||||
&& config.uiItem.enableUpdateSubOnlyRemarksExist ? t.remarks == profileItem.remarks : CompareProfileItem(t, profileItem, true));
|
||||
@@ -1239,7 +1241,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
}
|
||||
//filter
|
||||
if (!Utils.IsNullOrEmpty(subFilter))
|
||||
if (Utils.IsNotEmpty(subFilter))
|
||||
{
|
||||
if (!Regex.IsMatch(profileItem.remarks, subFilter))
|
||||
{
|
||||
@@ -1303,7 +1305,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
if (lstProfiles != null && lstProfiles.Count > 0)
|
||||
{
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
}
|
||||
@@ -1359,7 +1361,7 @@ namespace ServiceLib.Handler
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
}
|
||||
@@ -1387,7 +1389,7 @@ namespace ServiceLib.Handler
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
}
|
||||
@@ -1419,7 +1421,7 @@ namespace ServiceLib.Handler
|
||||
return -1;
|
||||
}
|
||||
List<ProfileItem>? lstOriSub = null;
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
lstOriSub = LazyConfig.Instance.ProfileItems(subid);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
}
|
||||
|
||||
string addressFileName = node.address;
|
||||
if (string.IsNullOrEmpty(addressFileName))
|
||||
if (Utils.IsNullOrEmpty(addressFileName))
|
||||
{
|
||||
msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return -1;
|
||||
@@ -64,6 +64,12 @@
|
||||
var txtFile = File.ReadAllText(addressFileName);
|
||||
txtFile = txtFile.Replace(tagYamlStr1, tagYamlStr2);
|
||||
|
||||
//YAML anchors
|
||||
if (txtFile.Contains("<<:") && txtFile.Contains("*") && txtFile.Contains("&"))
|
||||
{
|
||||
txtFile = YamlUtils.PreprocessYaml(txtFile);
|
||||
}
|
||||
|
||||
var fileContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
|
||||
if (fileContent == null)
|
||||
{
|
||||
@@ -111,7 +117,7 @@
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
string tun = Utils.GetEmbedText(Global.ClashTunYaml);
|
||||
if (!string.IsNullOrEmpty(tun))
|
||||
if (Utils.IsNotEmpty(tun))
|
||||
{
|
||||
var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun);
|
||||
if (tunContent != null)
|
||||
|
||||
@@ -370,7 +370,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
}
|
||||
|
||||
string addressFileName = node.address;
|
||||
if (string.IsNullOrEmpty(addressFileName))
|
||||
if (Utils.IsNullOrEmpty(addressFileName))
|
||||
{
|
||||
msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return -1;
|
||||
@@ -489,7 +489,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
if (_config.routingBasicItem.enableRoutingAdvanced)
|
||||
{
|
||||
var routing = ConfigHandler.GetDefaultRouting(_config);
|
||||
if (!Utils.IsNullOrEmpty(routing.domainStrategy4Singbox))
|
||||
if (Utils.IsNotEmpty(routing.domainStrategy4Singbox))
|
||||
{
|
||||
inbound.domain_strategy = routing.domainStrategy4Singbox;
|
||||
}
|
||||
@@ -512,7 +512,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
singboxConfig.inbounds.Add(inbound4);
|
||||
|
||||
//auth
|
||||
if (!Utils.IsNullOrEmpty(_config.inbound[0].user) && !Utils.IsNullOrEmpty(_config.inbound[0].pass))
|
||||
if (Utils.IsNotEmpty(_config.inbound[0].user) && Utils.IsNotEmpty(_config.inbound[0].pass))
|
||||
{
|
||||
inbound3.users = new() { new() { username = _config.inbound[0].user, password = _config.inbound[0].pass } };
|
||||
inbound4.users = new() { new() { username = _config.inbound[0].user, password = _config.inbound[0].pass } };
|
||||
@@ -604,8 +604,8 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
case EConfigType.Socks:
|
||||
{
|
||||
outbound.version = "5";
|
||||
if (!Utils.IsNullOrEmpty(node.security)
|
||||
&& !Utils.IsNullOrEmpty(node.id))
|
||||
if (Utils.IsNotEmpty(node.security)
|
||||
&& Utils.IsNotEmpty(node.id))
|
||||
{
|
||||
outbound.username = node.security;
|
||||
outbound.password = node.id;
|
||||
@@ -614,8 +614,8 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
}
|
||||
case EConfigType.Http:
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(node.security)
|
||||
&& !Utils.IsNullOrEmpty(node.id))
|
||||
if (Utils.IsNotEmpty(node.security)
|
||||
&& Utils.IsNotEmpty(node.id))
|
||||
{
|
||||
outbound.username = node.security;
|
||||
outbound.password = node.id;
|
||||
@@ -649,7 +649,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
outbound.password = node.id;
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
outbound.obfs = new()
|
||||
{
|
||||
@@ -695,7 +695,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_config.coreBasicItem.muxEnabled && !Utils.IsNullOrEmpty(_config.mux4SboxItem.protocol))
|
||||
if (_config.coreBasicItem.muxEnabled && Utils.IsNotEmpty(_config.mux4SboxItem.protocol))
|
||||
{
|
||||
var mux = new Multiplex4Sbox()
|
||||
{
|
||||
@@ -721,11 +721,11 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
if (node.streamSecurity == Global.StreamSecurityReality || node.streamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
var server_name = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(node.sni))
|
||||
if (Utils.IsNotEmpty(node.sni))
|
||||
{
|
||||
server_name = node.sni;
|
||||
}
|
||||
else if (!Utils.IsNullOrEmpty(node.requestHost))
|
||||
else if (Utils.IsNotEmpty(node.requestHost))
|
||||
{
|
||||
server_name = Utils.String2List(node.requestHost)[0];
|
||||
}
|
||||
@@ -736,7 +736,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
insecure = Utils.ToBool(node.allowInsecure.IsNullOrEmpty() ? _config.coreBasicItem.defAllowInsecure.ToString().ToLower() : node.allowInsecure),
|
||||
alpn = node.GetAlpn(),
|
||||
};
|
||||
if (!Utils.IsNullOrEmpty(node.fingerprint))
|
||||
if (Utils.IsNotEmpty(node.fingerprint))
|
||||
{
|
||||
tls.utls = new Utls4Sbox()
|
||||
{
|
||||
@@ -798,7 +798,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
case nameof(ETransport.ws):
|
||||
transport.type = nameof(ETransport.ws);
|
||||
transport.path = Utils.IsNullOrEmpty(node.path) ? null : node.path;
|
||||
if (!Utils.IsNullOrEmpty(node.requestHost))
|
||||
if (Utils.IsNotEmpty(node.requestHost))
|
||||
{
|
||||
transport.headers = new()
|
||||
{
|
||||
@@ -1020,7 +1020,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
outbound = item.outboundTag,
|
||||
};
|
||||
|
||||
if (!Utils.IsNullOrEmpty(item.port))
|
||||
if (Utils.IsNotEmpty(item.port))
|
||||
{
|
||||
if (item.port.Contains("-"))
|
||||
{
|
||||
@@ -1031,7 +1031,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
rule.port = new List<int> { Utils.ToInt(item.port) };
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.network))
|
||||
if (Utils.IsNotEmpty(item.network))
|
||||
{
|
||||
rule.network = Utils.String2List(item.network);
|
||||
}
|
||||
@@ -1221,7 +1221,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
});
|
||||
|
||||
var lstDomain = singboxConfig.outbounds
|
||||
.Where(t => !Utils.IsNullOrEmpty(t.server) && Utils.IsDomain(t.server))
|
||||
.Where(t => Utils.IsNotEmpty(t.server) && Utils.IsDomain(t.server))
|
||||
.Select(t => t.server)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
@@ -1250,7 +1250,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
|
||||
private int GenExperimental(SingboxConfig singboxConfig)
|
||||
{
|
||||
if (_config.guiItem.enableStatistics)
|
||||
//if (_config.guiItem.enableStatistics)
|
||||
{
|
||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
||||
singboxConfig.experimental.clash_api = new Clash_Api4Sbox()
|
||||
@@ -1324,10 +1324,10 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
if (_config.routingBasicItem.enableRoutingAdvanced)
|
||||
{
|
||||
var routing = ConfigHandler.GetDefaultRouting(_config);
|
||||
if (!Utils.IsNullOrEmpty(routing.customRulesetPath4Singbox))
|
||||
if (Utils.IsNotEmpty(routing.customRulesetPath4Singbox))
|
||||
{
|
||||
var result = Utils.LoadResource(routing.customRulesetPath4Singbox);
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
if (Utils.IsNotEmpty(result))
|
||||
{
|
||||
customRulesets = (JsonUtils.Deserialize<List<Ruleset4Sbox>>(result) ?? [])
|
||||
.Where(t => t.tag != null)
|
||||
|
||||
@@ -392,7 +392,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
v2rayConfig.inbounds.Add(inbound4);
|
||||
|
||||
//auth
|
||||
if (!Utils.IsNullOrEmpty(_config.inbound[0].user) && !Utils.IsNullOrEmpty(_config.inbound[0].pass))
|
||||
if (Utils.IsNotEmpty(_config.inbound[0].user) && Utils.IsNotEmpty(_config.inbound[0].pass))
|
||||
{
|
||||
inbound3.settings.auth = "password";
|
||||
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.inbound[0].user, pass = _config.inbound[0].pass } };
|
||||
@@ -453,7 +453,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
var routing = ConfigHandler.GetDefaultRouting(_config);
|
||||
if (routing != null)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(routing.domainStrategy))
|
||||
if (Utils.IsNotEmpty(routing.domainStrategy))
|
||||
{
|
||||
v2rayConfig.routing.domainStrategy = routing.domainStrategy;
|
||||
}
|
||||
@@ -550,7 +550,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
}
|
||||
if (!hasDomainIp)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(rule.port)
|
||||
if (Utils.IsNotEmpty(rule.port)
|
||||
|| rule.protocol?.Count > 0
|
||||
|| rule.inboundTag?.Count > 0
|
||||
)
|
||||
@@ -660,8 +660,8 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
serversItem.method = null;
|
||||
serversItem.password = null;
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.security)
|
||||
&& !Utils.IsNullOrEmpty(node.id))
|
||||
if (Utils.IsNotEmpty(node.security)
|
||||
&& Utils.IsNotEmpty(node.id))
|
||||
{
|
||||
SocksUsersItem4Ray socksUsersItem = new()
|
||||
{
|
||||
@@ -712,7 +712,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
if (node.streamSecurity == Global.StreamSecurityReality
|
||||
|| node.streamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(node.flow))
|
||||
if (Utils.IsNotEmpty(node.flow))
|
||||
{
|
||||
usersItem.flow = node.flow;
|
||||
|
||||
@@ -818,11 +818,11 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
alpn = node.GetAlpn(),
|
||||
fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint
|
||||
};
|
||||
if (!Utils.IsNullOrEmpty(sni))
|
||||
if (Utils.IsNotEmpty(sni))
|
||||
{
|
||||
tlsSettings.serverName = sni;
|
||||
}
|
||||
else if (!Utils.IsNullOrEmpty(host))
|
||||
else if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
tlsSettings.serverName = Utils.String2List(host)[0];
|
||||
}
|
||||
@@ -867,7 +867,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
type = node.headerType
|
||||
};
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
kcpSettings.seed = node.path;
|
||||
}
|
||||
@@ -878,15 +878,15 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
WsSettings4Ray wsSettings = new();
|
||||
wsSettings.headers = new Headers4Ray();
|
||||
string path = node.path;
|
||||
if (!Utils.IsNullOrEmpty(host))
|
||||
if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
wsSettings.headers.Host = host;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(path))
|
||||
if (Utils.IsNotEmpty(path))
|
||||
{
|
||||
wsSettings.path = path;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(useragent))
|
||||
if (Utils.IsNotEmpty(useragent))
|
||||
{
|
||||
wsSettings.headers.UserAgent = useragent;
|
||||
}
|
||||
@@ -897,11 +897,11 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
case nameof(ETransport.httpupgrade):
|
||||
HttpupgradeSettings4Ray httpupgradeSettings = new();
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
httpupgradeSettings.path = node.path;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(host))
|
||||
if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
httpupgradeSettings.host = host;
|
||||
}
|
||||
@@ -916,11 +916,11 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
maxConcurrentUploads = 10
|
||||
};
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
splithttpSettings.path = node.path;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(host))
|
||||
if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
splithttpSettings.host = host;
|
||||
}
|
||||
@@ -931,7 +931,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
case nameof(ETransport.h2):
|
||||
HttpSettings4Ray httpSettings = new();
|
||||
|
||||
if (!Utils.IsNullOrEmpty(host))
|
||||
if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
httpSettings.host = Utils.String2List(host);
|
||||
}
|
||||
@@ -954,7 +954,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
streamSettings.quicSettings = quicsettings;
|
||||
if (node.streamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(sni))
|
||||
if (Utils.IsNotEmpty(sni))
|
||||
{
|
||||
streamSettings.tlsSettings.serverName = sni;
|
||||
}
|
||||
@@ -1000,7 +1000,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
request = request.Replace("$requestUserAgent$", $"\"{useragent}\"");
|
||||
//Path
|
||||
string pathHttp = @"/";
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
string[] arrPath = node.path.Split(',');
|
||||
pathHttp = string.Join("\",\"", arrPath);
|
||||
@@ -1033,7 +1033,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
}
|
||||
|
||||
//Outbound Freedom domainStrategy
|
||||
if (!Utils.IsNullOrEmpty(domainStrategy4Freedom))
|
||||
if (Utils.IsNotEmpty(domainStrategy4Freedom))
|
||||
{
|
||||
var outbound = v2rayConfig.outbounds[1];
|
||||
outbound.settings.domainStrategy = domainStrategy4Freedom;
|
||||
@@ -1157,7 +1157,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
//fragment proxy
|
||||
if (_config.coreBasicItem.enableFragment
|
||||
&& !Utils.IsNullOrEmpty(v2rayConfig.outbounds[0].streamSettings?.security))
|
||||
&& Utils.IsNotEmpty(v2rayConfig.outbounds[0].streamSettings?.security))
|
||||
{
|
||||
var fragmentOutbound = new Outbounds4Ray
|
||||
{
|
||||
|
||||
@@ -302,7 +302,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
proc.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(e.Data))
|
||||
if (Utils.IsNotEmpty(e.Data))
|
||||
{
|
||||
string msg = e.Data + Environment.NewLine;
|
||||
ShowMsg(false, msg);
|
||||
@@ -310,7 +310,7 @@ namespace ServiceLib.Handler
|
||||
};
|
||||
proc.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(e.Data))
|
||||
if (Utils.IsNotEmpty(e.Data))
|
||||
{
|
||||
string msg = e.Data + Environment.NewLine;
|
||||
ShowMsg(false, msg);
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace ServiceLib.Handler
|
||||
try
|
||||
{
|
||||
var result1 = await DownloadStringAsync(url, blProxy, userAgent);
|
||||
if (!Utils.IsNullOrEmpty(result1))
|
||||
if (Utils.IsNotEmpty(result1))
|
||||
{
|
||||
return result1;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ namespace ServiceLib.Handler
|
||||
try
|
||||
{
|
||||
var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent);
|
||||
if (!Utils.IsNullOrEmpty(result2))
|
||||
if (Utils.IsNotEmpty(result2))
|
||||
{
|
||||
return result2;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ namespace ServiceLib.Handler
|
||||
using var wc = new WebClient();
|
||||
wc.Proxy = GetWebProxy(blProxy);
|
||||
var result3 = await wc.DownloadStringTaskAsync(url);
|
||||
if (!Utils.IsNullOrEmpty(result3))
|
||||
if (Utils.IsNotEmpty(result3))
|
||||
{
|
||||
return result3;
|
||||
}
|
||||
@@ -197,7 +197,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
Uri uri = new(url);
|
||||
//Authorization Header
|
||||
if (!Utils.IsNullOrEmpty(uri.UserInfo))
|
||||
if (Utils.IsNotEmpty(uri.UserInfo))
|
||||
{
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utils.Base64Encode(uri.UserInfo));
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ namespace ServiceLib.Handler.Fmt
|
||||
|
||||
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(item.flow))
|
||||
if (Utils.IsNotEmpty(item.flow))
|
||||
{
|
||||
dicQuery.Add("flow", item.flow);
|
||||
}
|
||||
|
||||
if (!Utils.IsNullOrEmpty(item.streamSecurity))
|
||||
if (Utils.IsNotEmpty(item.streamSecurity))
|
||||
{
|
||||
dicQuery.Add("security", item.streamSecurity);
|
||||
}
|
||||
@@ -32,27 +32,27 @@ namespace ServiceLib.Handler.Fmt
|
||||
dicQuery.Add("security", securityDef);
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
if (Utils.IsNotEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
if (Utils.IsNotEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.fingerprint))
|
||||
if (Utils.IsNotEmpty(item.fingerprint))
|
||||
{
|
||||
dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
if (Utils.IsNotEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
if (Utils.IsNotEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("sid", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.spiderX))
|
||||
if (Utils.IsNotEmpty(item.spiderX))
|
||||
{
|
||||
dicQuery.Add("spx", Utils.UrlEncode(item.spiderX));
|
||||
}
|
||||
@@ -61,21 +61,21 @@ namespace ServiceLib.Handler.Fmt
|
||||
dicQuery.Add("allowInsecure", "1");
|
||||
}
|
||||
|
||||
dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : nameof(ETransport.tcp));
|
||||
dicQuery.Add("type", Utils.IsNotEmpty(item.network) ? item.network : nameof(ETransport.tcp));
|
||||
|
||||
switch (item.network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (Utils.IsNotEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("seed", Utils.UrlEncode(item.path));
|
||||
}
|
||||
@@ -84,11 +84,11 @@ namespace ServiceLib.Handler.Fmt
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
case nameof(ETransport.splithttp):
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
if (Utils.IsNotEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
@@ -97,24 +97,24 @@ namespace ServiceLib.Handler.Fmt
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
dicQuery["type"] = nameof(ETransport.http);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
if (Utils.IsNotEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.requestHost));
|
||||
dicQuery.Add("key", Utils.UrlEncode(item.path));
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("authority", Utils.UrlEncode(item.requestHost));
|
||||
dicQuery.Add("serviceName", Utils.UrlEncode(item.path));
|
||||
|
||||
@@ -31,20 +31,20 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
if (Utils.IsNotEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
if (Utils.IsNotEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("obfs", "salamander");
|
||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.path));
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace ServiceLib.Handler.Fmt
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace ServiceLib.Handler.Fmt
|
||||
ProfileItem item = new();
|
||||
var base64 = match.Groups["base64"].Value.TrimEnd('/');
|
||||
var tag = match.Groups["tag"].Value;
|
||||
if (!Utils.IsNullOrEmpty(tag))
|
||||
if (Utils.IsNotEmpty(tag))
|
||||
{
|
||||
item.remarks = Utils.UrlDecode(tag);
|
||||
}
|
||||
@@ -128,7 +128,7 @@ namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
//obfs-host exists
|
||||
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
|
||||
if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost))
|
||||
if (queryParameters["plugin"].Contains("obfs=http") && Utils.IsNotEmpty(obfsHost))
|
||||
{
|
||||
obfsHost = obfsHost?.Replace("obfs-host=", "");
|
||||
item.network = Global.DefaultNetwork;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
|
||||
@@ -36,16 +36,16 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
if (Utils.IsNotEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
if (Utils.IsNotEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
|
||||
@@ -33,12 +33,12 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.security))
|
||||
if (Utils.IsNotEmpty(item.security))
|
||||
{
|
||||
dicQuery.Add("encryption", item.security);
|
||||
}
|
||||
|
||||
@@ -78,12 +78,12 @@
|
||||
item.alterId = Utils.ToInt(vmessQRCode.aid);
|
||||
item.security = Utils.ToString(vmessQRCode.scy);
|
||||
|
||||
item.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
if (!Utils.IsNullOrEmpty(vmessQRCode.net))
|
||||
item.security = Utils.IsNotEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
if (Utils.IsNotEmpty(vmessQRCode.net))
|
||||
{
|
||||
item.network = vmessQRCode.net;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(vmessQRCode.type))
|
||||
if (Utils.IsNotEmpty(vmessQRCode.type))
|
||||
{
|
||||
item.headerType = vmessQRCode.type;
|
||||
}
|
||||
|
||||
@@ -34,25 +34,25 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
if (Utils.IsNotEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("publickey", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("reserved", Utils.UrlEncode(item.path));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
if (Utils.IsNotEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("address", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
if (Utils.IsNotEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("mtu", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
|
||||
@@ -104,11 +104,11 @@
|
||||
from ProfileItem a
|
||||
left join SubItem b on a.subid = b.id
|
||||
where 1=1 ";
|
||||
if (!Utils.IsNullOrEmpty(subid))
|
||||
if (Utils.IsNotEmpty(subid))
|
||||
{
|
||||
sql += $" and a.subid = '{subid}'";
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(filter))
|
||||
if (Utils.IsNotEmpty(filter))
|
||||
{
|
||||
if (filter.Contains('\''))
|
||||
{
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace ServiceLib.Handler
|
||||
MessageBus.Current.SendMessage(content, Global.CommandSendMsgView);
|
||||
}
|
||||
|
||||
public void SendMessage(string? content, bool time)
|
||||
public void SendMessageEx(string? content)
|
||||
{
|
||||
if (content.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
content = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} {content}";
|
||||
content = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss} {content}";
|
||||
SendMessage(content);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
private void IndexIdEnqueue(string indexId)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||
if (Utils.IsNotEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||
{
|
||||
_queIndexIds.Enqueue(indexId);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace ServiceLib.Handler.Statistics
|
||||
while (!res.CloseStatus.HasValue)
|
||||
{
|
||||
var result = Encoding.UTF8.GetString(buffer, 0, res.Count);
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
if (Utils.IsNotEmpty(result))
|
||||
{
|
||||
ParseOutput(result, out ulong up, out ulong down);
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
{
|
||||
if ((dtNow - autoUpdateGeoTime).Hours % config.guiItem.autoUpdateInterval == 0)
|
||||
{
|
||||
updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
|
||||
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
|
||||
{
|
||||
update(false, msg);
|
||||
});
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace ServiceLib.Handler
|
||||
string url = item.url.TrimEx();
|
||||
string userAgent = item.userAgent.TrimEx();
|
||||
string hashCode = $"{item.remarks}->";
|
||||
if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || (!Utils.IsNullOrEmpty(subId) && item.id != subId))
|
||||
if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || (Utils.IsNotEmpty(subId) && item.id != subId))
|
||||
{
|
||||
//_updateFunc(false, $"{hashCode}{ResUI.MsgNoValidSubscription}");
|
||||
continue;
|
||||
@@ -169,7 +169,7 @@ namespace ServiceLib.Handler
|
||||
//one url
|
||||
url = Utils.GetPunycode(url);
|
||||
//convert
|
||||
if (!Utils.IsNullOrEmpty(item.convertTarget))
|
||||
if (Utils.IsNotEmpty(item.convertTarget))
|
||||
{
|
||||
var subConvertUrl = Utils.IsNullOrEmpty(config.constItem.subConvertUrl) ? Global.SubConvertUrls.FirstOrDefault() : config.constItem.subConvertUrl;
|
||||
url = string.Format(subConvertUrl!, Utils.UrlEncode(url));
|
||||
@@ -189,9 +189,9 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
|
||||
//more url
|
||||
if (Utils.IsNullOrEmpty(item.convertTarget) && !Utils.IsNullOrEmpty(item.moreUrl.TrimEx()))
|
||||
if (Utils.IsNullOrEmpty(item.convertTarget) && Utils.IsNotEmpty(item.moreUrl.TrimEx()))
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(result) && Utils.IsBase64String(result!))
|
||||
if (Utils.IsNotEmpty(result) && Utils.IsBase64String(result!))
|
||||
{
|
||||
result = Utils.Base64Decode(result);
|
||||
}
|
||||
@@ -210,7 +210,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
result2 = await downloadHandle.TryDownloadString(url2, false, userAgent);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(result2))
|
||||
if (Utils.IsNotEmpty(result2))
|
||||
{
|
||||
if (Utils.IsBase64String(result2!))
|
||||
{
|
||||
@@ -261,14 +261,10 @@ namespace ServiceLib.Handler
|
||||
_updateFunc(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
|
||||
}
|
||||
|
||||
public void RunAvailabilityCheck(Action<bool, string> update)
|
||||
public async Task RunAvailabilityCheck(Action<bool, string> update)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var time = await (new DownloadHandler()).RunAvailabilityCheck(null);
|
||||
|
||||
update(false, string.Format(ResUI.TestMeOutput, time));
|
||||
});
|
||||
var time = await (new DownloadHandler()).RunAvailabilityCheck(null);
|
||||
update(false, string.Format(ResUI.TestMeOutput, time));
|
||||
}
|
||||
|
||||
#region private
|
||||
@@ -281,7 +277,7 @@ namespace ServiceLib.Handler
|
||||
var url = coreInfo?.coreReleaseApiUrl;
|
||||
|
||||
var result = await downloadHandle.DownloadStringAsync(url, true, Global.AppName);
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
if (Utils.IsNotEmpty(result))
|
||||
{
|
||||
return await ParseDownloadUrl(type, result, preRelease);
|
||||
}
|
||||
@@ -437,6 +433,15 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
//Check for standalone windows .Net version
|
||||
if (coreInfo?.coreType == ECoreType.v2rayN
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
|
||||
)
|
||||
{
|
||||
return coreInfo?.coreDownloadUrl64.Replace("v2rayN.zip", "zz_v2rayN-SelfContained.zip");
|
||||
}
|
||||
|
||||
return RuntimeInformation.ProcessArchitecture switch
|
||||
{
|
||||
Architecture.Arm64 => coreInfo?.coreDownloadUrlArm64,
|
||||
|
||||
171
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
171
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System.Net;
|
||||
using WebDav;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class WebDavHandler
|
||||
{
|
||||
private static readonly Lazy<WebDavHandler> _instance = new(() => new());
|
||||
public static WebDavHandler Instance => _instance.Value;
|
||||
|
||||
private Config? _config;
|
||||
private WebDavClient? _client;
|
||||
private string? _lastDescription;
|
||||
private string _webDir = Global.AppName + "_backup";
|
||||
private string _webFileName = "backup.zip";
|
||||
private string _logTitle = "WebDav--";
|
||||
|
||||
public WebDavHandler()
|
||||
{
|
||||
_config = LazyConfig.Instance.Config;
|
||||
}
|
||||
|
||||
private async Task<bool> GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_config.webDavItem.url.IsNullOrEmpty()
|
||||
|| _config.webDavItem.userName.IsNullOrEmpty()
|
||||
|| _config.webDavItem.password.IsNullOrEmpty())
|
||||
{
|
||||
throw new ArgumentException("webdav parameter error or null");
|
||||
}
|
||||
if (_client != null)
|
||||
{
|
||||
_client?.Dispose();
|
||||
_client = null;
|
||||
}
|
||||
if (_config.webDavItem.dirName.IsNullOrEmpty())
|
||||
{
|
||||
_webDir = Global.AppName + "_backup";
|
||||
}
|
||||
else
|
||||
{
|
||||
_webDir = _config.webDavItem.dirName.TrimEx();
|
||||
}
|
||||
|
||||
var clientParams = new WebDavClientParams
|
||||
{
|
||||
BaseAddress = new Uri(_config.webDavItem.url),
|
||||
Credentials = new NetworkCredential(_config.webDavItem.userName, _config.webDavItem.password)
|
||||
};
|
||||
_client = new WebDavClient(clientParams);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
return false;
|
||||
}
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
private async Task<bool> TryCreateDir()
|
||||
{
|
||||
if (_client is null) return false;
|
||||
try
|
||||
{
|
||||
var result2 = await _client.Mkcol(_webDir);
|
||||
if (result2.IsSuccessful)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
SaveLog(result2.Description);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SaveLog(string desc)
|
||||
{
|
||||
_lastDescription = desc;
|
||||
Logging.SaveLog(_logTitle + desc);
|
||||
}
|
||||
|
||||
private void SaveLog(Exception ex)
|
||||
{
|
||||
_lastDescription = ex.Message;
|
||||
Logging.SaveLog(_logTitle, ex);
|
||||
}
|
||||
|
||||
public async Task<bool> CheckConnection()
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
var testName = "readme_test";
|
||||
var myContent = new StringContent(testName);
|
||||
var result = await _client.PutFile($"{_webDir}/{testName}", myContent);
|
||||
if (result.IsSuccessful)
|
||||
{
|
||||
await _client.Delete($"{_webDir}/{testName}");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveLog(result.Description);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> PutFile(string fileName)
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
using var fs = File.OpenRead(fileName);
|
||||
var result = await _client.PutFile($"{_webDir}/{_webFileName}", fs); // upload a resource
|
||||
if (result.IsSuccessful)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SaveLog(result.Description);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> GetRawFile(string fileName)
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _client.GetRawFile($"{_webDir}/{_webFileName}");
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
SaveLog(response.Description);
|
||||
return false;
|
||||
}
|
||||
using var outputFileStream = new FileStream(fileName, FileMode.Create);
|
||||
response.Stream.CopyTo(outputFileStream);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetLastError() => _lastDescription ?? string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
public HysteriaItem hysteriaItem { get; set; }
|
||||
public ClashUIItem clashUIItem { get; set; }
|
||||
public SystemProxyItem systemProxyItem { get; set; }
|
||||
public WebDavItem webDavItem { get; set; }
|
||||
public List<InItem> inbound { get; set; }
|
||||
public List<KeyEventItem> globalHotkeys { get; set; }
|
||||
public List<CoreTypeItem> coreTypeItem { get; set; }
|
||||
|
||||
@@ -248,4 +248,13 @@
|
||||
public bool notProxyLocalAddress { get; set; } = true;
|
||||
public string systemProxyAdvancedProtocol { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WebDavItem
|
||||
{
|
||||
public string? url { get; set; }
|
||||
public string? userName { get; set; }
|
||||
public string? password { get; set; }
|
||||
public string? dirName { get; set; }
|
||||
}
|
||||
}
|
||||
126
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
126
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -105,6 +105,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Host filter 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string ConnectionsHostFilterTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionsHostFilterTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Note that custom configuration relies entirely on your own configuration and does not work with all settings. If you want to use the system proxy, please modify the listening port manually. 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -321,6 +330,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Invalid backup file 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LocalRestoreInvalidZipTips {
|
||||
get {
|
||||
return ResourceManager.GetString("LocalRestoreInvalidZipTips", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Address 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -582,6 +600,51 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 WebDav Check 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavCheck {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavCheck", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Remote folder name (optional) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavDirName {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavDirName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 WebDav Password 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavPassword {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 WebDav Url 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavUrl {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavUrl", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 WebDav User Name 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavUserName {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavUserName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add a custom configuration server 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -690,6 +753,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Backup and Restore 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuBackupAndRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuBackupAndRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Check Update 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -861,6 +933,33 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Backup to local 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuLocalBackup {
|
||||
get {
|
||||
return ResourceManager.GetString("menuLocalBackup", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Local 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuLocalBackupAndRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuLocalBackupAndRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Restore from local 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuLocalRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuLocalRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 One-click multi test Latency and speed (Ctrl+E) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -1095,6 +1194,33 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Backup to remote (WebDAV) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuRemoteBackup {
|
||||
get {
|
||||
return ResourceManager.GetString("menuRemoteBackup", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Remote (WebDAV) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuRemoteBackupAndRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuRemoteBackupAndRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Restore from remote (WebDAV) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuRemoteRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuRemoteRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Remove duplicate servers 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -1279,4 +1279,46 @@
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>Custom config socks port</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>Backup and Restore</value>
|
||||
</data>
|
||||
<data name="menuLocalBackup" xml:space="preserve">
|
||||
<value>Backup to local</value>
|
||||
</data>
|
||||
<data name="menuLocalRestore" xml:space="preserve">
|
||||
<value>Restore from local</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>Backup to remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>Restore from remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>Local</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>Remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav Url</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav User Name</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav Password</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav Check</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>Remote folder name (optional)</value>
|
||||
</data>
|
||||
<data name="LocalRestoreInvalidZipTips" xml:space="preserve">
|
||||
<value>Invalid backup file</value>
|
||||
</data>
|
||||
<data name="ConnectionsHostFilterTitle" xml:space="preserve">
|
||||
<value>Host filter</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1276,4 +1276,46 @@
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>自定义配置的Socks端口</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>备份和还原</value>
|
||||
</data>
|
||||
<data name="menuLocalBackup" xml:space="preserve">
|
||||
<value>备份到本地</value>
|
||||
</data>
|
||||
<data name="menuLocalRestore" xml:space="preserve">
|
||||
<value>从本地恢复</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>备份到远程 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>从远程恢复 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>本地</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>远程 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav 账户</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav 可用检查</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav 密码</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav 服务器地址</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>远程文件夹名称(可选)</value>
|
||||
</data>
|
||||
<data name="LocalRestoreInvalidZipTips" xml:space="preserve">
|
||||
<value>无效备份文件</value>
|
||||
</data>
|
||||
<data name="ConnectionsHostFilterTitle" xml:space="preserve">
|
||||
<value>主机过滤器</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1156,4 +1156,46 @@
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>自訂配置的Socks端口</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>備份和還原</value>
|
||||
</data>
|
||||
<data name="menuLocalBackup" xml:space="preserve">
|
||||
<value>備份到本地</value>
|
||||
</data>
|
||||
<data name="menuLocalRestore" xml:space="preserve">
|
||||
<value>從本地恢復</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>備份到遠端 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>從遠端恢復 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>本地</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>遠端 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav 賬戶</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav 可用檢查</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav 密碼</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav 服務器地址</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>遠端資料夾名稱(可選)</value>
|
||||
</data>
|
||||
<data name="LocalRestoreInvalidZipTips" xml:space="preserve">
|
||||
<value>無效備份文件</value>
|
||||
</data>
|
||||
<data name="ConnectionsHostFilterTitle" xml:space="preserve">
|
||||
<value>主機過濾器</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -4,7 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>6.57</Version>
|
||||
<Version>6.59.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -13,7 +13,8 @@
|
||||
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
<PackageReference Include="Splat.NLog" Version="15.1.1" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.1.0" />
|
||||
<PackageReference Include="WebDav.Client" Version="2.8.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.1.2" />
|
||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace ServiceLib.ViewModels
|
||||
if (ConfigHandler.AddCustomServer(_config, item, false) == 0)
|
||||
{
|
||||
_noticeHandler?.Enqueue(ResUI.SuccessfullyImportedCustomServer);
|
||||
if (!Utils.IsNullOrEmpty(item.indexId))
|
||||
if (Utils.IsNotEmpty(item.indexId))
|
||||
{
|
||||
SelectedSource = JsonUtils.DeepCopy(item);
|
||||
}
|
||||
|
||||
166
v2rayN/ServiceLib/ViewModels/BackupAndRestoreViewModel.cs
Normal file
166
v2rayN/ServiceLib/ViewModels/BackupAndRestoreViewModel.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Reactive;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
{
|
||||
public class BackupAndRestoreViewModel : MyReactiveObject
|
||||
{
|
||||
private readonly string _guiConfigs = "guiConfigs";
|
||||
private static string BackupFileName => $"backup_{DateTime.Now:yyyyMMddHHmmss}.zip";
|
||||
|
||||
public ReactiveCommand<Unit, Unit> RemoteBackupCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RemoteRestoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> WebDavCheckCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public WebDavItem SelectedSource { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string OperationMsg { get; set; }
|
||||
|
||||
public BackupAndRestoreViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = LazyConfig.Instance.Config;
|
||||
_updateView = updateView;
|
||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||
|
||||
WebDavCheckCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await WebDavCheck();
|
||||
});
|
||||
|
||||
RemoteBackupCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RemoteBackup();
|
||||
});
|
||||
RemoteRestoreCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RemoteRestore();
|
||||
});
|
||||
|
||||
SelectedSource = JsonUtils.DeepCopy(_config.webDavItem);
|
||||
}
|
||||
|
||||
private void DisplayOperationMsg(string msg = "")
|
||||
{
|
||||
OperationMsg = msg;
|
||||
}
|
||||
|
||||
private async Task WebDavCheck()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
_config.webDavItem = SelectedSource;
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
|
||||
var result = await WebDavHandler.Instance.CheckConnection();
|
||||
if (result)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RemoteBackup()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var fileName = Utils.GetBackupPath(BackupFileName);
|
||||
var result = await CreateZipFileFromDirectory(fileName);
|
||||
if (result)
|
||||
{
|
||||
var result2 = await WebDavHandler.Instance.PutFile(fileName);
|
||||
if (result2)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
|
||||
private async Task RemoteRestore()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var fileName = Utils.GetTempPath(Utils.GetGUID());
|
||||
var result = await WebDavHandler.Instance.GetRawFile(fileName);
|
||||
if (result)
|
||||
{
|
||||
await LocalRestore(fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
|
||||
public async Task<bool> LocalBackup(string fileName)
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var result = await CreateZipFileFromDirectory(fileName);
|
||||
if (result)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task LocalRestore(string fileName)
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//exist
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//check
|
||||
var lstFiles = FileManager.GetFilesFromZip(fileName);
|
||||
if (lstFiles is null || !lstFiles.Where(t => t.Contains(_guiConfigs)).Any())
|
||||
{
|
||||
DisplayOperationMsg(ResUI.LocalRestoreInvalidZipTips);
|
||||
return;
|
||||
}
|
||||
|
||||
//backup first
|
||||
var fileBackup = Utils.GetBackupPath(BackupFileName);
|
||||
var result = await CreateZipFileFromDirectory(fileBackup);
|
||||
if (result)
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.V2rayUpgrade(fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> CreateZipFileFromDirectory(string fileName)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var configDir = Utils.GetConfigPath();
|
||||
var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}");
|
||||
var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs);
|
||||
|
||||
await Task.Run(() => FileManager.CopyDirectory(configDir, configDirTemp, false, "cache.db"));
|
||||
var ret = await Task.Run(() => FileManager.CreateFromDirectory(configDirZipTemp, fileName));
|
||||
await Task.Run(() => Directory.Delete(configDirZipTemp, true));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Diagnostics;
|
||||
using System.Reactive;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
@@ -11,6 +10,7 @@ namespace ServiceLib.ViewModels
|
||||
public class CheckUpdateViewModel : MyReactiveObject
|
||||
{
|
||||
private const string _geo = "GeoFiles";
|
||||
private string _v2rayN = ECoreType.v2rayN.ToString();
|
||||
private List<CheckUpdateItem> _lstUpdated = [];
|
||||
|
||||
private IObservableCollection<CheckUpdateItem> _checkUpdateItem = new ObservableCollectionExtended<CheckUpdateItem>();
|
||||
@@ -52,7 +52,7 @@ namespace ServiceLib.ViewModels
|
||||
_checkUpdateItem.Add(new CheckUpdateItem()
|
||||
{
|
||||
isSelected = false,
|
||||
coreType = ECoreType.v2rayN.ToString(),
|
||||
coreType = _v2rayN,
|
||||
remarks = ResUI.menuCheckUpdate,
|
||||
});
|
||||
_checkUpdateItem.Add(new CheckUpdateItem()
|
||||
@@ -98,7 +98,7 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
await CheckUpdateGeo();
|
||||
}
|
||||
else if (item.coreType == ECoreType.v2rayN.ToString())
|
||||
else if (item.coreType == _v2rayN)
|
||||
{
|
||||
await CheckUpdateN(EnableCheckPreReleaseUpdate);
|
||||
}
|
||||
@@ -147,29 +147,29 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
private async Task CheckUpdateN(bool preRelease)
|
||||
{
|
||||
//Check for standalone windows .Net version
|
||||
if (Utils.IsWindows()
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
|
||||
)
|
||||
{
|
||||
UpdateView(ResUI.UpdateStandalonePackageTip, ResUI.UpdateStandalonePackageTip);
|
||||
return;
|
||||
}
|
||||
////Check for standalone windows .Net version
|
||||
//if (Utils.IsWindows()
|
||||
// && File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
|
||||
// && File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
|
||||
// )
|
||||
//{
|
||||
// UpdateView(_v2rayN, ResUI.UpdateStandalonePackageTip);
|
||||
// return;
|
||||
//}
|
||||
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
UpdateView(ECoreType.v2rayN.ToString(), msg);
|
||||
UpdateView(_v2rayN, msg);
|
||||
if (success)
|
||||
{
|
||||
UpdateView(ECoreType.v2rayN.ToString(), ResUI.OperationSuccess);
|
||||
UpdatedPlusPlus(ECoreType.v2rayN.ToString(), msg);
|
||||
UpdateView(_v2rayN, ResUI.OperationSuccess);
|
||||
UpdatedPlusPlus(_v2rayN, msg);
|
||||
}
|
||||
}
|
||||
await (new UpdateHandler()).CheckUpdateGuiN(_config, _updateUI, preRelease)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(ECoreType.v2rayN.ToString(), "");
|
||||
UpdatedPlusPlus(_v2rayN, "");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace ServiceLib.ViewModels
|
||||
Task.Delay(1000);
|
||||
UpgradeCore();
|
||||
|
||||
if (_lstUpdated.Any(x => x.coreType == ECoreType.v2rayN.ToString() && x.isFinished == true))
|
||||
if (_lstUpdated.Any(x => x.coreType == _v2rayN && x.isFinished == true))
|
||||
{
|
||||
Task.Delay(1000);
|
||||
UpgradeN();
|
||||
@@ -228,30 +228,16 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileName = _lstUpdated.FirstOrDefault(x => x.coreType == ECoreType.v2rayN.ToString())?.fileName;
|
||||
var fileName = _lstUpdated.FirstOrDefault(x => x.coreType == _v2rayN)?.fileName;
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "v2rayUpgrade",
|
||||
Arguments = fileName.AppendQuotes(),
|
||||
WorkingDirectory = Utils.StartupPath()
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
if (process.Id > 0)
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.MyAppExitAsync(false);
|
||||
}
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.V2rayUpgrade(fileName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UpdateView(ECoreType.v2rayN.ToString(), ex.Message);
|
||||
UpdateView(_v2rayN, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ namespace ServiceLib.ViewModels
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseAllCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public string HostFilter { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int SortingSelected { get; set; }
|
||||
|
||||
@@ -34,7 +37,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && !string.IsNullOrEmpty(selectedSource.id));
|
||||
selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.id));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SortingSelected,
|
||||
@@ -77,7 +80,7 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
var lastTime = DateTime.Now;
|
||||
|
||||
Observable.Interval(TimeSpan.FromSeconds(10))
|
||||
Observable.Interval(TimeSpan.FromSeconds(5))
|
||||
.Subscribe(x =>
|
||||
{
|
||||
if (!(AutoRefresh && _config.uiItem.showInTaskbar && _config.IsRunningCore(ECoreType.clash)))
|
||||
@@ -118,12 +121,18 @@ namespace ServiceLib.ViewModels
|
||||
var lstModel = new List<ClashConnectionModel>();
|
||||
foreach (var item in connections ?? [])
|
||||
{
|
||||
var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ClashConnectionModel model = new();
|
||||
|
||||
model.id = item.id;
|
||||
model.network = item.metadata.network;
|
||||
model.type = item.metadata.type;
|
||||
model.host = $"{(string.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||
model.host = host;
|
||||
var sp = (dtNow - item.start);
|
||||
model.time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
|
||||
model.upload = item.upload;
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedGroup,
|
||||
y => y != null && !string.IsNullOrEmpty(y.name))
|
||||
y => y != null && Utils.IsNotEmpty(y.name))
|
||||
.Subscribe(c => RefreshProxyDetails(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
@@ -135,7 +135,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
private void UpdateHandler(bool notify, string msg)
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg, true);
|
||||
_noticeHandler?.SendMessageEx(msg);
|
||||
}
|
||||
|
||||
public void ProxiesReload()
|
||||
@@ -194,7 +194,7 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
foreach (var it in proxyGroups)
|
||||
{
|
||||
if (string.IsNullOrEmpty(it.name) || !_proxies.ContainsKey(it.name))
|
||||
if (Utils.IsNullOrEmpty(it.name) || !_proxies.ContainsKey(it.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -220,7 +220,7 @@ namespace ServiceLib.ViewModels
|
||||
continue;
|
||||
}
|
||||
var item = _proxyGroups.Where(t => t.name == kv.Key).FirstOrDefault();
|
||||
if (item != null && !string.IsNullOrEmpty(item.name))
|
||||
if (item != null && Utils.IsNotEmpty(item.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -257,7 +257,7 @@ namespace ServiceLib.ViewModels
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup?.name;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
if (Utils.IsNullOrEmpty(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -342,21 +342,21 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
public void SetActiveProxy()
|
||||
{
|
||||
if (SelectedGroup == null || string.IsNullOrEmpty(SelectedGroup.name))
|
||||
if (SelectedGroup == null || Utils.IsNullOrEmpty(SelectedGroup.name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedDetail == null || string.IsNullOrEmpty(SelectedDetail.name))
|
||||
if (SelectedDetail == null || Utils.IsNullOrEmpty(SelectedDetail.name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup.name;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
if (Utils.IsNullOrEmpty(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var nameNode = SelectedDetail.name;
|
||||
if (string.IsNullOrEmpty(nameNode))
|
||||
if (Utils.IsNullOrEmpty(nameNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -393,7 +393,7 @@ namespace ServiceLib.ViewModels
|
||||
GetClashProxies(true);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(result))
|
||||
if (Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(normalDNS))
|
||||
if (Utils.IsNotEmpty(normalDNS))
|
||||
{
|
||||
var obj = JsonUtils.ParseJson(normalDNS);
|
||||
if (obj != null && obj["servers"] != null)
|
||||
@@ -73,7 +73,7 @@ namespace ServiceLib.ViewModels
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(normalDNS2))
|
||||
if (Utils.IsNotEmpty(normalDNS2))
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(normalDNS2);
|
||||
if (obj2 == null)
|
||||
@@ -82,7 +82,7 @@ namespace ServiceLib.ViewModels
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(tunDNS2))
|
||||
if (Utils.IsNotEmpty(tunDNS2))
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(tunDNS2);
|
||||
if (obj2 == null)
|
||||
|
||||
@@ -71,13 +71,6 @@ namespace ServiceLib.ViewModels
|
||||
public ReactiveCommand<Unit, Unit> ClearServerStatisticsCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> OpenTheFileLocationCmd { get; }
|
||||
|
||||
//CheckUpdate
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateNCmd { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateXrayCoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateClashMetaCoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateSingBoxCoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateGeoCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ReloadCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
@@ -295,28 +288,6 @@ namespace ServiceLib.ViewModels
|
||||
Utils.ProcessStart("Explorer", $"/select,{Utils.GetConfigPath()}");
|
||||
});
|
||||
|
||||
//CheckUpdate
|
||||
CheckUpdateNCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateN();
|
||||
});
|
||||
CheckUpdateXrayCoreCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateCore(ECoreType.Xray, null);
|
||||
});
|
||||
CheckUpdateClashMetaCoreCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateCore(ECoreType.mihomo, false);
|
||||
});
|
||||
CheckUpdateSingBoxCoreCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateCore(ECoreType.sing_box, null);
|
||||
});
|
||||
CheckUpdateGeoCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateGeo();
|
||||
});
|
||||
|
||||
ReloadCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
Reload();
|
||||
@@ -378,10 +349,6 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
private void UpdateHandler(bool notify, string msg)
|
||||
{
|
||||
if (!_config.uiItem.showInTaskbar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_noticeHandler?.SendMessage(msg);
|
||||
if (notify)
|
||||
{
|
||||
@@ -391,7 +358,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
private void UpdateTaskHandler(bool success, string msg)
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg);
|
||||
_noticeHandler?.SendMessageEx(msg);
|
||||
if (success)
|
||||
{
|
||||
var indexIdOld = _config.indexId;
|
||||
@@ -457,6 +424,24 @@ namespace ServiceLib.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public async Task V2rayUpgrade(string fileName)
|
||||
{
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "v2rayUpgrade",
|
||||
Arguments = fileName.AppendQuotes(),
|
||||
WorkingDirectory = Utils.StartupPath()
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
if (process.Id > 0)
|
||||
{
|
||||
await MyAppExitAsync(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Actions
|
||||
|
||||
#region Servers && Groups
|
||||
@@ -622,21 +607,16 @@ namespace ServiceLib.ViewModels
|
||||
SetDefaultServer(SelectedServer.ID);
|
||||
}
|
||||
|
||||
public void TestServerAvailability()
|
||||
public async Task TestServerAvailability()
|
||||
{
|
||||
var item = ConfigHandler.GetDefaultServer(_config);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
(new UpdateHandler()).RunAvailabilityCheck(async (bool success, string msg) =>
|
||||
await (new UpdateHandler()).RunAvailabilityCheck(async (bool success, string msg) =>
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg, true);
|
||||
|
||||
if (!_config.uiItem.showInTaskbar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_noticeHandler?.SendMessageEx(msg);
|
||||
await _updateView?.Invoke(EViewAction.DispatcherServerAvailability, msg);
|
||||
});
|
||||
}
|
||||
@@ -718,99 +698,6 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
#endregion Setting
|
||||
|
||||
#region CheckUpdate
|
||||
|
||||
private void CheckUpdateN()
|
||||
{
|
||||
//Check for standalone windows .Net version
|
||||
if (Utils.IsWindows()
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
|
||||
)
|
||||
{
|
||||
_noticeHandler?.SendMessageAndEnqueue(ResUI.UpdateStandalonePackageTip);
|
||||
return;
|
||||
}
|
||||
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg);
|
||||
if (success)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileName = msg;
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "v2rayUpgrade",
|
||||
Arguments = fileName.AppendQuotes(),
|
||||
WorkingDirectory = Utils.StartupPath()
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
if (process.Id > 0)
|
||||
{
|
||||
MyAppExitAsync(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_noticeHandler?.SendMessage(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
(new UpdateHandler()).CheckUpdateGuiN(_config, _updateUI, _config.guiItem.checkPreReleaseUpdate);
|
||||
}
|
||||
|
||||
private void CheckUpdateCore(ECoreType type, bool? preRelease)
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg);
|
||||
if (success)
|
||||
{
|
||||
CloseCore();
|
||||
|
||||
string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(msg));
|
||||
string toPath = Utils.GetBinPath("", type.ToString());
|
||||
|
||||
if (fileName.Contains(".tar.gz"))
|
||||
{
|
||||
//It's too complicated to unzip. TODO
|
||||
}
|
||||
else if (fileName.Contains(".gz"))
|
||||
{
|
||||
FileManager.UncompressedFile(fileName, toPath, type.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
FileManager.ZipExtractToFile(fileName, toPath, _config.guiItem.ignoreGeoUpdateCore ? "geo" : "");
|
||||
}
|
||||
|
||||
_noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfullyMore);
|
||||
|
||||
Reload();
|
||||
|
||||
_noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfully);
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
(new UpdateHandler()).CheckUpdateCore(type, _config, _updateUI, preRelease ?? _config.guiItem.checkPreReleaseUpdate);
|
||||
}
|
||||
|
||||
private void CheckUpdateGeo()
|
||||
{
|
||||
(new UpdateHandler()).UpdateGeoFileAll(_config, UpdateTaskHandler);
|
||||
}
|
||||
|
||||
#endregion CheckUpdate
|
||||
|
||||
#region core job
|
||||
|
||||
public void Reload()
|
||||
@@ -819,7 +706,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
LoadCore().ContinueWith(async task =>
|
||||
{
|
||||
TestServerAvailability();
|
||||
await TestServerAvailability();
|
||||
|
||||
await _updateView?.Invoke(EViewAction.DispatcherReload, null);
|
||||
});
|
||||
@@ -881,7 +768,7 @@ namespace ServiceLib.ViewModels
|
||||
private async Task ChangeSystemProxyStatusAsync(ESysProxyType type, bool blChange)
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.UpdateSysProxy, _config.tunModeItem.enableTun ? true : false);
|
||||
_noticeHandler?.SendMessage($"{ResUI.TipChangeSystemProxy} - {_config.systemProxyItem.sysProxyType.ToString()}", true);
|
||||
_noticeHandler?.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.systemProxyItem.sysProxyType.ToString()}");
|
||||
|
||||
BlSystemProxyClear = (type == ESysProxyType.ForcedClear);
|
||||
BlSystemProxySet = (type == ESysProxyType.ForcedChange);
|
||||
@@ -941,7 +828,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
if (ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||
{
|
||||
_noticeHandler?.SendMessage(ResUI.TipChangeRouting, true);
|
||||
_noticeHandler?.SendMessageEx(ResUI.TipChangeRouting);
|
||||
Reload();
|
||||
await _updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,10 @@ namespace ServiceLib.ViewModels
|
||||
}
|
||||
|
||||
_blLockShow = true;
|
||||
if (!_config.uiItem.showInTaskbar)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
await Task.Delay(100);
|
||||
var txt = string.Join("", _queueMsg.ToArray());
|
||||
@@ -72,7 +76,7 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
//filter msg
|
||||
if (MsgFilter != _lastMsgFilter) _lastMsgFilterNotAvailable = false;
|
||||
if (!Utils.IsNullOrEmpty(MsgFilter) && !_lastMsgFilterNotAvailable)
|
||||
if (Utils.IsNotEmpty(MsgFilter) && !_lastMsgFilterNotAvailable)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -258,20 +258,20 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(result.IndexId))
|
||||
{
|
||||
_noticeHandler?.SendMessage(result.Delay, true);
|
||||
_noticeHandler?.SendMessageEx(result.Delay);
|
||||
_noticeHandler?.Enqueue(result.Delay);
|
||||
return;
|
||||
}
|
||||
var item = _profileItems.Where(it => it.indexId == result.IndexId).FirstOrDefault();
|
||||
if (item != null)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(result.Delay))
|
||||
if (Utils.IsNotEmpty(result.Delay))
|
||||
{
|
||||
int.TryParse(result.Delay, out int temp);
|
||||
item.delay = temp;
|
||||
item.delayVal = $"{result.Delay} {Global.DelayUnit}";
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(result.Speed))
|
||||
if (Utils.IsNotEmpty(result.Speed))
|
||||
{
|
||||
item.speedVal = $"{result.Speed} {Global.SpeedUnit}";
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace ServiceLib.ViewModels
|
||||
|| SelectedSource.ip?.Count > 0
|
||||
|| SelectedSource.protocol?.Count > 0
|
||||
|| SelectedSource.process?.Count > 0
|
||||
|| !Utils.IsNullOrEmpty(SelectedSource.port);
|
||||
|| Utils.IsNotEmpty(SelectedSource.port);
|
||||
|
||||
if (!hasRule)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
|
||||
<TrayIcon.Icons>
|
||||
<TrayIcons>
|
||||
<TrayIcon Clicked="TrayIcon_Clicked" Icon="/Assets/NotifyIcon1.ico">
|
||||
<TrayIcon
|
||||
Clicked="TrayIcon_Clicked"
|
||||
Icon="/Assets/NotifyIcon1.ico"
|
||||
ToolTipText="v2rayN Desktop">
|
||||
<TrayIcon.Menu>
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Click="MenuAddServerViaClipboardClick" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
|
||||
|
||||
@@ -5,22 +5,6 @@
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
|
||||
<Style Selector="TextBlock.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
<Style Selector="StackPanel.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
<Style Selector="DockPanel.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
<Style Selector="WrapPanel.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
<Style Selector="Grid.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="TextBlock.Margin8">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace v2rayN.Desktop.ViewModels
|
||||
y => y != null && !y.IsNullOrEmpty())
|
||||
.Subscribe(c =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(CurrentLanguage) && _config.uiItem.currentLanguage != CurrentLanguage)
|
||||
if (Utils.IsNotEmpty(CurrentLanguage) && _config.uiItem.currentLanguage != CurrentLanguage)
|
||||
{
|
||||
_config.uiItem.currentLanguage = CurrentLanguage;
|
||||
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
@@ -57,14 +57,14 @@
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.menuServers}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbRemarks}" />
|
||||
|
||||
<TextBox
|
||||
@@ -74,13 +74,13 @@
|
||||
Width="400"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbAddress}" />
|
||||
<TextBox
|
||||
x:Name="txtAddress"
|
||||
@@ -89,7 +89,7 @@
|
||||
Width="400"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
IsReadOnly="True" />
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
@@ -110,7 +110,7 @@
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbCoreType}" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType"
|
||||
@@ -118,24 +118,24 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
MaxDropDownHeight="1000" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbDisplayLog}" />
|
||||
<StackPanel
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Orientation="Horizontal">
|
||||
<ToggleSwitch
|
||||
x:Name="togDisplayLog"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
@@ -146,7 +146,7 @@
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbPreSocksPort}" />
|
||||
<TextBox
|
||||
x:Name="txtPreSocksPort"
|
||||
@@ -154,12 +154,12 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
<StackPanel
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Classes="Margin4">
|
||||
Classes="Margin8">
|
||||
<TextBlock
|
||||
Width="500"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
96
v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml
Normal file
96
v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml
Normal file
@@ -0,0 +1,96 @@
|
||||
<UserControl
|
||||
x:Class="v2rayN.Desktop.Views.CheckUpdateView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
x:DataType="vms:CheckUpdateViewModel"
|
||||
mc:Ignorable="d">
|
||||
<Button Classes="Tertiary">
|
||||
<Button.Content>
|
||||
<TextBlock Text="{x:Static resx:ResUI.menuCheckUpdate}" />
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<Flyout Placement="RightEdgeAlignedTop">
|
||||
<DockPanel Margin="16">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Right"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableCheckPreReleaseUpdate"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin8" />
|
||||
|
||||
<Button
|
||||
x:Name="btnCheckUpdate"
|
||||
Width="100"
|
||||
Classes="Margin8"
|
||||
Content="{x:Static resx:ResUI.menuCheckUpdate}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel>
|
||||
<ListBox
|
||||
x:Name="lstCheckUpdates"
|
||||
BorderThickness="1"
|
||||
ItemsSource="{Binding CheckUpdateItems}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Width="500"
|
||||
Height="80"
|
||||
Margin="0"
|
||||
VerticalAlignment="Center"
|
||||
Theme="{StaticResource CardBorder}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="3*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ToggleSwitch
|
||||
x:Name="togAutoRefresh"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding isSelected}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding coreType}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding remarks}"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListBox>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</UserControl>
|
||||
48
v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml.cs
Normal file
48
v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using System.Reactive.Disposables;
|
||||
|
||||
namespace v2rayN.Desktop.Views
|
||||
{
|
||||
public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
|
||||
{
|
||||
public CheckUpdateView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ViewModel = new CheckUpdateViewModel(UpdateViewHandler);
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.OneWayBind(ViewModel, vm => vm.CheckUpdateItems, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.IsCheckUpdate, v => v.btnCheckUpdate.IsEnabled).DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case EViewAction.DispatcherCheckUpdate:
|
||||
if (obj is null) return false;
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
ViewModel?.UpdateViewResult((CheckUpdateItem)obj),
|
||||
DispatcherPriority.Default);
|
||||
break;
|
||||
|
||||
case EViewAction.DispatcherCheckUpdateFinished:
|
||||
if (obj is null) return false;
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
ViewModel?.UpdateFinishedResult((bool)obj),
|
||||
DispatcherPriority.Default);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,13 @@
|
||||
DockPanel.Dock="Top"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBox
|
||||
x:Name="txtHostFilter"
|
||||
Width="200"
|
||||
Margin="8,0"
|
||||
VerticalContentAlignment="Center"
|
||||
Watermark="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" />
|
||||
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace v2rayN.Desktop.Views
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseCmd, v => v.menuConnectionClose).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||
|
||||
@@ -92,7 +92,6 @@
|
||||
<DockPanel>
|
||||
<ListBox
|
||||
x:Name="lstProxyGroups"
|
||||
Margin="0,0,5,0"
|
||||
DockPanel.Dock="Left"
|
||||
ItemsSource="{Binding ProxyGroups}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
@@ -104,20 +103,21 @@
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Width="160"
|
||||
Margin="4,0"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
Theme="{StaticResource CardBorder}">
|
||||
<DockPanel>
|
||||
<Grid Grid.Column="0" Classes="Margin4">
|
||||
<Grid Grid.Column="0" Classes="Margin8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="8" />
|
||||
<RowDefinition Height="1*" />
|
||||
</Grid.RowDefinitions>
|
||||
<DockPanel Grid.Row="0">
|
||||
<TextBlock DockPanel.Dock="Right" Text="{Binding type}" />
|
||||
<TextBlock Text="{Binding name}" />
|
||||
</DockPanel>
|
||||
<TextBlock Grid.Row="1" Text="{Binding now}" />
|
||||
<TextBlock Grid.Row="2" Text="{Binding now}" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
@@ -141,6 +141,7 @@
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Width="160"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
Theme="{StaticResource CardBorder}">
|
||||
<DockPanel>
|
||||
@@ -152,16 +153,17 @@
|
||||
CornerRadius="4"
|
||||
DockPanel.Dock="Left"
|
||||
IsVisible="{Binding isActive}" />
|
||||
<Grid Classes="Margin4">
|
||||
<Grid Classes="Margin8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="2*" />
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="8" />
|
||||
<RowDefinition Height="1*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Text="{Binding name}"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
<DockPanel Grid.Row="1">
|
||||
<DockPanel Grid.Row="2">
|
||||
<TextBlock
|
||||
DockPanel.Dock="Right"
|
||||
Foreground="{Binding Path=delay, Converter={StaticResource DelayColorConverter}}"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
@@ -43,9 +43,9 @@
|
||||
Text="{x:Static resx:ResUI.TbSettingsRemoteDNS}" />
|
||||
|
||||
<TextBlock VerticalAlignment="Center" Classes="Margin8">
|
||||
<Button Click="linkDnsObjectDoc_Click">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkDnsObjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbDnsObjectDoc}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<Button
|
||||
x:Name="btnImportDefConfig4V2ray"
|
||||
@@ -95,7 +95,7 @@
|
||||
BorderThickness="1"
|
||||
Classes="TextArea Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="Http/Socks" />
|
||||
Watermark="Http/Socks" />
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
@@ -103,9 +103,9 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Classes="Margin8">
|
||||
<Button Click="linkDnsSingboxObjectDoc_Click">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkDnsSingboxObjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbDnsSingboxObjectDoc}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<Button
|
||||
x:Name="btnImportDefConfig4Singbox"
|
||||
@@ -153,7 +153,7 @@
|
||||
BorderThickness="1"
|
||||
Classes="TextArea Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="Http/Socks" />
|
||||
Watermark="Http/Socks" />
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
BorderThickness="1"
|
||||
Classes="TextArea Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.TbSettingsTunMode}" />
|
||||
Watermark="{x:Static resx:ResUI.TbSettingsTunMode}" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
mc:Ignorable="d">
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
HorizontalAlignment="Center"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
@@ -60,13 +60,13 @@
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbGlobalHotkeySetting}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbDisplayGUI}" />
|
||||
|
||||
@@ -74,68 +74,68 @@
|
||||
x:Name="txtGlobalHotkey0"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbClearSystemProxy}" />
|
||||
<TextBox
|
||||
x:Name="txtGlobalHotkey1"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSetSystemProxy}" />
|
||||
<TextBox
|
||||
x:Name="txtGlobalHotkey2"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbNotChangeSystemProxy}" />
|
||||
<TextBox
|
||||
x:Name="txtGlobalHotkey3"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSystemProxyPac}" />
|
||||
<TextBox
|
||||
x:Name="txtGlobalHotkey4"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbGlobalHotkeySettingTip}" />
|
||||
</Grid>
|
||||
|
||||
@@ -87,16 +87,8 @@
|
||||
|
||||
<MenuItem Padding="8,0">
|
||||
<MenuItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{x:Static resx:ResUI.menuCheckUpdate}" />
|
||||
</StackPanel>
|
||||
<ContentControl x:Name="conCheckUpdate" />
|
||||
</MenuItem.Header>
|
||||
<MenuItem x:Name="menuCheckUpdateN" Header="V2rayN" />
|
||||
<MenuItem x:Name="menuCheckUpdateXrayCore" Header="Xray Core" />
|
||||
<MenuItem x:Name="menuCheckUpdateMihomoCore" Header="Mihomo Core" />
|
||||
<MenuItem x:Name="menuCheckUpdateSingBoxCore" Header="Sing-box Core" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuCheckUpdateGeo" Header="Geo files" />
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem x:Name="menuHelp" Padding="8,0">
|
||||
@@ -156,7 +148,8 @@
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableTun"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8"
|
||||
Theme="{StaticResource SimpleToggleSwitch}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
|
||||
@@ -72,13 +72,7 @@ namespace v2rayN.Desktop.Views
|
||||
this.BindCommand(ViewModel, vm => vm.ClearServerStatisticsCmd, v => v.menuClearServerStatistics).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.OpenTheFileLocationCmd, v => v.menuOpenTheFileLocation).DisposeWith(disposables);
|
||||
|
||||
//check update
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateNCmd, v => v.menuCheckUpdateN).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateXrayCoreCmd, v => v.menuCheckUpdateXrayCore).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateClashMetaCoreCmd, v => v.menuCheckUpdateMihomoCore).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateSingBoxCoreCmd, v => v.menuCheckUpdateSingBoxCore).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateGeoCmd, v => v.menuCheckUpdateGeo).DisposeWith(disposables);
|
||||
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
|
||||
|
||||
@@ -172,6 +166,7 @@ namespace v2rayN.Desktop.Views
|
||||
tabClashConnections2.Content ??= new ClashConnectionsView();
|
||||
}
|
||||
conTheme.Content ??= new ThemeSettingView();
|
||||
conCheckUpdate.Content ??= new CheckUpdateView();
|
||||
|
||||
RestoreUI();
|
||||
AddHelpMenuItem();
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
Width="200"
|
||||
Margin="8,0"
|
||||
VerticalContentAlignment="Center"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.MsgFilterTitle}" />
|
||||
Watermark="{x:Static resx:ResUI.MsgFilterTitle}" />
|
||||
|
||||
<Button
|
||||
x:Name="btnCopy"
|
||||
@@ -62,7 +62,8 @@
|
||||
x:Name="togAutoRefresh"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Left"
|
||||
IsChecked="True" />
|
||||
IsChecked="True"
|
||||
Theme="{StaticResource SimpleToggleSwitch}" />
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
@@ -71,7 +72,8 @@
|
||||
x:Name="togScrollToEnd"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Left"
|
||||
IsChecked="True" />
|
||||
IsChecked="True"
|
||||
Theme="{StaticResource SimpleToggleSwitch}" />
|
||||
</WrapPanel>
|
||||
<TextBox
|
||||
Name="txtMsg"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
@@ -113,7 +113,7 @@
|
||||
<ListBox
|
||||
x:Name="clbdestOverride"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
SelectionMode="Multiple"
|
||||
Theme="{DynamicResource PureCardRadioGroupListBox}" />
|
||||
</StackPanel>
|
||||
@@ -309,12 +309,12 @@
|
||||
x:Name="txtUpMbps"
|
||||
Width="90"
|
||||
Classes="Margin8"
|
||||
ToolTip.Tip="Up" />
|
||||
Watermark="Up" />
|
||||
<TextBox
|
||||
x:Name="txtDownMbps"
|
||||
Width="90"
|
||||
Classes="Margin8"
|
||||
ToolTip.Tip="Down" />
|
||||
Watermark="Down" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
Width="200"
|
||||
Margin="4,0"
|
||||
VerticalContentAlignment="Center"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.MsgServerTitle}" />
|
||||
Watermark="{x:Static resx:ResUI.MsgServerTitle}" />
|
||||
</WrapPanel>
|
||||
<DataGrid
|
||||
x:Name="lstProfiles"
|
||||
|
||||
@@ -32,27 +32,27 @@
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="outboundTag" />
|
||||
<ComboBox
|
||||
x:Name="cmbOutboundTag"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
MaxDropDownHeight="1000" />
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="port" />
|
||||
<TextBox
|
||||
x:Name="txtPort"
|
||||
@@ -60,29 +60,29 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4">
|
||||
<Button Click="linkRuleobjectDoc_Click">
|
||||
Classes="Margin8">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkRuleobjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="protocol" />
|
||||
<ListBox
|
||||
x:Name="clbProtocol"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
SelectionMode="Multiple"
|
||||
Theme="{DynamicResource PureCardRadioGroupListBox}" />
|
||||
|
||||
@@ -90,13 +90,13 @@
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="inboundTag" />
|
||||
<ListBox
|
||||
x:Name="clbInboundTag"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
SelectionMode="Multiple"
|
||||
Theme="{DynamicResource PureCardRadioGroupListBox}" />
|
||||
|
||||
@@ -104,34 +104,34 @@
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="network" />
|
||||
<ComboBox
|
||||
x:Name="cmbNetwork"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
MaxDropDownHeight="1000" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="enabled" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnabled"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbRoutingTips}" />
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
@@ -76,25 +76,25 @@
|
||||
Width="300"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvSort}" />
|
||||
<TextBox
|
||||
x:Name="txtSort"
|
||||
Width="100"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
@@ -103,22 +103,22 @@
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy"
|
||||
Width="200"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Singbox"
|
||||
Width="200"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvUrl}" />
|
||||
<TextBox
|
||||
x:Name="txtUrl"
|
||||
@@ -127,14 +127,14 @@
|
||||
Width="600"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvCustomIcon}" />
|
||||
<TextBox
|
||||
x:Name="txtCustomIcon"
|
||||
@@ -143,23 +143,23 @@
|
||||
Width="600"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="btnBrowseCustomIcon"
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Content="{x:Static resx:ResUI.TbBrowse}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4">
|
||||
<Button Click="linkCustomRulesetPath4Singbox">
|
||||
Classes="Margin8">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkCustomRulesetPath4Singbox">
|
||||
<TextBlock Text="{x:Static resx:ResUI.LvCustomRulesetPath4Singbox}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<TextBox
|
||||
x:Name="txtCustomRulesetPath4Singbox"
|
||||
@@ -168,13 +168,13 @@
|
||||
Width="600"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="btnBrowseCustomRulesetPath4Singbox"
|
||||
Grid.Row="4"
|
||||
Grid.Column="2"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Content="{x:Static resx:ResUI.TbBrowse}" />
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
</Menu>
|
||||
|
||||
<TextBlock Margin="8,0,0,0" VerticalAlignment="Center">
|
||||
<Button Click="linkdomainStrategy_Click">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy"
|
||||
@@ -45,9 +45,9 @@
|
||||
Margin="8,0,0,0" />
|
||||
<Separator />
|
||||
<TextBlock Margin="8,0,0,0" VerticalAlignment="Center">
|
||||
<Button Click="linkdomainStrategy4Singbox_Click">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy4Singbox_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Singbox"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
@@ -60,14 +60,14 @@
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.menuSubscription}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvRemarks}" />
|
||||
|
||||
<TextBox
|
||||
@@ -75,53 +75,53 @@
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvUrl}" />
|
||||
<TextBox
|
||||
x:Name="txtUrl"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvEnabled}" />
|
||||
|
||||
<DockPanel
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4">
|
||||
Classes="Margin8">
|
||||
<ToggleSwitch
|
||||
x:Name="togEnable"
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Left" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtAutoUpdateInterval"
|
||||
Width="200"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Right"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvAutoUpdateInterval}" />
|
||||
</DockPanel>
|
||||
|
||||
@@ -129,21 +129,21 @@
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvFilter}" />
|
||||
<TextBox
|
||||
x:Name="txtFilter"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Classes="Margin8"
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvConvertTarget}" />
|
||||
<ComboBox
|
||||
x:Name="cmbConvertTarget"
|
||||
@@ -151,29 +151,29 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
VerticalAlignment=" "
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.LvConvertTargetTip}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvUserAgent}" />
|
||||
<TextBox
|
||||
x:Name="txtUserAgent"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvSort}" />
|
||||
<TextBox
|
||||
x:Name="txtSort"
|
||||
@@ -181,42 +181,42 @@
|
||||
Grid.Column="1"
|
||||
Width="100"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvPrevProfile}" />
|
||||
<TextBox
|
||||
x:Name="txtPrevProfile"
|
||||
Grid.Row="9"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
Classes="Margin8"
|
||||
Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="10"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvNextProfile}" />
|
||||
<TextBox
|
||||
x:Name="txtNextProfile"
|
||||
Grid.Row="10"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
Classes="Margin8"
|
||||
Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="11"
|
||||
Grid.Column="0"
|
||||
Margin="4"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbPreSocksPort4Sub}" />
|
||||
<TextBox
|
||||
x:Name="txtPreSocksPort"
|
||||
@@ -225,15 +225,16 @@
|
||||
Width="200"
|
||||
Margin="4"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.TipPreSocksPort}" />
|
||||
Classes="Margin8"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.TipPreSocksPort}"
|
||||
Watermark="{x:Static resx:ResUI.TipPreSocksPort}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="12"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvMoreUrl}" />
|
||||
<TextBox
|
||||
x:Name="txtMoreUrl"
|
||||
@@ -242,10 +243,10 @@
|
||||
MinHeight="100"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Classes="TextArea Margin4"
|
||||
Classes="TextArea Margin8"
|
||||
MinLines="4"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.3" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.1.3" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.1.6" />
|
||||
<PackageReference Include="Semi.Avalonia" Version="11.1.0.3" />
|
||||
<PackageReference Include="Semi.Avalonia.DataGrid" Version="11.1.0.3" />
|
||||
<PackageReference Include="Semi.Avalonia" Version="11.1.0.4" />
|
||||
<PackageReference Include="Semi.Avalonia.DataGrid" Version="11.1.0.4" />
|
||||
<PackageReference Include="ReactiveUI" Version="20.1.1" />
|
||||
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -35,5 +35,25 @@ namespace v2rayN
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool? SaveFileDialog(out string fileName, string filter)
|
||||
{
|
||||
fileName = string.Empty;
|
||||
|
||||
SaveFileDialog fileDialog = new()
|
||||
{
|
||||
Filter = filter,
|
||||
FilterIndex = 2,
|
||||
RestoreDirectory = true
|
||||
};
|
||||
if (fileDialog.ShowDialog() != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fileName = fileDialog.FileName;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace v2rayN.Converters
|
||||
try
|
||||
{
|
||||
var fontFamily = LazyConfig.Instance.Config.uiItem.currentFontFamily;
|
||||
if (!Utils.IsNullOrEmpty(fontFamily))
|
||||
if (Utils.IsNotEmpty(fontFamily))
|
||||
{
|
||||
var fontPath = Utils.GetFontsPath();
|
||||
MyFont = new FontFamily(new Uri(@$"file:///{fontPath}\"), $"./#{fontFamily}");
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace v2rayN.ViewModels
|
||||
y => y != null && !y.IsNullOrEmpty())
|
||||
.Subscribe(c =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(CurrentLanguage) && _config.uiItem.currentLanguage != CurrentLanguage)
|
||||
if (Utils.IsNotEmpty(CurrentLanguage) && _config.uiItem.currentLanguage != CurrentLanguage)
|
||||
{
|
||||
_config.uiItem.currentLanguage = CurrentLanguage;
|
||||
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
|
||||
|
||||
256
v2rayN/v2rayN/Views/BackupAndRestoreView.xaml
Normal file
256
v2rayN/v2rayN/Views/BackupAndRestoreView.xaml
Normal file
@@ -0,0 +1,256 @@
|
||||
<reactiveui:ReactiveUserControl
|
||||
x:Class="v2rayN.Views.BackupAndRestoreView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:v2rayN.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800"
|
||||
x:TypeArguments="vms:BackupAndRestoreViewModel"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Popupbox.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<DockPanel Margin="16">
|
||||
<DockPanel Margin="8" DockPanel.Dock="Bottom">
|
||||
<Button
|
||||
Width="100"
|
||||
Margin="8"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
Content="{x:Static resx:ResUI.menuClose}"
|
||||
DockPanel.Dock="Right"
|
||||
IsCancel="True"
|
||||
IsDefault="True"
|
||||
Style="{StaticResource MaterialDesignFlatButton}" />
|
||||
|
||||
<TextBlock
|
||||
x:Name="txtMsg"
|
||||
Margin="8"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource ToolbarTextBlock}" />
|
||||
</DockPanel>
|
||||
|
||||
<StackPanel>
|
||||
<materialDesign:Card Width="Auto" Margin="8">
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300" />
|
||||
<ColumnDefinition Width="200" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
Style="{StaticResource ModuleTitle}"
|
||||
Text="{x:Static resx:ResUI.menuLocalBackupAndRestore}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.menuLocalBackup}" />
|
||||
<Button
|
||||
x:Name="menuLocalBackup"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.menuLocalBackup}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
|
||||
<Separator
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Style="{StaticResource MaterialDesignLightSeparator}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.menuLocalRestore}" />
|
||||
<Button
|
||||
x:Name="menuLocalRestore"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.menuLocalRestore}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
<materialDesign:Card Width="Auto" Margin="8">
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300" />
|
||||
<ColumnDefinition Width="200" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
Style="{StaticResource ModuleTitle}"
|
||||
Text="{x:Static resx:ResUI.menuRemoteBackupAndRestore}" />
|
||||
|
||||
<materialDesign:PopupBox
|
||||
Padding="8,0"
|
||||
HorizontalAlignment="Right"
|
||||
StaysOpen="True"
|
||||
Style="{StaticResource MaterialDesignToolForegroundPopupBox}">
|
||||
<StackPanel Margin="16">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="300" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.LvWebDavUrl}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtWebDavUrl"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource DefTextBox}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.LvWebDavUserName}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtWebDavUserName"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.LvWebDavPassword}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtWebDavPassword"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.LvWebDavDirName}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtWebDavDirName"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<Button
|
||||
x:Name="menuWebDavCheck"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.LvWebDavCheck}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.menuRemoteBackup}" />
|
||||
<Button
|
||||
x:Name="menuRemoteBackup"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.menuRemoteBackup}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
|
||||
<Separator
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
Style="{StaticResource MaterialDesignLightSeparator}" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.menuRemoteRestore}" />
|
||||
<Button
|
||||
x:Name="menuRemoteRestore"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.menuRemoteRestore}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</reactiveui:ReactiveUserControl>
|
||||
65
v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs
Normal file
65
v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
|
||||
namespace v2rayN.Views
|
||||
{
|
||||
public partial class BackupAndRestoreView
|
||||
{
|
||||
private NoticeHandler? _noticeHandler;
|
||||
|
||||
public BackupAndRestoreView()
|
||||
{
|
||||
InitializeComponent();
|
||||
menuLocalBackup.Click += MenuLocalBackup_Click;
|
||||
menuLocalRestore.Click += MenuLocalRestore_Click;
|
||||
|
||||
ViewModel = new BackupAndRestoreViewModel(UpdateViewHandler);
|
||||
|
||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.Bind(ViewModel, vm => vm.OperationMsg, v => v.txtMsg.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.url, v => v.txtWebDavUrl.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.userName, v => v.txtWebDavUserName.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.password, v => v.txtWebDavPassword.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.dirName, v => v.txtWebDavDirName.Text).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.WebDavCheckCmd, v => v.menuWebDavCheck).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.RemoteBackupCmd, v => v.menuRemoteBackup).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.RemoteRestoreCmd, v => v.menuRemoteRestore).DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
private void MenuRemoteRestore_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void MenuLocalBackup_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (UI.SaveFileDialog(out string fileName, "Zip|*.zip") != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ViewModel?.LocalBackup(fileName);
|
||||
}
|
||||
|
||||
private void MenuLocalRestore_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (UI.OpenFileDialog(out string fileName, "Zip|*.zip|All|*.*") != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ViewModel?.LocalRestore(fileName);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
{
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,32 +22,25 @@
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource SettingItemMargin}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
|
||||
<ToggleButton
|
||||
x:Name="togEnableCheckPreReleaseUpdate"
|
||||
Grid.Row="9"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource SettingItemMargin}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<Button
|
||||
x:Name="btnCheckUpdate"
|
||||
Grid.Row="2"
|
||||
Width="100"
|
||||
Margin="8"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
Content="{x:Static resx:ResUI.menuCheckUpdate}"
|
||||
IsCancel="True"
|
||||
IsDefault="True"
|
||||
Style="{StaticResource MaterialDesignFlatButton}" />
|
||||
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Width="100"
|
||||
Margin="8"
|
||||
HorizontalAlignment="Right"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<reactiveui:ReactiveUserControl
|
||||
x:Class="v2rayN.Views.ClashConnectionsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
d:DesignHeight="450"
|
||||
@@ -20,6 +20,15 @@
|
||||
DockPanel.Dock="Top"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBox
|
||||
x:Name="txtHostFilter"
|
||||
Width="200"
|
||||
Margin="8,0"
|
||||
VerticalContentAlignment="Center"
|
||||
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.ConnectionsHostFilterTitle}"
|
||||
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace v2rayN.Views
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseCmd, v => v.menuConnectionClose).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||
|
||||
@@ -186,14 +186,14 @@
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuClearServerStatistics}" />
|
||||
<Separator Margin="-40,5" />
|
||||
<MenuItem
|
||||
x:Name="menuBackupAndRestore"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuBackupAndRestore}" />
|
||||
<MenuItem
|
||||
x:Name="menuOpenTheFileLocation"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" />
|
||||
<!--<MenuItem
|
||||
x:Name="menuImportOldGuiConfig"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuImportOldGuiConfig}" />-->
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Separator />
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace v2rayN.Views
|
||||
{
|
||||
private static Config _config;
|
||||
private CheckUpdateView? _checkUpdateView;
|
||||
private BackupAndRestoreView? _backupAndRestoreView;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
@@ -33,6 +34,7 @@ namespace v2rayN.Views
|
||||
menuClose.Click += menuClose_Click;
|
||||
menuExit.Click += menuExit_Click;
|
||||
menuCheckUpdate.Click += MenuCheckUpdate_Click;
|
||||
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
|
||||
|
||||
MessageBus.Current.Listen<string>(Global.CommandSendSnackMsg).Subscribe(x => DelegateSnackMsg(x));
|
||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
||||
@@ -71,14 +73,6 @@ namespace v2rayN.Views
|
||||
this.BindCommand(ViewModel, vm => vm.RebootAsAdminCmd, v => v.menuRebootAsAdmin).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ClearServerStatisticsCmd, v => v.menuClearServerStatistics).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.OpenTheFileLocationCmd, v => v.menuOpenTheFileLocation).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.ImportOldGuiConfigCmd, v => v.menuImportOldGuiConfig).DisposeWith(disposables);
|
||||
|
||||
//check update
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateNCmd, v => v.menuCheckUpdateN).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateXrayCoreCmd, v => v.menuCheckUpdateXrayCore).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateClashMetaCoreCmd, v => v.menuCheckUpdateMihomoCore).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateSingBoxCoreCmd, v => v.menuCheckUpdateSingBoxCore).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateGeoCmd, v => v.menuCheckUpdateGeo).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
|
||||
@@ -196,12 +190,6 @@ namespace v2rayN.Views
|
||||
AddHelpMenuItem();
|
||||
}
|
||||
|
||||
private void MenuCheckUpdate_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_checkUpdateView ??= new CheckUpdateView();
|
||||
DialogHost.Show(_checkUpdateView, "RootDialog");
|
||||
}
|
||||
|
||||
#region Event
|
||||
|
||||
private void OnProgramStarted(object state, bool timeout)
|
||||
@@ -433,6 +421,18 @@ namespace v2rayN.Views
|
||||
ViewModel?.ScanScreenTaskAsync(result);
|
||||
}
|
||||
|
||||
private void MenuCheckUpdate_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_checkUpdateView ??= new CheckUpdateView();
|
||||
DialogHost.Show(_checkUpdateView, "RootDialog");
|
||||
}
|
||||
|
||||
private void MenuBackupAndRestore_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_backupAndRestoreView ??= new BackupAndRestoreView();
|
||||
DialogHost.Show(_backupAndRestoreView, "RootDialog");
|
||||
}
|
||||
|
||||
#endregion Event
|
||||
|
||||
#region UI
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using Microsoft.Win32;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using System.Reactive.Disposables;
|
||||
@@ -129,17 +128,11 @@ namespace v2rayN.Views
|
||||
|
||||
case EViewAction.SaveFileDialog:
|
||||
if (obj is null) return false;
|
||||
SaveFileDialog fileDialog = new()
|
||||
{
|
||||
Filter = "Config|*.json",
|
||||
FilterIndex = 2,
|
||||
RestoreDirectory = true
|
||||
};
|
||||
if (fileDialog.ShowDialog() != true)
|
||||
if (UI.SaveFileDialog(out string fileName, "Config|*.json") != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ViewModel?.Export2ClientConfigResult(fileDialog.FileName, (ProfileItem)obj);
|
||||
ViewModel?.Export2ClientConfigResult(fileName, (ProfileItem)obj);
|
||||
break;
|
||||
|
||||
case EViewAction.AddServerWindow:
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace v2rayUpgrade
|
||||
{
|
||||
string thisAppOldFile = $"{GetExePath()}.tmp";
|
||||
File.Delete(thisAppOldFile);
|
||||
string startKey = "v2rayN/";
|
||||
string splitKey = "/";
|
||||
|
||||
using ZipArchive archive = ZipFile.OpenRead(fileName);
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
@@ -77,11 +77,11 @@ namespace v2rayUpgrade
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string fullName = entry.FullName;
|
||||
if (fullName.StartsWith(startKey))
|
||||
{
|
||||
fullName = fullName[startKey.Length..];
|
||||
}
|
||||
|
||||
var lst = entry.FullName.Split(splitKey);
|
||||
if (lst.Length == 1) continue;
|
||||
string fullName = string.Join(splitKey, lst[1..lst.Length]);
|
||||
|
||||
if (string.Equals(GetExePath(), GetPath(fullName), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
File.Move(GetExePath(), thisAppOldFile);
|
||||
@@ -90,6 +90,8 @@ namespace v2rayUpgrade
|
||||
string entryOutputPath = GetPath(fullName);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
|
||||
entry.ExtractToFile(entryOutputPath, true);
|
||||
|
||||
Console.WriteLine(entryOutputPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -104,11 +106,12 @@ namespace v2rayUpgrade
|
||||
}
|
||||
if (sb.Length > 0)
|
||||
{
|
||||
Console.WriteLine("Upgrade Failed,Hold ctrl + c to copy to clipboard.\n" +
|
||||
"(升级失败,按住ctrl+c可以复制到剪贴板)." + sb.ToString());
|
||||
Console.WriteLine("Upgrade Failed.\n" +
|
||||
"(升级失败)." + sb.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Start v2rayN, please wait...(正在重启,请等待)");
|
||||
Process.Start("v2rayN");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user