Native Aot (#10295)

* Protect models

* Json Context

* YAML AOT-friendly optimization

* Run the Test in AOT mode

* Remove TaskScheduler

* Clean build output

* Workflows
This commit is contained in:
DHR60
2026-11-01 12:29:06 +00:00
committed by GitHub
parent e2f4292321
commit 78b0b2224b
57 changed files with 617 additions and 273 deletions
+16
View File
@@ -29,6 +29,22 @@ jobs:
release_tag: ${{ inputs.release_tag }} release_tag: ${{ inputs.release_tag }}
secrets: inherit secrets: inherit
build-aot:
uses: ./.github/workflows/build.yml
with:
target: linux
isAot: true
release-aot-zip:
if: inputs.release_tag != ''
needs: build-aot
uses: ./.github/workflows/package-zip.yml
with:
target: linux
release_tag: ${{ inputs.release_tag }}
isAot: true
secrets: inherit
deb: deb:
name: build and release deb x64 & arm64 name: build and release deb x64 & arm64
if: (github.event_name == 'workflow_dispatch' && inputs.release_tag != '') || (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) if: (github.event_name == 'workflow_dispatch' && inputs.release_tag != '') || (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
+17 -1
View File
@@ -28,7 +28,23 @@ jobs:
target: macos target: macos
release_tag: ${{ inputs.release_tag }} release_tag: ${{ inputs.release_tag }}
secrets: inherit secrets: inherit
build-aot:
uses: ./.github/workflows/build.yml
with:
target: macos
isAot: true
release-aot-zip:
if: inputs.release_tag != ''
needs: build-aot
uses: ./.github/workflows/package-zip.yml
with:
target: macos
release_tag: ${{ inputs.release_tag }}
isAot: true
secrets: inherit
dmg: dmg:
name: package and release macOS dmg name: package and release macOS dmg
if: inputs.release_tag != '' if: inputs.release_tag != ''
@@ -28,3 +28,19 @@ jobs:
target: windows-desktop target: windows-desktop
release_tag: ${{ inputs.release_tag }} release_tag: ${{ inputs.release_tag }}
secrets: inherit secrets: inherit
build-aot:
uses: ./.github/workflows/build.yml
with:
target: windows
isAot: true
release-aot-zip:
if: inputs.release_tag != ''
needs: build-aot
uses: ./.github/workflows/package-zip.yml
with:
target: windows-desktop
release_tag: ${{ inputs.release_tag }}
isAot: true
secrets: inherit
+54 -10
View File
@@ -10,6 +10,10 @@ on:
required: false required: false
type: string type: string
default: './v2rayN.Desktop/v2rayN.Desktop.csproj' default: './v2rayN.Desktop/v2rayN.Desktop.csproj'
isAot:
required: false
type: boolean
default: false
jobs: jobs:
build: build:
@@ -27,7 +31,6 @@ jobs:
) )
}} }}
env: env:
Output: "${{ github.workspace }}/${{ matrix.arch }}"
RID: |- RID: |-
${{ ${{
case( case(
@@ -41,6 +44,7 @@ jobs:
${{ ${{
case( case(
inputs.target == 'windows', '-p:EnableWindowsTargeting=true', inputs.target == 'windows', '-p:EnableWindowsTargeting=true',
inputs.target == 'linux', (matrix.arch == 'arm64' && '-p:LinkerFlavor=lld -p:ObjCopyName=llvm-objcopy' || ''),
'' ''
) )
}} }}
@@ -71,25 +75,65 @@ jobs:
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v6 uses: actions/setup-dotnet@v6
with: with:
dotnet-version: 10.0.1xx dotnet-version: 10.0.x
- name: Build v2rayN - name: Install dependencies
shell: bash shell: bash
working-directory: ./v2rayN if: inputs.target == 'linux' && inputs.isAot
run: dotnet publish $Project -c Release -r $RID -p:SelfContained=true $ExtOpt -o $Output run: |
sudo apt-get update
sudo apt-get install -y clang zlib1g-dev build-essential
- name: Install dependencies cross-arch
shell: bash
if: inputs.target == 'linux' && inputs.isAot && matrix.arch == 'arm64'
run: |
sudo apt-get update
sudo apt-get install -y lld llvm binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev-arm64-cross
- name: Set AOT Environment Variable
shell: bash
run: |
if [ "${{ inputs.isAot }}" = "true" ]; then
echo "DOTNET_AOT_ARG=-p:IsAot=true" >> $GITHUB_ENV
else
echo "DOTNET_AOT_ARG=-p:IsAot=false" >> $GITHUB_ENV
fi
- name: Set OUTPUT Environment Variable
shell: bash
run: |
RAW_PATH="${{ github.workspace }}/${{ matrix.arch }}${{ inputs.isAot && '-aot' || '' }}"
echo "OUTPUT=${RAW_PATH//\\//}" >> "$GITHUB_ENV"
- name: Build AmazTool - name: Build AmazTool
shell: bash shell: bash
working-directory: ./v2rayN working-directory: ./v2rayN
run: dotnet publish ./AmazTool/AmazTool.csproj -c Release -r $RID -p:SelfContained=true -p:PublishTrimmed=true $ExtOpt -o $Output run: dotnet publish ./AmazTool/AmazTool.csproj -c Release -r $RID -p:SelfContained=true -p:PublishTrimmed=true $ExtOpt ${{ env.DOTNET_AOT_ARG }} -o ${{ env.OUTPUT }}
- name: Build v2rayN WPF
shell: bash
if: inputs.project == './v2rayN/v2rayN.csproj'
working-directory: ./v2rayN
run: dotnet publish $Project -c Release -r $RID -p:SelfContained=true $ExtOpt -o ${{ env.OUTPUT }}
- name: Build v2rayN Avalonia
shell: bash
if: inputs.project == './v2rayN.Desktop/v2rayN.Desktop.csproj'
working-directory: ./v2rayN
run: dotnet publish $Project -c Release -r $RID -p:SelfContained=true $ExtOpt ${{ env.DOTNET_AOT_ARG }} -o ${{ env.OUTPUT }}
- name: Remove Debug Symbols - name: Remove Debug Symbols
shell: bash shell: bash
run: | run: |
find ${{ matrix.arch }} -type f -name '*.pdb' -delete find ${{ env.OUTPUT }} -type f -name '*.pdb' -delete
find ${{ env.OUTPUT }} -type f -name '*.debug' -delete
find ${{ env.OUTPUT }} -type f -name '*.dbg' -delete
find ${{ env.OUTPUT }} -type f -name '*.sym' -delete
find ${{ env.OUTPUT }} -name '*.dSYM' -exec rm -rf {} +
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: ${{ matrix.arch }} name: ${{ matrix.arch }}${{ inputs.isAot && '-aot' || '' }}
path: ${{ matrix.arch }} path: ${{ env.OUTPUT }}
+52 -13
View File
@@ -9,6 +9,10 @@ on:
target: # windows linux macos windows-desktop target: # windows linux macos windows-desktop
required: true required: true
type: string type: string
isAot:
required: false
type: boolean
default: false
secrets: secrets:
GPG_PRIVATE_KEY: GPG_PRIVATE_KEY:
required: true required: true
@@ -43,27 +47,62 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v7 uses: actions/checkout@v7
- name: Set package variables
id: package-vars
shell: bash
env:
INPUT_TARGET: ${{ inputs.target }}
INPUT_IS_AOT: ${{ inputs.isAot }}
run: |
set -euo pipefail
package_dir="v2rayN-${Target}-${Arch}"
package_file="${package_dir}.zip"
build_artifact_name="${{ matrix.arch }}"
rename_suffix=""
if [[ "$INPUT_IS_AOT" == "true" ]]; then
build_artifact_name="${build_artifact_name}-aot"
rename_suffix="-aot"
fi
if [[ "$INPUT_TARGET" == "windows-desktop" ]]; then
rename_suffix="-desktop${rename_suffix}"
fi
final_package_file="${package_dir}${rename_suffix}.zip"
{
echo "build_artifact_name=${build_artifact_name}"
echo "package_dir=${package_dir}"
echo "package_file=${package_file}"
echo "final_package_file=${final_package_file}"
echo "rename_suffix=${rename_suffix}"
} >> "$GITHUB_OUTPUT"
- name: Restore build artifacts - name: Restore build artifacts
uses: actions/download-artifact@v8 uses: actions/download-artifact@v8
with: with:
name: ${{ matrix.arch }} name: ${{ steps.package-vars.outputs.build_artifact_name }}
path: v2rayN-${{ env.Target }}-${{ env.Arch }} path: ${{ steps.package-vars.outputs.package_dir }}
- name: Get v2rayN-core-bin - name: Get v2rayN-core-bin
run: wget -nv -O v2rayN-$Target-$Arch.zip "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/v2rayN-$Target-$Arch.zip" run: |
wget -nv -O "${{ steps.package-vars.outputs.package_file }}" "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/v2rayN-$Target-$Arch.zip"
- name: Package zip archive - name: Package zip archive
run: 7z a -tZip v2rayN-$Target-$Arch.zip v2rayN-$Target-$Arch -mx1 shell: bash
run: |
7z a -tZip "${{ steps.package-vars.outputs.package_file }}" "${{ steps.package-vars.outputs.package_dir }}" -mx1
- name: Rename package archive
if: steps.package-vars.outputs.rename_suffix != ''
shell: bash
run: |
mv "${{ steps.package-vars.outputs.package_file }}" \
"${{ steps.package-vars.outputs.package_dir }}${{ steps.package-vars.outputs.rename_suffix }}.zip"
- name: Rename windows-desktop
if: inputs.target == 'windows-desktop'
run: mv "v2rayN-$Target-$Arch.zip" "v2rayN-$Target-$Arch-desktop.zip"
- name: Upload zip artifact - name: Upload zip artifact
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: release-${{ inputs.target }}-${{ matrix.arch }} name: release-${{ inputs.target }}-${{ matrix.arch }}${{ inputs.isAot && '-aot' || '' }}
path: v2rayN*.zip path: ${{ steps.package-vars.outputs.final_package_file }}
if-no-files-found: error if-no-files-found: error
release: release:
@@ -75,7 +114,7 @@ jobs:
uses: ./.github/workflows/upload-sign.yml uses: ./.github/workflows/upload-sign.yml
with: with:
release_tag: ${{ inputs.release_tag }} release_tag: ${{ inputs.release_tag }}
artifact_name: release-${{ inputs.target }}-${{ matrix.arch }} artifact_name: release-${{ inputs.target }}-${{ matrix.arch }}${{ inputs.isAot && '-aot' || '' }}
file_pattern: '*.zip' file_pattern: '*.zip'
secrets: secrets:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
+20 -5
View File
@@ -5,16 +5,31 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Update="Resx\Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Update="Resx\Resource.Designer.cs"> <Compile Update="Resx\Resource.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon> <DependentUpon>Resource.resx</DependentUpon>
</Compile> </Compile>
<EmbeddedResource Update="Resx\Resource.resx">
<SubType>Designer</SubType>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.zh-Hans.resx">
<SubType>Designer</SubType>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.zh-Hant.resx">
<SubType>Designer</SubType>
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<Target Name="RemoveAotSatelliteOutputs" AfterTargets="ComputeFilesToPublish" Condition="'$(PublishAot)' == 'true'">
<ItemGroup>
<ResolvedFileToPublish Remove="@(ResolvedFileToPublish)"
Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Contains('resources.dll'))" />
</ItemGroup>
</Target>
</Project> </Project>
+1 -1
View File
@@ -19,7 +19,7 @@ namespace AmazTool.Resx {
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。 // (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource { internal class Resource {
+10
View File
@@ -29,4 +29,14 @@
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>false</PublishReadyToRun> <PublishReadyToRun>false</PublishReadyToRun>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<IsAot Condition="'$(IsAot)' == ''">false</IsAot>
</PropertyGroup>
<PropertyGroup Condition="'$(IsAot)' == 'true'">
<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
<DefineConstants>$(DefineConstants);V2RAYN_IS_AOT</DefineConstants>
</PropertyGroup>
</Project> </Project>
+1 -2
View File
@@ -25,8 +25,7 @@
<PackageVersion Include="NLog" Version="6.2.1" /> <PackageVersion Include="NLog" Version="6.2.1" />
<PackageVersion Include="sqlite-net-e" Version="1.11.285" /> <PackageVersion Include="sqlite-net-e" Version="1.11.285" />
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.4.1" /> <PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.4.1" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" /> <PackageVersion Include="TUnit" Version="1.73.19" />
<PackageVersion Include="TUnit" Version="1.75.0" />
<PackageVersion Include="TUnit.Assertions.Should" Version="1.72.16-beta" /> <PackageVersion Include="TUnit.Assertions.Should" Version="1.72.16-beta" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" /> <PackageVersion Include="WebDav.Client" Version="2.9.0" />
<PackageVersion Include="YamlDotNet" Version="18.1.0" /> <PackageVersion Include="YamlDotNet" Version="18.1.0" />
@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<PublishAot>true</PublishAot>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+121 -10
View File
@@ -7,40 +7,45 @@ public class JsonUtils
private static readonly JsonSerializerOptions _defaultDeserializeOptions = new() private static readonly JsonSerializerOptions _defaultDeserializeOptions = new()
{ {
PropertyNameCaseInsensitive = true, PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip ReadCommentHandling = JsonCommentHandling.Skip,
TypeInfoResolver = AppJsonContext.Default,
}; };
private static readonly JsonSerializerOptions _defaultSerializeOptions = new() private static readonly JsonSerializerOptions _defaultSerializeOptions = new()
{ {
WriteIndented = true, WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
TypeInfoResolver = AppJsonContext.Default,
}; };
private static readonly JsonSerializerOptions _defaultSerializeNoIndentedOptions = new() private static readonly JsonSerializerOptions _defaultSerializeNoIndentedOptions = new()
{ {
WriteIndented = false, WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
TypeInfoResolver = AppJsonContext.Default,
}; };
private static readonly JsonSerializerOptions _nullValueSerializeOptions = new() private static readonly JsonSerializerOptions _nullValueSerializeOptions = new()
{ {
WriteIndented = true, WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never, DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
TypeInfoResolver = AppJsonContext.Default,
}; };
private static readonly JsonSerializerOptions _nullValueSerializeNoIndentedOptions = new() private static readonly JsonSerializerOptions _nullValueSerializeNoIndentedOptions = new()
{ {
WriteIndented = false, WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.Never, DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
TypeInfoResolver = AppJsonContext.Default,
}; };
private static readonly JsonDocumentOptions _defaultDocumentOptions = new() private static readonly JsonDocumentOptions _defaultDocumentOptions = new()
{ {
CommentHandling = JsonCommentHandling.Skip CommentHandling = JsonCommentHandling.Skip,
}; };
/// <summary> /// <summary>
@@ -109,7 +114,7 @@ public class JsonUtils
/// <param name="indented"></param> /// <param name="indented"></param>
/// <param name="nullValue"></param> /// <param name="nullValue"></param>
/// <returns></returns> /// <returns></returns>
public static string Serialize(object? obj, bool indented = true, bool nullValue = false) public static string Serialize<T>(T? obj, bool indented = true, bool nullValue = false)
{ {
var result = string.Empty; var result = string.Empty;
try try
@@ -140,7 +145,7 @@ public class JsonUtils
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="options"></param> /// <param name="options"></param>
/// <returns></returns> /// <returns></returns>
public static string Serialize(object? obj, JsonSerializerOptions? options) public static string SerializeCamelCase<T>(T? obj)
{ {
var result = string.Empty; var result = string.Empty;
try try
@@ -149,7 +154,14 @@ public class JsonUtils
{ {
return result; return result;
} }
result = JsonSerializer.Serialize(obj, options ?? _defaultSerializeOptions); var options = new JsonSerializerOptions
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
TypeInfoResolver = AppJsonContext.Default,
};
result = JsonSerializer.Serialize(obj, options);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -162,9 +174,108 @@ public class JsonUtils
/// SerializeToNode /// SerializeToNode
/// </summary> /// </summary>
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="nullValue"></param>
/// <returns></returns> /// <returns></returns>
public static JsonNode? SerializeToNode(object? obj, JsonSerializerOptions? options = null) public static JsonNode? SerializeToNode<T>(T? obj, bool nullValue = false)
{ {
var options = nullValue ? _nullValueSerializeOptions : _defaultSerializeOptions;
return JsonSerializer.SerializeToNode(obj, options); return JsonSerializer.SerializeToNode(obj, options);
} }
public static JsonArray? SerializeToArray(IEnumerable<string>? values)
{
if (values is null)
{
return null;
}
var array = new JsonArray();
foreach (var value in values)
{
var valueNode = JsonSerializer.SerializeToNode(value, _defaultSerializeOptions);
array.Add(valueNode);
}
return array;
}
} }
[JsonSerializable(typeof(Config))]
[JsonSerializable(typeof(RulesItem))]
[JsonSerializable(typeof(ProfileItem))]
[JsonSerializable(typeof(List<RulesItem>))]
[JsonSerializable(typeof(List<ProfileItem>))]
[JsonSerializable(typeof(ProfileItemModel))]
[JsonSerializable(typeof(List<ProfileItemModel>))]
[JsonSerializable(typeof(RoutingTemplate))]
[JsonSerializable(typeof(DNSItem))]
[JsonSerializable(typeof(SimpleDNSItem))]
[JsonSerializable(typeof(IPAPIInfo))]
[JsonSerializable(typeof(SsSIP008))]
[JsonSerializable(typeof(List<SsServer>))]
[JsonSerializable(typeof(SsServer))]
[JsonSerializable(typeof(VmessQRCode))]
[JsonSerializable(typeof(ClashProxies))]
[JsonSerializable(typeof(ClashProviders))]
[JsonSerializable(typeof(ClashConnections))]
[JsonSerializable(typeof(ProtocolExtraItem))]
[JsonSerializable(typeof(TransportExtraItem))]
[JsonSerializable(typeof(TrafficItem))]
[JsonSerializable(typeof(V2rayMetricsVars))]
[JsonSerializable(typeof(V2rayMetricsVarsLink))]
[JsonSerializable(typeof(GitHubRelease))]
[JsonSerializable(typeof(List<GitHubRelease>))]
[JsonSerializable(typeof(ServerStatItem))]
[JsonSerializable(typeof(WebDavItem))]
[JsonSerializable(typeof(KeyEventItem))]
[JsonSerializable(typeof(SubItem))]
[JsonSerializable(typeof(SingboxConfig))]
[JsonSerializable(typeof(Dns4Sbox))]
[JsonSerializable(typeof(Inbound4Sbox))]
[JsonSerializable(typeof(List<Inbound4Sbox>))]
[JsonSerializable(typeof(Endpoints4Sbox))]
[JsonSerializable(typeof(List<Endpoints4Sbox>))]
[JsonSerializable(typeof(Outbound4Sbox))]
[JsonSerializable(typeof(List<Outbound4Sbox>))]
[JsonSerializable(typeof(Rule4Sbox))]
[JsonSerializable(typeof(Ruleset4Sbox))]
[JsonSerializable(typeof(List<Rule4Sbox>))]
[JsonSerializable(typeof(List<Ruleset4Sbox>))]
[JsonSerializable(typeof(V2rayConfig))]
[JsonSerializable(typeof(Dns4Ray))]
[JsonSerializable(typeof(DnsServer4Ray))]
[JsonSerializable(typeof(Inbounds4Ray))]
[JsonSerializable(typeof(List<Inbounds4Ray>))]
[JsonSerializable(typeof(Outbounds4Ray))]
[JsonSerializable(typeof(List<Outbounds4Ray>))]
[JsonSerializable(typeof(RulesItem4Ray))]
[JsonSerializable(typeof(List<RulesItem4Ray>))]
[JsonSerializable(typeof(Finalmask4Ray))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(long))]
[JsonSerializable(typeof(double))]
[JsonSerializable(typeof(decimal))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(Guid))]
[JsonSerializable(typeof(DateTime))]
[JsonSerializable(typeof(DateTimeOffset))]
[JsonSerializable(typeof(TimeSpan))]
[JsonSerializable(typeof(int?))]
[JsonSerializable(typeof(long?))]
[JsonSerializable(typeof(double?))]
[JsonSerializable(typeof(bool?))]
[JsonSerializable(typeof(DateTime?))]
[JsonSerializable(typeof(Guid?))]
[JsonSerializable(typeof(string[]))]
[JsonSerializable(typeof(int[]))]
[JsonSerializable(typeof(byte[]))]
[JsonSerializable(typeof(List<string>))]
[JsonSerializable(typeof(List<int>))]
[JsonSerializable(typeof(Dictionary<string, string>))]
[JsonSerializable(typeof(Dictionary<string, object>))]
[JsonSerializable(typeof(object))]
[JsonSerializable(typeof(JsonElement))]
[JsonSerializable(typeof(JsonDocument))]
[JsonSerializable(typeof(JsonNode))]
[JsonSerializable(typeof(JsonObject))]
[JsonSerializable(typeof(JsonArray))]
internal partial class AppJsonContext : JsonSerializerContext;
+2 -3
View File
@@ -348,10 +348,9 @@ public class Utils
.ToList()); .ToList());
} }
public static List<string> GetEnumNames<TEnum>() where TEnum : Enum public static List<string> GetEnumNames<TEnum>() where TEnum : struct, Enum
{ {
return Enum.GetValues(typeof(TEnum)) return Enum.GetValues<TEnum>()
.Cast<TEnum>()
.Select(e => e.ToString()) .Select(e => e.ToString())
.ToList(); .ToList();
} }
+38 -21
View File
@@ -1,6 +1,5 @@
using YamlDotNet.Core; using YamlDotNet.Core;
using YamlDotNet.Serialization; using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization.NamingConventions;
namespace ServiceLib.Common; namespace ServiceLib.Common;
@@ -13,45 +12,57 @@ public class YamlUtils
/// <summary> /// <summary>
/// Deserialize /// Deserialize
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param> /// <param name="str"></param>
/// <returns></returns> /// <returns></returns>
public static T FromYaml<T>(string str) public static YamlMappingNode FromYaml(string str)
{ {
var deserializer = new DeserializerBuilder() //var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance) // .WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build(); // .Build();
//try
//{
// var obj = deserializer.Deserialize<T>(str);
// return obj;
//}
//catch (Exception ex)
//{
// Logging.SaveLog(_tag, ex);
// return deserializer.Deserialize<T>("");
//}
try try
{ {
var obj = deserializer.Deserialize<T>(str); var yaml = new YamlStream();
return obj; using var reader = new StringReader(str);
yaml.Load(reader);
var rootNode = (YamlMappingNode)yaml.Documents[0].RootNode;
return rootNode;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
return deserializer.Deserialize<T>(""); return new YamlMappingNode();
} }
} }
/// <summary> /// <summary>
/// Serialize /// Serialize
/// </summary> /// </summary>
/// <param name="obj"></param> /// <param name="node"></param>
/// <returns></returns> /// <returns></returns>
public static string ToYaml(object? obj) public static string ToYaml(YamlMappingNode? node)
{ {
var result = string.Empty; var result = string.Empty;
if (obj == null) if (node == null)
{ {
return result; return result;
} }
var serializer = new SerializerBuilder()
.WithNamingConvention(HyphenatedNamingConvention.Instance)
.Build();
try try
{ {
result = serializer.Serialize(obj); var doc = new YamlDocument(node);
var stream = new YamlStream(doc);
using var writer = new StringWriter();
stream.Save(writer, assignAnchors: false);
result = writer.ToString();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -64,9 +75,15 @@ public class YamlUtils
{ {
try try
{ {
var mergingParser = new MergingParser(new Parser(new StringReader(str))); using var reader = new StringReader(str);
var obj = new DeserializerBuilder().Build().Deserialize(mergingParser); var parser = new Parser(reader);
return ToYaml(obj); var mergingParser = new MergingParser(parser);
var yamlStream = new YamlStream();
yamlStream.Load(mergingParser);
using var writer = new StringWriter();
yamlStream.Save(writer, assignAnchors: false);
return writer.ToString();
} }
catch (Exception ex) catch (Exception ex)
{ {
+1
View File
@@ -42,6 +42,7 @@ public class Global
public const string SingboxFakeIPFilterFileName = NamespaceSample + "singbox_fakeip_filter"; public const string SingboxFakeIPFilterFileName = NamespaceSample + "singbox_fakeip_filter";
public const string ChromeRootCertFileName = NamespaceSample + "chrome_roots_pem"; public const string ChromeRootCertFileName = NamespaceSample + "chrome_roots_pem";
public const string MozillaRootCertFileName = NamespaceSample + "mozilla_roots_pem"; public const string MozillaRootCertFileName = NamespaceSample + "mozilla_roots_pem";
public const string TaskXmlTemplateFileName = NamespaceSample + "TaskXmlTemplate";
public const string ChromeRootProvider = "chrome"; public const string ChromeRootProvider = "chrome";
public const string MozillaRootProvider = "mozilla"; public const string MozillaRootProvider = "mozilla";
@@ -87,6 +87,7 @@ public static class AutoStartupHandler
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
public static void AutoStartTaskService(string taskName, string fileName, string description) public static void AutoStartTaskService(string taskName, string fileName, string description)
{ {
#if false
if (taskName.IsNullOrEmpty()) if (taskName.IsNullOrEmpty())
{ {
return; return;
@@ -117,6 +118,78 @@ public static class AutoStartupHandler
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName))); task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
taskService.RootFolder.RegisterTaskDefinition(taskName, task); taskService.RootFolder.RegisterTaskDefinition(taskName, task);
#endif
if (string.IsNullOrEmpty(taskName))
{
return;
}
RunSchtasks($"/Delete /TN \"{taskName}\" /F");
if (string.IsNullOrEmpty(fileName))
{
return;
}
var workingDir = Path.GetDirectoryName(fileName) ?? string.Empty;
var taskXml = EmbedUtils.GetEmbedText(Global.TaskXmlTemplateFileName);
taskXml = taskXml
.Replace("{{DESCRIPTION}}", System.Security.SecurityElement.Escape(description ?? ""))
.Replace("{{EXEC_PATH}}", System.Security.SecurityElement.Escape(fileName ?? ""))
.Replace("{{ARGUMENTS}}", string.Empty)
.Replace("{{WORKING_DIRECTORY}}", System.Security.SecurityElement.Escape(workingDir ?? ""));
var taskXmlFileName = Utils.GetTempPath($"{Utils.GetGuid(false)}.xml");
File.WriteAllText(taskXmlFileName, taskXml);
RunSchtasks(
"/Create",
"/TN", taskName,
"/XML", taskXmlFileName,
"/F"
);
}
private static bool RunSchtasks(params string[] arguments)
{
var psi = new ProcessStartInfo
{
FileName = "schtasks.exe",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
foreach (var argument in arguments)
{
psi.ArgumentList.Add(argument);
}
using var process = Process.Start(psi);
if (process == null)
{
return false;
}
process.WaitForExit();
var output = process.StandardOutput.ReadToEnd();
var error = process.StandardError.ReadToEnd();
if (!string.IsNullOrEmpty(output))
{
Logging.SaveLog($"RunSchtasks output: {output}");
}
if (!string.IsNullOrEmpty(error))
{
Logging.SaveLog($"RunSchtasks error: {error}");
}
return process.ExitCode == 0;
} }
private static string GetAutoRunNameWindows() private static string GetAutoRunNameWindows()
+4 -24
View File
@@ -80,12 +80,7 @@ public class BaseFmt
{ {
var node = JsonUtils.ParseJson(item.Finalmask); var node = JsonUtils.ParseJson(item.Finalmask);
var finalmask = node != null var finalmask = node != null
? JsonUtils.Serialize(node, new JsonSerializerOptions ? JsonUtils.Serialize(node, indented: false, nullValue: true)
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
})
: item.Finalmask; : item.Finalmask;
dicQuery.Add("fm", Utils.UrlEncode(finalmask)); dicQuery.Add("fm", Utils.UrlEncode(finalmask));
} }
@@ -154,12 +149,7 @@ public class BaseFmt
{ {
var node = JsonUtils.ParseJson(transport.XhttpExtra); var node = JsonUtils.ParseJson(transport.XhttpExtra);
var extra = node != null var extra = node != null
? JsonUtils.Serialize(node, new JsonSerializerOptions ? JsonUtils.Serialize(node, indented: false, nullValue: true)
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
})
: transport.XhttpExtra; : transport.XhttpExtra;
dicQuery.Add("extra", UrlEncodeSafe(extra)); dicQuery.Add("extra", UrlEncodeSafe(extra));
} }
@@ -215,12 +205,7 @@ public class BaseFmt
{ {
var node = JsonUtils.ParseJson(finalmaskDecoded); var node = JsonUtils.ParseJson(finalmaskDecoded);
item.Finalmask = node != null item.Finalmask = node != null
? JsonUtils.Serialize(node, new JsonSerializerOptions ? JsonUtils.Serialize(node, indented: true, nullValue: true)
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
})
: finalmaskDecoded; : finalmaskDecoded;
} }
else else
@@ -278,12 +263,7 @@ public class BaseFmt
var node = JsonUtils.ParseJson(xhttpExtra); var node = JsonUtils.ParseJson(xhttpExtra);
if (node != null) if (node != null)
{ {
xhttpExtra = JsonUtils.Serialize(node, new JsonSerializerOptions xhttpExtra = JsonUtils.Serialize(node, indented: true, nullValue: true);
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
} }
} }
+1 -6
View File
@@ -179,12 +179,7 @@ public class InnerFmt : BaseFmt
if (jsonObj.TryGetPropertyValue("CustomOutboundObj", out var customOutboundNode) if (jsonObj.TryGetPropertyValue("CustomOutboundObj", out var customOutboundNode)
&& customOutboundNode is JsonObject customOutboundObj) && customOutboundNode is JsonObject customOutboundObj)
{ {
var customOutboundContent = JsonUtils.Serialize(customOutboundObj, new JsonSerializerOptions var customOutboundContent = JsonUtils.Serialize(customOutboundObj, indented: true, nullValue: true);
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
customOutboundFilePath = WriteAllText(customOutboundContent); customOutboundFilePath = WriteAllText(customOutboundContent);
jsonObj.Remove("CustomOutboundObj"); jsonObj.Remove("CustomOutboundObj");
} }
+2 -12
View File
@@ -29,12 +29,7 @@ public class WireguardFmt : BaseFmt
{ {
var node = JsonUtils.ParseJson(finalmaskDecoded); var node = JsonUtils.ParseJson(finalmaskDecoded);
item.Finalmask = node != null item.Finalmask = node != null
? JsonUtils.Serialize(node, new JsonSerializerOptions ? JsonUtils.Serialize(node, indented: true, nullValue: true)
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
})
: finalmaskDecoded; : finalmaskDecoded;
} }
else else
@@ -98,12 +93,7 @@ public class WireguardFmt : BaseFmt
{ {
var node = JsonUtils.ParseJson(item.Finalmask); var node = JsonUtils.ParseJson(item.Finalmask);
var finalmask = node != null var finalmask = node != null
? JsonUtils.Serialize(node, new JsonSerializerOptions ? JsonUtils.Serialize(node, indented: false, nullValue: true)
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
})
: item.Finalmask; : item.Finalmask;
dicQuery.Add("fm", Utils.UrlEncode(finalmask)); dicQuery.Add("fm", Utils.UrlEncode(finalmask));
} }
@@ -133,7 +133,7 @@ public static class ProxySettingWindows
list.dwOptionCount = options.Length; list.dwOptionCount = options.Length;
list.dwOptionError = 0; list.dwOptionError = 0;
var optSize = Marshal.SizeOf(typeof(InternetConnectionOption)); var optSize = Marshal.SizeOf<InternetConnectionOption>();
// make a pointer out of all that ... // make a pointer out of all that ...
var optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4 var optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
// copy the array over into that spot in memory ... // copy the array over into that spot in memory ...
@@ -207,17 +207,17 @@ public static class ProxySettingWindows
var entries = 0; var entries = 0;
// attempt to query with 1 entry buffer // attempt to query with 1 entry buffer
var rasEntryNames = new RASENTRYNAME[1]; var rasEntryNames = new RASENTRYNAME[1];
var bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME)); var bufferSize = Marshal.SizeOf<RASENTRYNAME>();
rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME)); rasEntryNames[0].dwSize = Marshal.SizeOf<RASENTRYNAME>();
var result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries); var result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
// increase buffer if the buffer is not large enough // increase buffer if the buffer is not large enough
if (result == (uint)ErrorCode.ERROR_BUFFER_TOO_SMALL) if (result == (uint)ErrorCode.ERROR_BUFFER_TOO_SMALL)
{ {
rasEntryNames = new RASENTRYNAME[bufferSize / Marshal.SizeOf(typeof(RASENTRYNAME))]; rasEntryNames = new RASENTRYNAME[bufferSize / Marshal.SizeOf<RASENTRYNAME>()];
for (var i = 0; i < rasEntryNames.Length; i++) for (var i = 0; i < rasEntryNames.Length; i++)
{ {
rasEntryNames[i].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME)); rasEntryNames[i].dwSize = Marshal.SizeOf<RASENTRYNAME>();
} }
result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries); result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
@@ -258,7 +258,7 @@ public static class ProxySettingWindows
static InternetConnectionOption() static InternetConnectionOption()
{ {
Size = Marshal.SizeOf(typeof(InternetConnectionOption)); Size = Marshal.SizeOf<InternetConnectionOption>();
} }
// Nested Types // Nested Types
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Configs; namespace ServiceLib.Models.Configs;
[Serializable]
public class Config public class Config
{ {
#region property #region property
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Configs; namespace ServiceLib.Models.Configs;
[Serializable]
public class CoreBasicItem public class CoreBasicItem
{ {
public bool LogEnabled { get; set; } public bool LogEnabled { get; set; }
@@ -22,7 +21,6 @@ public class CoreBasicItem
public bool EnableCacheFile4Sbox { get; set; } = true; public bool EnableCacheFile4Sbox { get; set; } = true;
} }
[Serializable]
public class InItem public class InItem
{ {
public int LocalPort { get; set; } public int LocalPort { get; set; }
@@ -38,7 +36,6 @@ public class InItem
public bool SecondLocalPortEnabled { get; set; } public bool SecondLocalPortEnabled { get; set; }
} }
[Serializable]
public class KcpItem public class KcpItem
{ {
public int Mtu { get; set; } public int Mtu { get; set; }
@@ -54,7 +51,6 @@ public class KcpItem
public int MaxSendingWindow { get; set; } public int MaxSendingWindow { get; set; }
} }
[Serializable]
public class GrpcItem public class GrpcItem
{ {
public int? IdleTimeout { get; set; } public int? IdleTimeout { get; set; }
@@ -63,7 +59,6 @@ public class GrpcItem
public int? InitialWindowsSize { get; set; } public int? InitialWindowsSize { get; set; }
} }
[Serializable]
public class GUIItem public class GUIItem
{ {
public bool AutoRun { get; set; } public bool AutoRun { get; set; }
@@ -77,14 +72,12 @@ public class GUIItem
public string? RootCertProvider { get; set; } public string? RootCertProvider { get; set; }
} }
[Serializable]
public class MsgUIItem public class MsgUIItem
{ {
public string? MainMsgFilter { get; set; } public string? MainMsgFilter { get; set; }
public bool? AutoRefresh { get; set; } public bool? AutoRefresh { get; set; }
} }
[Serializable]
public class UIItem public class UIItem
{ {
public bool EnableAutoAdjustMainLvColWidth { get; set; } public bool EnableAutoAdjustMainLvColWidth { get; set; }
@@ -106,7 +99,6 @@ public class UIItem
public bool HideColumnIpInfo { get; set; } public bool HideColumnIpInfo { get; set; }
} }
[Serializable]
public class ConstItem public class ConstItem
{ {
public string? SubConvertUrl { get; set; } public string? SubConvertUrl { get; set; }
@@ -115,7 +107,6 @@ public class ConstItem
public string? RouteRulesTemplateSourceUrl { get; set; } public string? RouteRulesTemplateSourceUrl { get; set; }
} }
[Serializable]
public class KeyEventItem public class KeyEventItem
{ {
public EGlobalHotkey EGlobalHotkey { get; set; } public EGlobalHotkey EGlobalHotkey { get; set; }
@@ -129,7 +120,6 @@ public class KeyEventItem
public int? KeyCode { get; set; } public int? KeyCode { get; set; }
} }
[Serializable]
public class CoreTypeItem public class CoreTypeItem
{ {
public EConfigType ConfigType { get; set; } public EConfigType ConfigType { get; set; }
@@ -137,7 +127,6 @@ public class CoreTypeItem
public ECoreType CoreType { get; set; } public ECoreType CoreType { get; set; }
} }
[Serializable]
public class TunModeItem public class TunModeItem
{ {
public bool EnableTun { get; set; } public bool EnableTun { get; set; }
@@ -153,7 +142,6 @@ public class TunModeItem
public string IPv6Address { get; set; } public string IPv6Address { get; set; }
} }
[Serializable]
public class SpeedTestItem public class SpeedTestItem
{ {
public int SpeedTestTimeout { get; set; } public int SpeedTestTimeout { get; set; }
@@ -166,7 +154,6 @@ public class SpeedTestItem
public int? SpeedTestDelayInterval { get; set; } public int? SpeedTestDelayInterval { get; set; }
} }
[Serializable]
public class RoutingBasicItem public class RoutingBasicItem
{ {
public string DomainStrategy { get; set; } public string DomainStrategy { get; set; }
@@ -174,7 +161,6 @@ public class RoutingBasicItem
public string RoutingIndexId { get; set; } public string RoutingIndexId { get; set; }
} }
[Serializable]
public class ColumnItem public class ColumnItem
{ {
public string Name { get; set; } public string Name { get; set; }
@@ -182,7 +168,6 @@ public class ColumnItem
public int Index { get; set; } public int Index { get; set; }
} }
[Serializable]
public class Mux4RayItem public class Mux4RayItem
{ {
public int? Concurrency { get; set; } public int? Concurrency { get; set; }
@@ -190,7 +175,6 @@ public class Mux4RayItem
public string? XudpProxyUDP443 { get; set; } public string? XudpProxyUDP443 { get; set; }
} }
[Serializable]
public class Mux4SboxItem public class Mux4SboxItem
{ {
public string Protocol { get; set; } public string Protocol { get; set; }
@@ -198,7 +182,6 @@ public class Mux4SboxItem
public bool? Padding { get; set; } public bool? Padding { get; set; }
} }
[Serializable]
public class HysteriaItem public class HysteriaItem
{ {
public int UpMbps { get; set; } public int UpMbps { get; set; }
@@ -206,7 +189,6 @@ public class HysteriaItem
public int HopInterval { get; set; } = Global.Hysteria2DefaultHopInt; public int HopInterval { get; set; } = Global.Hysteria2DefaultHopInt;
} }
[Serializable]
public class ClashUIItem public class ClashUIItem
{ {
public bool EnableIPv6 { get; set; } public bool EnableIPv6 { get; set; }
@@ -219,7 +201,6 @@ public class ClashUIItem
public List<ColumnItem> ConnectionsColumnItem { get; set; } public List<ColumnItem> ConnectionsColumnItem { get; set; }
} }
[Serializable]
public class SystemProxyItem public class SystemProxyItem
{ {
public ESysProxyType SysProxyType { get; set; } public ESysProxyType SysProxyType { get; set; }
@@ -230,7 +211,6 @@ public class SystemProxyItem
public string? CustomSystemProxyScriptPath { get; set; } public string? CustomSystemProxyScriptPath { get; set; }
} }
[Serializable]
public class WebDavItem public class WebDavItem
{ {
public string? Url { get; set; } public string? Url { get; set; }
@@ -239,7 +219,6 @@ public class WebDavItem
public string? DirName { get; set; } public string? DirName { get; set; }
} }
[Serializable]
public class CheckUpdateItem public class CheckUpdateItem
{ {
public bool UpdateViaProxy { get; set; } = true; public bool UpdateViaProxy { get; set; } = true;
@@ -247,7 +226,6 @@ public class CheckUpdateItem
public List<string>? CheckPreReleaseCoreTypes { get; set; } public List<string>? CheckPreReleaseCoreTypes { get; set; }
} }
[Serializable]
public class Fragment4RayItem public class Fragment4RayItem
{ {
public string? Packets { get; set; } public string? Packets { get; set; }
@@ -262,7 +240,6 @@ public class Fragment4RayItem
// migration end // migration end
} }
[Serializable]
public class WindowSizeItem public class WindowSizeItem
{ {
public string TypeName { get; set; } public string TypeName { get; set; }
@@ -270,7 +247,6 @@ public class WindowSizeItem
public int Height { get; set; } public int Height { get; set; }
} }
[Serializable]
public class SimpleDNSItem public class SimpleDNSItem
{ {
public bool? UseSystemHosts { get; set; } public bool? UseSystemHosts { get; set; }
@@ -293,7 +269,6 @@ public class SimpleDNSItem
public bool? EnableHappyEyeballs { get; set; } public bool? EnableHappyEyeballs { get; set; }
} }
[Serializable]
public class HappyEyeballs4RayItem public class HappyEyeballs4RayItem
{ {
public int? TryDelayMs { get; set; } public int? TryDelayMs { get; set; }
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.CoreConfigs; namespace ServiceLib.Models.CoreConfigs;
[Serializable]
public class CoreInfo public class CoreInfo
{ {
public ECoreType CoreType { get; set; } public ECoreType CoreType { get; set; }
@@ -44,7 +44,6 @@ public class Route4Sbox
public string? final { get; set; } public string? final { get; set; }
} }
[Serializable]
public class Rule4Sbox public class Rule4Sbox
{ {
public string? outbound { get; set; } public string? outbound { get; set; }
@@ -103,7 +102,6 @@ public class Rule4Sbox
public bool? speculative { get; set; } public bool? speculative { get; set; }
} }
[Serializable]
public class Inbound4Sbox public class Inbound4Sbox
{ {
public string type { get; set; } public string type { get; set; }
@@ -232,7 +230,7 @@ public class Reality4Sbox
public class Transport4Sbox public class Transport4Sbox
{ {
public string? type { get; set; } public string? type { get; set; }
public object? host { get; set; } public JsonNode? host { get; set; }
public string? path { get; set; } public string? path { get; set; }
public Headers4Sbox? headers { get; set; } public Headers4Sbox? headers { get; set; }
@@ -3,7 +3,7 @@ namespace ServiceLib.Models.CoreConfigs;
public class V2rayConfig public class V2rayConfig
{ {
public Log4Ray log { get; set; } public Log4Ray log { get; set; }
public object dns { get; set; } public JsonObject? dns { get; set; }
public FakeDns4Ray? fakedns { get; set; } public FakeDns4Ray? fakedns { get; set; }
public List<Inbounds4Ray> inbounds { get; set; } public List<Inbounds4Ray> inbounds { get; set; }
public List<Outbounds4Ray> outbounds { get; set; } public List<Outbounds4Ray> outbounds { get; set; }
@@ -130,7 +130,7 @@ public class Outboundsettings4Ray
public string? secretKey { get; set; } public string? secretKey { get; set; }
public object? address { get; set; } public JsonNode? address { get; set; }
public int? port { get; set; } public int? port { get; set; }
@@ -142,7 +142,7 @@ public class Outboundsettings4Ray
public string? email { get; set; } public string? email { get; set; }
public object? headers { get; set; } public JsonNode? headers { get; set; }
public List<WireguardPeer4Ray>? peers { get; set; } public List<WireguardPeer4Ray>? peers { get; set; }
@@ -199,8 +199,8 @@ public class Response4Ray
public class Dns4Ray public class Dns4Ray
{ {
public Dictionary<string, object>? hosts { get; set; } public Dictionary<string, JsonNode>? hosts { get; set; }
public List<object> servers { get; set; } public List<JsonNode> servers { get; set; }
public bool? serveStale { get; set; } public bool? serveStale { get; set; }
public bool? enableParallelQuery { get; set; } public bool? enableParallelQuery { get; set; }
public string? queryStrategy { get; set; } public string? queryStrategy { get; set; }
@@ -226,7 +226,6 @@ public class Routing4Ray
public List<BalancersItem4Ray>? balancers { get; set; } public List<BalancersItem4Ray>? balancers { get; set; }
} }
[Serializable]
public class RulesItem4Ray public class RulesItem4Ray
{ {
public string? type { get; set; } public string? type { get; set; }
@@ -321,7 +320,7 @@ public class StreamSettings4Ray
public HysteriaSettings4Ray? hysteriaSettings { get; set; } public HysteriaSettings4Ray? hysteriaSettings { get; set; }
public MasqueSettings4Ray? masqueSettings { get; set; } public MasqueSettings4Ray? masqueSettings { get; set; }
public object? finalmask { get; set; } public JsonObject? finalmask { get; set; }
public Sockopt4Ray? sockopt { get; set; } public Sockopt4Ray? sockopt { get; set; }
} }
@@ -363,9 +362,9 @@ public class Header4Ray
{ {
public string type { get; set; } public string type { get; set; }
public object request { get; set; } public JsonObject request { get; set; }
public object response { get; set; } public JsonObject response { get; set; }
} }
public class KcpSettings4Ray public class KcpSettings4Ray
@@ -411,7 +410,7 @@ public class XhttpSettings4Ray
public string? path { get; set; } public string? path { get; set; }
public string? host { get; set; } public string? host { get; set; }
public string? mode { get; set; } public string? mode { get; set; }
public object? extra { get; set; } public JsonObject? extra { get; set; }
} }
public class HttpSettings4Ray public class HttpSettings4Ray
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Dto; namespace ServiceLib.Models.Dto;
[Serializable]
public partial class ClashProxyModel : ReactiveObject public partial class ClashProxyModel : ReactiveObject
{ {
public string Name { get; set; } public string Name { get; set; }
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Dto; namespace ServiceLib.Models.Dto;
[Serializable]
public partial class ProfileItemModel : ReactiveObject public partial class ProfileItemModel : ReactiveObject
{ {
public bool IsActive { get; set; } public bool IsActive { get; set; }
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Dto; namespace ServiceLib.Models.Dto;
[Serializable]
public class RoutingItemModel : RoutingItem public class RoutingItemModel : RoutingItem
{ {
} }
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Dto; namespace ServiceLib.Models.Dto;
[Serializable]
public class RoutingTemplate public class RoutingTemplate
{ {
public string Version { get; set; } public string Version { get; set; }
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Dto; namespace ServiceLib.Models.Dto;
[Serializable]
public class RulesItemModel : RulesItem public class RulesItemModel : RulesItem
{ {
public string InboundTags { get; set; } public string InboundTags { get; set; }
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Dto; namespace ServiceLib.Models.Dto;
[Serializable]
public class ServerSpeedItem : ServerStatItem public class ServerSpeedItem : ServerStatItem
{ {
public long ProxyUp { get; set; } public long ProxyUp { get; set; }
@@ -12,7 +11,6 @@ public class ServerSpeedItem : ServerStatItem
public long DirectDown { get; set; } public long DirectDown { get; set; }
} }
[Serializable]
public class TrafficItem public class TrafficItem
{ {
public ulong Up { get; set; } public ulong Up { get; set; }
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Dto; namespace ServiceLib.Models.Dto;
[Serializable]
public class ServerTestItem public class ServerTestItem
{ {
public string? IndexId { get; set; } public string? IndexId { get; set; }
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Dto; namespace ServiceLib.Models.Dto;
[Serializable]
public class SpeedTestResult public class SpeedTestResult
{ {
public string? IndexId { get; set; } public string? IndexId { get; set; }
-1
View File
@@ -5,7 +5,6 @@ public class SsSIP008
public List<SsServer>? servers { get; set; } public List<SsServer>? servers { get; set; }
} }
[Serializable]
public class SsServer public class SsServer
{ {
public string? remarks { get; set; } public string? remarks { get; set; }
@@ -3,7 +3,6 @@ namespace ServiceLib.Models.Dto;
/// <summary> /// <summary>
/// https://github.com/2dust/v2rayN/wiki/ /// https://github.com/2dust/v2rayN/wiki/
/// </summary> /// </summary>
[Serializable]
public class VmessQRCode public class VmessQRCode
{ {
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)] [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Entities; namespace ServiceLib.Models.Entities;
[Serializable]
public class DNSItem public class DNSItem
{ {
[PrimaryKey] [PrimaryKey]
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Entities; namespace ServiceLib.Models.Entities;
[Serializable]
public class FullConfigTemplateItem public class FullConfigTemplateItem
{ {
[PrimaryKey] [PrimaryKey]
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Entities; namespace ServiceLib.Models.Entities;
[Serializable]
public class ProfileExItem public class ProfileExItem
{ {
[PrimaryKey] [PrimaryKey]
@@ -1,7 +1,6 @@
namespace ServiceLib.Models.Entities; namespace ServiceLib.Models.Entities;
[Obsolete("Use ProtocolExtraItem instead.")] [Obsolete("Use ProtocolExtraItem instead.")]
[Serializable]
public class ProfileGroupItem public class ProfileGroupItem
{ {
[PrimaryKey] [PrimaryKey]
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Entities; namespace ServiceLib.Models.Entities;
[Serializable]
public class ProfileItem public class ProfileItem
{ {
private ProtocolExtraItem? _protocolExtraCache; private ProtocolExtraItem? _protocolExtraCache;
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Entities; namespace ServiceLib.Models.Entities;
[Serializable]
public class RoutingItem public class RoutingItem
{ {
[PrimaryKey] [PrimaryKey]
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Entities; namespace ServiceLib.Models.Entities;
[Serializable]
public class RulesItem public class RulesItem
{ {
public string Id { get; set; } public string Id { get; set; }
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Entities; namespace ServiceLib.Models.Entities;
[Serializable]
public class ServerStatItem public class ServerStatItem
{ {
[PrimaryKey] [PrimaryKey]
@@ -1,6 +1,5 @@
namespace ServiceLib.Models.Entities; namespace ServiceLib.Models.Entities;
[Serializable]
public class SubItem public class SubItem
{ {
[PrimaryKey] [PrimaryKey]
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Description>{{DESCRIPTION}}</Description>
</RegistrationInfo>
<Triggers>
<LogonTrigger>
<Enabled>true</Enabled>
</LogonTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Actions Context="Author">
<Exec>
<Command>{{EXEC_PATH}}</Command>
<Arguments>{{ARGUMENTS}}</Arguments>
<WorkingDirectory>{{WORKING_DIRECTORY}}</WorkingDirectory>
</Exec>
</Actions>
</Task>
+2 -2
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
@@ -18,7 +18,6 @@
<PackageReference Include="QRCoder" /> <PackageReference Include="QRCoder" />
<PackageReference Include="CliWrap" /> <PackageReference Include="CliWrap" />
<PackageReference Include="ZXing.Net.Bindings.SkiaSharp" /> <PackageReference Include="ZXing.Net.Bindings.SkiaSharp" />
<PackageReference Include="TaskScheduler" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -45,6 +44,7 @@
<EmbeddedResource Include="Sample\SampleTunRules" /> <EmbeddedResource Include="Sample\SampleTunRules" />
<EmbeddedResource Include="Sample\SingboxSampleClientConfig" /> <EmbeddedResource Include="Sample\SingboxSampleClientConfig" />
<EmbeddedResource Include="Sample\SingboxSampleOutbound" /> <EmbeddedResource Include="Sample\SingboxSampleOutbound" />
<EmbeddedResource Include="Sample\TaskXmlTemplate" />
<EmbeddedResource Include="Sample\tun_singbox_dns" /> <EmbeddedResource Include="Sample\tun_singbox_dns" />
<EmbeddedResource Include="Sample\tun_singbox_inbound" /> <EmbeddedResource Include="Sample\tun_singbox_inbound" />
<EmbeddedResource Include="Sample\tun_singbox_rules" /> <EmbeddedResource Include="Sample\tun_singbox_rules" />
@@ -61,10 +61,10 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
//YAML anchors //YAML anchors
if (txtFile.Contains("<<:") && txtFile.Contains('*') && txtFile.Contains('&')) if (txtFile.Contains("<<:") && txtFile.Contains('*') && txtFile.Contains('&'))
{ {
txtFile = YamlUtils.PreprocessYaml(txtFile); txtFile = YamlUtils.PreprocessYaml(txtFile) ?? "";
} }
var fileContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile); var fileContent = YamlUtils.FromYaml(txtFile);
if (fileContent == null) if (fileContent == null)
{ {
ret.Msg = ResUI.FailedConversionConfiguration; ret.Msg = ResUI.FailedConversionConfiguration;
@@ -72,29 +72,38 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
} }
//mixed-port //mixed-port
fileContent["mixed-port"] = AppManager.Instance.GetLocalPort(EInboundProtocol.socks); //fileContent["mixed-port"] = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
fileContent.Children[new YamlScalarNode("mixed-port")] = new YamlScalarNode(AppManager.Instance.GetLocalPort(EInboundProtocol.socks).ToString());
//log-level //log-level
fileContent["log-level"] = GetLogLevel(config.CoreBasicItem.Loglevel); //fileContent["log-level"] = GetLogLevel(config.CoreBasicItem.Loglevel);
fileContent.Children[new YamlScalarNode("log-level")] = new YamlScalarNode(GetLogLevel(config.CoreBasicItem.Loglevel));
//external-controller //external-controller
fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}"; //fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}";
fileContent.Remove("secret"); fileContent.Children[new YamlScalarNode("external-controller")] = new YamlScalarNode($"{Global.Loopback}:{AppManager.Instance.StatePort2}");
//fileContent.Remove("secret");
fileContent.Children.Remove(new YamlScalarNode("secret"));
//allow-lan //allow-lan
if (config.Inbound.First().AllowLANConn) if (config.Inbound.First().AllowLANConn)
{ {
fileContent["allow-lan"] = "true"; //fileContent["allow-lan"] = "true";
fileContent["bind-address"] = "*"; fileContent.Children[new YamlScalarNode("allow-lan")] = new YamlScalarNode("true");
//fileContent["bind-address"] = "*";
fileContent.Children[new YamlScalarNode("bind-address")] = new YamlScalarNode("*");
} }
else else
{ {
fileContent["allow-lan"] = "false"; //fileContent["allow-lan"] = "false";
fileContent.Children[new YamlScalarNode("allow-lan")] = new YamlScalarNode("false");
} }
//ipv6 //ipv6
fileContent["ipv6"] = config.ClashUIItem.EnableIPv6; //fileContent["ipv6"] = config.ClashUIItem.EnableIPv6;
fileContent.Children[new YamlScalarNode("ipv6")] = new YamlScalarNode(config.ClashUIItem.EnableIPv6.ToString().ToLowerInvariant());
//mode //mode
fileContent.TryAdd("mode", nameof(ERuleMode.Rule)); //fileContent.TryAdd("mode", nameof(ERuleMode.Rule));
fileContent.Children.TryAdd(new YamlScalarNode("mode"), new YamlScalarNode(nameof(ERuleMode.Rule)));
//enable tun mode //enable tun mode
if (isTunEnabled) if (isTunEnabled)
@@ -102,10 +111,11 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
var tun = EmbedUtils.GetEmbedText(Global.ClashTunYaml); var tun = EmbedUtils.GetEmbedText(Global.ClashTunYaml);
if (tun.IsNotEmpty()) if (tun.IsNotEmpty())
{ {
var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun); var tunContent = YamlUtils.FromYaml(tun);
if (tunContent != null) if (tunContent != null)
{ {
fileContent["tun"] = tunContent["tun"]; //fileContent["tun"] = tunContent["tun"];
fileContent.Children[new YamlScalarNode("tun")] = tunContent.Children[new YamlScalarNode("tun")];
} }
} }
} }
@@ -120,27 +130,27 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
Logging.SaveLog($"{_tag}-Mixin", ex); Logging.SaveLog($"{_tag}-Mixin", ex);
} }
// Mihomo parses plain values such as 815458e4 as floats, so quote REALITY short IDs. //// Mihomo parses plain values such as 815458e4 as floats, so quote REALITY short IDs.
var originalRealityShortIds = new List<(Dictionary<object, object> RealityOptions, string ShortId)>(); //var originalRealityShortIds = new List<(Dictionary<object, object> RealityOptions, string ShortId)>();
if (fileContent.GetValueOrDefault("proxies") is List<object> proxies) //if (fileContent.GetValueOrDefault("proxies") is List<object> proxies)
{ //{
foreach (var proxy in proxies.OfType<Dictionary<object, object>>()) // foreach (var proxy in proxies.OfType<Dictionary<object, object>>())
{ // {
if (proxy.GetValueOrDefault("reality-opts") is Dictionary<object, object> realityOptions // if (proxy.GetValueOrDefault("reality-opts") is Dictionary<object, object> realityOptions
&& realityOptions.GetValueOrDefault("short-id") is string shortId // && realityOptions.GetValueOrDefault("short-id") is string shortId
&& !shortId.StartsWith(tagYamlStr2, StringComparison.Ordinal)) // && !shortId.StartsWith(tagYamlStr2, StringComparison.Ordinal))
{ // {
originalRealityShortIds.Add((realityOptions, shortId)); // originalRealityShortIds.Add((realityOptions, shortId));
realityOptions["short-id"] = new YamlScalarNode(shortId) { Style = ScalarStyle.DoubleQuoted }; // realityOptions["short-id"] = new YamlScalarNode(shortId) { Style = ScalarStyle.DoubleQuoted };
} // }
} // }
} //}
var txtFileNew = YamlUtils.ToYaml(fileContent).Replace(tagYamlStr2, tagYamlStr3); var txtFileNew = YamlUtils.ToYaml(fileContent).Replace(tagYamlStr2, tagYamlStr3);
foreach (var (realityOptions, shortId) in originalRealityShortIds) //foreach (var (realityOptions, shortId) in originalRealityShortIds)
{ //{
realityOptions["short-id"] = shortId; // realityOptions["short-id"] = shortId;
} //}
await File.WriteAllTextAsync(fileName, txtFileNew); await File.WriteAllTextAsync(fileName, txtFileNew);
//check again //check again
if (!File.Exists(fileName)) if (!File.Exists(fileName))
@@ -161,7 +171,7 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
} }
} }
private async Task MixinContent(Dictionary<string, object> fileContent, ProfileItem node) private async Task MixinContent(YamlMappingNode fileContent, ProfileItem node)
{ {
if (!config.ClashUIItem.EnableMixinContent) if (!config.ClashUIItem.EnableMixinContent)
{ {
@@ -177,80 +187,116 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
var txtFile = await File.ReadAllTextAsync(Utils.GetConfigPath(Global.ClashMixinConfigFileName)); var txtFile = await File.ReadAllTextAsync(Utils.GetConfigPath(Global.ClashMixinConfigFileName));
var mixinContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile); var mixinContent = YamlUtils.FromYaml(txtFile);
if (mixinContent == null) if (mixinContent == null)
{ {
return; return;
} }
foreach (var item in mixinContent) foreach (var (keyNode, valueNode) in mixinContent.Children)
{ {
if (!isTunEnabled && item.Key == "tun") if (keyNode is not YamlScalarNode scalarKey)
{
continue;
}
var key = scalarKey.Value ?? "";
if (!isTunEnabled && key == "tun")
{ {
continue; continue;
} }
if (item.Key.StartsWith("prepend-") if (key.StartsWith("prepend-")
|| item.Key.StartsWith("append-") || key.StartsWith("append-")
|| item.Key.StartsWith("removed-")) || key.StartsWith("removed-"))
{ {
ModifyContentMerge(fileContent, item.Key, item.Value); ModifyContentMerge(fileContent, key, valueNode);
} }
else else
{ {
fileContent[item.Key] = item.Value; fileContent.Children[keyNode] = valueNode;
} }
} }
return; return;
} }
private void ModifyContentMerge(Dictionary<string, object> fileContent, string key, object value) private void ModifyContentMerge(YamlMappingNode fileContent, YamlNode keyNode, YamlNode valueNode)
{ {
if (keyNode is not YamlScalarNode { Value: string key })
{
return;
}
var blPrepend = false; var blPrepend = false;
var blRemoved = false; var blRemoved = false;
string realKey;
if (key.StartsWith("prepend-")) if (key.StartsWith("prepend-"))
{ {
blPrepend = true; blPrepend = true;
key = key.Replace("prepend-", ""); realKey = key["prepend-".Length..];
} }
else if (key.StartsWith("append-")) else if (key.StartsWith("append-"))
{ {
blPrepend = false; realKey = key["append-".Length..];
key = key.Replace("append-", "");
} }
else if (key.StartsWith("removed-")) else if (key.StartsWith("removed-"))
{ {
blRemoved = true; blRemoved = true;
key = key.Replace("removed-", ""); realKey = key["removed-".Length..];
} }
else else
{ {
return; return;
} }
if (!blRemoved && fileContent.TryAdd(key, value)) var targetKeyNode = new YamlScalarNode(realKey);
if (!blRemoved && fileContent.Children.TryAdd(targetKeyNode, valueNode))
{
return;
}
if (!fileContent.Children.TryGetValue(targetKeyNode, out var existingNode) ||
existingNode is not YamlSequenceNode lstOri ||
valueNode is not YamlSequenceNode lstValue)
{ {
return; return;
} }
var lstOri = (List<object>)fileContent[key];
var lstValue = (List<object>)value;
if (blRemoved) if (blRemoved)
{ {
foreach (var item in lstValue) var removePrefixes = lstValue.Children
.OfType<YamlScalarNode>()
.Select(s => s.Value)
.Where(v => !string.IsNullOrEmpty(v))
.ToList();
for (var i = lstOri.Children.Count - 1; i >= 0; i--)
{ {
lstOri.RemoveAll(t => t.ToString().StartsWith(item.ToString())); if (lstOri.Children[i] is YamlScalarNode { Value: not null } itemScalar)
{
if (removePrefixes.Any(prefix => itemScalar.Value.StartsWith(prefix!)))
{
lstOri.Children.RemoveAt(i);
}
}
} }
return; return;
} }
if (blPrepend) if (blPrepend)
{ {
lstValue.Reverse(); // Insert in reverse order to maintain original sequence at index 0
lstValue.ForEach(item => lstOri.Insert(0, item)); for (var i = lstValue.Children.Count - 1; i >= 0; i--)
{
lstOri.Children.Insert(0, lstValue.Children[i]);
}
} }
else else
{ {
lstValue.ForEach(lstOri.Add); foreach (var item in lstValue.Children)
{
lstOri.Children.Add(item);
}
} }
} }
@@ -525,7 +525,7 @@ public partial class CoreConfigSingboxService
transport.type = nameof(ETransport.http); transport.type = nameof(ETransport.http);
transport.host = transportExtra.Host.IsNullOrEmpty() transport.host = transportExtra.Host.IsNullOrEmpty()
? null ? null
: Utils.String2List(transportExtra.Host); : JsonUtils.SerializeToArray(Utils.String2List(transportExtra.Host));
transport.path = transportExtra.Path.NullIfEmpty(); transport.path = transportExtra.Path.NullIfEmpty();
if (!useragentValue.IsNullOrEmpty()) if (!useragentValue.IsNullOrEmpty())
{ {
@@ -17,14 +17,14 @@ public partial class CoreConfigV2rayService
} }
// DNS routing // DNS routing
var dnsObj = JsonUtils.SerializeToNode(_coreConfig.dns); var dnsNode = JsonUtils.SerializeToNode(_coreConfig.dns);
if (dnsObj == null) if (dnsNode is not JsonObject dnsObj)
{ {
return; return;
} }
dnsObj["tag"] = Global.DnsTag; dnsObj["tag"] = Global.DnsTag;
_coreConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(dnsObj)); _coreConfig.dns = dnsObj;
_coreConfig.routing.rules.Add(new RulesItem4Ray _coreConfig.routing.rules.Add(new RulesItem4Ray
{ {
type = "field", type = "field",
@@ -34,7 +34,7 @@ public partial class CoreConfigV2rayService
return; return;
} }
var simpleDnsItem = context.SimpleDnsItem; var simpleDnsItem = context.SimpleDnsItem;
var dnsItem = _coreConfig.dns as Dns4Ray ?? new Dns4Ray(); var dnsItem = new Dns4Ray();
var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs; var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs;
//Outbound Freedom domainStrategy //Outbound Freedom domainStrategy
@@ -117,7 +117,7 @@ public partial class CoreConfigV2rayService
balancerTag = finalRule.balancerTag, balancerTag = finalRule.balancerTag,
}); });
_coreConfig.dns = dnsItem; _coreConfig.dns = (JsonObject)JsonUtils.SerializeToNode(dnsItem);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -317,7 +317,7 @@ public partial class CoreConfigV2rayService
if (!useDirectDns) if (!useDirectDns)
{ {
dnsItem.servers.AddRange(remoteDNSAddress); dnsItem.servers.AddRange(JsonUtils.SerializeToArray(remoteDNSAddress));
} }
else else
{ {
@@ -326,8 +326,7 @@ public partial class CoreConfigV2rayService
var dnsServer = CreateDnsServer(dns, []); var dnsServer = CreateDnsServer(dns, []);
dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}"; dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}";
dnsServer.skipFallback = false; dnsServer.skipFallback = false;
dnsItem.servers.Add(JsonUtils.SerializeToNode(dnsServer, dnsItem.servers.Add(JsonUtils.SerializeToNode(dnsServer));
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }));
} }
} }
return; return;
@@ -382,8 +381,7 @@ public partial class CoreConfigV2rayService
{ {
dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}"; dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}";
} }
var dnsServerNode = JsonUtils.SerializeToNode(dnsServer, var dnsServerNode = JsonUtils.SerializeToNode(dnsServer);
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
dnsItem.servers.Add(dnsServerNode); dnsItem.servers.Add(dnsServerNode);
} }
} }
@@ -396,12 +394,12 @@ public partial class CoreConfigV2rayService
{ {
return; return;
} }
dnsItem.hosts ??= new Dictionary<string, object>(); dnsItem.hosts ??= new Dictionary<string, JsonNode>();
if (simpleDNSItem.AddCommonHosts == true) if (simpleDNSItem.AddCommonHosts == true)
{ {
dnsItem.hosts = Global.PredefinedHosts.ToDictionary( dnsItem.hosts = Global.PredefinedHosts.ToDictionary(
kvp => kvp.Key, kvp => kvp.Key,
kvp => (object)kvp.Value kvp => (JsonNode)JsonUtils.SerializeToArray(kvp.Value)
); );
} }
@@ -414,14 +412,14 @@ public partial class CoreConfigV2rayService
{ {
foreach (var host in systemHosts) foreach (var host in systemHosts)
{ {
normalHost.TryAdd(host.Key, new List<string> { host.Value }); normalHost.TryAdd(host.Key, JsonUtils.SerializeToArray(new List<string> { host.Value }));
} }
} }
} }
foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts)) foreach (var kvp in Utils.ParseHostsToDictionary(simpleDNSItem.Hosts))
{ {
dnsItem.hosts[kvp.Key] = kvp.Value; dnsItem.hosts[kvp.Key] = JsonUtils.SerializeToArray(kvp.Value);
} }
} }
@@ -449,8 +447,8 @@ public partial class CoreConfigV2rayService
} }
} }
var obj = JsonUtils.ParseJson(customDNS); var node = JsonUtils.ParseJson(customDNS);
if (obj is null) if (node is not JsonObject obj)
{ {
List<string> servers = []; List<string> servers = [];
var arrDNS = customDNS.Split(','); var arrDNS = customDNS.Split(',');
@@ -458,8 +456,7 @@ public partial class CoreConfigV2rayService
{ {
servers.Add(str); servers.Add(str);
} }
obj = JsonUtils.ParseJson("{}"); obj = new JsonObject { ["servers"] = JsonUtils.SerializeToNode(servers) };
obj["servers"] = JsonUtils.SerializeToNode(servers);
} }
// Append to dns settings // Append to dns settings
@@ -203,7 +203,7 @@ public partial class CoreConfigV2rayService
}; };
var setting = new Outboundsettings4Ray var setting = new Outboundsettings4Ray
{ {
address = Utils.String2List(protocolExtra.WgInterfaceAddress)?.Select(s => s.Trim()).ToList() ?? ["172.16.0.2/32"], address = JsonUtils.SerializeToArray(Utils.String2List(protocolExtra.WgInterfaceAddress)?.Select(s => s.Trim()).ToList() ?? ["172.16.0.2/32"]),
secretKey = _node.Password, secretKey = _node.Password,
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(s => s.Trim()).Select(int.Parse).ToList(), reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(s => s.Trim()).Select(int.Parse).ToList(),
mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First(), mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First(),
@@ -428,7 +428,7 @@ public partial class CoreConfigV2rayService
} }
kcpFinalmask.udp?.Reverse(); kcpFinalmask.udp?.Reverse();
streamSettings.kcpSettings = kcpSettings; streamSettings.kcpSettings = kcpSettings;
streamSettings.finalmask = kcpFinalmask; streamSettings.finalmask = (JsonObject)JsonUtils.SerializeToNode(kcpFinalmask);
break; break;
//ws //ws
case nameof(ETransport.ws): case nameof(ETransport.ws):
@@ -489,7 +489,7 @@ public partial class CoreConfigV2rayService
} }
if (xhttpExtra.IsNotEmpty()) if (xhttpExtra.IsNotEmpty())
{ {
xhttpSettings.extra = JsonUtils.ParseJson(xhttpExtra); xhttpSettings.extra = JsonUtils.ParseJson(xhttpExtra) as JsonObject;
} }
streamSettings.xhttpSettings = xhttpSettings; streamSettings.xhttpSettings = xhttpSettings;
@@ -581,7 +581,7 @@ public partial class CoreConfigV2rayService
auth = _node.Password, auth = _node.Password,
}; };
hy2Finalmask.udp?.Reverse(); hy2Finalmask.udp?.Reverse();
streamSettings.finalmask = hy2Finalmask; streamSettings.finalmask = (JsonObject)JsonUtils.SerializeToNode(hy2Finalmask);
break; break;
case "masque": case "masque":
@@ -637,7 +637,7 @@ public partial class CoreConfigV2rayService
pathHttp = string.Join(",".AppendQuotes(), arrPath); pathHttp = string.Join(",".AppendQuotes(), arrPath);
} }
request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}"); request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
rawSettings.header.request = JsonUtils.Deserialize<object>(request); rawSettings.header.request = JsonUtils.SerializeToNode(request) as JsonObject;
streamSettings.rawSettings = rawSettings; streamSettings.rawSettings = rawSettings;
} }
@@ -646,7 +646,7 @@ public partial class CoreConfigV2rayService
if (!_node.Finalmask.IsNullOrEmpty()) if (!_node.Finalmask.IsNullOrEmpty())
{ {
streamSettings.finalmask = JsonUtils.ParseJson(_node.Finalmask); streamSettings.finalmask = JsonUtils.ParseJson(_node.Finalmask) as JsonObject;
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -837,10 +837,10 @@ public partial class CoreConfigV2rayService
protocol = "freedom", protocol = "freedom",
streamSettings = new StreamSettings4Ray streamSettings = new StreamSettings4Ray
{ {
finalmask = new Finalmask4Ray finalmask = JsonUtils.SerializeToNode(new Finalmask4Ray
{ {
tcp = [fragmentMask], tcp = [fragmentMask],
}, }) as JsonObject,
}, },
}; };
+5 -1
View File
@@ -335,9 +335,13 @@ public partial class UpdateService(Config config, Func<bool, string, Task> updat
//Check for avalonia desktop windows version //Check for avalonia desktop windows version
if (File.Exists(Path.Combine(Utils.GetBaseDirectory(), "libHarfBuzzSharp.dll"))) if (File.Exists(Path.Combine(Utils.GetBaseDirectory(), "libHarfBuzzSharp.dll")))
{ {
return url?.Replace(".zip", "-desktop.zip"); url = url?.Replace(".zip", "-desktop.zip");
} }
#if V2RAYN_IS_AOT
url = url?.Replace(".zip", "-aot.zip");
#endif
return url; return url;
} }
else if (Utils.IsLinux()) else if (Utils.IsLinux())
@@ -24,7 +24,7 @@ public sealed class WindowsJobService : IDisposable
try try
{ {
var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); var length = Marshal.SizeOf<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>();
extendedInfoPtr = Marshal.AllocHGlobal(length); extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
@@ -448,7 +448,7 @@ public partial class ProfilesViewModel : MyReactiveObject
return null; return null;
} }
var orderProfiles = SelectedProfiles?.OrderBy(t => t.Sort); var orderProfiles = SelectedProfiles?.OrderBy(t => t.Sort).ToList();
if (latest) if (latest)
{ {
lstSelected.AddRange(await AppManager.Instance.GetProfileItemsOrderedByIndexIds(orderProfiles.Select(sp => sp?.IndexId))); lstSelected.AddRange(await AppManager.Instance.GetProfileItemsOrderedByIndexIds(orderProfiles.Select(sp => sp?.IndexId)));
@@ -712,7 +712,7 @@ public partial class ProfilesViewModel : MyReactiveObject
actionType = ESpeedActionType.Realping; actionType = ESpeedActionType.Realping;
} }
lstSelected = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(ProfileItems?.OrderBy(t => t.Sort))); lstSelected = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(ProfileItems?.OrderBy(t => t.Sort).ToList()));
} }
else else
{ {
@@ -191,13 +191,7 @@ public partial class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable
} }
if (lst.Count > 0) if (lst.Count > 0)
{ {
var options = new JsonSerializerOptions await SetClipboardDataInteraction.HandleSafe(JsonUtils.SerializeCamelCase(lst));
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
await SetClipboardDataInteraction.HandleSafe(JsonUtils.Serialize(lst, options));
} }
} }
@@ -69,7 +69,7 @@ public partial class QRCodeAvaloniaUtils
var bmi = new BITMAPINFO var bmi = new BITMAPINFO
{ {
biSize = Marshal.SizeOf(typeof(BITMAPINFO)), biSize = Marshal.SizeOf<BITMAPINFO>(),
biWidth = width, biWidth = width,
biHeight = -height, biHeight = -height,
biPlanes = 1, biPlanes = 1,
+6
View File
@@ -0,0 +1,6 @@
<linker>
<assembly fullname="ServiceLib">
<type fullname="ServiceLib.Models.*" preserve="all" />
<type fullname="ServiceLib.Enums.*" preserve="all" />
</assembly>
</linker>
@@ -7,8 +7,6 @@ namespace v2rayN.Desktop.Views;
public partial class JsonEditor : UserControl public partial class JsonEditor : UserControl
{ {
private static readonly JsonSerializerOptions SIndentedOptions = new() { WriteIndented = true };
private static readonly Lazy<IHighlightingDefinition> SHighlightingDark = private static readonly Lazy<IHighlightingDefinition> SHighlightingDark =
new(() => BuildHighlighting(dark: true), isThreadSafe: true); new(() => BuildHighlighting(dark: true), isThreadSafe: true);
@@ -88,7 +86,7 @@ public partial class JsonEditor : UserControl
try try
{ {
var obj = JsonUtils.ParseJson(Editor.Text); var obj = JsonUtils.ParseJson(Editor.Text);
Editor.Text = JsonUtils.Serialize(obj, SIndentedOptions); Editor.Text = JsonUtils.Serialize(obj, indented: true, nullValue: true);
} }
catch catch
{ {
+4 -1
View File
@@ -55,5 +55,8 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<TrimmerRootDescriptor Include="TrimmerRoots.xml" />
</ItemGroup>
</Project> </Project>