mirror of
https://github.com/2dust/v2rayN.git
synced 2026-11-01 18:15:00 +03:00
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:
@@ -29,6 +29,22 @@ jobs:
|
||||
release_tag: ${{ inputs.release_tag }}
|
||||
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:
|
||||
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/'))
|
||||
|
||||
@@ -28,7 +28,23 @@ jobs:
|
||||
target: macos
|
||||
release_tag: ${{ inputs.release_tag }}
|
||||
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:
|
||||
name: package and release macOS dmg
|
||||
if: inputs.release_tag != ''
|
||||
|
||||
@@ -28,3 +28,19 @@ jobs:
|
||||
target: windows-desktop
|
||||
release_tag: ${{ inputs.release_tag }}
|
||||
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
@@ -10,6 +10,10 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: './v2rayN.Desktop/v2rayN.Desktop.csproj'
|
||||
isAot:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -27,7 +31,6 @@ jobs:
|
||||
)
|
||||
}}
|
||||
env:
|
||||
Output: "${{ github.workspace }}/${{ matrix.arch }}"
|
||||
RID: |-
|
||||
${{
|
||||
case(
|
||||
@@ -41,6 +44,7 @@ jobs:
|
||||
${{
|
||||
case(
|
||||
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
|
||||
uses: actions/setup-dotnet@v6
|
||||
with:
|
||||
dotnet-version: 10.0.1xx
|
||||
|
||||
- name: Build v2rayN
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
working-directory: ./v2rayN
|
||||
run: dotnet publish $Project -c Release -r $RID -p:SelfContained=true $ExtOpt -o $Output
|
||||
if: inputs.target == 'linux' && inputs.isAot
|
||||
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
|
||||
shell: bash
|
||||
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
|
||||
shell: bash
|
||||
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
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ matrix.arch }}
|
||||
path: ${{ matrix.arch }}
|
||||
name: ${{ matrix.arch }}${{ inputs.isAot && '-aot' || '' }}
|
||||
path: ${{ env.OUTPUT }}
|
||||
|
||||
@@ -9,6 +9,10 @@ on:
|
||||
target: # windows linux macos windows-desktop
|
||||
required: true
|
||||
type: string
|
||||
isAot:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
GPG_PRIVATE_KEY:
|
||||
required: true
|
||||
@@ -43,27 +47,62 @@ jobs:
|
||||
- name: Checkout
|
||||
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
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: ${{ matrix.arch }}
|
||||
path: v2rayN-${{ env.Target }}-${{ env.Arch }}
|
||||
|
||||
name: ${{ steps.package-vars.outputs.build_artifact_name }}
|
||||
path: ${{ steps.package-vars.outputs.package_dir }}
|
||||
|
||||
- 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
|
||||
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
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: release-${{ inputs.target }}-${{ matrix.arch }}
|
||||
path: v2rayN*.zip
|
||||
name: release-${{ inputs.target }}-${{ matrix.arch }}${{ inputs.isAot && '-aot' || '' }}
|
||||
path: ${{ steps.package-vars.outputs.final_package_file }}
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
@@ -75,7 +114,7 @@ jobs:
|
||||
uses: ./.github/workflows/upload-sign.yml
|
||||
with:
|
||||
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'
|
||||
secrets:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
|
||||
@@ -5,16 +5,31 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resx\Resource.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
|
||||
<Compile Update="Resx\Resource.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resource.resx</DependentUpon>
|
||||
</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>
|
||||
|
||||
<Target Name="RemoveAotSatelliteOutputs" AfterTargets="ComputeFilesToPublish" Condition="'$(PublishAot)' == 'true'">
|
||||
<ItemGroup>
|
||||
<ResolvedFileToPublish Remove="@(ResolvedFileToPublish)"
|
||||
Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Contains('resources.dll'))" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
+1
-1
@@ -19,7 +19,7 @@ namespace AmazTool.Resx {
|
||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||
// (以 /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.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resource {
|
||||
|
||||
@@ -29,4 +29,14 @@
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishReadyToRun>false</PublishReadyToRun>
|
||||
</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>
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
<PackageVersion Include="NLog" Version="6.2.1" />
|
||||
<PackageVersion Include="sqlite-net-e" Version="1.11.285" />
|
||||
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.4.1" />
|
||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||
<PackageVersion Include="TUnit" Version="1.75.0" />
|
||||
<PackageVersion Include="TUnit" Version="1.73.19" />
|
||||
<PackageVersion Include="TUnit.Assertions.Should" Version="1.72.16-beta" />
|
||||
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||
<PackageVersion Include="YamlDotNet" Version="18.1.0" />
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PublishAot>true</PublishAot>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,40 +7,45 @@ public class JsonUtils
|
||||
private static readonly JsonSerializerOptions _defaultDeserializeOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
ReadCommentHandling = JsonCommentHandling.Skip
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
TypeInfoResolver = AppJsonContext.Default,
|
||||
};
|
||||
|
||||
private static readonly JsonSerializerOptions _defaultSerializeOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
TypeInfoResolver = AppJsonContext.Default,
|
||||
};
|
||||
|
||||
private static readonly JsonSerializerOptions _defaultSerializeNoIndentedOptions = new()
|
||||
{
|
||||
WriteIndented = false,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
TypeInfoResolver = AppJsonContext.Default,
|
||||
};
|
||||
|
||||
private static readonly JsonSerializerOptions _nullValueSerializeOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
TypeInfoResolver = AppJsonContext.Default,
|
||||
};
|
||||
|
||||
private static readonly JsonSerializerOptions _nullValueSerializeNoIndentedOptions = new()
|
||||
{
|
||||
WriteIndented = false,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
TypeInfoResolver = AppJsonContext.Default,
|
||||
};
|
||||
|
||||
private static readonly JsonDocumentOptions _defaultDocumentOptions = new()
|
||||
{
|
||||
CommentHandling = JsonCommentHandling.Skip
|
||||
CommentHandling = JsonCommentHandling.Skip,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -109,7 +114,7 @@ public class JsonUtils
|
||||
/// <param name="indented"></param>
|
||||
/// <param name="nullValue"></param>
|
||||
/// <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;
|
||||
try
|
||||
@@ -140,7 +145,7 @@ public class JsonUtils
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static string Serialize(object? obj, JsonSerializerOptions? options)
|
||||
public static string SerializeCamelCase<T>(T? obj)
|
||||
{
|
||||
var result = string.Empty;
|
||||
try
|
||||
@@ -149,7 +154,14 @@ public class JsonUtils
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -162,9 +174,108 @@ public class JsonUtils
|
||||
/// SerializeToNode
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="nullValue"></param>
|
||||
/// <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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -348,10 +348,9 @@ public class Utils
|
||||
.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))
|
||||
.Cast<TEnum>()
|
||||
return Enum.GetValues<TEnum>()
|
||||
.Select(e => e.ToString())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
@@ -13,45 +12,57 @@ public class YamlUtils
|
||||
/// <summary>
|
||||
/// Deserialize
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static T FromYaml<T>(string str)
|
||||
public static YamlMappingNode FromYaml(string str)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
//var deserializer = new DeserializerBuilder()
|
||||
// .WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||
// .Build();
|
||||
//try
|
||||
//{
|
||||
// var obj = deserializer.Deserialize<T>(str);
|
||||
// return obj;
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// Logging.SaveLog(_tag, ex);
|
||||
// return deserializer.Deserialize<T>("");
|
||||
//}
|
||||
try
|
||||
{
|
||||
var obj = deserializer.Deserialize<T>(str);
|
||||
return obj;
|
||||
var yaml = new YamlStream();
|
||||
using var reader = new StringReader(str);
|
||||
yaml.Load(reader);
|
||||
var rootNode = (YamlMappingNode)yaml.Documents[0].RootNode;
|
||||
return rootNode;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
return deserializer.Deserialize<T>("");
|
||||
return new YamlMappingNode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToYaml(object? obj)
|
||||
public static string ToYaml(YamlMappingNode? node)
|
||||
{
|
||||
var result = string.Empty;
|
||||
if (obj == null)
|
||||
if (node == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
var serializer = new SerializerBuilder()
|
||||
.WithNamingConvention(HyphenatedNamingConvention.Instance)
|
||||
.Build();
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -64,9 +75,15 @@ public class YamlUtils
|
||||
{
|
||||
try
|
||||
{
|
||||
var mergingParser = new MergingParser(new Parser(new StringReader(str)));
|
||||
var obj = new DeserializerBuilder().Build().Deserialize(mergingParser);
|
||||
return ToYaml(obj);
|
||||
using var reader = new StringReader(str);
|
||||
var parser = new Parser(reader);
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -42,6 +42,7 @@ public class Global
|
||||
public const string SingboxFakeIPFilterFileName = NamespaceSample + "singbox_fakeip_filter";
|
||||
public const string ChromeRootCertFileName = NamespaceSample + "chrome_roots_pem";
|
||||
public const string MozillaRootCertFileName = NamespaceSample + "mozilla_roots_pem";
|
||||
public const string TaskXmlTemplateFileName = NamespaceSample + "TaskXmlTemplate";
|
||||
|
||||
public const string ChromeRootProvider = "chrome";
|
||||
public const string MozillaRootProvider = "mozilla";
|
||||
|
||||
@@ -87,6 +87,7 @@ public static class AutoStartupHandler
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static void AutoStartTaskService(string taskName, string fileName, string description)
|
||||
{
|
||||
#if false
|
||||
if (taskName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
@@ -117,6 +118,78 @@ public static class AutoStartupHandler
|
||||
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
|
||||
|
||||
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()
|
||||
|
||||
@@ -80,12 +80,7 @@ public class BaseFmt
|
||||
{
|
||||
var node = JsonUtils.ParseJson(item.Finalmask);
|
||||
var finalmask = node != null
|
||||
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
})
|
||||
? JsonUtils.Serialize(node, indented: false, nullValue: true)
|
||||
: item.Finalmask;
|
||||
dicQuery.Add("fm", Utils.UrlEncode(finalmask));
|
||||
}
|
||||
@@ -154,12 +149,7 @@ public class BaseFmt
|
||||
{
|
||||
var node = JsonUtils.ParseJson(transport.XhttpExtra);
|
||||
var extra = node != null
|
||||
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
})
|
||||
? JsonUtils.Serialize(node, indented: false, nullValue: true)
|
||||
: transport.XhttpExtra;
|
||||
dicQuery.Add("extra", UrlEncodeSafe(extra));
|
||||
}
|
||||
@@ -215,12 +205,7 @@ public class BaseFmt
|
||||
{
|
||||
var node = JsonUtils.ParseJson(finalmaskDecoded);
|
||||
item.Finalmask = node != null
|
||||
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
})
|
||||
? JsonUtils.Serialize(node, indented: true, nullValue: true)
|
||||
: finalmaskDecoded;
|
||||
}
|
||||
else
|
||||
@@ -278,12 +263,7 @@ public class BaseFmt
|
||||
var node = JsonUtils.ParseJson(xhttpExtra);
|
||||
if (node != null)
|
||||
{
|
||||
xhttpExtra = JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
});
|
||||
xhttpExtra = JsonUtils.Serialize(node, indented: true, nullValue: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -179,12 +179,7 @@ public class InnerFmt : BaseFmt
|
||||
if (jsonObj.TryGetPropertyValue("CustomOutboundObj", out var customOutboundNode)
|
||||
&& customOutboundNode is JsonObject customOutboundObj)
|
||||
{
|
||||
var customOutboundContent = JsonUtils.Serialize(customOutboundObj, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
});
|
||||
var customOutboundContent = JsonUtils.Serialize(customOutboundObj, indented: true, nullValue: true);
|
||||
customOutboundFilePath = WriteAllText(customOutboundContent);
|
||||
jsonObj.Remove("CustomOutboundObj");
|
||||
}
|
||||
|
||||
@@ -29,12 +29,7 @@ public class WireguardFmt : BaseFmt
|
||||
{
|
||||
var node = JsonUtils.ParseJson(finalmaskDecoded);
|
||||
item.Finalmask = node != null
|
||||
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
})
|
||||
? JsonUtils.Serialize(node, indented: true, nullValue: true)
|
||||
: finalmaskDecoded;
|
||||
}
|
||||
else
|
||||
@@ -98,12 +93,7 @@ public class WireguardFmt : BaseFmt
|
||||
{
|
||||
var node = JsonUtils.ParseJson(item.Finalmask);
|
||||
var finalmask = node != null
|
||||
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
})
|
||||
? JsonUtils.Serialize(node, indented: false, nullValue: true)
|
||||
: item.Finalmask;
|
||||
dicQuery.Add("fm", Utils.UrlEncode(finalmask));
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ public static class ProxySettingWindows
|
||||
list.dwOptionCount = options.Length;
|
||||
list.dwOptionError = 0;
|
||||
|
||||
var optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
|
||||
var optSize = Marshal.SizeOf<InternetConnectionOption>();
|
||||
// make a pointer out of all that ...
|
||||
var optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
|
||||
// copy the array over into that spot in memory ...
|
||||
@@ -207,17 +207,17 @@ public static class ProxySettingWindows
|
||||
var entries = 0;
|
||||
// attempt to query with 1 entry buffer
|
||||
var rasEntryNames = new RASENTRYNAME[1];
|
||||
var bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
|
||||
rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
|
||||
var bufferSize = Marshal.SizeOf<RASENTRYNAME>();
|
||||
rasEntryNames[0].dwSize = Marshal.SizeOf<RASENTRYNAME>();
|
||||
|
||||
var result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
|
||||
// increase buffer if the buffer is not large enough
|
||||
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++)
|
||||
{
|
||||
rasEntryNames[i].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
|
||||
rasEntryNames[i].dwSize = Marshal.SizeOf<RASENTRYNAME>();
|
||||
}
|
||||
|
||||
result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
|
||||
@@ -258,7 +258,7 @@ public static class ProxySettingWindows
|
||||
|
||||
static InternetConnectionOption()
|
||||
{
|
||||
Size = Marshal.SizeOf(typeof(InternetConnectionOption));
|
||||
Size = Marshal.SizeOf<InternetConnectionOption>();
|
||||
}
|
||||
|
||||
// Nested Types
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Configs;
|
||||
|
||||
[Serializable]
|
||||
public class Config
|
||||
{
|
||||
#region property
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Configs;
|
||||
|
||||
[Serializable]
|
||||
public class CoreBasicItem
|
||||
{
|
||||
public bool LogEnabled { get; set; }
|
||||
@@ -22,7 +21,6 @@ public class CoreBasicItem
|
||||
public bool EnableCacheFile4Sbox { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class InItem
|
||||
{
|
||||
public int LocalPort { get; set; }
|
||||
@@ -38,7 +36,6 @@ public class InItem
|
||||
public bool SecondLocalPortEnabled { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class KcpItem
|
||||
{
|
||||
public int Mtu { get; set; }
|
||||
@@ -54,7 +51,6 @@ public class KcpItem
|
||||
public int MaxSendingWindow { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GrpcItem
|
||||
{
|
||||
public int? IdleTimeout { get; set; }
|
||||
@@ -63,7 +59,6 @@ public class GrpcItem
|
||||
public int? InitialWindowsSize { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GUIItem
|
||||
{
|
||||
public bool AutoRun { get; set; }
|
||||
@@ -77,14 +72,12 @@ public class GUIItem
|
||||
public string? RootCertProvider { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MsgUIItem
|
||||
{
|
||||
public string? MainMsgFilter { get; set; }
|
||||
public bool? AutoRefresh { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UIItem
|
||||
{
|
||||
public bool EnableAutoAdjustMainLvColWidth { get; set; }
|
||||
@@ -106,7 +99,6 @@ public class UIItem
|
||||
public bool HideColumnIpInfo { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ConstItem
|
||||
{
|
||||
public string? SubConvertUrl { get; set; }
|
||||
@@ -115,7 +107,6 @@ public class ConstItem
|
||||
public string? RouteRulesTemplateSourceUrl { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class KeyEventItem
|
||||
{
|
||||
public EGlobalHotkey EGlobalHotkey { get; set; }
|
||||
@@ -129,7 +120,6 @@ public class KeyEventItem
|
||||
public int? KeyCode { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CoreTypeItem
|
||||
{
|
||||
public EConfigType ConfigType { get; set; }
|
||||
@@ -137,7 +127,6 @@ public class CoreTypeItem
|
||||
public ECoreType CoreType { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TunModeItem
|
||||
{
|
||||
public bool EnableTun { get; set; }
|
||||
@@ -153,7 +142,6 @@ public class TunModeItem
|
||||
public string IPv6Address { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SpeedTestItem
|
||||
{
|
||||
public int SpeedTestTimeout { get; set; }
|
||||
@@ -166,7 +154,6 @@ public class SpeedTestItem
|
||||
public int? SpeedTestDelayInterval { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RoutingBasicItem
|
||||
{
|
||||
public string DomainStrategy { get; set; }
|
||||
@@ -174,7 +161,6 @@ public class RoutingBasicItem
|
||||
public string RoutingIndexId { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ColumnItem
|
||||
{
|
||||
public string Name { get; set; }
|
||||
@@ -182,7 +168,6 @@ public class ColumnItem
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Mux4RayItem
|
||||
{
|
||||
public int? Concurrency { get; set; }
|
||||
@@ -190,7 +175,6 @@ public class Mux4RayItem
|
||||
public string? XudpProxyUDP443 { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Mux4SboxItem
|
||||
{
|
||||
public string Protocol { get; set; }
|
||||
@@ -198,7 +182,6 @@ public class Mux4SboxItem
|
||||
public bool? Padding { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HysteriaItem
|
||||
{
|
||||
public int UpMbps { get; set; }
|
||||
@@ -206,7 +189,6 @@ public class HysteriaItem
|
||||
public int HopInterval { get; set; } = Global.Hysteria2DefaultHopInt;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ClashUIItem
|
||||
{
|
||||
public bool EnableIPv6 { get; set; }
|
||||
@@ -219,7 +201,6 @@ public class ClashUIItem
|
||||
public List<ColumnItem> ConnectionsColumnItem { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SystemProxyItem
|
||||
{
|
||||
public ESysProxyType SysProxyType { get; set; }
|
||||
@@ -230,7 +211,6 @@ public class SystemProxyItem
|
||||
public string? CustomSystemProxyScriptPath { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WebDavItem
|
||||
{
|
||||
public string? Url { get; set; }
|
||||
@@ -239,7 +219,6 @@ public class WebDavItem
|
||||
public string? DirName { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CheckUpdateItem
|
||||
{
|
||||
public bool UpdateViaProxy { get; set; } = true;
|
||||
@@ -247,7 +226,6 @@ public class CheckUpdateItem
|
||||
public List<string>? CheckPreReleaseCoreTypes { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Fragment4RayItem
|
||||
{
|
||||
public string? Packets { get; set; }
|
||||
@@ -262,7 +240,6 @@ public class Fragment4RayItem
|
||||
// migration end
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WindowSizeItem
|
||||
{
|
||||
public string TypeName { get; set; }
|
||||
@@ -270,7 +247,6 @@ public class WindowSizeItem
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SimpleDNSItem
|
||||
{
|
||||
public bool? UseSystemHosts { get; set; }
|
||||
@@ -293,7 +269,6 @@ public class SimpleDNSItem
|
||||
public bool? EnableHappyEyeballs { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HappyEyeballs4RayItem
|
||||
{
|
||||
public int? TryDelayMs { get; set; }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.CoreConfigs;
|
||||
|
||||
[Serializable]
|
||||
public class CoreInfo
|
||||
{
|
||||
public ECoreType CoreType { get; set; }
|
||||
|
||||
@@ -44,7 +44,6 @@ public class Route4Sbox
|
||||
public string? final { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Rule4Sbox
|
||||
{
|
||||
public string? outbound { get; set; }
|
||||
@@ -103,7 +102,6 @@ public class Rule4Sbox
|
||||
public bool? speculative { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Inbound4Sbox
|
||||
{
|
||||
public string type { get; set; }
|
||||
@@ -232,7 +230,7 @@ public class Reality4Sbox
|
||||
public class Transport4Sbox
|
||||
{
|
||||
public string? type { get; set; }
|
||||
public object? host { get; set; }
|
||||
public JsonNode? host { get; set; }
|
||||
public string? path { get; set; }
|
||||
public Headers4Sbox? headers { get; set; }
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace ServiceLib.Models.CoreConfigs;
|
||||
public class V2rayConfig
|
||||
{
|
||||
public Log4Ray log { get; set; }
|
||||
public object dns { get; set; }
|
||||
public JsonObject? dns { get; set; }
|
||||
public FakeDns4Ray? fakedns { get; set; }
|
||||
public List<Inbounds4Ray> inbounds { get; set; }
|
||||
public List<Outbounds4Ray> outbounds { get; set; }
|
||||
@@ -130,7 +130,7 @@ public class Outboundsettings4Ray
|
||||
|
||||
public string? secretKey { get; set; }
|
||||
|
||||
public object? address { get; set; }
|
||||
public JsonNode? address { get; set; }
|
||||
|
||||
public int? port { get; set; }
|
||||
|
||||
@@ -142,7 +142,7 @@ public class Outboundsettings4Ray
|
||||
|
||||
public string? email { get; set; }
|
||||
|
||||
public object? headers { get; set; }
|
||||
public JsonNode? headers { get; set; }
|
||||
|
||||
public List<WireguardPeer4Ray>? peers { get; set; }
|
||||
|
||||
@@ -199,8 +199,8 @@ public class Response4Ray
|
||||
|
||||
public class Dns4Ray
|
||||
{
|
||||
public Dictionary<string, object>? hosts { get; set; }
|
||||
public List<object> servers { get; set; }
|
||||
public Dictionary<string, JsonNode>? hosts { get; set; }
|
||||
public List<JsonNode> servers { get; set; }
|
||||
public bool? serveStale { get; set; }
|
||||
public bool? enableParallelQuery { get; set; }
|
||||
public string? queryStrategy { get; set; }
|
||||
@@ -226,7 +226,6 @@ public class Routing4Ray
|
||||
public List<BalancersItem4Ray>? balancers { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RulesItem4Ray
|
||||
{
|
||||
public string? type { get; set; }
|
||||
@@ -321,7 +320,7 @@ public class StreamSettings4Ray
|
||||
public HysteriaSettings4Ray? hysteriaSettings { get; set; }
|
||||
public MasqueSettings4Ray? masqueSettings { get; set; }
|
||||
|
||||
public object? finalmask { get; set; }
|
||||
public JsonObject? finalmask { get; set; }
|
||||
|
||||
public Sockopt4Ray? sockopt { get; set; }
|
||||
}
|
||||
@@ -363,9 +362,9 @@ public class Header4Ray
|
||||
{
|
||||
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
|
||||
@@ -411,7 +410,7 @@ public class XhttpSettings4Ray
|
||||
public string? path { get; set; }
|
||||
public string? host { get; set; }
|
||||
public string? mode { get; set; }
|
||||
public object? extra { get; set; }
|
||||
public JsonObject? extra { get; set; }
|
||||
}
|
||||
|
||||
public class HttpSettings4Ray
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
[Serializable]
|
||||
public partial class ClashProxyModel : ReactiveObject
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
[Serializable]
|
||||
public partial class ProfileItemModel : ReactiveObject
|
||||
{
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
[Serializable]
|
||||
public class RoutingItemModel : RoutingItem
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
[Serializable]
|
||||
public class RoutingTemplate
|
||||
{
|
||||
public string Version { get; set; }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
[Serializable]
|
||||
public class RulesItemModel : RulesItem
|
||||
{
|
||||
public string InboundTags { get; set; }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
[Serializable]
|
||||
public class ServerSpeedItem : ServerStatItem
|
||||
{
|
||||
public long ProxyUp { get; set; }
|
||||
@@ -12,7 +11,6 @@ public class ServerSpeedItem : ServerStatItem
|
||||
public long DirectDown { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TrafficItem
|
||||
{
|
||||
public ulong Up { get; set; }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
[Serializable]
|
||||
public class ServerTestItem
|
||||
{
|
||||
public string? IndexId { get; set; }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
[Serializable]
|
||||
public class SpeedTestResult
|
||||
{
|
||||
public string? IndexId { get; set; }
|
||||
|
||||
@@ -5,7 +5,6 @@ public class SsSIP008
|
||||
public List<SsServer>? servers { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SsServer
|
||||
{
|
||||
public string? remarks { get; set; }
|
||||
|
||||
@@ -3,7 +3,6 @@ namespace ServiceLib.Models.Dto;
|
||||
/// <summary>
|
||||
/// https://github.com/2dust/v2rayN/wiki/
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class VmessQRCode
|
||||
{
|
||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Entities;
|
||||
|
||||
[Serializable]
|
||||
public class DNSItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Entities;
|
||||
|
||||
[Serializable]
|
||||
public class FullConfigTemplateItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Entities;
|
||||
|
||||
[Serializable]
|
||||
public class ProfileExItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace ServiceLib.Models.Entities;
|
||||
|
||||
[Obsolete("Use ProtocolExtraItem instead.")]
|
||||
[Serializable]
|
||||
public class ProfileGroupItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Entities;
|
||||
|
||||
[Serializable]
|
||||
public class ProfileItem
|
||||
{
|
||||
private ProtocolExtraItem? _protocolExtraCache;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Entities;
|
||||
|
||||
[Serializable]
|
||||
public class RoutingItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Entities;
|
||||
|
||||
[Serializable]
|
||||
public class RulesItem
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Entities;
|
||||
|
||||
[Serializable]
|
||||
public class ServerStatItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace ServiceLib.Models.Entities;
|
||||
|
||||
[Serializable]
|
||||
public class SubItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
|
||||
@@ -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>
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
@@ -18,7 +18,6 @@
|
||||
<PackageReference Include="QRCoder" />
|
||||
<PackageReference Include="CliWrap" />
|
||||
<PackageReference Include="ZXing.Net.Bindings.SkiaSharp" />
|
||||
<PackageReference Include="TaskScheduler" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -45,6 +44,7 @@
|
||||
<EmbeddedResource Include="Sample\SampleTunRules" />
|
||||
<EmbeddedResource Include="Sample\SingboxSampleClientConfig" />
|
||||
<EmbeddedResource Include="Sample\SingboxSampleOutbound" />
|
||||
<EmbeddedResource Include="Sample\TaskXmlTemplate" />
|
||||
<EmbeddedResource Include="Sample\tun_singbox_dns" />
|
||||
<EmbeddedResource Include="Sample\tun_singbox_inbound" />
|
||||
<EmbeddedResource Include="Sample\tun_singbox_rules" />
|
||||
|
||||
@@ -61,10 +61,10 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
|
||||
//YAML anchors
|
||||
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)
|
||||
{
|
||||
ret.Msg = ResUI.FailedConversionConfiguration;
|
||||
@@ -72,29 +72,38 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
|
||||
}
|
||||
|
||||
//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
|
||||
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
|
||||
fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}";
|
||||
fileContent.Remove("secret");
|
||||
//fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}";
|
||||
fileContent.Children[new YamlScalarNode("external-controller")] = new YamlScalarNode($"{Global.Loopback}:{AppManager.Instance.StatePort2}");
|
||||
//fileContent.Remove("secret");
|
||||
fileContent.Children.Remove(new YamlScalarNode("secret"));
|
||||
//allow-lan
|
||||
if (config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
fileContent["allow-lan"] = "true";
|
||||
fileContent["bind-address"] = "*";
|
||||
//fileContent["allow-lan"] = "true";
|
||||
fileContent.Children[new YamlScalarNode("allow-lan")] = new YamlScalarNode("true");
|
||||
//fileContent["bind-address"] = "*";
|
||||
fileContent.Children[new YamlScalarNode("bind-address")] = new YamlScalarNode("*");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileContent["allow-lan"] = "false";
|
||||
//fileContent["allow-lan"] = "false";
|
||||
fileContent.Children[new YamlScalarNode("allow-lan")] = new YamlScalarNode("false");
|
||||
}
|
||||
|
||||
//ipv6
|
||||
fileContent["ipv6"] = config.ClashUIItem.EnableIPv6;
|
||||
//fileContent["ipv6"] = config.ClashUIItem.EnableIPv6;
|
||||
fileContent.Children[new YamlScalarNode("ipv6")] = new YamlScalarNode(config.ClashUIItem.EnableIPv6.ToString().ToLowerInvariant());
|
||||
|
||||
//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
|
||||
if (isTunEnabled)
|
||||
@@ -102,10 +111,11 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
|
||||
var tun = EmbedUtils.GetEmbedText(Global.ClashTunYaml);
|
||||
if (tun.IsNotEmpty())
|
||||
{
|
||||
var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun);
|
||||
var tunContent = YamlUtils.FromYaml(tun);
|
||||
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);
|
||||
}
|
||||
|
||||
// Mihomo parses plain values such as 815458e4 as floats, so quote REALITY short IDs.
|
||||
var originalRealityShortIds = new List<(Dictionary<object, object> RealityOptions, string ShortId)>();
|
||||
if (fileContent.GetValueOrDefault("proxies") is List<object> proxies)
|
||||
{
|
||||
foreach (var proxy in proxies.OfType<Dictionary<object, object>>())
|
||||
{
|
||||
if (proxy.GetValueOrDefault("reality-opts") is Dictionary<object, object> realityOptions
|
||||
&& realityOptions.GetValueOrDefault("short-id") is string shortId
|
||||
&& !shortId.StartsWith(tagYamlStr2, StringComparison.Ordinal))
|
||||
{
|
||||
originalRealityShortIds.Add((realityOptions, shortId));
|
||||
realityOptions["short-id"] = new YamlScalarNode(shortId) { Style = ScalarStyle.DoubleQuoted };
|
||||
}
|
||||
}
|
||||
}
|
||||
//// Mihomo parses plain values such as 815458e4 as floats, so quote REALITY short IDs.
|
||||
//var originalRealityShortIds = new List<(Dictionary<object, object> RealityOptions, string ShortId)>();
|
||||
//if (fileContent.GetValueOrDefault("proxies") is List<object> proxies)
|
||||
//{
|
||||
// foreach (var proxy in proxies.OfType<Dictionary<object, object>>())
|
||||
// {
|
||||
// if (proxy.GetValueOrDefault("reality-opts") is Dictionary<object, object> realityOptions
|
||||
// && realityOptions.GetValueOrDefault("short-id") is string shortId
|
||||
// && !shortId.StartsWith(tagYamlStr2, StringComparison.Ordinal))
|
||||
// {
|
||||
// originalRealityShortIds.Add((realityOptions, shortId));
|
||||
// realityOptions["short-id"] = new YamlScalarNode(shortId) { Style = ScalarStyle.DoubleQuoted };
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
var txtFileNew = YamlUtils.ToYaml(fileContent).Replace(tagYamlStr2, tagYamlStr3);
|
||||
foreach (var (realityOptions, shortId) in originalRealityShortIds)
|
||||
{
|
||||
realityOptions["short-id"] = shortId;
|
||||
}
|
||||
//foreach (var (realityOptions, shortId) in originalRealityShortIds)
|
||||
//{
|
||||
// realityOptions["short-id"] = shortId;
|
||||
//}
|
||||
await File.WriteAllTextAsync(fileName, txtFileNew);
|
||||
//check again
|
||||
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)
|
||||
{
|
||||
@@ -177,80 +187,116 @@ public class CoreConfigClashService(Config config, bool isTunEnabled)
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (item.Key.StartsWith("prepend-")
|
||||
|| item.Key.StartsWith("append-")
|
||||
|| item.Key.StartsWith("removed-"))
|
||||
if (key.StartsWith("prepend-")
|
||||
|| key.StartsWith("append-")
|
||||
|| key.StartsWith("removed-"))
|
||||
{
|
||||
ModifyContentMerge(fileContent, item.Key, item.Value);
|
||||
ModifyContentMerge(fileContent, key, valueNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileContent[item.Key] = item.Value;
|
||||
fileContent.Children[keyNode] = valueNode;
|
||||
}
|
||||
}
|
||||
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 blRemoved = false;
|
||||
string realKey;
|
||||
|
||||
if (key.StartsWith("prepend-"))
|
||||
{
|
||||
blPrepend = true;
|
||||
key = key.Replace("prepend-", "");
|
||||
realKey = key["prepend-".Length..];
|
||||
}
|
||||
else if (key.StartsWith("append-"))
|
||||
{
|
||||
blPrepend = false;
|
||||
key = key.Replace("append-", "");
|
||||
realKey = key["append-".Length..];
|
||||
}
|
||||
else if (key.StartsWith("removed-"))
|
||||
{
|
||||
blRemoved = true;
|
||||
key = key.Replace("removed-", "");
|
||||
realKey = key["removed-".Length..];
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
var lstOri = (List<object>)fileContent[key];
|
||||
var lstValue = (List<object>)value;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (blPrepend)
|
||||
{
|
||||
lstValue.Reverse();
|
||||
lstValue.ForEach(item => lstOri.Insert(0, item));
|
||||
// Insert in reverse order to maintain original sequence at index 0
|
||||
for (var i = lstValue.Children.Count - 1; i >= 0; i--)
|
||||
{
|
||||
lstOri.Children.Insert(0, lstValue.Children[i]);
|
||||
}
|
||||
}
|
||||
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.host = transportExtra.Host.IsNullOrEmpty()
|
||||
? null
|
||||
: Utils.String2List(transportExtra.Host);
|
||||
: JsonUtils.SerializeToArray(Utils.String2List(transportExtra.Host));
|
||||
transport.path = transportExtra.Path.NullIfEmpty();
|
||||
if (!useragentValue.IsNullOrEmpty())
|
||||
{
|
||||
|
||||
@@ -17,14 +17,14 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
|
||||
// DNS routing
|
||||
var dnsObj = JsonUtils.SerializeToNode(_coreConfig.dns);
|
||||
if (dnsObj == null)
|
||||
var dnsNode = JsonUtils.SerializeToNode(_coreConfig.dns);
|
||||
if (dnsNode is not JsonObject dnsObj)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dnsObj["tag"] = Global.DnsTag;
|
||||
_coreConfig.dns = JsonUtils.Deserialize<Dns4Ray>(JsonUtils.Serialize(dnsObj));
|
||||
_coreConfig.dns = dnsObj;
|
||||
_coreConfig.routing.rules.Add(new RulesItem4Ray
|
||||
{
|
||||
type = "field",
|
||||
@@ -34,7 +34,7 @@ public partial class CoreConfigV2rayService
|
||||
return;
|
||||
}
|
||||
var simpleDnsItem = context.SimpleDnsItem;
|
||||
var dnsItem = _coreConfig.dns as Dns4Ray ?? new Dns4Ray();
|
||||
var dnsItem = new Dns4Ray();
|
||||
|
||||
var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs;
|
||||
//Outbound Freedom domainStrategy
|
||||
@@ -117,7 +117,7 @@ public partial class CoreConfigV2rayService
|
||||
balancerTag = finalRule.balancerTag,
|
||||
});
|
||||
|
||||
_coreConfig.dns = dnsItem;
|
||||
_coreConfig.dns = (JsonObject)JsonUtils.SerializeToNode(dnsItem);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -317,7 +317,7 @@ public partial class CoreConfigV2rayService
|
||||
|
||||
if (!useDirectDns)
|
||||
{
|
||||
dnsItem.servers.AddRange(remoteDNSAddress);
|
||||
dnsItem.servers.AddRange(JsonUtils.SerializeToArray(remoteDNSAddress));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -326,8 +326,7 @@ public partial class CoreConfigV2rayService
|
||||
var dnsServer = CreateDnsServer(dns, []);
|
||||
dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}";
|
||||
dnsServer.skipFallback = false;
|
||||
dnsItem.servers.Add(JsonUtils.SerializeToNode(dnsServer,
|
||||
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }));
|
||||
dnsItem.servers.Add(JsonUtils.SerializeToNode(dnsServer));
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -382,8 +381,7 @@ public partial class CoreConfigV2rayService
|
||||
{
|
||||
dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}";
|
||||
}
|
||||
var dnsServerNode = JsonUtils.SerializeToNode(dnsServer,
|
||||
new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
|
||||
var dnsServerNode = JsonUtils.SerializeToNode(dnsServer);
|
||||
dnsItem.servers.Add(dnsServerNode);
|
||||
}
|
||||
}
|
||||
@@ -396,12 +394,12 @@ public partial class CoreConfigV2rayService
|
||||
{
|
||||
return;
|
||||
}
|
||||
dnsItem.hosts ??= new Dictionary<string, object>();
|
||||
dnsItem.hosts ??= new Dictionary<string, JsonNode>();
|
||||
if (simpleDNSItem.AddCommonHosts == true)
|
||||
{
|
||||
dnsItem.hosts = Global.PredefinedHosts.ToDictionary(
|
||||
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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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);
|
||||
if (obj is null)
|
||||
var node = JsonUtils.ParseJson(customDNS);
|
||||
if (node is not JsonObject obj)
|
||||
{
|
||||
List<string> servers = [];
|
||||
var arrDNS = customDNS.Split(',');
|
||||
@@ -458,8 +456,7 @@ public partial class CoreConfigV2rayService
|
||||
{
|
||||
servers.Add(str);
|
||||
}
|
||||
obj = JsonUtils.ParseJson("{}");
|
||||
obj["servers"] = JsonUtils.SerializeToNode(servers);
|
||||
obj = new JsonObject { ["servers"] = JsonUtils.SerializeToNode(servers) };
|
||||
}
|
||||
|
||||
// Append to dns settings
|
||||
|
||||
@@ -203,7 +203,7 @@ public partial class CoreConfigV2rayService
|
||||
};
|
||||
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,
|
||||
reserved = Utils.String2List(protocolExtra.WgReserved)?.Select(s => s.Trim()).Select(int.Parse).ToList(),
|
||||
mtu = protocolExtra.WgMtu > 0 ? protocolExtra.WgMtu : Global.TunMtus.First(),
|
||||
@@ -428,7 +428,7 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
kcpFinalmask.udp?.Reverse();
|
||||
streamSettings.kcpSettings = kcpSettings;
|
||||
streamSettings.finalmask = kcpFinalmask;
|
||||
streamSettings.finalmask = (JsonObject)JsonUtils.SerializeToNode(kcpFinalmask);
|
||||
break;
|
||||
//ws
|
||||
case nameof(ETransport.ws):
|
||||
@@ -489,7 +489,7 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
if (xhttpExtra.IsNotEmpty())
|
||||
{
|
||||
xhttpSettings.extra = JsonUtils.ParseJson(xhttpExtra);
|
||||
xhttpSettings.extra = JsonUtils.ParseJson(xhttpExtra) as JsonObject;
|
||||
}
|
||||
|
||||
streamSettings.xhttpSettings = xhttpSettings;
|
||||
@@ -581,7 +581,7 @@ public partial class CoreConfigV2rayService
|
||||
auth = _node.Password,
|
||||
};
|
||||
hy2Finalmask.udp?.Reverse();
|
||||
streamSettings.finalmask = hy2Finalmask;
|
||||
streamSettings.finalmask = (JsonObject)JsonUtils.SerializeToNode(hy2Finalmask);
|
||||
break;
|
||||
|
||||
case "masque":
|
||||
@@ -637,7 +637,7 @@ public partial class CoreConfigV2rayService
|
||||
pathHttp = string.Join(",".AppendQuotes(), arrPath);
|
||||
}
|
||||
request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
|
||||
rawSettings.header.request = JsonUtils.Deserialize<object>(request);
|
||||
rawSettings.header.request = JsonUtils.SerializeToNode(request) as JsonObject;
|
||||
|
||||
streamSettings.rawSettings = rawSettings;
|
||||
}
|
||||
@@ -646,7 +646,7 @@ public partial class CoreConfigV2rayService
|
||||
|
||||
if (!_node.Finalmask.IsNullOrEmpty())
|
||||
{
|
||||
streamSettings.finalmask = JsonUtils.ParseJson(_node.Finalmask);
|
||||
streamSettings.finalmask = JsonUtils.ParseJson(_node.Finalmask) as JsonObject;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -837,10 +837,10 @@ public partial class CoreConfigV2rayService
|
||||
protocol = "freedom",
|
||||
streamSettings = new StreamSettings4Ray
|
||||
{
|
||||
finalmask = new Finalmask4Ray
|
||||
finalmask = JsonUtils.SerializeToNode(new Finalmask4Ray
|
||||
{
|
||||
tcp = [fragmentMask],
|
||||
},
|
||||
}) as JsonObject,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -335,9 +335,13 @@ public partial class UpdateService(Config config, Func<bool, string, Task> updat
|
||||
//Check for avalonia desktop windows version
|
||||
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;
|
||||
}
|
||||
else if (Utils.IsLinux())
|
||||
|
||||
@@ -24,7 +24,7 @@ public sealed class WindowsJobService : IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
||||
var length = Marshal.SizeOf<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>();
|
||||
extendedInfoPtr = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@ public partial class ProfilesViewModel : MyReactiveObject
|
||||
return null;
|
||||
}
|
||||
|
||||
var orderProfiles = SelectedProfiles?.OrderBy(t => t.Sort);
|
||||
var orderProfiles = SelectedProfiles?.OrderBy(t => t.Sort).ToList();
|
||||
if (latest)
|
||||
{
|
||||
lstSelected.AddRange(await AppManager.Instance.GetProfileItemsOrderedByIndexIds(orderProfiles.Select(sp => sp?.IndexId)));
|
||||
@@ -712,7 +712,7 @@ public partial class ProfilesViewModel : MyReactiveObject
|
||||
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
|
||||
{
|
||||
|
||||
@@ -191,13 +191,7 @@ public partial class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable
|
||||
}
|
||||
if (lst.Count > 0)
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
};
|
||||
await SetClipboardDataInteraction.HandleSafe(JsonUtils.Serialize(lst, options));
|
||||
await SetClipboardDataInteraction.HandleSafe(JsonUtils.SerializeCamelCase(lst));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ public partial class QRCodeAvaloniaUtils
|
||||
|
||||
var bmi = new BITMAPINFO
|
||||
{
|
||||
biSize = Marshal.SizeOf(typeof(BITMAPINFO)),
|
||||
biSize = Marshal.SizeOf<BITMAPINFO>(),
|
||||
biWidth = width,
|
||||
biHeight = -height,
|
||||
biPlanes = 1,
|
||||
|
||||
@@ -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
|
||||
{
|
||||
private static readonly JsonSerializerOptions SIndentedOptions = new() { WriteIndented = true };
|
||||
|
||||
private static readonly Lazy<IHighlightingDefinition> SHighlightingDark =
|
||||
new(() => BuildHighlighting(dark: true), isThreadSafe: true);
|
||||
|
||||
@@ -88,7 +86,7 @@ public partial class JsonEditor : UserControl
|
||||
try
|
||||
{
|
||||
var obj = JsonUtils.ParseJson(Editor.Text);
|
||||
Editor.Text = JsonUtils.Serialize(obj, SIndentedOptions);
|
||||
Editor.Text = JsonUtils.Serialize(obj, indented: true, nullValue: true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -55,5 +55,8 @@
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<TrimmerRootDescriptor Include="TrimmerRoots.xml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user