mirror of
https://github.com/2dust/v2rayN.git
synced 2025-10-27 02:34:41 +00:00
Compare commits
22 commits
5851785156
...
4f225f2cbd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f225f2cbd | ||
|
|
a87a015c03 | ||
|
|
8c1d48f2d0 | ||
|
|
54fcbafe2a | ||
|
|
05e598e5fa | ||
|
|
f11707011d | ||
|
|
2482e599cc | ||
|
|
c559914ff7 | ||
|
|
436d95576e | ||
|
|
1ff017ee4d | ||
|
|
db3c1a1f15 | ||
|
|
ed6ddefcd9 | ||
|
|
2f3fcb2584 | ||
|
|
25845f79a2 | ||
|
|
b31f02511a | ||
|
|
8e44e202ca | ||
|
|
b8d40c1d05 | ||
|
|
68027332fe | ||
|
|
3a89be9ddd | ||
|
|
c20d5b3208 | ||
|
|
54e83391d0 | ||
|
|
3e0578f775 |
14 changed files with 167 additions and 42 deletions
|
|
@ -9,6 +9,31 @@ public class JsonUtils
|
||||||
{
|
{
|
||||||
private static readonly string _tag = "JsonUtils";
|
private static readonly string _tag = "JsonUtils";
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions _defaultDeserializeOptions = new()
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
ReadCommentHandling = JsonCommentHandling.Skip
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions _defaultSerializeOptions = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions _nullValueSerializeOptions = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly JsonDocumentOptions _defaultDocumentOptions = new()
|
||||||
|
{
|
||||||
|
CommentHandling = JsonCommentHandling.Skip
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DeepCopy
|
/// DeepCopy
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -34,11 +59,7 @@ public class JsonUtils
|
||||||
{
|
{
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
var options = new JsonSerializerOptions
|
return JsonSerializer.Deserialize<T>(strJson, _defaultDeserializeOptions);
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
};
|
|
||||||
return JsonSerializer.Deserialize<T>(strJson, options);
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
@ -59,7 +80,7 @@ public class JsonUtils
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return JsonNode.Parse(strJson);
|
return JsonNode.Parse(strJson, nodeOptions: null, _defaultDocumentOptions);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
@ -84,12 +105,7 @@ public class JsonUtils
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
var options = new JsonSerializerOptions
|
var options = nullValue ? _nullValueSerializeOptions : _defaultSerializeOptions;
|
||||||
{
|
|
||||||
WriteIndented = indented,
|
|
||||||
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull,
|
|
||||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
|
||||||
};
|
|
||||||
result = JsonSerializer.Serialize(obj, options);
|
result = JsonSerializer.Serialize(obj, options);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -331,6 +331,32 @@ public class Utils
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, List<string>> ParseHostsToDictionary(string hostsContent)
|
||||||
|
{
|
||||||
|
var userHostsMap = hostsContent
|
||||||
|
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(line => line.Trim())
|
||||||
|
// skip full-line comments
|
||||||
|
.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#"))
|
||||||
|
// strip inline comments (truncate at '#')
|
||||||
|
.Select(line =>
|
||||||
|
{
|
||||||
|
var index = line.IndexOf('#');
|
||||||
|
return index >= 0 ? line.Substring(0, index).Trim() : line;
|
||||||
|
})
|
||||||
|
// ensure line still contains valid parts
|
||||||
|
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
|
||||||
|
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
.Where(parts => parts.Length >= 2)
|
||||||
|
.GroupBy(parts => parts[0])
|
||||||
|
.ToDictionary(
|
||||||
|
group => group.Key,
|
||||||
|
group => group.SelectMany(parts => parts.Skip(1)).ToList()
|
||||||
|
);
|
||||||
|
|
||||||
|
return userHostsMap;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion 转换函数
|
#endregion 转换函数
|
||||||
|
|
||||||
#region 数据检查
|
#region 数据检查
|
||||||
|
|
@ -857,6 +883,55 @@ public class Utils
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsPackagedInstall()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (IsWindows() || IsOSX())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE")))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var exePath = GetExePath();
|
||||||
|
var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? "";
|
||||||
|
var p = baseDir.Replace('\\', '/');
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(p))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.Contains("/.mount_", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.StartsWith("/usr/lib/v2rayN", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.StartsWith("/usr/share/v2rayN", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task<string?> GetLinuxUserId()
|
private static async Task<string?> GetLinuxUserId()
|
||||||
{
|
{
|
||||||
var arg = new List<string>() { "-c", "id -u" };
|
var arg = new List<string>() { "-c", "id -u" };
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ public class ClashFmt : BaseFmt
|
||||||
{
|
{
|
||||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||||
{
|
{
|
||||||
if (Contains(strData, "port", "socks-port", "proxies"))
|
if (Contains(strData, "external-controller", "-port", "proxies"))
|
||||||
{
|
{
|
||||||
var fileName = WriteAllText(strData, "yaml");
|
var fileName = WriteAllText(strData, "yaml");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,17 +103,7 @@ public partial class CoreConfigSingboxService
|
||||||
|
|
||||||
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
var userHostsMap = simpleDNSItem.Hosts
|
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
|
||||||
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
|
||||||
.Select(line => line.Trim())
|
|
||||||
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
|
|
||||||
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
.Where(parts => parts.Length >= 2)
|
|
||||||
.GroupBy(parts => parts[0])
|
|
||||||
.ToDictionary(
|
|
||||||
group => group.Key,
|
|
||||||
group => group.SelectMany(parts => parts.Skip(1)).ToList()
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach (var kvp in userHostsMap)
|
foreach (var kvp in userHostsMap)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,31 @@ public partial class CoreConfigSingboxService
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hostsDomains = new List<string>();
|
||||||
|
var systemHostsMap = Utils.GetSystemHosts();
|
||||||
|
foreach (var kvp in systemHostsMap)
|
||||||
|
{
|
||||||
|
hostsDomains.Add(kvp.Key);
|
||||||
|
}
|
||||||
|
var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
|
||||||
|
if (dnsItem == null || dnsItem.Enabled == false)
|
||||||
|
{
|
||||||
|
var simpleDNSItem = _config.SimpleDNSItem;
|
||||||
|
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
|
||||||
|
foreach (var kvp in userHostsMap)
|
||||||
|
{
|
||||||
|
hostsDomains.Add(kvp.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
singboxConfig.route.rules.Add(new()
|
||||||
|
{
|
||||||
|
action = "resolve",
|
||||||
|
domain = hostsDomains,
|
||||||
|
});
|
||||||
|
|
||||||
singboxConfig.route.rules.Add(new()
|
singboxConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
outbound = Global.DirectTag,
|
outbound = Global.DirectTag,
|
||||||
|
|
|
||||||
|
|
@ -261,17 +261,7 @@ public partial class CoreConfigV2rayService
|
||||||
|
|
||||||
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
var userHostsMap = simpleDNSItem.Hosts
|
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
|
||||||
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
|
||||||
.Select(line => line.Trim())
|
|
||||||
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
|
|
||||||
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
.Where(parts => parts.Length >= 2)
|
|
||||||
.GroupBy(parts => parts[0])
|
|
||||||
.ToDictionary(
|
|
||||||
group => group.Key,
|
|
||||||
group => group.SelectMany(parts => parts.Skip(1)).ToList()
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach (var kvp in userHostsMap)
|
foreach (var kvp in userHostsMap)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -39,26 +39,30 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
_updateView = updateView;
|
||||||
|
|
||||||
|
var canEditRemove = this.WhenAnyValue(
|
||||||
|
x => x.SelectedChild,
|
||||||
|
SelectedChild => SelectedChild != null && !SelectedChild.Remarks.IsNullOrEmpty());
|
||||||
|
|
||||||
RemoveCmd = ReactiveCommand.CreateFromTask(async () =>
|
RemoveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await ChildRemoveAsync();
|
await ChildRemoveAsync();
|
||||||
});
|
}, canEditRemove);
|
||||||
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
|
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await MoveServer(EMove.Top);
|
await MoveServer(EMove.Top);
|
||||||
});
|
}, canEditRemove);
|
||||||
MoveUpCmd = ReactiveCommand.CreateFromTask(async () =>
|
MoveUpCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await MoveServer(EMove.Up);
|
await MoveServer(EMove.Up);
|
||||||
});
|
}, canEditRemove);
|
||||||
MoveDownCmd = ReactiveCommand.CreateFromTask(async () =>
|
MoveDownCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await MoveServer(EMove.Down);
|
await MoveServer(EMove.Down);
|
||||||
});
|
}, canEditRemove);
|
||||||
MoveBottomCmd = ReactiveCommand.CreateFromTask(async () =>
|
MoveBottomCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await MoveServer(EMove.Bottom);
|
await MoveServer(EMove.Bottom);
|
||||||
});
|
}, canEditRemove);
|
||||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SaveServerAsync();
|
await SaveServerAsync();
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,16 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||||
|
|
||||||
private CheckUpdateModel GetCheckUpdateModel(string coreType)
|
private CheckUpdateModel GetCheckUpdateModel(string coreType)
|
||||||
{
|
{
|
||||||
|
if (coreType == _v2rayN && Utils.IsPackagedInstall())
|
||||||
|
{
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
IsSelected = false,
|
||||||
|
CoreType = coreType,
|
||||||
|
Remarks = ResUI.menuCheckUpdate + " (Not Support)",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true,
|
IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true,
|
||||||
|
|
@ -104,6 +114,11 @@ public class CheckUpdateViewModel : MyReactiveObject
|
||||||
}
|
}
|
||||||
else if (item.CoreType == _v2rayN)
|
else if (item.CoreType == _v2rayN)
|
||||||
{
|
{
|
||||||
|
if (Utils.IsPackagedInstall())
|
||||||
|
{
|
||||||
|
await UpdateView(_v2rayN, "Not Support");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
await CheckUpdateN(EnableCheckPreReleaseUpdate);
|
await CheckUpdateN(EnableCheckPreReleaseUpdate);
|
||||||
}
|
}
|
||||||
else if (item.CoreType == ECoreType.Xray.ToString())
|
else if (item.CoreType == ECoreType.Xray.ToString())
|
||||||
|
|
|
||||||
|
|
@ -400,7 +400,7 @@
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="400"
|
Width="400"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Watermark="1000:2000,3000:4000" />
|
Watermark="1000-2000,3000,4000" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,7 @@
|
||||||
<DataGrid
|
<DataGrid
|
||||||
x:Name="lstRules"
|
x:Name="lstRules"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
|
Background="Transparent"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CanUserResizeColumns="True"
|
CanUserResizeColumns="True"
|
||||||
GridLinesVisibility="All"
|
GridLinesVisibility="All"
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@
|
||||||
<DataGrid
|
<DataGrid
|
||||||
x:Name="lstRoutings"
|
x:Name="lstRoutings"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
|
Background="Transparent"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CanUserResizeColumns="True"
|
CanUserResizeColumns="True"
|
||||||
GridLinesVisibility="All"
|
GridLinesVisibility="All"
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ public partial class AddGroupServerWindow
|
||||||
this.Loaded += Window_Loaded;
|
this.Loaded += Window_Loaded;
|
||||||
this.PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
this.PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
||||||
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||||
|
menuSelectAllChild.Click += MenuSelectAllChild_Click;
|
||||||
|
|
||||||
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
||||||
|
|
||||||
|
|
@ -138,4 +139,9 @@ public partial class AddGroupServerWindow
|
||||||
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MenuSelectAllChild_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
lstChild.SelectAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -538,7 +538,7 @@
|
||||||
Width="400"
|
Width="400"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
materialDesign:HintAssist.Hint="1000:2000,3000:4000"
|
materialDesign:HintAssist.Hint="1000-2000,3000,4000"
|
||||||
Style="{StaticResource DefTextBox}" />
|
Style="{StaticResource DefTextBox}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,8 @@ public partial class RoutingRuleSettingWindow
|
||||||
|
|
||||||
private void RoutingRuleSettingWindow_PreviewKeyDown(object sender, KeyEventArgs e)
|
private void RoutingRuleSettingWindow_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (!lstRules.IsKeyboardFocusWithin)
|
||||||
|
return;
|
||||||
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
||||||
{
|
{
|
||||||
if (e.Key == Key.A)
|
if (e.Key == Key.A)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue