Compare commits

...

147 Commits

Author SHA1 Message Date
2dust
24ccfb8077 up 7.11.0 2025-04-03 14:24:20 +08:00
2dust
204451db6c Bug fix
https://github.com/2dust/v2rayN/issues/7058
2025-04-03 14:22:12 +08:00
Pk-web6936
f553bbc41e Update Persian Translation (#7053) 2025-04-03 10:19:09 +08:00
2dust
8cb4f2f961 Adjusted the server configuration right-click menu 2025-04-02 15:53:28 +08:00
2dust
4d3db56065 csharp_style_namespace_declarations = file_scoped 2025-04-02 11:44:23 +08:00
NeonSweet
d92540121f Update proxy_set_linux_sh (#7042)
Co-authored-by: neonsweet <neonsweet@126.com>
2025-04-02 09:39:51 +08:00
2dust
17d586ea26 Update Directory.Packages.props 2025-03-31 15:05:07 +08:00
2dust
9a096d31fc Remove ads rules from default routing rules and DNS 2025-03-30 11:07:59 +08:00
2dust
bf83dbdfea Global setting ScrollViewer AllowAutoHide = False for desktop 2025-03-29 20:42:02 +08:00
2dust
e31cd0e199 Update Directory.Packages.props 2025-03-29 20:40:47 +08:00
2dust
1e11477e27 When add a new routing rule, add it to the top 2025-03-29 19:48:15 +08:00
2dust
e0750df96c Update v2rayN.sln 2025-03-29 19:40:32 +08:00
DHR60
e3580b05f7 add xray core leastPing support (#7023)
* add xray core leastPing support

* Refactor multi-server configuration UI logic

* Remove unused functions
2025-03-29 16:44:42 +08:00
patterniha
6ad0762731 set xray.location.cert to asset(bin) path (#7004)
* Update Global.cs

* Update CoreHandler.cs
2025-03-27 09:50:19 +08:00
2dust
70b05d7812 Update ResUI.fa-Ir.resx 2025-03-24 09:50:25 +08:00
Pk-web6936
5403fc9e21 Update ResUI.fa-Ir.resx (#6986)
Update Persian translate
2025-03-24 09:17:28 +08:00
dashi
5bffca9584 Update translate for ResUI.ru.resx (#6983)
It may contain spelling errors.
2025-03-24 09:17:11 +08:00
Pk-web6936
2060539c34 Update Persian translate (#6973)
Update Persian translate
2025-03-23 10:28:21 +08:00
2dust
de3cdb4f7e up Resx 2025-03-23 10:18:36 +08:00
2dust
2a4ba2a751 up Resx 2025-03-21 11:11:53 +08:00
dependabot[bot]
48747aabe0 Bump actions/upload-artifact from 4.6.1 to 4.6.2 (#6950)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.6.1...v4.6.2)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-20 09:13:01 +08:00
2dust
7182be921d Update proxy_set_linux_sh
https://github.com/2dust/v2rayN/issues/6937
2025-03-19 10:04:06 +08:00
2dust
9d7dcd2c4f Update Directory.Packages.props 2025-03-19 10:03:55 +08:00
2dust
c3e56e84f1 Bug fix
https://github.com/2dust/v2rayN/issues/6932
2025-03-18 16:18:27 +08:00
dependabot[bot]
f1ef5a1f51 Bump actions/setup-dotnet from 4.3.0 to 4.3.1 (#6929)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v4.3.0...v4.3.1)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-18 10:01:15 +08:00
2dust
d1e6898290 up 7.10.5 2025-03-17 11:07:31 +08:00
2dust
8597332b21 Fixed warnings 2025-03-17 11:07:01 +08:00
2dust
7cc42ae249 Simply check the file hash value and delete the old pac file 2025-03-17 11:05:04 +08:00
Wydy
e054d4487d Update pac (#6924) 2025-03-17 09:06:21 +08:00
2dust
6ef36f521d Optimize and improve code 2025-03-16 15:48:42 +08:00
2dust
a02a122dd1 Update proxy_set_linux_sh
https://github.com/2dust/v2rayN/issues/6886
2025-03-16 11:01:23 +08:00
2dust
701138617c Update Directory.Packages.props 2025-03-16 10:56:33 +08:00
Avery Lynn
d0e2cc9442 Fix server deduplicaiton failure (#6900)
- Add a local method AreEquel to compare string values, return TRUE when compare between 'null' and 'string.Emtpy'
2025-03-13 20:11:10 +08:00
DHR60
d561f10edc Modify default fallback load balancing rule (#6889)
* Modify default fallback load balancing rule

* Refine default fallback load balancing rule based on domain strategy
2025-03-12 18:36:51 +08:00
2dust
2df412476a If it is a Windows WinGet installation, the configuration file is stored in the user directory
https://github.com/2dust/v2rayN/issues/6803
2025-03-09 19:04:49 +08:00
2dust
e6011cfede AI-optimized code 2025-03-07 12:11:19 +08:00
2dust
bcf43e2928 Enhanced subscription customization configuration
https://github.com/2dust/v2rayN/issues/6875
2025-03-07 11:37:01 +08:00
2dust
3f0f895424 up 7.10.4 2025-03-06 17:35:10 +08:00
2dust
07a3bdc618 AI-optimized code 2025-03-06 16:36:39 +08:00
2dust
7e348c196e AI-optimized code 2025-03-06 14:53:25 +08:00
2dust
51e5885e76 AI-optimized code 2025-03-06 14:42:21 +08:00
2dust
50d7912f62 AI-optimized code 2025-03-06 14:34:31 +08:00
2dust
3869148fc8 AI-optimized code 2025-03-06 14:30:02 +08:00
2dust
a0af4fb30c Update DownloadService.cs
AI-optimized code
2025-03-06 14:08:38 +08:00
2dust
c374b8565b Update SpeedtestService.cs
AI-optimized code
2025-03-06 14:08:29 +08:00
2dust
7e8b405555 AI-optimized code 2025-03-06 12:21:21 +08:00
2dust
c3439c5abe AI-optimized code 2025-03-06 11:10:55 +08:00
2dust
d4a8787356 AI-optimized code 2025-03-06 10:57:39 +08:00
2dust
23b27575a0 AI-optimized code 2025-03-06 10:48:18 +08:00
2dust
8d8a887c42 Update HttpClientHelper.cs
AI-optimized code
2025-03-06 10:47:57 +08:00
2dust
1229c967ba Update SemanticVersion.cs
AI-optimized code
2025-03-06 10:47:31 +08:00
2dust
d35f65f86d Update Utils.cs
AI-optimized code
2025-03-06 10:47:06 +08:00
2dust
0a8ce0f961 Update ProxySettingWindows.cs
AI-optimized code
2025-03-06 10:46:50 +08:00
2dust
8092481d26 Update Job.cs
AI-optimized code
2025-03-06 10:46:41 +08:00
2dust
764014e49a Replace all Utils.ToInt(variable) with variable.ToInt() 2025-03-05 20:26:26 +08:00
2dust
71cc6d7a88 Replace all Utils.IsNullOrEmpty(variable) with variable.IsNullOrEmpty() 2025-03-05 19:44:49 +08:00
2dust
f3af831cf2 Replace all Utils.IsNotEmpty(variable) with variable.IsNotEmpty() 2025-03-05 16:42:43 +08:00
2dust
78fde575d7 up 7.10.3 2025-03-04 17:03:26 +08:00
2dust
6e5af34877 Revert "If the update fails during the upgrade, the update will be retried."
This reverts commit 9748fbb076.
2025-03-04 17:02:05 +08:00
2dust
8d1853e991 up 7.10.2 2025-03-04 15:11:05 +08:00
DHR60
859299c712 Add kcp DNS masquerade support (#6852)
* Add kcp DNS masquerade support

* Update V2rayConfig.cs

* Update CoreConfigV2rayService.cs

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-03-04 10:32:07 +08:00
2dust
7fbb0013b0 Optimizing Task Code 2025-03-03 16:57:55 +08:00
2dust
837cfbd03b Optimize UI prompts 2025-03-03 14:36:30 +08:00
2dust
cdc5d72cfa Update CoreConfigSingboxService.cs 2025-03-03 14:36:01 +08:00
DHR60
8dcf5c5b90 Add Hy2 Port hopping URI support (#6848) 2025-03-03 14:11:53 +08:00
2dust
67fe6ac3d8 Optimizing Task Code, add unified processing of scheduled tasks 2025-03-03 12:20:58 +08:00
2dust
438eaba4d5 up 7.10.1 2025-03-02 10:30:08 +08:00
2dust
3c8baa99d5 Update Directory.Packages.props 2025-03-02 10:29:43 +08:00
2dust
e70658f311 Add Hy2 Port hopping for sing-box 1.11+
https://github.com/2dust/v2rayN/issues/6772
2025-03-01 21:13:37 +08:00
2dust
2dd10cf5a1 Optimize QrcodeView 2025-03-01 19:56:52 +08:00
2dust
96781a784b git submodule update --remote 2025-03-01 15:29:54 +08:00
2dust
9748fbb076 If the update fails during the upgrade, the update will be retried. 2025-03-01 14:23:43 +08:00
2dust
aa5e4378ab Update AutoStartupHandler.cs 2025-03-01 14:14:07 +08:00
2dust
a7de149fd7 up 7.10.0 2025-02-28 09:46:43 +08:00
2dust
ae38be36f5 Checkout submodules 2025-02-27 20:53:29 +08:00
2dust
a20e989211 Update build-windows-desktop.yml 2025-02-27 20:50:52 +08:00
2dust
579f47ba0d Create GlobalHotKeys 2025-02-27 20:25:20 +08:00
2dust
24cad87954 Bug fix
https://github.com/2dust/v2rayN/issues/6812
2025-02-27 20:12:53 +08:00
2dust
84d72cd110 Use project to implement Windows global hotkey
https://github.com/2dust/GlobalHotKeys
2025-02-27 20:12:07 +08:00
2dust
c0cd46a5aa Optimize HotkeyHandler 2025-02-27 17:50:54 +08:00
2dust
98613c43ca Fix tun mtu setting 2025-02-26 20:46:31 +08:00
2dust
555960e210 Optimize and improve code 2025-02-26 17:01:57 +08:00
2dust
a18ae5582b Jump to the selected item when refreshing the server list
https://github.com/2dust/v2rayN/issues/6800
2025-02-26 16:36:36 +08:00
2dust
6d6894591c Update Global.cs 2025-02-26 15:51:47 +08:00
2dust
984b97fc14 Optimize latency and IP address information testing
If the first test fails, it will be tested again after 500ms.
2025-02-26 14:30:10 +08:00
2dust
a7f35d4495 Windows desktop version add global hotkey function 2025-02-26 10:52:36 +08:00
2dust
add92cfa7c Update desktop global hotkey setting 2025-02-25 16:14:50 +08:00
2dust
6079e76be5 Improved global hotkey setting 2025-02-25 14:26:12 +08:00
2dust
166c7cb2f5 Fix window title 2025-02-25 14:15:54 +08:00
nayeko
dbd3ca44c2 Fix Package AppImage script (#6794)
Co-authored-by: nayeko <nayeko@users.noreply.github.com>
2025-02-25 10:06:33 +08:00
dependabot[bot]
ae495dde54 Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#6797)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.0 to 4.6.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.6.0...v4.6.1)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-25 09:46:08 +08:00
2dust
4ae25b2f34 Improved global hotkey setting 2025-02-24 19:53:38 +08:00
2dust
cdfb621c59 Update MsgView.axaml 2025-02-24 19:07:05 +08:00
2dust
29e8df7d2e When set the linux system proxy, use shell scripts instead of command lines 2025-02-24 11:02:51 +08:00
2dust
72ff947d95 Bug fix for ProxySettingOSX 2025-02-23 19:59:01 +08:00
2dust
3edaac5739 When set the macOS system proxy, use shell scripts instead of command lines 2025-02-23 19:52:13 +08:00
2dust
5777a97119 Create proxy_set_osx_sh 2025-02-23 19:41:34 +08:00
2dust
aeddbc1dcc CreateLinuxShellFile in the binConfigs folder 2025-02-23 17:19:45 +08:00
2dust
2f3e409487 Add linux bash param 2025-02-23 16:59:22 +08:00
2dust
aa133bb50b Update SpeedtestService.cs 2025-02-23 16:34:40 +08:00
2dust
3bc79a4ba1 Update 01_bug_report.yml 2025-02-23 12:05:51 +08:00
2dust
064a04fbad up 7.9.3 2025-02-21 17:47:30 +08:00
2dust
39ed13cf8a Create build-all.yml 2025-02-21 17:38:47 +08:00
2dust
a1edacb196 Update UpdateService.cs 2025-02-21 17:37:36 +08:00
2dust
c9f79e4b47 Added update function for Country.mmdb, geoip-only-cn-private.dat, geoip.metadb
https://github.com/2dust/v2rayN/issues/6752
2025-02-21 15:38:11 +08:00
2dust
40c1498226 When testing speed, skip items with incorrect latency 2025-02-21 14:33:43 +08:00
2dust
390061f9bd Fix the desktop UI bug 2025-02-21 12:40:45 +08:00
2dust
42324a2c9e Adjust controls margin 2025-02-20 18:34:25 +08:00
2dust
b9b4ca6360 The desktop version use SemiFontFamilyRegular, Fall back to built-in fonts 2025-02-20 16:23:46 +08:00
2dust
565697bc0b The desktop version unified icon size 2025-02-20 14:32:38 +08:00
2dust
2e6c82c851 Adjust UI
https://github.com/2dust/v2rayN/issues/6751
2025-02-20 14:07:12 +08:00
2dust
ee75dd37af The desktop version add wrap to the subscription group 2025-02-20 11:08:28 +08:00
2dust
4859dcda08 up 7.9.2 2025-02-19 21:10:47 +08:00
2dust
2e962e555d Use AutoCompleteBox instead of editable Combobox for desktop
Thanks to https://github.com/irihitech/Ursa.Avalonia
2025-02-19 17:41:58 +08:00
2dust
ab73e5acb2 Micro-adjustment interface 2025-02-19 17:39:47 +08:00
2dust
4e3e5ce130 Add CanUserReorderColumns for desktop 2025-02-19 15:03:29 +08:00
2dust
bb8eef3bf5 Removed the Hide to tray when closing the window feature for Windows version
https://github.com/2dust/v2rayN/issues/6726
2025-02-19 10:42:31 +08:00
2dust
eee87ded29 Fix cache.db storage location
https://github.com/2dust/v2rayN/issues/6731
2025-02-19 10:06:47 +08:00
2dust
e0f005bd96 up 7.9.1 2025-02-18 18:58:19 +08:00
2dust
2cacc372ad Optimize desktop DataGrid RowHeight adjusts with font size 2025-02-18 18:48:29 +08:00
2dust
0b1b681655 Add system proxy pac to the Windows desktop version 2025-02-18 14:38:08 +08:00
2dust
deafd73306 Optimize RemoveInvalidServerResult 2025-02-17 20:00:28 +08:00
2dust
317a5da120 up build 2025-02-17 14:38:26 +08:00
bonjour
071cefc511 Revert "Fix Package AppImage script (#6681)" (#6714)
This reverts commit 41cc260b5c.
2025-02-17 13:59:36 +08:00
2dust
32a5cc8aa3 Check for avalonia desktop windows version 2025-02-17 12:27:00 +08:00
2dust
c41378a085 Update build-windows-desktop.yml 2025-02-17 12:24:55 +08:00
2dust
6910e03ef4 up 7.9.0 2025-02-16 20:34:14 +08:00
2dust
5060a358db Clear resx 2025-02-16 19:12:58 +08:00
2dust
b3e9a957c4 Remove invalid by test results 2025-02-16 15:07:09 +08:00
2dust
f6dbfc2dac up PackageVersion 2025-02-16 15:05:10 +08:00
2dust
2966a34e63 Update Directory.Build.props 2025-02-16 11:15:32 +08:00
2dust
50959951ae The number of concurrent during multi-test 2025-02-15 20:27:20 +08:00
2dust
c2e1cf7bdb Fix windows TaskbarIcon 2025-02-15 17:26:20 +08:00
2dust
51ac7cc8be Improved and optimized speedtest 2025-02-15 11:21:35 +08:00
2dust
3144f1d1c2 Remove SpeedTestPageSize 2025-02-14 15:00:23 +08:00
2dust
a176e7b912 Optimize the prompt function of speed test
Save friendly prompt information during speed test.
2025-02-14 14:55:43 +08:00
2dust
1198ec0f74 Improved and optimized speedtest
When testing server speed, start the Core for each server and generate the same configuration files as when using the server.
Add a folder binConfigs to store temporary configuration files for Core.
2025-02-13 19:46:51 +08:00
2dust
4104964e38 up 7.8.3 2025-02-12 20:04:29 +08:00
2dust
3bbd1edf06 Update V2rayConfig.cs 2025-02-12 19:18:59 +08:00
lyranico
41cc260b5c Fix Package AppImage script (#6681) 2025-02-12 14:33:02 +08:00
2dust
6cd5063c9b Fix call core file
https://github.com/2dust/v2rayN/issues/6680
2025-02-12 11:23:22 +08:00
2dust
e544df6d01 up 7.8.2 2025-02-11 16:30:16 +08:00
2dust
f952d2383c Remove IncludeNativeLibrariesForSelfExtract 2025-02-11 10:16:19 +08:00
2dust
8a29e147d3 Added Enable Mixin parameter 2025-02-10 20:04:47 +08:00
Alphax-Hue3682
8a19128e7f Update Persian translate (#6651)
* Update Persian translate

* Update ResUI.fa-Ir.resx
2025-02-10 17:40:21 +08:00
2dust
7dc9fbd8ff Improved and optimized speedtest 2025-02-10 10:08:03 +08:00
2dust
31a179e647 Code clean 2025-02-09 20:17:56 +08:00
253 changed files with 26750 additions and 49502 deletions

View File

@@ -3,6 +3,13 @@ description: 在提出问题前请先自行排除服务器端问题和升级到
title: "[Bug]: " title: "[Bug]: "
labels: ["bug"] labels: ["bug"]
body: body:
- type: input
id: "os-version"
attributes:
label: "操作系统和版本"
description: "操作系统和版本"
validations:
required: true
- type: input - type: input
id: "expectation" id: "expectation"
attributes: attributes:

69
.github/workflows/build-all.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: release all platforms
on:
workflow_dispatch:
inputs:
release_tag:
required: false
type: string
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Trigger build windows
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-windows.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"
- name: Trigger build linux
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-linux.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"
- name: Trigger build osx
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-osx.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"
- name: Trigger build windows desktop
if: github.event.inputs.release_tag != ''
run: |
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/build-windows-desktop.yml/dispatches \
-d "{
\"ref\": \"master\",
\"inputs\": {
\"release_tag\": \"${{ github.event.inputs.release_tag }}\"
}
}"

View File

@@ -27,22 +27,25 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.1
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
- name: Build - name: Build
run: | run: |
cd v2rayN cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.0 uses: actions/upload-artifact@v4.6.2
with: with:
name: v2rayN-linux name: v2rayN-linux
path: | path: |
@@ -68,9 +71,8 @@ jobs:
- name: Package AppImage - name: Package AppImage
if: github.event.inputs.release_tag != '' if: github.event.inputs.release_tag != ''
run: | run: |
chmod 755 package-appimage.sh chmod a+x package-appimage.sh
./package-appimage.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }} ./package-appimage.sh
./package-appimage.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
- name: Upload AppImage to release - name: Upload AppImage to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2

View File

@@ -27,22 +27,25 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.1
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
- name: Build - name: Build
run: | run: |
cd v2rayN cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPath64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPath64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.0 uses: actions/upload-artifact@v4.6.2
with: with:
name: v2rayN-macos name: v2rayN-macos
path: | path: |

View File

@@ -27,24 +27,45 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.1
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
- name: Build - name: Build
run: | run: |
cd v2rayN cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableWindowsTargeting=true -o $OutputPath64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableWindowsTargeting=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.0 uses: actions/upload-artifact@v4.6.2
with: with:
name: v2rayN-windows-desktop name: v2rayN-windows-desktop
path: | path: |
${{ github.workspace }}/v2rayN/Release/windows* ${{ github.workspace }}/v2rayN/Release/windows*
# release zip archive
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
mv "v2rayN-${OutputArch}.zip" "v2rayN-${OutputArch}-desktop.zip"
./package-release-zip.sh $OutputArchArm $OutputPathArm64
mv "v2rayN-${OutputArchArm}.zip" "v2rayN-${OutputArchArm}-desktop.zip"
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.zip
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true

View File

@@ -30,23 +30,23 @@ jobs:
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.1
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
- name: Build - name: Build
run: | run: |
cd v2rayN cd v2rayN
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64 dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained=false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64 dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPath64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=false -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:EnableWindowsTargeting=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64Sc
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.0 uses: actions/upload-artifact@v4.6.2
with: with:
name: v2rayN-windows name: v2rayN-windows
path: | path: |

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "v2rayN/GlobalHotKeys"]
path = v2rayN/GlobalHotKeys
url = https://github.com/2dust/GlobalHotKeys

View File

@@ -1,71 +1,14 @@
#!/bin/bash #!/bin/bash
Arch="$1" sudo apt update -y
OutputPath="$2"
Version="$3"
FileName="v2rayN-${Arch}.zip"
wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/$FileName"
7z x $FileName -aoa
cp -rf v2rayN-${Arch}/* $OutputPath
PackagePath="v2rayN-Package-${Arch}"
mkdir -p "${PackagePath}/AppDir/opt"
cp -rf $OutputPath "${PackagePath}/AppDir/opt/v2rayN"
echo "When this file exists, app will not store configs under this folder" >"${PackagePath}/AppDir/opt/v2rayN/NotStoreConfigHere.txt"
if [ $Arch = "linux-64" ]; then
Arch2="x86_64"
Arch3="amd64"
else
Arch2="aarch64"
Arch3="arm64"
fi
echo $Arch2
# basic
cat >"${PackagePath}/AppDir/AppRun" <<-EOF
#!/bin/sh
HERE="\$(dirname "\$(readlink -f "\${0}")")"
export PATH="\${HERE}"/opt/v2rayN/:"\${PATH}"
export LD_LIBRARY_PATH="\${HERE}"/opt/v2rayN/:"\${LD_LIBRARY_PATH}"
exec "\${HERE}/opt/v2rayN/v2rayN" \$@
EOF
cat >"${PackagePath}/AppDir/v2rayN.desktop" <<-EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
Exec=v2rayN
Icon=v2rayN
Terminal=false
Type=Application
Categories=Network;
EOF
sudo cp "${PackagePath}/AppDir/opt/v2rayN/v2rayN.png" "${PackagePath}/AppDir/v2rayN.png"
sudo dpkg --add-architecture ${Arch3}
mkdir deb_folder
cd deb_folder
apt download libicu74:${Arch3}
apt download libfontconfig1:${Arch3} || true
apt download libfontconfig:${Arch3} || true
mkdir ../output_folder
for deb in *.deb; do
dpkg-deb -x "$deb" ../output_folder/
done
cd ..
find output_folder -type f -name "*.so*" -exec cp {} ${PackagePath}/AppDir/opt/v2rayN/ \;
find output_folder -type l -name "*.so*" -exec cp {} ${PackagePath}/AppDir/opt/v2rayN/ \;
rm -rf deb_folder output_folder
sudo chmod 0755 "${PackagePath}/AppDir/opt/v2rayN/v2rayN"
sudo chmod 0755 "${PackagePath}/AppDir/AppRun"
# desktop && PATH
wget "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod a+x appimagetool-x86_64.AppImage
sudo apt install -y libfuse2 sudo apt install -y libfuse2
sudo ./appimagetool-x86_64.AppImage "${PackagePath}/AppDir" wget -O pkg2appimage https://github.com/AppImageCommunity/pkg2appimage/releases/download/continuous/pkg2appimage-1eceb30-x86_64.AppImage
sudo mv "v2rayN-${Arch2}.AppImage" "v2rayN-${Arch}.AppImage" chmod a+x pkg2appimage
export AppImageOutputArch=$OutputArch
export OutputPath=$OutputPath64
./pkg2appimage ./pkg2appimage.yml
mv out/*.AppImage v2rayN-${AppImageOutputArch}.AppImage
export AppImageOutputArch=$OutputArchArm
export OutputPath=$OutputPathArm64
./pkg2appimage ./pkg2appimage.yml
mv out/*.AppImage v2rayN-${AppImageOutputArch}.AppImage

37
pkg2appimage.yml Normal file
View File

@@ -0,0 +1,37 @@
app: v2rayN
binpatch: true
ingredients:
script:
- export FileName="v2rayN-${AppImageOutputArch}.zip"
- wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/${FileName}"
- 7z x $FileName -aoa
- cp -rf v2rayN-${AppImageOutputArch}/* $OutputPath
script:
- mkdir -p usr/bin usr/lib
- cp -rf $OutputPath usr/lib/v2rayN
- echo "When this file exists, app will not store configs under this folder" > usr/lib/v2rayN/NotStoreConfigHere.txt
- ln -sf usr/lib/v2rayN/v2rayN usr/bin/v2rayN
- chmod a+x usr/lib/v2rayN/v2rayN
- find usr -type f -exec sh -c 'file "{}" | grep -qi "executable" && chmod +x "{}"' \;
- install -Dm644 usr/lib/v2rayN/v2rayN.png v2rayN.png
- install -Dm644 usr/lib/v2rayN/v2rayN.png usr/share/pixmaps/v2rayN.png
- cat > v2rayN.desktop <<EOF
- [Desktop Entry]
- Name=v2rayN
- Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
- Exec=v2rayN
- Icon=v2rayN
- Terminal=false
- Type=Application
- Categories=Network;
- EOF
- install -Dm644 v2rayN.desktop usr/share/applications/v2rayN.desktop
- cat > AppRun <<\EOF
- #!/bin/sh
- HERE="$(dirname "$(readlink -f "${0}")")"
- cd ${HERE}/usr/lib/v2rayN
- exec ${HERE}/usr/lib/v2rayN/v2rayN $@
- EOF
- chmod a+x AppRun

View File

@@ -1,10 +1,7 @@
namespace AmazTool namespace AmazTool;
{
internal static class Program internal static class Program
{ {
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread] [STAThread]
private static void Main(string[] args) private static void Main(string[] args)
{ {
@@ -26,4 +23,3 @@
UpgradeApp.Upgrade(argData); UpgradeApp.Upgrade(argData);
} }
} }
}

View File

@@ -0,0 +1,156 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Restartv2rayN" xml:space="preserve">
<value>正在重啟,請等待...</value>
</data>
<data name="Guidelines" xml:space="preserve">
<value>請從主應用程式運行。</value>
</data>
<data name="UpgradeFileNotFound" xml:space="preserve">
<value>升級失敗,檔案不存在。</value>
</data>
<data name="InProgress" xml:space="preserve">
<value>正在進行中,請等待...</value>
</data>
<data name="TryTerminateProcess" xml:space="preserve">
<value>嘗試結束 v2rayN 進程...</value>
</data>
<data name="FailedTerminateProcess" xml:space="preserve">
<value>請手動關閉正在執行的 v2rayN否則可能會升級失敗。</value>
</data>
<data name="StartUnzipping" xml:space="preserve">
<value>開始解壓縮更新包...</value>
</data>
<data name="SuccessUnzipping" xml:space="preserve">
<value>解壓縮更新包成功。</value>
</data>
<data name="FailedUnzipping" xml:space="preserve">
<value>解壓縮更新包失敗。</value>
</data>
<data name="FailedUpgrade" xml:space="preserve">
<value>升級失敗。</value>
</data>
<data name="SuccessUpgrade" xml:space="preserve">
<value>升級成功。</value>
</data>
<data name="Information" xml:space="preserve">
<value>提示</value>
</data>
</root>

View File

@@ -2,8 +2,8 @@ using System.Diagnostics;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
namespace AmazTool namespace AmazTool;
{
internal class UpgradeApp internal class UpgradeApp
{ {
public static void Upgrade(string fileName) public static void Upgrade(string fileName)
@@ -60,7 +60,10 @@ namespace AmazTool
var lst = entry.FullName.Split(splitKey); var lst = entry.FullName.Split(splitKey);
if (lst.Length == 1) if (lst.Length == 1)
{
continue; continue;
}
var fullName = string.Join(splitKey, lst[1..lst.Length]); var fullName = string.Join(splitKey, lst[1..lst.Length]);
if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase)) if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase))
@@ -75,7 +78,16 @@ namespace AmazTool
{ {
continue; continue;
} }
try
{
entry.ExtractToFile(entryOutputPath, true); entry.ExtractToFile(entryOutputPath, true);
}
catch
{
Thread.Sleep(1000);
entry.ExtractToFile(entryOutputPath, true);
}
Console.WriteLine(entryOutputPath); Console.WriteLine(entryOutputPath);
} }
@@ -102,4 +114,3 @@ namespace AmazTool
Utils.StartV2RayN(); Utils.StartV2RayN();
} }
} }
}

View File

@@ -1,7 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace AmazTool;
namespace AmazTool
{
internal class Utils internal class Utils
{ {
public static string GetExePath() public static string GetExePath()
@@ -16,7 +16,7 @@ namespace AmazTool
public static string GetPath(string fileName) public static string GetPath(string fileName)
{ {
string startupPath = StartupPath(); var startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName)) if (string.IsNullOrEmpty(fileName))
{ {
return startupPath; return startupPath;
@@ -49,4 +49,3 @@ namespace AmazTool
} }
} }
} }
}

View File

@@ -1,14 +1,14 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.8.1</Version> <Version>7.11.0</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<NoWarn>CA1031;CS1591;NU1507;CA1416</NoWarn> <NoWarn>CA1031;CS1591;NU1507;CA1416;IDE0058</NoWarn>
<Nullable>annotations</Nullable> <Nullable>annotations</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Authors>2dust</Authors> <Authors>2dust</Authors>
@@ -28,6 +28,6 @@
<UseSystemResourceKeys>true</UseSystemResourceKeys> <UseSystemResourceKeys>true</UseSystemResourceKeys>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> <PublishReadyToRun>false</PublishReadyToRun>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -5,25 +5,25 @@
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled> <CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.3" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.6" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.3" /> <PackageVersion Include="Avalonia.Desktop" Version="11.2.6" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.3" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.2.6" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.3" /> <PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.6" />
<PackageVersion Include="CliWrap" Version="3.7.1" /> <PackageVersion Include="CliWrap" Version="3.8.2" />
<PackageVersion Include="Downloader" Version="3.3.3" /> <PackageVersion Include="Downloader" Version="3.3.4" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.2.0" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" /> <PackageVersion Include="MaterialDesignThemes" Version="5.2.1" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" /> <PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageVersion Include="QRCoder" Version="1.6.0" /> <PackageVersion Include="QRCoder" Version="1.6.0" />
<PackageVersion Include="ReactiveUI" Version="20.1.63" /> <PackageVersion Include="ReactiveUI" Version="20.2.45" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.1.63" /> <PackageVersion Include="ReactiveUI.WPF" Version="20.2.45" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.4" /> <PackageVersion Include="Semi.Avalonia" Version="11.2.1.6" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.4" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.6" />
<PackageVersion Include="Splat.NLog" Version="15.3.1" /> <PackageVersion Include="Splat.NLog" Version="15.3.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.11.0" /> <PackageVersion Include="TaskScheduler" Version="2.12.1" />
<PackageVersion Include="WebDav.Client" Version="2.8.0" /> <PackageVersion Include="WebDav.Client" Version="2.9.0" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" /> <PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" /> <PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
</ItemGroup> </ItemGroup>

1
v2rayN/GlobalHotKeys Submodule

Submodule v2rayN/GlobalHotKeys added at ef73fa22c4

View File

@@ -1,10 +1,9 @@
using ReactiveUI; using ReactiveUI;
namespace ServiceLib.Base;
namespace ServiceLib.Base
{
public class MyReactiveObject : ReactiveObject public class MyReactiveObject : ReactiveObject
{ {
protected static Config? _config; protected static Config? _config;
protected Func<EViewAction, object?, Task<bool>>? _updateView; protected Func<EViewAction, object?, Task<bool>>? _updateView;
} }
}

View File

@@ -1,8 +1,8 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class AesUtils public class AesUtils
{ {
private const int KeySize = 256; // AES-256 private const int KeySize = 256; // AES-256
@@ -98,4 +98,3 @@ namespace ServiceLib.Common
return randomNumber; return randomNumber;
} }
} }
}

View File

@@ -1,8 +1,8 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class DesUtils public class DesUtils
{ {
/// <summary> /// <summary>
@@ -72,4 +72,3 @@ namespace ServiceLib.Common
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils"); return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
} }
} }
}

View File

@@ -1,8 +1,8 @@
using System.Net; using System.Net;
using Downloader; using Downloader;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class DownloaderHelper public class DownloaderHelper
{ {
private static readonly Lazy<DownloaderHelper> _instance = new(() => new()); private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
@@ -10,7 +10,7 @@ namespace ServiceLib.Common
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout) public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
{ {
if (Utils.IsNullOrEmpty(url)) if (url.IsNullOrEmpty())
{ {
return null; return null;
} }
@@ -18,7 +18,7 @@ namespace ServiceLib.Common
Uri uri = new(url); Uri uri = new(url);
//Authorization Header //Authorization Header
var headers = new WebHeaderCollection(); var headers = new WebHeaderCollection();
if (Utils.IsNotEmpty(uri.UserInfo)) if (uri.UserInfo.IsNotEmpty())
{ {
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo)); headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
} }
@@ -56,7 +56,7 @@ namespace ServiceLib.Common
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout) public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
{ {
if (Utils.IsNullOrEmpty(url)) if (url.IsNullOrEmpty())
{ {
throw new ArgumentNullException(nameof(url)); throw new ArgumentNullException(nameof(url));
} }
@@ -86,7 +86,7 @@ namespace ServiceLib.Common
//}; //};
downloader.DownloadProgressChanged += (sender, value) => downloader.DownloadProgressChanged += (sender, value) =>
{ {
var ts = (DateTime.Now - totalDatetime); var ts = DateTime.Now - totalDatetime;
if (progress != null && ts.Seconds > totalSecond) if (progress != null && ts.Seconds > totalSecond)
{ {
hasValue = true; hasValue = true;
@@ -119,11 +119,11 @@ namespace ServiceLib.Common
public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout) public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout)
{ {
if (Utils.IsNullOrEmpty(url)) if (url.IsNullOrEmpty())
{ {
throw new ArgumentNullException(nameof(url)); throw new ArgumentNullException(nameof(url));
} }
if (Utils.IsNullOrEmpty(fileName)) if (fileName.IsNullOrEmpty())
{ {
throw new ArgumentNullException(nameof(fileName)); throw new ArgumentNullException(nameof(fileName));
} }
@@ -146,10 +146,7 @@ namespace ServiceLib.Common
var progressPercentage = 0; var progressPercentage = 0;
var hasValue = false; var hasValue = false;
await using var downloader = new Downloader.DownloadService(downloadOpt); await using var downloader = new Downloader.DownloadService(downloadOpt);
downloader.DownloadStarted += (sender, value) => downloader.DownloadStarted += (sender, value) => progress?.Report(0);
{
progress?.Report(0);
};
downloader.DownloadProgressChanged += (sender, value) => downloader.DownloadProgressChanged += (sender, value) =>
{ {
hasValue = true; hasValue = true;
@@ -181,4 +178,3 @@ namespace ServiceLib.Common
downloadOpt = null; downloadOpt = null;
} }
} }
}

View File

@@ -1,9 +1,9 @@
using System.Formats.Tar; using System.Formats.Tar;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public static class FileManager public static class FileManager
{ {
private static readonly string _tag = "FileManager"; private static readonly string _tag = "FileManager";
@@ -99,7 +99,7 @@ namespace ServiceLib.Common
} }
try try
{ {
if (Utils.IsNotEmpty(ignoredName) && entry.Name.Contains(ignoredName)) if (ignoredName.IsNotEmpty() && entry.Name.Contains(ignoredName))
{ {
continue; continue;
} }
@@ -163,18 +163,20 @@ namespace ServiceLib.Common
// Check if the source directory exists // Check if the source directory exists
if (!dir.Exists) if (!dir.Exists)
{
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}"); throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
}
// Cache directories before we start copying // Cache directories before we start copying
var dirs = dir.GetDirectories(); var dirs = dir.GetDirectories();
// Create the destination directory // Create the destination directory
Directory.CreateDirectory(destinationDir); _ = Directory.CreateDirectory(destinationDir);
// Get the files in the source directory and copy to the destination directory // Get the files in the source directory and copy to the destination directory
foreach (var file in dir.GetFiles()) foreach (var file in dir.GetFiles())
{ {
if (Utils.IsNotEmpty(ignoredName) && file.Name.Contains(ignoredName)) if (ignoredName.IsNotEmpty() && file.Name.Contains(ignoredName))
{ {
continue; continue;
} }
@@ -187,7 +189,7 @@ namespace ServiceLib.Common
{ {
continue; continue;
} }
file.CopyTo(targetFilePath, overwrite); _ = file.CopyTo(targetFilePath, overwrite);
} }
// If recursive and copying subdirectories, recursively call this method // If recursive and copying subdirectories, recursively call this method
@@ -222,4 +224,3 @@ namespace ServiceLib.Common
} }
} }
} }
}

View File

@@ -2,8 +2,8 @@ using System.Net.Http.Headers;
using System.Net.Mime; using System.Net.Mime;
using System.Text; using System.Text;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
/// <summary> /// <summary>
/// </summary> /// </summary>
public class HttpClientHelper public class HttpClientHelper
@@ -18,12 +18,17 @@ namespace ServiceLib.Common
public static HttpClientHelper Instance => _instance.Value; public static HttpClientHelper Instance => _instance.Value;
private readonly HttpClient httpClient; private readonly HttpClient httpClient;
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient; private HttpClientHelper(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async Task<string?> TryGetAsync(string url) public async Task<string?> TryGetAsync(string url)
{ {
if (Utils.IsNullOrEmpty(url)) if (url.IsNullOrEmpty())
{
return null; return null;
}
try try
{ {
@@ -38,15 +43,19 @@ namespace ServiceLib.Common
public async Task<string?> GetAsync(string url) public async Task<string?> GetAsync(string url)
{ {
if (Utils.IsNullOrEmpty(url)) if (url.IsNullOrEmpty())
{
return null; return null;
}
return await httpClient.GetStringAsync(url); return await httpClient.GetStringAsync(url);
} }
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default) public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
{ {
if (Utils.IsNullOrEmpty(url)) if (url.IsNullOrEmpty())
{
return null; return null;
}
return await client.GetStringAsync(url, token); return await client.GetStringAsync(url, token);
} }
@@ -55,13 +64,13 @@ namespace ServiceLib.Common
var jsonContent = JsonUtils.Serialize(headers); var jsonContent = JsonUtils.Serialize(headers);
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json); var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
var result = await httpClient.PutAsync(url, content); await httpClient.PutAsync(url, content);
} }
public async Task PatchAsync(string url, Dictionary<string, string> headers) public async Task PatchAsync(string url, Dictionary<string, string> headers)
{ {
var myContent = JsonUtils.Serialize(headers); var myContent = JsonUtils.Serialize(headers);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent); var buffer = Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer); var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
@@ -78,12 +87,16 @@ namespace ServiceLib.Common
ArgumentNullException.ThrowIfNull(url); ArgumentNullException.ThrowIfNull(url);
ArgumentNullException.ThrowIfNull(fileName); ArgumentNullException.ThrowIfNull(fileName);
if (File.Exists(fileName)) if (File.Exists(fileName))
{
File.Delete(fileName); File.Delete(fileName);
}
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
{
throw new Exception(response.StatusCode.ToString()); throw new Exception(response.StatusCode.ToString());
}
var total = response.Content.Headers.ContentLength ?? -1L; var total = response.Content.Headers.ContentLength ?? -1L;
var canReportProgress = total != -1 && progress != null; var canReportProgress = total != -1 && progress != null;
@@ -102,7 +115,9 @@ namespace ServiceLib.Common
totalRead += read; totalRead += read;
if (read == 0) if (read == 0)
{
break; break;
}
await file.WriteAsync(buffer.AsMemory(0, read), token); await file.WriteAsync(buffer.AsMemory(0, read), token);
if (canReportProgress) if (canReportProgress)
@@ -123,7 +138,7 @@ namespace ServiceLib.Common
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default) public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{ {
if (Utils.IsNullOrEmpty(url)) if (url.IsNullOrEmpty())
{ {
throw new ArgumentNullException(nameof(url)); throw new ArgumentNullException(nameof(url));
} }
@@ -173,7 +188,7 @@ namespace ServiceLib.Common
totalRead += read; totalRead += read;
var ts = (DateTime.Now - totalDatetime); var ts = DateTime.Now - totalDatetime;
if (progress != null && ts.Seconds > totalSecond) if (progress != null && ts.Seconds > totalSecond)
{ {
totalSecond = ts.Seconds; totalSecond = ts.Seconds;
@@ -188,4 +203,3 @@ namespace ServiceLib.Common
} while (isMoreToRead); } while (isMoreToRead);
} }
} }
}

View File

@@ -1,8 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
/* /*
* See: * See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net * http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
@@ -15,28 +14,30 @@ namespace ServiceLib.Common
public Job() public Job()
{ {
handle = CreateJobObject(IntPtr.Zero, null); handle = CreateJobObject(IntPtr.Zero, null);
IntPtr extendedInfoPtr = IntPtr.Zero; var extendedInfoPtr = IntPtr.Zero;
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new() var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
{ {
LimitFlags = 0x2000 LimitFlags = 0x2000
}; };
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new() var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{ {
BasicLimitInformation = info BasicLimitInformation = info
}; };
try try
{ {
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length); extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint)length)) (uint)length))
{
throw new Exception(string.Format("Unable to set information. Error: {0}", throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error())); Marshal.GetLastWin32Error()));
} }
}
finally finally
{ {
if (extendedInfoPtr != IntPtr.Zero) if (extendedInfoPtr != IntPtr.Zero)
@@ -48,7 +49,7 @@ namespace ServiceLib.Common
public bool AddProcess(IntPtr processHandle) public bool AddProcess(IntPtr processHandle)
{ {
bool succ = AssignProcessToJobObject(handle, processHandle); var succ = AssignProcessToJobObject(handle, processHandle);
if (!succ) if (!succ)
{ {
@@ -76,7 +77,9 @@ namespace ServiceLib.Common
private void Dispose(bool disposing) private void Dispose(bool disposing)
{ {
if (disposed) if (disposed)
{
return; return;
}
disposed = true; disposed = true;
if (disposing) if (disposing)
@@ -104,7 +107,7 @@ namespace ServiceLib.Common
private static extern IntPtr CreateJobObject(IntPtr a, string? lpName); private static extern IntPtr CreateJobObject(IntPtr a, string? lpName);
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength); private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
@@ -121,34 +124,34 @@ namespace ServiceLib.Common
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct IO_COUNTERS internal struct IO_COUNTERS
{ {
public UInt64 ReadOperationCount; public ulong ReadOperationCount;
public UInt64 WriteOperationCount; public ulong WriteOperationCount;
public UInt64 OtherOperationCount; public ulong OtherOperationCount;
public UInt64 ReadTransferCount; public ulong ReadTransferCount;
public UInt64 WriteTransferCount; public ulong WriteTransferCount;
public UInt64 OtherTransferCount; public ulong OtherTransferCount;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{ {
public Int64 PerProcessUserTimeLimit; public long PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit; public long PerJobUserTimeLimit;
public UInt32 LimitFlags; public uint LimitFlags;
public UIntPtr MinimumWorkingSetSize; public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize; public UIntPtr MaximumWorkingSetSize;
public UInt32 ActiveProcessLimit; public uint ActiveProcessLimit;
public UIntPtr Affinity; public UIntPtr Affinity;
public UInt32 PriorityClass; public uint PriorityClass;
public UInt32 SchedulingClass; public uint SchedulingClass;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES public struct SECURITY_ATTRIBUTES
{ {
public UInt32 nLength; public uint nLength;
public IntPtr lpSecurityDescriptor; public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle; public int bInheritHandle;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@@ -174,4 +177,4 @@ namespace ServiceLib.Common
} }
#endregion Helper classes #endregion Helper classes
}

View File

@@ -1,9 +1,9 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class JsonUtils public class JsonUtils
{ {
private static readonly string _tag = "JsonUtils"; private static readonly string _tag = "JsonUtils";
@@ -128,4 +128,3 @@ namespace ServiceLib.Common
/// <returns></returns> /// <returns></returns>
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj); public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj);
} }
}

View File

@@ -2,10 +2,13 @@ using NLog;
using NLog.Config; using NLog.Config;
using NLog.Targets; using NLog.Targets;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class Logging public class Logging
{ {
private static readonly Logger _logger1 = LogManager.GetLogger("Log1");
private static readonly Logger _logger2 = LogManager.GetLogger("Log2");
public static void Setup() public static void Setup()
{ {
LoggingConfiguration config = new(); LoggingConfiguration config = new();
@@ -28,23 +31,25 @@ namespace ServiceLib.Common
public static void SaveLog(string strContent) public static void SaveLog(string strContent)
{ {
if (!LogManager.IsLoggingEnabled()) if (!LogManager.IsLoggingEnabled())
{
return; return;
}
LogManager.GetLogger("Log1").Info(strContent); _logger1.Info(strContent);
} }
public static void SaveLog(string strTitle, Exception ex) public static void SaveLog(string strTitle, Exception ex)
{ {
if (!LogManager.IsLoggingEnabled()) if (!LogManager.IsLoggingEnabled())
{
return; return;
}
var logger = LogManager.GetLogger("Log2"); _logger2.Debug($"{strTitle},{ex.Message}");
logger.Debug($"{strTitle},{ex.Message}"); _logger2.Debug(ex.StackTrace);
logger.Debug(ex.StackTrace);
if (ex?.InnerException != null) if (ex?.InnerException != null)
{ {
logger.Error(ex.InnerException); _logger2.Error(ex.InnerException);
}
} }
} }
} }

View File

@@ -8,7 +8,7 @@ public static class ProcUtils
public static void ProcessStart(string? fileName, string arguments = "") public static void ProcessStart(string? fileName, string arguments = "")
{ {
ProcessStart(fileName, arguments, null); _ = ProcessStart(fileName, arguments, null);
} }
public static int? ProcessStart(string? fileName, string arguments, string? dir) public static int? ProcessStart(string? fileName, string arguments, string? dir)
@@ -20,9 +20,13 @@ public static class ProcUtils
try try
{ {
if (fileName.Contains(' ')) if (fileName.Contains(' '))
{
fileName = fileName.AppendQuotes(); fileName = fileName.AppendQuotes();
}
if (arguments.Contains(' ')) if (arguments.Contains(' '))
{
arguments = arguments.AppendQuotes(); arguments = arguments.AppendQuotes();
}
Process proc = new() Process proc = new()
{ {
@@ -34,7 +38,7 @@ public static class ProcUtils
WorkingDirectory = dir ?? string.Empty WorkingDirectory = dir ?? string.Empty
} }
}; };
proc.Start(); _ = proc.Start();
return dir is null ? null : proc.Id; return dir is null ? null : proc.Id;
} }
catch (Exception ex) catch (Exception ex)
@@ -56,7 +60,7 @@ public static class ProcUtils
FileName = Utils.GetExePath().AppendQuotes(), FileName = Utils.GetExePath().AppendQuotes(),
Verb = blAdmin ? "runas" : null, Verb = blAdmin ? "runas" : null,
}; };
Process.Start(startInfo); _ = Process.Start(startInfo);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -86,17 +90,43 @@ public static class ProcUtils
GetProcessKeyInfo(proc, review, out var procId, out var fileName, out var processName); GetProcessKeyInfo(proc, review, out var procId, out var fileName, out var processName);
try try
{ proc?.Kill(true); } {
catch (Exception ex) { Logging.SaveLog(_tag, ex); } if (Utils.IsNonWindows())
{
proc?.Kill(true);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
try try
{ proc?.Kill(); } {
catch (Exception ex) { Logging.SaveLog(_tag, ex); } proc?.Kill();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
try try
{ proc?.Close(); } {
catch (Exception ex) { Logging.SaveLog(_tag, ex); } proc?.Close();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
try try
{ proc?.Dispose(); } {
catch (Exception ex) { Logging.SaveLog(_tag, ex); } proc?.Dispose();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
await Task.Delay(300); await Task.Delay(300);
await ProcessKillByKeyInfo(review, procId, fileName, processName); await ProcessKillByKeyInfo(review, procId, fileName, processName);
@@ -108,7 +138,9 @@ public static class ProcUtils
fileName = null; fileName = null;
processName = null; processName = null;
if (!review) if (!review)
{
return; return;
}
try try
{ {
procId = proc?.Id; procId = proc?.Id;

View File

@@ -1,9 +1,9 @@
using QRCoder; using QRCoder;
using SkiaSharp; using SkiaSharp;
using ZXing.SkiaSharp; using ZXing.SkiaSharp;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class QRCodeHelper public class QRCodeHelper
{ {
public static byte[]? GenQRCode(string? url) public static byte[]? GenQRCode(string? url)
@@ -60,7 +60,7 @@ namespace ServiceLib.Common
var reader = new BarcodeReader(); var reader = new BarcodeReader();
var result = reader.Decode(bitmap); var result = reader.Decode(bitmap);
if (result != null && Utils.IsNotEmpty(result.Text)) if (result != null && result.Text.IsNotEmpty())
{ {
return result.Text; return result.Text;
} }
@@ -87,4 +87,3 @@ namespace ServiceLib.Common
return flipped; return flipped;
} }
} }
}

View File

@@ -1,29 +1,29 @@
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class SemanticVersion public class SemanticVersion
{ {
private int major; private readonly int major;
private int minor; private readonly int minor;
private int patch; private readonly int patch;
private string version; private readonly string version;
public SemanticVersion(int major, int minor, int patch) public SemanticVersion(int major, int minor, int patch)
{ {
this.major = major; this.major = major;
this.minor = minor; this.minor = minor;
this.patch = patch; this.patch = patch;
this.version = $"{major}.{minor}.{patch}"; version = $"{major}.{minor}.{patch}";
} }
public SemanticVersion(string? version) public SemanticVersion(string? version)
{ {
try try
{ {
if (version.IsNullOrEmpty()) if (string.IsNullOrEmpty(version))
{ {
this.major = 0; major = 0;
this.minor = 0; minor = 0;
this.patch = 0; patch = 0;
return; return;
} }
this.version = version.RemovePrefix('v'); this.version = version.RemovePrefix('v');
@@ -31,15 +31,15 @@
var parts = this.version.Split('.'); var parts = this.version.Split('.');
if (parts.Length == 2) if (parts.Length == 2)
{ {
this.major = int.Parse(parts.First()); major = int.Parse(parts.First());
this.minor = int.Parse(parts.Last()); minor = int.Parse(parts.Last());
this.patch = 0; patch = 0;
} }
else if (parts.Length is 3 or 4) else if (parts.Length is 3 or 4)
{ {
this.major = int.Parse(parts[0]); major = int.Parse(parts[0]);
this.minor = int.Parse(parts[1]); minor = int.Parse(parts[1]);
this.patch = int.Parse(parts[2]); patch = int.Parse(parts[2]);
} }
else else
{ {
@@ -48,9 +48,9 @@
} }
catch catch
{ {
this.major = 0; major = 0;
this.minor = 0; minor = 0;
this.patch = 0; patch = 0;
} }
} }
@@ -58,7 +58,7 @@
{ {
if (obj is SemanticVersion other) if (obj is SemanticVersion other)
{ {
return this.major == other.major && this.minor == other.minor && this.patch == other.patch; return major == other.major && minor == other.minor && patch == other.patch;
} }
else else
{ {
@@ -68,7 +68,7 @@
public override int GetHashCode() public override int GetHashCode()
{ {
return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode(); return major.GetHashCode() ^ minor.GetHashCode() ^ patch.GetHashCode();
} }
/// <summary> /// <summary>
@@ -77,18 +77,18 @@
/// <returns>major.minor.patch</returns> /// <returns>major.minor.patch</returns>
public override string ToString() public override string ToString()
{ {
return this.version; return version;
} }
public string ToVersionString(string? prefix = null) public string ToVersionString(string? prefix = null)
{ {
if (prefix == null) if (prefix == null)
{ {
return this.version; return version;
} }
else else
{ {
return $"{prefix}{this.version}"; return $"{prefix}{version}";
} }
} }
@@ -108,31 +108,31 @@
private bool GreaterEquals(SemanticVersion other) private bool GreaterEquals(SemanticVersion other)
{ {
if (this.major < other.major) if (major < other.major)
{ {
return false; return false;
} }
else if (this.major > other.major) else if (major > other.major)
{ {
return true; return true;
} }
else else
{ {
if (this.minor < other.minor) if (minor < other.minor)
{ {
return false; return false;
} }
else if (this.minor > other.minor) else if (minor > other.minor)
{ {
return true; return true;
} }
else else
{ {
if (this.patch < other.patch) if (patch < other.patch)
{ {
return false; return false;
} }
else if (this.patch > other.patch) else if (patch > other.patch)
{ {
return true; return true;
} }
@@ -146,31 +146,31 @@
private bool LessEquals(SemanticVersion other) private bool LessEquals(SemanticVersion other)
{ {
if (this.major < other.major) if (major < other.major)
{ {
return true; return true;
} }
else if (this.major > other.major) else if (major > other.major)
{ {
return false; return false;
} }
else else
{ {
if (this.minor < other.minor) if (minor < other.minor)
{ {
return true; return true;
} }
else if (this.minor > other.minor) else if (minor > other.minor)
{ {
return false; return false;
} }
else else
{ {
if (this.patch < other.patch) if (patch < other.patch)
{ {
return true; return true;
} }
else if (this.patch > other.patch) else if (patch > other.patch)
{ {
return false; return false;
} }
@@ -184,4 +184,3 @@
#endregion Private #endregion Private
} }
}

View File

@@ -1,13 +1,13 @@
using System.Collections; using System.Collections;
using SQLite; using SQLite;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public sealed class SQLiteHelper public sealed class SQLiteHelper
{ {
private static readonly Lazy<SQLiteHelper> _instance = new(() => new()); private static readonly Lazy<SQLiteHelper> _instance = new(() => new());
public static SQLiteHelper Instance => _instance.Value; public static SQLiteHelper Instance => _instance.Value;
private string _connstr; private readonly string _connstr;
private SQLiteConnection _db; private SQLiteConnection _db;
private SQLiteAsyncConnection _dbAsync; private SQLiteAsyncConnection _dbAsync;
private readonly string _configDB = "guiNDB.db"; private readonly string _configDB = "guiNDB.db";
@@ -88,4 +88,3 @@ namespace ServiceLib.Common
}); });
} }
} }
}

View File

@@ -1,12 +1,12 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public static class StringEx public static class StringEx
{ {
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
{ {
return string.IsNullOrEmpty(value); return string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value);
} }
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value) public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
@@ -22,7 +22,9 @@ namespace ServiceLib.Common
public static bool BeginWithAny(this string s, IEnumerable<char> chars) public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{ {
if (s.IsNullOrEmpty()) if (s.IsNullOrEmpty())
{
return false; return false;
}
return chars.Contains(s.First()); return chars.Contains(s.First());
} }
@@ -36,7 +38,9 @@ namespace ServiceLib.Common
while (reader.ReadLine() is { } line) while (reader.ReadLine() is { } line)
{ {
if (line.IsWhiteSpace()) if (line.IsWhiteSpace())
{
continue; continue;
}
yield return line; yield return line;
} }
} }
@@ -70,5 +74,9 @@ namespace ServiceLib.Common
{ {
return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\""; return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\"";
} }
public static int ToInt(this string? value, int defaultValue = 0)
{
return int.TryParse(value, out var result) ? result : defaultValue;
} }
} }

View File

@@ -11,8 +11,8 @@ using System.Text;
using CliWrap; using CliWrap;
using CliWrap.Buffered; using CliWrap.Buffered;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class Utils public class Utils
{ {
private static readonly string _tag = "Utils"; private static readonly string _tag = "Utils";
@@ -20,85 +20,69 @@ namespace ServiceLib.Common
#region #region
/// <summary> /// <summary>
/// 转逗号分隔的字符串 /// Convert to comma-separated string
/// </summary> /// </summary>
/// <param name="lst"></param> /// <param name="lst"></param>
/// <param name="wrap"></param> /// <param name="wrap"></param>
/// <returns></returns> /// <returns></returns>
public static string List2String(List<string>? lst, bool wrap = false) public static string List2String(List<string>? lst, bool wrap = false)
{ {
try if (lst == null || lst.Count == 0)
{
if (lst == null)
{ {
return string.Empty; return string.Empty;
} }
if (wrap)
var separator = wrap ? "," + Environment.NewLine : ",";
try
{ {
return string.Join("," + Environment.NewLine, lst); return string.Join(separator, lst);
}
else
{
return string.Join(",", lst);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
}
return string.Empty; return string.Empty;
} }
}
/// <summary> /// <summary>
/// 逗号分隔的字符串 /// Comma-separated string
/// </summary> /// </summary>
/// <param name="str"></param> /// <param name="str"></param>
/// <returns></returns> /// <returns></returns>
public static List<string>? String2List(string? str) public static List<string>? String2List(string? str)
{ {
try if (string.IsNullOrWhiteSpace(str))
{
if (str == null)
{ {
return null; return null;
} }
str = str.Replace(Environment.NewLine, ""); try
{
str = str.Replace(Environment.NewLine, string.Empty);
return new List<string>(str.Split(',', StringSplitOptions.RemoveEmptyEntries)); return new List<string>(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
}
return null; return null;
} }
}
/// <summary> /// <summary>
/// 逗号分隔的字符串,先排序后转List /// Comma-separated string, sorted and then converted to List
/// </summary> /// </summary>
/// <param name="str"></param> /// <param name="str"></param>
/// <returns></returns> /// <returns></returns>
public static List<string>? String2ListSorted(string str) public static List<string>? String2ListSorted(string str)
{ {
try var lst = String2List(str);
{ lst?.Sort();
str = str.Replace(Environment.NewLine, ""); return lst;
List<string> list = new(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
list.Sort();
return list;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return null;
} }
/// <summary> /// <summary>
/// Base64编码 /// Base64 Encode
/// </summary> /// </summary>
/// <param name="plainText"></param> /// <param name="plainText"></param>
/// <returns></returns> /// <returns></returns>
@@ -118,7 +102,7 @@ namespace ServiceLib.Common
} }
/// <summary> /// <summary>
/// Base64解码 /// Base64 Decode
/// </summary> /// </summary>
/// <param name="plainText"></param> /// <param name="plainText"></param>
/// <returns></returns> /// <returns></returns>
@@ -127,7 +111,10 @@ namespace ServiceLib.Common
try try
{ {
if (plainText.IsNullOrEmpty()) if (plainText.IsNullOrEmpty())
{
return ""; return "";
}
plainText = plainText.Trim() plainText = plainText.Trim()
.Replace(Environment.NewLine, "") .Replace(Environment.NewLine, "")
.Replace("\n", "") .Replace("\n", "")
@@ -152,18 +139,6 @@ namespace ServiceLib.Common
return string.Empty; return string.Empty;
} }
public static int ToInt(object? obj)
{
try
{
return Convert.ToInt32(obj ?? string.Empty);
}
catch
{
return 0;
}
}
public static bool ToBool(object obj) public static bool ToBool(object obj)
{ {
try try
@@ -188,55 +163,25 @@ namespace ServiceLib.Common
} }
} }
private static void ToHumanReadable(long amount, out double result, out string unit)
{
var factor = 1024u;
//long KBs = amount / factor;
var KBs = amount;
if (KBs > 0)
{
// multi KB
var MBs = KBs / factor;
if (MBs > 0)
{
// multi MB
var GBs = MBs / factor;
if (GBs > 0)
{
// multi GB
var TBs = GBs / factor;
if (TBs > 0)
{
result = TBs + ((GBs % factor) / (factor + 0.0));
unit = "TB";
return;
}
result = GBs + ((MBs % factor) / (factor + 0.0));
unit = "GB";
return;
}
result = MBs + ((KBs % factor) / (factor + 0.0));
unit = "MB";
return;
}
result = KBs + ((amount % factor) / (factor + 0.0));
unit = "KB";
return;
}
else
{
result = amount;
unit = "B";
}
}
public static string HumanFy(long amount) public static string HumanFy(long amount)
{ {
ToHumanReadable(amount, out var result, out var unit); if (amount <= 0)
return $"{result:f1} {unit}"; {
return $"{amount:f1} B";
}
string[] units = ["KB", "MB", "GB", "TB", "PB"];
var unitIndex = 0;
double size = amount;
// Loop and divide by 1024 until a suitable unit is found
while (size >= 1024 && unitIndex < units.Length - 1)
{
size /= 1024;
unitIndex++;
}
return $"{size:f1} {units[unitIndex]}";
} }
public static string UrlEncode(string url) public static string UrlEncode(string url)
@@ -252,7 +197,7 @@ namespace ServiceLib.Common
public static NameValueCollection ParseQueryString(string query) public static NameValueCollection ParseQueryString(string query)
{ {
var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase); var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
if (IsNullOrEmpty(query)) if (query.IsNullOrEmpty())
{ {
return result; return result;
} }
@@ -279,6 +224,13 @@ namespace ServiceLib.Common
} }
public static string GetMd5(string str) public static string GetMd5(string str)
{
if (string.IsNullOrEmpty(str))
{
return string.Empty;
}
try
{ {
var byteOld = Encoding.UTF8.GetBytes(str); var byteOld = Encoding.UTF8.GetBytes(str);
var byteNew = MD5.HashData(byteOld); var byteNew = MD5.HashData(byteOld);
@@ -290,6 +242,38 @@ namespace ServiceLib.Common
return sb.ToString(); return sb.ToString();
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return string.Empty;
}
}
public static string GetFileHash(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return string.Empty;
}
if (!File.Exists(filePath))
{
return string.Empty;
}
try
{
using var md5 = MD5.Create();
using var stream = File.OpenRead(filePath);
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return string.Empty;
}
}
/// <summary> /// <summary>
/// idn to idc /// idn to idc
@@ -298,7 +282,7 @@ namespace ServiceLib.Common
/// <returns></returns> /// <returns></returns>
public static string GetPunycode(string url) public static string GetPunycode(string url)
{ {
if (Utils.IsNullOrEmpty(url)) if (url.IsNullOrEmpty())
{ {
return url; return url;
} }
@@ -331,7 +315,7 @@ namespace ServiceLib.Common
public static string Convert2Comma(string text) public static string Convert2Comma(string text)
{ {
if (IsNullOrEmpty(text)) if (text.IsNullOrEmpty())
{ {
return text; return text;
} }
@@ -344,7 +328,7 @@ namespace ServiceLib.Common
#region #region
/// <summary> /// <summary>
/// 判断输入的是否是数字 /// Determine if the input is a number
/// </summary> /// </summary>
/// <param name="oText"></param> /// <param name="oText"></param>
/// <returns></returns> /// <returns></returns>
@@ -353,28 +337,13 @@ namespace ServiceLib.Common
return oText.All(char.IsNumber); return oText.All(char.IsNumber);
} }
public static bool IsNullOrEmpty(string? text)
{
if (string.IsNullOrWhiteSpace(text))
{
return true;
}
return text == "null";
}
public static bool IsNotEmpty(string? text)
{
return !string.IsNullOrEmpty(text);
}
/// <summary> /// <summary>
/// 验证Domain地址是否合法 /// Validate if the domain address is valid
/// </summary> /// </summary>
/// <param name="domain"></param> /// <param name="domain"></param>
public static bool IsDomain(string? domain) public static bool IsDomain(string? domain)
{ {
if (IsNullOrEmpty(domain)) if (domain.IsNullOrEmpty())
{ {
return false; return false;
} }
@@ -433,10 +402,22 @@ namespace ServiceLib.Common
{ {
try try
{ {
var ipProperties = IPGlobalProperties.GetIPGlobalProperties(); List<IPEndPoint> lstIpEndPoints = new();
var ipEndPoints = ipProperties.GetActiveTcpListeners(); List<TcpConnectionInformation> lstTcpConns = new();
//var lstIpEndPoints = new List<IPEndPoint>(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
return ipEndPoints.Any(endPoint => endPoint.Port == port); lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners());
lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections());
if (lstIpEndPoints?.FindIndex(it => it.Port == port) >= 0)
{
return true;
}
if (lstTcpConns?.FindIndex(it => it.LocalEndPoint.Port == port) >= 0)
{
return true;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -479,7 +460,7 @@ namespace ServiceLib.Common
} }
/// <summary> /// <summary>
/// 取得版本 /// Get version
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static string GetVersion(bool blFull = true) public static string GetVersion(bool blFull = true)
@@ -517,7 +498,7 @@ namespace ServiceLib.Common
} }
/// <summary> /// <summary>
/// 取得GUID /// GUID
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static string GetGuid(bool full = true) public static string GetGuid(bool full = true)
@@ -622,13 +603,20 @@ namespace ServiceLib.Common
{ {
try try
{ {
var basePath = GetBaseDirectory();
//When this file exists, it is equivalent to having no permission to read and write //When this file exists, it is equivalent to having no permission to read and write
if (File.Exists(Path.Combine(GetBaseDirectory(), "NotStoreConfigHere.txt"))) if (File.Exists(Path.Combine(basePath, "NotStoreConfigHere.txt")))
{ {
return false; return false;
} }
var tempPath = Path.Combine(GetBaseDirectory(), "guiTemps"); //Check if it is installed by Windows WinGet
if (IsWindows() && basePath.Contains("Users") && basePath.Contains("WinGet"))
{
return false;
}
var tempPath = Path.Combine(basePath, "guiTemps");
if (!Directory.Exists(tempPath)) if (!Directory.Exists(tempPath))
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
@@ -648,7 +636,7 @@ namespace ServiceLib.Common
public static string GetPath(string fileName) public static string GetPath(string fileName)
{ {
var startupPath = StartupPath(); var startupPath = StartupPath();
if (IsNullOrEmpty(fileName)) if (fileName.IsNullOrEmpty())
{ {
return startupPath; return startupPath;
} }
@@ -684,7 +672,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
if (IsNullOrEmpty(filename)) if (filename.IsNullOrEmpty())
{ {
return tempPath; return tempPath;
} }
@@ -713,7 +701,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
if (Utils.IsNullOrEmpty(filename)) if (filename.IsNullOrEmpty())
{ {
return tempPath; return tempPath;
} }
@@ -740,7 +728,7 @@ namespace ServiceLib.Common
} }
} }
if (IsNullOrEmpty(filename)) if (filename.IsNullOrEmpty())
{ {
return tempPath; return tempPath;
} }
@@ -758,7 +746,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
if (Utils.IsNullOrEmpty(filename)) if (filename.IsNullOrEmpty())
{ {
return tempPath; return tempPath;
} }
@@ -776,7 +764,25 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
if (Utils.IsNullOrEmpty(filename)) if (filename.IsNullOrEmpty())
{
return tempPath;
}
else
{
return Path.Combine(tempPath, filename);
}
}
public static string GetBinConfigPath(string filename = "")
{
var tempPath = Path.Combine(StartupPath(), "binConfigs");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
if (filename.IsNullOrEmpty())
{ {
return tempPath; return tempPath;
} }
@@ -827,7 +833,7 @@ namespace ServiceLib.Common
private static async Task<string?> GetLinuxUserId() private static async Task<string?> GetLinuxUserId()
{ {
var arg = new List<string>() { "-c", "id -u" }; var arg = new List<string>() { "-c", "id -u" };
return await GetCliWrapOutput("/bin/bash", arg); return await GetCliWrapOutput(Global.LinuxBash, arg);
} }
public static async Task<string?> SetLinuxChmod(string? fileName) public static async Task<string?> SetLinuxChmod(string? fileName)
@@ -838,14 +844,14 @@ namespace ServiceLib.Common
fileName = fileName.AppendQuotes(); fileName = fileName.AppendQuotes();
//File.SetUnixFileMode(fileName, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute); //File.SetUnixFileMode(fileName, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
var arg = new List<string>() { "-c", $"chmod +x {fileName}" }; var arg = new List<string>() { "-c", $"chmod +x {fileName}" };
return await GetCliWrapOutput("/bin/bash", arg); return await GetCliWrapOutput(Global.LinuxBash, arg);
} }
public static async Task<string?> GetLinuxFontFamily(string lang) public static async Task<string?> GetLinuxFontFamily(string lang)
{ {
// var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" }; // var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" };
var arg = new List<string>() { "-c", $"fc-list : family" }; var arg = new List<string>() { "-c", $"fc-list : family" };
return await GetCliWrapOutput("/bin/bash", arg); return await GetCliWrapOutput(Global.LinuxBash, arg);
} }
public static string? GetHomePath() public static string? GetHomePath()
@@ -855,12 +861,5 @@ namespace ServiceLib.Common
: Environment.GetEnvironmentVariable("HOME"); : Environment.GetEnvironmentVariable("HOME");
} }
public static async Task<string?> GetListNetworkServices()
{
var arg = new List<string>() { "-c", $"networksetup -listallnetworkservices" };
return await GetCliWrapOutput("/bin/bash", arg);
}
#endregion Platform #endregion Platform
} }
}

View File

@@ -2,8 +2,8 @@ using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.Win32; using Microsoft.Win32;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
internal static class WindowsUtils internal static class WindowsUtils
{ {
private static readonly string _tag = "WindowsUtils"; private static readonly string _tag = "WindowsUtils";
@@ -15,7 +15,7 @@ namespace ServiceLib.Common
{ {
regKey = Registry.CurrentUser.OpenSubKey(path, false); regKey = Registry.CurrentUser.OpenSubKey(path, false);
var value = regKey?.GetValue(name) as string; var value = regKey?.GetValue(name) as string;
return Utils.IsNullOrEmpty(value) ? def : value; return value.IsNullOrEmpty() ? def : value;
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -34,7 +34,7 @@ namespace ServiceLib.Common
try try
{ {
regKey = Registry.CurrentUser.CreateSubKey(path); regKey = Registry.CurrentUser.CreateSubKey(path);
if (Utils.IsNullOrEmpty(value.ToString())) if (value.ToString().IsNullOrEmpty())
{ {
regKey?.DeleteValue(name, false); regKey?.DeleteValue(name, false);
} }
@@ -63,7 +63,7 @@ namespace ServiceLib.Common
var arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """; var arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """;
// Try to remove the device // Try to remove the device
await Utils.GetCliWrapOutput(pnpUtilPath, arg); _ = await Utils.GetCliWrapOutput(pnpUtilPath, arg);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -71,4 +71,3 @@ namespace ServiceLib.Common
} }
} }
} }
}

View File

@@ -1,9 +1,9 @@
using YamlDotNet.Core; using YamlDotNet.Core;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NamingConventions;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class YamlUtils public class YamlUtils
{ {
private static readonly string _tag = "YamlUtils"; private static readonly string _tag = "YamlUtils";
@@ -62,9 +62,6 @@ namespace ServiceLib.Common
public static string? PreprocessYaml(string str) public static string? PreprocessYaml(string str)
{ {
var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build();
try try
{ {
var mergingParser = new MergingParser(new Parser(new StringReader(str))); var mergingParser = new MergingParser(new Parser(new StringReader(str)));
@@ -80,4 +77,3 @@ namespace ServiceLib.Common
#endregion YAML #endregion YAML
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EConfigType public enum EConfigType
{ {
VMess = 1, VMess = 1,
@@ -13,4 +13,3 @@
WireGuard = 9, WireGuard = 9,
HTTP = 10 HTTP = 10
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum ECoreType public enum ECoreType
{ {
v2fly = 1, v2fly = 1,
@@ -16,4 +16,3 @@ namespace ServiceLib.Enums
overtls = 28, overtls = 28,
v2rayN = 99 v2rayN = 99
} }
}

View File

@@ -1,9 +1,8 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EGirdOrientation public enum EGirdOrientation
{ {
Horizontal, Horizontal,
Vertical, Vertical,
Tab, Tab,
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EGlobalHotkey public enum EGlobalHotkey
{ {
ShowForm = 0, ShowForm = 0,
@@ -8,4 +8,3 @@
SystemProxyUnchanged = 3, SystemProxyUnchanged = 3,
SystemProxyPac = 4, SystemProxyPac = 4,
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EInboundProtocol public enum EInboundProtocol
{ {
socks = 0, socks = 0,
@@ -11,4 +11,3 @@
mixed, mixed,
speedtest = 21 speedtest = 21
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EMove public enum EMove
{ {
Top = 1, Top = 1,
@@ -8,4 +8,3 @@
Bottom = 4, Bottom = 4,
Position = 5 Position = 5
} }
}

View File

@@ -1,12 +1,10 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EMsgCommand public enum EMsgCommand
{ {
ClearMsg, ClearMsg,
SendMsgView, SendMsgView,
SendSnackMsg, SendSnackMsg,
RefreshProfiles, RefreshProfiles,
StopSpeedtest,
AppExit AppExit
} }
}

View File

@@ -0,0 +1,9 @@
namespace ServiceLib.Enums;
public enum EMultipleLoad
{
Random,
RoundRobin,
LeastPing,
LeastLoad
}

View File

@@ -1,9 +1,8 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EPresetType public enum EPresetType
{ {
Default = 0, Default = 0,
Russia = 1, Russia = 1,
Iran = 2, Iran = 2,
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum ERuleMode public enum ERuleMode
{ {
Rule = 0, Rule = 0,
@@ -7,4 +7,3 @@
Direct = 2, Direct = 2,
Unchanged = 3 Unchanged = 3
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EServerColName public enum EServerColName
{ {
Def = 0, Def = 0,
@@ -18,4 +18,3 @@
TotalDown, TotalDown,
TotalUp TotalUp
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum ESpeedActionType public enum ESpeedActionType
{ {
Tcping, Tcping,
@@ -7,4 +7,3 @@
Speedtest, Speedtest,
Mixedtest Mixedtest
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum ESysProxyType public enum ESysProxyType
{ {
ForcedClear = 0, ForcedClear = 0,
@@ -7,4 +7,3 @@
Unchanged = 2, Unchanged = 2,
Pac = 3 Pac = 3
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum ETheme public enum ETheme
{ {
FollowSystem, FollowSystem,
@@ -10,4 +10,3 @@
Dusk, Dusk,
NightSky NightSky
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum ETransport public enum ETransport
{ {
tcp, tcp,
@@ -12,4 +12,3 @@
quic, quic,
grpc grpc
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EViewAction public enum EViewAction
{ {
CloseWindow, CloseWindow,
@@ -43,4 +43,3 @@
DispatcherCheckUpdateFinished, DispatcherCheckUpdateFinished,
DispatcherShowMsg, DispatcherShowMsg,
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib namespace ServiceLib;
{
public class Global public class Global
{ {
#region const #region const
@@ -16,7 +16,7 @@ namespace ServiceLib
public const string ConfigFileName = "guiNConfig.json"; public const string ConfigFileName = "guiNConfig.json";
public const string CoreConfigFileName = "config.json"; public const string CoreConfigFileName = "config.json";
public const string CorePreConfigFileName = "configPre.json"; public const string CorePreConfigFileName = "configPre.json";
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json"; public const string CoreSpeedtestConfigFileName = "configTest{0}.json";
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json"; public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
public const string ClashMixinConfigFileName = "Mixin.yaml"; public const string ClashMixinConfigFileName = "Mixin.yaml";
@@ -38,6 +38,8 @@ namespace ServiceLib
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml"; public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config"; public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
public const string PacFileName = NamespaceSample + "pac"; public const string PacFileName = NamespaceSample + "pac";
public const string ProxySetOSXShellFileName = NamespaceSample + "proxy_set_osx_sh";
public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh";
public const string DefaultSecurity = "auto"; public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp"; public const string DefaultNetwork = "tcp";
@@ -64,15 +66,15 @@ namespace ServiceLib
public const string GrpcGunMode = "gun"; public const string GrpcGunMode = "gun";
public const string GrpcMultiMode = "multi"; public const string GrpcMultiMode = "multi";
public const int MaxPort = 65536; public const int MaxPort = 65536;
public const string DelayUnit = "";
public const string SpeedUnit = "";
public const int MinFontSize = 8; public const int MinFontSize = 8;
public const string RebootAs = "rebootas"; public const string RebootAs = "rebootas";
public const string AvaAssets = "avares://v2rayN/Assets/"; public const string AvaAssets = "avares://v2rayN/Assets/";
public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA"; public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2";
public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET"; public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET";
public const string XrayLocalAsset = "XRAY_LOCATION_ASSET"; public const string XrayLocalAsset = "XRAY_LOCATION_ASSET";
public const string XrayLocalCert = "XRAY_LOCATION_CERT";
public const int SpeedTestPageSize = 1000; public const int SpeedTestPageSize = 1000;
public const string LinuxBash = "/bin/bash";
public static readonly List<string> IEProxyProtocols = public static readonly List<string> IEProxyProtocols =
[ [
@@ -105,10 +107,10 @@ namespace ServiceLib
public static readonly List<string> SpeedTestUrls = public static readonly List<string> SpeedTestUrls =
[ [
@"https://speed.cloudflare.com/__down?bytes=100000000", @"https://cachefly.cachefly.net/50mb.test",
@"https://speed.cloudflare.com/__down?bytes=50000000",
@"https://speed.cloudflare.com/__down?bytes=10000000", @"https://speed.cloudflare.com/__down?bytes=10000000",
@"https://cachefly.cachefly.net/50mb.test" @"https://speed.cloudflare.com/__down?bytes=50000000",
@"https://speed.cloudflare.com/__down?bytes=100000000",
]; ];
public static readonly List<string> SpeedPingTestUrls = public static readonly List<string> SpeedPingTestUrls =
@@ -264,7 +266,8 @@ namespace ServiceLib
"utp", "utp",
"wechat-video", "wechat-video",
"dtls", "dtls",
"wireguard" "wireguard",
"dns"
]; ];
public static readonly List<string> CoreTypes = public static readonly List<string> CoreTypes =
@@ -428,12 +431,12 @@ namespace ServiceLib
"fakedns+others" "fakedns+others"
]; ];
public static readonly List<string> TunMtus = public static readonly List<int> TunMtus =
[ [
"1280", 1280,
"1408", 1408,
"1500", 1500,
"9000" 9000
]; ];
public static readonly List<string> TunStacks = public static readonly List<string> TunStacks =
@@ -509,6 +512,12 @@ namespace ServiceLib
{ ECoreType.v2rayN, "2dust/v2rayN" }, { ECoreType.v2rayN, "2dust/v2rayN" },
}; };
public static readonly List<string> OtherGeoUrls =
[
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat",
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb",
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
];
#endregion const #endregion const
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public sealed class AppHandler public sealed class AppHandler
{ {
#region Property #region Property
@@ -80,7 +80,9 @@
Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}"); Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}");
Logging.LoggingEnabled(_config.GuiItem.EnableLog); Logging.LoggingEnabled(_config.GuiItem.EnableLog);
ClearExpiredFiles(); //First determine the port value
_ = StatePort;
_ = StatePort2;
return true; return true;
} }
@@ -92,15 +94,6 @@
return true; return true;
} }
private void ClearExpiredFiles()
{
Task.Run(() =>
{
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
});
}
#endregion Init #endregion Init
#region Config #region Config
@@ -142,7 +135,7 @@
public async Task<List<ProfileItem>?> ProfileItems(string subid) public async Task<List<ProfileItem>?> ProfileItems(string subid)
{ {
if (Utils.IsNullOrEmpty(subid)) if (subid.IsNullOrEmpty())
{ {
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync(); return await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync();
} }
@@ -164,11 +157,11 @@
from ProfileItem a from ProfileItem a
left join SubItem b on a.subid = b.id left join SubItem b on a.subid = b.id
where 1=1 "; where 1=1 ";
if (Utils.IsNotEmpty(subid)) if (subid.IsNotEmpty())
{ {
sql += $" and a.subid = '{subid}'"; sql += $" and a.subid = '{subid}'";
} }
if (Utils.IsNotEmpty(filter)) if (filter.IsNotEmpty())
{ {
if (filter.Contains('\'')) if (filter.Contains('\''))
{ {
@@ -182,7 +175,7 @@
public async Task<ProfileItem?> GetProfileItem(string indexId) public async Task<ProfileItem?> GetProfileItem(string indexId)
{ {
if (Utils.IsNullOrEmpty(indexId)) if (indexId.IsNullOrEmpty())
{ {
return null; return null;
} }
@@ -191,7 +184,7 @@
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks) public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
{ {
if (Utils.IsNullOrEmpty(remarks)) if (remarks.IsNullOrEmpty())
{ {
return null; return null;
} }
@@ -252,4 +245,3 @@
#endregion Core Type #endregion Core Type
} }
}

View File

@@ -1,8 +1,8 @@
using System.Security.Principal; using System.Security.Principal;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public static class AutoStartupHandler public static class AutoStartupHandler
{ {
private static readonly string _tag = "AutoStartupHandler"; private static readonly string _tag = "AutoStartupHandler";
@@ -85,7 +85,7 @@ namespace ServiceLib.Handler
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
public static void AutoStartTaskService(string taskName, string fileName, string description) public static void AutoStartTaskService(string taskName, string fileName, string description)
{ {
if (Utils.IsNullOrEmpty(taskName)) if (taskName.IsNullOrEmpty())
{ {
return; return;
} }
@@ -93,7 +93,7 @@ namespace ServiceLib.Handler
var logonUser = WindowsIdentity.GetCurrent().Name; var logonUser = WindowsIdentity.GetCurrent().Name;
using var taskService = new Microsoft.Win32.TaskScheduler.TaskService(); using var taskService = new Microsoft.Win32.TaskScheduler.TaskService();
var tasks = taskService.RootFolder.GetTasks(new Regex(taskName)); var tasks = taskService.RootFolder.GetTasks(new Regex(taskName));
if (Utils.IsNullOrEmpty(fileName)) if (fileName.IsNullOrEmpty())
{ {
foreach (var t in tasks) foreach (var t in tasks)
{ {
@@ -109,7 +109,7 @@ namespace ServiceLib.Handler
task.Settings.RunOnlyIfIdle = false; task.Settings.RunOnlyIfIdle = false;
task.Settings.IdleSettings.StopOnIdleEnd = false; task.Settings.IdleSettings.StopOnIdleEnd = false;
task.Settings.ExecutionTimeLimit = TimeSpan.Zero; task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) }); task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(30) });
task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest; task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest;
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)));
@@ -177,7 +177,7 @@ namespace ServiceLib.Handler
if (File.Exists(launchAgentPath)) if (File.Exists(launchAgentPath))
{ {
var args = new[] { "-c", $"launchctl unload -w \"{launchAgentPath}\"" }; var args = new[] { "-c", $"launchctl unload -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput("/bin/bash", args); await Utils.GetCliWrapOutput(Global.LinuxBash, args);
File.Delete(launchAgentPath); File.Delete(launchAgentPath);
} }
@@ -197,7 +197,7 @@ namespace ServiceLib.Handler
await File.WriteAllTextAsync(launchAgentPath, plistContent); await File.WriteAllTextAsync(launchAgentPath, plistContent);
var args = new[] { "-c", $"launchctl load -w \"{launchAgentPath}\"" }; var args = new[] { "-c", $"launchctl load -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput("/bin/bash", args); await Utils.GetCliWrapOutput(Global.LinuxBash, args);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -239,4 +239,3 @@ namespace ServiceLib.Handler
#endregion macOS #endregion macOS
} }
}

View File

@@ -1,19 +1,19 @@
using static ServiceLib.Models.ClashProxies; using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Handler;
namespace ServiceLib.Handler
{
public sealed class ClashApiHandler public sealed class ClashApiHandler
{ {
private static readonly Lazy<ClashApiHandler> instance = new(() => new()); private static readonly Lazy<ClashApiHandler> instance = new(() => new());
public static ClashApiHandler Instance => instance.Value; public static ClashApiHandler Instance => instance.Value;
private static readonly string _tag = "ClashApiHandler";
private Dictionary<string, ProxiesItem>? _proxies; private Dictionary<string, ProxiesItem>? _proxies;
public Dictionary<string, object> ProfileContent { get; set; } public Dictionary<string, object> ProfileContent { get; set; }
private static readonly string _tag = "ClashApiHandler";
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config) public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync()
{ {
for (var i = 0; i < 5; i++) for (var i = 0; i < 3; i++)
{ {
var url = $"{GetApiUrl()}/proxies"; var url = $"{GetApiUrl()}/proxies";
var result = await HttpClientHelper.Instance.TryGetAsync(url); var result = await HttpClientHelper.Instance.TryGetAsync(url);
@@ -37,45 +37,37 @@ namespace ServiceLib.Handler
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc) public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
{ {
Task.Run(() => Task.Run(async () =>
{ {
if (blAll) if (blAll)
{ {
for (int i = 0; i < 5; i++)
{
if (_proxies != null)
{
break;
}
Task.Delay(5000).Wait();
}
if (_proxies == null) if (_proxies == null)
{ {
return; await GetClashProxiesAsync();
} }
lstProxy = new List<ClashProxyModel>(); lstProxy = new List<ClashProxyModel>();
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies) foreach (var kv in _proxies ?? [])
{ {
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower())) if (Global.notAllowTestType.Contains(kv.Value.type?.ToLower()))
{ {
continue; continue;
} }
lstProxy.Add(new ClashProxyModel() lstProxy.Add(new ClashProxyModel()
{ {
Name = kv.Value.name, Name = kv.Value.name,
Type = kv.Value.type.ToLower(), Type = kv.Value.type?.ToLower(),
}); });
} }
} }
if (lstProxy == null) if (lstProxy is not { Count: > 0 })
{ {
return; return;
} }
var urlBase = $"{GetApiUrl()}/proxies"; var urlBase = $"{GetApiUrl()}/proxies";
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl; urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
List<Task> tasks = new List<Task>(); var tasks = new List<Task>();
foreach (var it in lstProxy) foreach (var it in lstProxy)
{ {
if (Global.notAllowTestType.Contains(it.Type.ToLower())) if (Global.notAllowTestType.Contains(it.Type.ToLower()))
@@ -90,9 +82,8 @@ namespace ServiceLib.Handler
updateFunc?.Invoke(it, result); updateFunc?.Invoke(it, result);
})); }));
} }
Task.WaitAll(tasks.ToArray()); await Task.WhenAll(tasks);
await Task.Delay(1000);
Task.Delay(1000).Wait();
updateFunc?.Invoke(null, ""); updateFunc?.Invoke(null, "");
}); });
} }
@@ -120,7 +111,7 @@ namespace ServiceLib.Handler
try try
{ {
var url = $"{GetApiUrl()}/proxies/{name}"; var url = $"{GetApiUrl()}/proxies/{name}";
Dictionary<string, string> headers = new Dictionary<string, string>(); var headers = new Dictionary<string, string>();
headers.Add("name", nameNode); headers.Add("name", nameNode);
await HttpClientHelper.Instance.PutAsync(url, headers); await HttpClientHelper.Instance.PutAsync(url, headers);
} }
@@ -148,7 +139,7 @@ namespace ServiceLib.Handler
try try
{ {
var url = $"{GetApiUrl()}/configs?force=true"; var url = $"{GetApiUrl()}/configs?force=true";
Dictionary<string, string> headers = new Dictionary<string, string>(); var headers = new Dictionary<string, string>();
headers.Add("path", filePath); headers.Add("path", filePath);
await HttpClientHelper.Instance.PutAsync(url, headers); await HttpClientHelper.Instance.PutAsync(url, headers);
} }
@@ -158,7 +149,7 @@ namespace ServiceLib.Handler
} }
} }
public async Task<ClashConnections?> GetClashConnectionsAsync(Config config) public async Task<ClashConnections?> GetClashConnectionsAsync()
{ {
try try
{ {
@@ -194,4 +185,3 @@ namespace ServiceLib.Handler
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}"; return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
} }
} }
}

View File

@@ -1,8 +1,8 @@
using System.Data; using System.Data;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
/// <summary> /// <summary>
/// 本软件配置文件处理类 /// 本软件配置文件处理类
/// </summary> /// </summary>
@@ -22,7 +22,7 @@ namespace ServiceLib.Handler
{ {
Config? config = null; Config? config = null;
var result = EmbedUtils.LoadResource(Utils.GetConfigPath(_configRes)); var result = EmbedUtils.LoadResource(Utils.GetConfigPath(_configRes));
if (Utils.IsNotEmpty(result)) if (result.IsNotEmpty())
{ {
config = JsonUtils.Deserialize<Config>(result); config = JsonUtils.Deserialize<Config>(result);
} }
@@ -67,7 +67,7 @@ namespace ServiceLib.Handler
} }
config.RoutingBasicItem ??= new(); config.RoutingBasicItem ??= new();
if (Utils.IsNullOrEmpty(config.RoutingBasicItem.DomainStrategy)) if (config.RoutingBasicItem.DomainStrategy.IsNullOrEmpty())
{ {
config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies.First(); config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies.First();
} }
@@ -103,7 +103,7 @@ namespace ServiceLib.Handler
}; };
config.UiItem.MainColumnItem ??= new(); config.UiItem.MainColumnItem ??= new();
if (Utils.IsNullOrEmpty(config.UiItem.CurrentLanguage)) if (config.UiItem.CurrentLanguage.IsNullOrEmpty())
{ {
config.UiItem.CurrentLanguage = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName.Equals("zh", StringComparison.CurrentCultureIgnoreCase) config.UiItem.CurrentLanguage = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName.Equals("zh", StringComparison.CurrentCultureIgnoreCase)
? Global.Languages.First() ? Global.Languages.First()
@@ -117,14 +117,18 @@ namespace ServiceLib.Handler
{ {
config.SpeedTestItem.SpeedTestTimeout = 10; config.SpeedTestItem.SpeedTestTimeout = 10;
} }
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedTestUrl)) if (config.SpeedTestItem.SpeedTestUrl.IsNullOrEmpty())
{ {
config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls.First(); config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls.First();
} }
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedPingTestUrl)) if (config.SpeedTestItem.SpeedPingTestUrl.IsNullOrEmpty())
{ {
config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl; config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl;
} }
if (config.SpeedTestItem.MixedConcurrencyCount < 1)
{
config.SpeedTestItem.MixedConcurrencyCount = 5;
}
config.Mux4RayItem ??= new() config.Mux4RayItem ??= new()
{ {
@@ -154,6 +158,7 @@ namespace ServiceLib.Handler
Length = "100-200", Length = "100-200",
Interval = "10-20" Interval = "10-20"
}; };
config.GlobalHotkeys ??= new();
if (config.SystemProxyItem.SystemProxyExceptions.IsNullOrEmpty()) if (config.SystemProxyItem.SystemProxyExceptions.IsNullOrEmpty())
{ {
@@ -212,6 +217,7 @@ namespace ServiceLib.Handler
item.Remarks = profileItem.Remarks; item.Remarks = profileItem.Remarks;
item.Address = profileItem.Address; item.Address = profileItem.Address;
item.Port = profileItem.Port; item.Port = profileItem.Port;
item.Ports = profileItem.Ports;
item.Id = profileItem.Id; item.Id = profileItem.Id;
item.AlterId = profileItem.AlterId; item.AlterId = profileItem.AlterId;
@@ -290,7 +296,7 @@ namespace ServiceLib.Handler
/// <param name="config"></param> /// <param name="config"></param>
/// <param name="indexes"></param> /// <param name="indexes"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<int> RemoveServer(Config config, List<ProfileItem> indexes) public static async Task<int> RemoveServers(Config config, List<ProfileItem> indexes)
{ {
var subid = "TempRemoveSubId"; var subid = "TempRemoveSubId";
foreach (var item in indexes) foreach (var item in indexes)
@@ -299,7 +305,7 @@ namespace ServiceLib.Handler
} }
await SQLiteHelper.Instance.UpdateAllAsync(indexes); await SQLiteHelper.Instance.UpdateAllAsync(indexes);
await RemoveServerViaSubid(config, subid, false); await RemoveServersViaSubid(config, subid, false);
return 0; return 0;
} }
@@ -348,7 +354,7 @@ namespace ServiceLib.Handler
/// <returns></returns> /// <returns></returns>
public static async Task<int> SetDefaultServerIndex(Config config, string? indexId) public static async Task<int> SetDefaultServerIndex(Config config, string? indexId)
{ {
if (Utils.IsNullOrEmpty(indexId)) if (indexId.IsNullOrEmpty())
{ {
return -1; return -1;
} }
@@ -500,7 +506,7 @@ namespace ServiceLib.Handler
profileItem.Address = newFileName; profileItem.Address = newFileName;
profileItem.ConfigType = EConfigType.Custom; profileItem.ConfigType = EConfigType.Custom;
if (Utils.IsNullOrEmpty(profileItem.Remarks)) if (profileItem.Remarks.IsNullOrEmpty())
{ {
profileItem.Remarks = $"import custom@{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}"; profileItem.Remarks = $"import custom@{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}";
} }
@@ -618,7 +624,7 @@ namespace ServiceLib.Handler
profileItem.Address = profileItem.Address.TrimEx(); profileItem.Address = profileItem.Address.TrimEx();
profileItem.Id = profileItem.Id.TrimEx(); profileItem.Id = profileItem.Id.TrimEx();
if (Utils.IsNullOrEmpty(profileItem.StreamSecurity)) if (profileItem.StreamSecurity.IsNullOrEmpty())
{ {
profileItem.StreamSecurity = Global.StreamSecurity; profileItem.StreamSecurity = Global.StreamSecurity;
} }
@@ -648,7 +654,7 @@ namespace ServiceLib.Handler
profileItem.Path = profileItem.Path.TrimEx(); profileItem.Path = profileItem.Path.TrimEx();
profileItem.Network = string.Empty; profileItem.Network = string.Empty;
if (Utils.IsNullOrEmpty(profileItem.StreamSecurity)) if (profileItem.StreamSecurity.IsNullOrEmpty())
{ {
profileItem.StreamSecurity = Global.StreamSecurity; profileItem.StreamSecurity = Global.StreamSecurity;
} }
@@ -683,11 +689,11 @@ namespace ServiceLib.Handler
profileItem.HeaderType = Global.TuicCongestionControls.FirstOrDefault()!; profileItem.HeaderType = Global.TuicCongestionControls.FirstOrDefault()!;
} }
if (Utils.IsNullOrEmpty(profileItem.StreamSecurity)) if (profileItem.StreamSecurity.IsNullOrEmpty())
{ {
profileItem.StreamSecurity = Global.StreamSecurity; profileItem.StreamSecurity = Global.StreamSecurity;
} }
if (Utils.IsNullOrEmpty(profileItem.Alpn)) if (profileItem.Alpn.IsNullOrEmpty())
{ {
profileItem.Alpn = "h3"; profileItem.Alpn = "h3";
} }
@@ -720,7 +726,7 @@ namespace ServiceLib.Handler
profileItem.Network = string.Empty; profileItem.Network = string.Empty;
if (profileItem.ShortId.IsNullOrEmpty()) if (profileItem.ShortId.IsNullOrEmpty())
{ {
profileItem.ShortId = Global.TunMtus.FirstOrDefault(); profileItem.ShortId = Global.TunMtus.First().ToString();
} }
if (profileItem.Id.IsNullOrEmpty()) if (profileItem.Id.IsNullOrEmpty())
@@ -754,9 +760,9 @@ namespace ServiceLib.Handler
Security = t.Security, Security = t.Security,
Network = t.Network, Network = t.Network,
StreamSecurity = t.StreamSecurity, StreamSecurity = t.StreamSecurity,
Delay = t33 == null ? 0 : t33.Delay, Delay = t33?.Delay ?? 0,
Speed = t33 == null ? 0 : t33.Speed, Speed = t33?.Speed ?? 0,
Sort = t33 == null ? 0 : t33.Sort Sort = t33?.Sort ?? 0
}).ToList(); }).ToList();
Enum.TryParse(colName, true, out EServerColName name); Enum.TryParse(colName, true, out EServerColName name);
@@ -852,7 +858,7 @@ namespace ServiceLib.Handler
{ {
return -1; return -1;
} }
if (Utils.IsNotEmpty(profileItem.Security) && profileItem.Security != Global.None) if (profileItem.Security.IsNotEmpty() && profileItem.Security != Global.None)
{ {
profileItem.Security = Global.None; profileItem.Security = Global.None;
} }
@@ -865,13 +871,19 @@ namespace ServiceLib.Handler
public static async Task<Tuple<int, int>> DedupServerList(Config config, string subId) public static async Task<Tuple<int, int>> DedupServerList(Config config, string subId)
{ {
var lstProfile = await AppHandler.Instance.ProfileItems(subId); var lstProfile = await AppHandler.Instance.ProfileItems(subId);
if (lstProfile == null)
{
return new Tuple<int, int>(0, 0);
}
List<ProfileItem> lstKeep = new(); List<ProfileItem> lstKeep = new();
List<ProfileItem> lstRemove = new(); List<ProfileItem> lstRemove = new();
if (!config.GuiItem.KeepOlderDedupl) if (!config.GuiItem.KeepOlderDedupl)
{
lstProfile.Reverse(); lstProfile.Reverse();
}
foreach (ProfileItem item in lstProfile) foreach (var item in lstProfile)
{ {
if (!lstKeep.Exists(i => CompareProfileItem(i, item, false))) if (!lstKeep.Exists(i => CompareProfileItem(i, item, false)))
{ {
@@ -882,7 +894,7 @@ namespace ServiceLib.Handler
lstRemove.Add(item); lstRemove.Add(item);
} }
} }
await RemoveServer(config, lstRemove); await RemoveServers(config, lstRemove);
return new Tuple<int, int>(lstProfile.Count, lstKeep.Count); return new Tuple<int, int>(lstProfile.Count, lstKeep.Count);
} }
@@ -891,7 +903,7 @@ namespace ServiceLib.Handler
{ {
profileItem.ConfigVersion = 2; profileItem.ConfigVersion = 2;
if (Utils.IsNotEmpty(profileItem.StreamSecurity)) if (profileItem.StreamSecurity.IsNotEmpty())
{ {
if (profileItem.StreamSecurity != Global.StreamSecurity if (profileItem.StreamSecurity != Global.StreamSecurity
&& profileItem.StreamSecurity != Global.StreamSecurityReality) && profileItem.StreamSecurity != Global.StreamSecurityReality)
@@ -900,24 +912,24 @@ namespace ServiceLib.Handler
} }
else else
{ {
if (Utils.IsNullOrEmpty(profileItem.AllowInsecure)) if (profileItem.AllowInsecure.IsNullOrEmpty())
{ {
profileItem.AllowInsecure = config.CoreBasicItem.DefAllowInsecure.ToString().ToLower(); profileItem.AllowInsecure = config.CoreBasicItem.DefAllowInsecure.ToString().ToLower();
} }
if (Utils.IsNullOrEmpty(profileItem.Fingerprint) && profileItem.StreamSecurity == Global.StreamSecurityReality) if (profileItem.Fingerprint.IsNullOrEmpty() && profileItem.StreamSecurity == Global.StreamSecurityReality)
{ {
profileItem.Fingerprint = config.CoreBasicItem.DefFingerprint; profileItem.Fingerprint = config.CoreBasicItem.DefFingerprint;
} }
} }
} }
if (Utils.IsNotEmpty(profileItem.Network) && !Global.Networks.Contains(profileItem.Network)) if (profileItem.Network.IsNotEmpty() && !Global.Networks.Contains(profileItem.Network))
{ {
profileItem.Network = Global.DefaultNetwork; profileItem.Network = Global.DefaultNetwork;
} }
var maxSort = -1; var maxSort = -1;
if (Utils.IsNullOrEmpty(profileItem.IndexId)) if (profileItem.IndexId.IsNullOrEmpty())
{ {
profileItem.IndexId = Utils.GetGuid(false); profileItem.IndexId = Utils.GetGuid(false);
maxSort = ProfileExHandler.Instance.GetMaxSort(); maxSort = ProfileExHandler.Instance.GetMaxSort();
@@ -938,7 +950,7 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
private static bool CompareProfileItem(ProfileItem o, ProfileItem n, bool remarks) private static bool CompareProfileItem(ProfileItem? o, ProfileItem? n, bool remarks)
{ {
if (o == null || n == null) if (o == null || n == null)
{ {
@@ -946,22 +958,27 @@ namespace ServiceLib.Handler
} }
return o.ConfigType == n.ConfigType return o.ConfigType == n.ConfigType
&& o.Address == n.Address && AreEqual(o.Address, n.Address)
&& o.Port == n.Port && o.Port == n.Port
&& o.Id == n.Id && AreEqual(o.Id, n.Id)
&& o.Security == n.Security && AreEqual(o.Security, n.Security)
&& o.Network == n.Network && AreEqual(o.Network, n.Network)
&& o.HeaderType == n.HeaderType && AreEqual(o.HeaderType, n.HeaderType)
&& o.RequestHost == n.RequestHost && AreEqual(o.RequestHost, n.RequestHost)
&& o.Path == n.Path && AreEqual(o.Path, n.Path)
&& (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity) && (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity)
&& o.Flow == n.Flow && AreEqual(o.Flow, n.Flow)
&& o.Sni == n.Sni && AreEqual(o.Sni, n.Sni)
&& o.Alpn == n.Alpn && AreEqual(o.Alpn, n.Alpn)
&& o.Fingerprint == n.Fingerprint && AreEqual(o.Fingerprint, n.Fingerprint)
&& o.PublicKey == n.PublicKey && AreEqual(o.PublicKey, n.PublicKey)
&& o.ShortId == n.ShortId && AreEqual(o.ShortId, n.ShortId)
&& (!remarks || o.Remarks == n.Remarks); && (!remarks || o.Remarks == n.Remarks);
static bool AreEqual(string? a, string? b)
{
return string.Equals(a, b) || (string.IsNullOrEmpty(a) && string.IsNullOrEmpty(b));
}
} }
private static async Task<int> RemoveProfileItem(Config config, string indexId) private static async Task<int> RemoveProfileItem(Config config, string indexId)
@@ -988,26 +1005,32 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
public static async Task<RetResult> AddCustomServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType) public static async Task<RetResult> AddCustomServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
{ {
var indexId = Utils.GetMd5(Global.CoreMultipleLoadConfigFileName); var indexId = Utils.GetMd5(Global.CoreMultipleLoadConfigFileName);
var configPath = Utils.GetConfigPath(Global.CoreMultipleLoadConfigFileName); var configPath = Utils.GetConfigPath(Global.CoreMultipleLoadConfigFileName);
var result = await CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, coreType); var result = await CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, coreType, multipleLoad);
if (result.Success != true) if (result.Success != true)
{ {
return result; return result;
} }
var fileName = configPath; if (!File.Exists(configPath))
if (!File.Exists(fileName))
{ {
return result; return result;
} }
var profileItem = await AppHandler.Instance.GetProfileItem(indexId) ?? new(); var profileItem = await AppHandler.Instance.GetProfileItem(indexId) ?? new();
profileItem.IndexId = indexId; profileItem.IndexId = indexId;
profileItem.Remarks = coreType == ECoreType.sing_box ? ResUI.menuSetDefaultMultipleServer : ResUI.menuSetDefaultLoadBalanceServer; profileItem.Remarks = multipleLoad switch
{
EMultipleLoad.Random => ResUI.menuSetDefaultMultipleServerXrayRandom,
EMultipleLoad.RoundRobin => ResUI.menuSetDefaultMultipleServerXrayRoundRobin,
EMultipleLoad.LeastPing => ResUI.menuSetDefaultMultipleServerXrayLeastPing,
EMultipleLoad.LeastLoad => ResUI.menuSetDefaultMultipleServerXrayLeastLoad,
_ => ResUI.menuSetDefaultMultipleServerXrayRoundRobin,
};
profileItem.Address = Global.CoreMultipleLoadConfigFileName; profileItem.Address = Global.CoreMultipleLoadConfigFileName;
profileItem.ConfigType = EConfigType.Custom; profileItem.ConfigType = EConfigType.Custom;
profileItem.CoreType = coreType; profileItem.CoreType = coreType;
@@ -1047,6 +1070,24 @@ namespace ServiceLib.Handler
return itemSocks; return itemSocks;
} }
public static async Task<int> RemoveInvalidServerResult(Config config, string subid)
{
var lstModel = await AppHandler.Instance.ProfileItems(subid, "");
if (lstModel is { Count: <= 0 })
{
return -1;
}
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
var lstProfile = (from t in lstModel
join t2 in lstProfileExs on t.IndexId equals t2.IndexId
where t2.Delay == -1
select t).ToList();
await RemoveServers(config, JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstProfile)));
return lstProfile.Count;
}
#endregion Server #endregion Server
#region Batch add servers #region Batch add servers
@@ -1060,16 +1101,16 @@ namespace ServiceLib.Handler
/// <returns>成功导入的数量</returns> /// <returns>成功导入的数量</returns>
private static async Task<int> AddBatchServersCommon(Config config, string strData, string subid, bool isSub) private static async Task<int> AddBatchServersCommon(Config config, string strData, string subid, bool isSub)
{ {
if (Utils.IsNullOrEmpty(strData)) if (strData.IsNullOrEmpty())
{ {
return -1; return -1;
} }
var subFilter = string.Empty; var subFilter = string.Empty;
//remove sub items //remove sub items
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && subid.IsNotEmpty())
{ {
await RemoveServerViaSubid(config, subid, isSub); await RemoveServersViaSubid(config, subid, isSub);
subFilter = (await AppHandler.Instance.GetSubItem(subid))?.Filter ?? ""; subFilter = (await AppHandler.Instance.GetSubItem(subid))?.Filter ?? "";
} }
@@ -1098,7 +1139,7 @@ namespace ServiceLib.Handler
} }
//exist sub items //filter //exist sub items //filter
if (isSub && Utils.IsNotEmpty(subid) && Utils.IsNotEmpty(subFilter)) if (isSub && subid.IsNotEmpty() && subFilter.IsNotEmpty())
{ {
if (!Regex.IsMatch(profileItem.Remarks, subFilter)) if (!Regex.IsMatch(profileItem.Remarks, subFilter))
{ {
@@ -1139,7 +1180,7 @@ namespace ServiceLib.Handler
private static async Task<int> AddBatchServers4Custom(Config config, string strData, string subid, bool isSub) private static async Task<int> AddBatchServers4Custom(Config config, string strData, string subid, bool isSub)
{ {
if (Utils.IsNullOrEmpty(strData)) if (strData.IsNullOrEmpty())
{ {
return -1; return -1;
} }
@@ -1161,9 +1202,9 @@ namespace ServiceLib.Handler
} }
if (lstProfiles != null && lstProfiles.Count > 0) if (lstProfiles != null && lstProfiles.Count > 0)
{ {
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && subid.IsNotEmpty())
{ {
await RemoveServerViaSubid(config, subid, isSub); await RemoveServersViaSubid(config, subid, isSub);
} }
int count = 0; int count = 0;
foreach (var it in lstProfiles) foreach (var it in lstProfiles)
@@ -1212,14 +1253,14 @@ namespace ServiceLib.Handler
{ {
profileItem = NaiveproxyFmt.ResolveFull(strData, subRemarks); profileItem = NaiveproxyFmt.ResolveFull(strData, subRemarks);
} }
if (profileItem is null || Utils.IsNullOrEmpty(profileItem.Address)) if (profileItem is null || profileItem.Address.IsNullOrEmpty())
{ {
return -1; return -1;
} }
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && subid.IsNotEmpty())
{ {
await RemoveServerViaSubid(config, subid, isSub); await RemoveServersViaSubid(config, subid, isSub);
} }
profileItem.Subid = subid; profileItem.Subid = subid;
@@ -1237,14 +1278,14 @@ namespace ServiceLib.Handler
private static async Task<int> AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub) private static async Task<int> AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub)
{ {
if (Utils.IsNullOrEmpty(strData)) if (strData.IsNullOrEmpty())
{ {
return -1; return -1;
} }
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && subid.IsNotEmpty())
{ {
await RemoveServerViaSubid(config, subid, isSub); await RemoveServersViaSubid(config, subid, isSub);
} }
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData); var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
@@ -1269,13 +1310,13 @@ namespace ServiceLib.Handler
public static async Task<int> AddBatchServers(Config config, string strData, string subid, bool isSub) public static async Task<int> AddBatchServers(Config config, string strData, string subid, bool isSub)
{ {
if (Utils.IsNullOrEmpty(strData)) if (strData.IsNullOrEmpty())
{ {
return -1; return -1;
} }
List<ProfileItem>? lstOriSub = null; List<ProfileItem>? lstOriSub = null;
ProfileItem? activeProfile = null; ProfileItem? activeProfile = null;
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && subid.IsNotEmpty())
{ {
lstOriSub = await AppHandler.Instance.ProfileItems(subid); lstOriSub = await AppHandler.Instance.ProfileItems(subid);
activeProfile = lstOriSub?.FirstOrDefault(t => t.IndexId == config.IndexId); activeProfile = lstOriSub?.FirstOrDefault(t => t.IndexId == config.IndexId);
@@ -1400,7 +1441,7 @@ namespace ServiceLib.Handler
item.Memo = subItem.Memo; item.Memo = subItem.Memo;
} }
if (Utils.IsNullOrEmpty(item.Id)) if (item.Id.IsNullOrEmpty())
{ {
item.Id = Utils.GetGuid(false); item.Id = Utils.GetGuid(false);
@@ -1431,9 +1472,9 @@ namespace ServiceLib.Handler
/// <param name="config"></param> /// <param name="config"></param>
/// <param name="subid"></param> /// <param name="subid"></param>
/// <returns></returns> /// <returns></returns>
public static async Task<int> RemoveServerViaSubid(Config config, string subid, bool isSub) public static async Task<int> RemoveServersViaSubid(Config config, string subid, bool isSub)
{ {
if (Utils.IsNullOrEmpty(subid)) if (subid.IsNullOrEmpty())
{ {
return -1; return -1;
} }
@@ -1462,7 +1503,7 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
await SQLiteHelper.Instance.DeleteAsync(item); await SQLiteHelper.Instance.DeleteAsync(item);
await RemoveServerViaSubid(config, id, false); await RemoveServersViaSubid(config, id, false);
return 0; return 0;
} }
@@ -1484,7 +1525,7 @@ namespace ServiceLib.Handler
public static async Task<int> SaveRoutingItem(Config config, RoutingItem item) public static async Task<int> SaveRoutingItem(Config config, RoutingItem item)
{ {
if (Utils.IsNullOrEmpty(item.Id)) if (item.Id.IsNullOrEmpty())
{ {
item.Id = Utils.GetGuid(false); item.Id = Utils.GetGuid(false);
} }
@@ -1507,7 +1548,7 @@ namespace ServiceLib.Handler
/// <returns></returns> /// <returns></returns>
public static async Task<int> AddBatchRoutingRules(RoutingItem routingItem, string strData) public static async Task<int> AddBatchRoutingRules(RoutingItem routingItem, string strData)
{ {
if (Utils.IsNullOrEmpty(strData)) if (strData.IsNullOrEmpty())
{ {
return -1; return -1;
} }
@@ -1525,7 +1566,7 @@ namespace ServiceLib.Handler
routingItem.RuleNum = lstRules.Count; routingItem.RuleNum = lstRules.Count;
routingItem.RuleSet = JsonUtils.Serialize(lstRules, false); routingItem.RuleSet = JsonUtils.Serialize(lstRules, false);
if (Utils.IsNullOrEmpty(routingItem.Id)) if (routingItem.Id.IsNullOrEmpty())
{ {
routingItem.Id = Utils.GetGuid(false); routingItem.Id = Utils.GetGuid(false);
} }
@@ -1644,7 +1685,7 @@ namespace ServiceLib.Handler
public static async Task<int> InitRouting(Config config, bool blImportAdvancedRules = false) public static async Task<int> InitRouting(Config config, bool blImportAdvancedRules = false)
{ {
if (string.IsNullOrEmpty(config.ConstItem.RouteRulesTemplateSourceUrl)) if (config.ConstItem.RouteRulesTemplateSourceUrl.IsNullOrEmpty())
{ {
await InitBuiltinRouting(config, blImportAdvancedRules); await InitBuiltinRouting(config, blImportAdvancedRules);
} }
@@ -1660,7 +1701,7 @@ namespace ServiceLib.Handler
{ {
var downloadHandle = new DownloadService(); var downloadHandle = new DownloadService();
var templateContent = await downloadHandle.TryDownloadString(config.ConstItem.RouteRulesTemplateSourceUrl, true, ""); var templateContent = await downloadHandle.TryDownloadString(config.ConstItem.RouteRulesTemplateSourceUrl, true, "");
if (string.IsNullOrEmpty(templateContent)) if (templateContent.IsNullOrEmpty())
return await InitBuiltinRouting(config, blImportAdvancedRules); // fallback return await InitBuiltinRouting(config, blImportAdvancedRules); // fallback
var template = JsonUtils.Deserialize<RoutingTemplate>(templateContent); var template = JsonUtils.Deserialize<RoutingTemplate>(templateContent);
@@ -1677,14 +1718,14 @@ namespace ServiceLib.Handler
{ {
var item = template.RoutingItems[i]; var item = template.RoutingItems[i];
if (string.IsNullOrEmpty(item.Url) && string.IsNullOrEmpty(item.RuleSet)) if (item.Url.IsNullOrEmpty() && item.RuleSet.IsNullOrEmpty())
continue; continue;
var ruleSetsString = !string.IsNullOrEmpty(item.RuleSet) var ruleSetsString = !item.RuleSet.IsNullOrEmpty()
? item.RuleSet ? item.RuleSet
: await downloadHandle.TryDownloadString(item.Url, true, ""); : await downloadHandle.TryDownloadString(item.Url, true, "");
if (string.IsNullOrEmpty(ruleSetsString)) if (ruleSetsString.IsNullOrEmpty())
continue; continue;
item.Remarks = $"{template.Version}-{item.Remarks}"; item.Remarks = $"{template.Version}-{item.Remarks}";
@@ -1796,7 +1837,7 @@ namespace ServiceLib.Handler
return -1; return -1;
} }
if (Utils.IsNullOrEmpty(item.Id)) if (item.Id.IsNullOrEmpty())
{ {
item.Id = Utils.GetGuid(false); item.Id = Utils.GetGuid(false);
} }
@@ -1817,17 +1858,17 @@ namespace ServiceLib.Handler
var downloadHandle = new DownloadService(); var downloadHandle = new DownloadService();
var templateContent = await downloadHandle.TryDownloadString(url, true, ""); var templateContent = await downloadHandle.TryDownloadString(url, true, "");
if (string.IsNullOrEmpty(templateContent)) if (templateContent.IsNullOrEmpty())
return currentItem; return currentItem;
var template = JsonUtils.Deserialize<DNSItem>(templateContent); var template = JsonUtils.Deserialize<DNSItem>(templateContent);
if (template == null) if (template == null)
return currentItem; return currentItem;
if (!string.IsNullOrEmpty(template.NormalDNS)) if (!template.NormalDNS.IsNullOrEmpty())
template.NormalDNS = await downloadHandle.TryDownloadString(template.NormalDNS, true, ""); template.NormalDNS = await downloadHandle.TryDownloadString(template.NormalDNS, true, "");
if (!string.IsNullOrEmpty(template.TunDNS)) if (!template.TunDNS.IsNullOrEmpty())
template.TunDNS = await downloadHandle.TryDownloadString(template.TunDNS, true, ""); template.TunDNS = await downloadHandle.TryDownloadString(template.TunDNS, true, "");
template.Id = currentItem.Id; template.Id = currentItem.Id;
@@ -1882,4 +1923,3 @@ namespace ServiceLib.Handler
#endregion Regional Presets #endregion Regional Presets
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
/// <summary> /// <summary>
/// Core configuration file processing class /// Core configuration file processing class
/// </summary> /// </summary>
@@ -33,7 +33,7 @@
{ {
return result; return result;
} }
if (Utils.IsNotEmpty(fileName) && result.Data != null) if (fileName.IsNotEmpty() && result.Data != null)
{ {
await File.WriteAllTextAsync(fileName, result.Data.ToString()); await File.WriteAllTextAsync(fileName, result.Data.ToString());
} }
@@ -109,16 +109,40 @@
return result; return result;
} }
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType) public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName)
{
var result = new RetResult();
var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
testItem.Port = port;
if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port);
}
else
{
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
{ {
var result = new RetResult(); var result = new RetResult();
if (coreType == ECoreType.sing_box) if (coreType == ECoreType.sing_box)
{ {
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds); result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
} }
else if (coreType == ECoreType.Xray) else
{ {
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds); result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad);
} }
if (result.Success != true) if (result.Success != true)
@@ -129,4 +153,3 @@
return result; return result;
} }
} }
}

View File

@@ -1,8 +1,8 @@
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
/// <summary> /// <summary>
/// Core process processing class /// Core process processing class
/// </summary> /// </summary>
@@ -24,6 +24,7 @@ namespace ServiceLib.Handler
Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalCert, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
//Copy the bin folder to the storage location (for init) //Copy the bin folder to the storage location (for init)
if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1") if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1")
@@ -70,7 +71,7 @@ namespace ServiceLib.Handler
return; return;
} }
var fileName = Utils.GetConfigPath(Global.CoreConfigFileName); var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName); var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
if (result.Success != true) if (result.Success != true)
{ {
@@ -101,7 +102,8 @@ namespace ServiceLib.Handler
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds) public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{ {
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray; var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName); var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
UpdateFunc(false, result.Msg); UpdateFunc(false, result.Msg);
if (result.Success != true) if (result.Success != true)
@@ -113,7 +115,34 @@ namespace ServiceLib.Handler
UpdateFunc(false, configPath); UpdateFunc(false, configPath);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false); var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null)
{
return -1;
}
return proc.Id;
}
public async Task<int> LoadCoreConfigSpeedtest(ServerTestItem testItem)
{
var node = await AppHandler.Instance.GetProfileItem(testItem.IndexId);
if (node is null)
{
return -1;
}
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
if (result.Success != true)
{
return -1;
}
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null) if (proc is null)
{ {
return -1; return -1;
@@ -175,7 +204,7 @@ namespace ServiceLib.Handler
if (itemSocks != null) if (itemSocks != null)
{ {
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box; var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
var fileName = Utils.GetConfigPath(Global.CorePreConfigFileName); var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName); var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
if (result.Success) if (result.Success)
{ {
@@ -212,7 +241,7 @@ namespace ServiceLib.Handler
private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo) private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
{ {
var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg); var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg);
if (Utils.IsNullOrEmpty(fileName)) if (fileName.IsNullOrEmpty())
{ {
UpdateFunc(false, msg); UpdateFunc(false, msg);
return null; return null;
@@ -225,8 +254,8 @@ namespace ServiceLib.Handler
StartInfo = new() StartInfo = new()
{ {
FileName = fileName, FileName = fileName,
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetConfigPath(configPath) : configPath), Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
WorkingDirectory = Utils.GetConfigPath(), WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = displayLog, RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog, RedirectStandardError = displayLog,
@@ -246,13 +275,13 @@ namespace ServiceLib.Handler
{ {
proc.OutputDataReceived += (sender, e) => proc.OutputDataReceived += (sender, e) =>
{ {
if (Utils.IsNullOrEmpty(e.Data)) if (e.Data.IsNullOrEmpty())
return; return;
UpdateFunc(false, e.Data + Environment.NewLine); UpdateFunc(false, e.Data + Environment.NewLine);
}; };
proc.ErrorDataReceived += (sender, e) => proc.ErrorDataReceived += (sender, e) =>
{ {
if (Utils.IsNullOrEmpty(e.Data)) if (e.Data.IsNullOrEmpty())
return; return;
UpdateFunc(false, e.Data + Environment.NewLine); UpdateFunc(false, e.Data + Environment.NewLine);
}; };
@@ -298,7 +327,7 @@ namespace ServiceLib.Handler
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath) private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
{ {
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetConfigPath(configPath).AppendQuotes())}"; var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh"); var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
proc.StartInfo.FileName = shFilePath; proc.StartInfo.FileName = shFilePath;
@@ -352,7 +381,7 @@ namespace ServiceLib.Handler
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName) private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{ {
//Shell scripts //Shell scripts
var shFilePath = Utils.GetBinPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName); var shFilePath = Utils.GetBinConfigPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
File.Delete(shFilePath); File.Delete(shFilePath);
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh"); sb.AppendLine("#!/bin/sh");
@@ -378,4 +407,3 @@ namespace ServiceLib.Handler
#endregion Linux #endregion Linux
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public sealed class CoreInfoHandler public sealed class CoreInfoHandler
{ {
private static readonly Lazy<CoreInfoHandler> _instance = new(() => new()); private static readonly Lazy<CoreInfoHandler> _instance = new(() => new());
@@ -75,7 +75,7 @@ namespace ServiceLib.Handler
new CoreInfo new CoreInfo
{ {
CoreType = ECoreType.v2fly, CoreType = ECoreType.v2fly,
CoreExes = ["wv2ray", "v2ray"], CoreExes = ["v2ray"],
Arguments = "{0}", Arguments = "{0}",
Url = GetCoreUrl(ECoreType.v2fly), Url = GetCoreUrl(ECoreType.v2fly),
Match = "V2Ray", Match = "V2Ray",
@@ -95,7 +95,7 @@ namespace ServiceLib.Handler
new CoreInfo new CoreInfo
{ {
CoreType = ECoreType.Xray, CoreType = ECoreType.Xray,
CoreExes = ["wxray","xray"], CoreExes = ["xray"],
Arguments = "run -c {0}", Arguments = "run -c {0}",
Url = GetCoreUrl(ECoreType.Xray), Url = GetCoreUrl(ECoreType.Xray),
ReleaseApiUrl = urlXray.Replace(Global.GithubUrl, Global.GithubApiUrl), ReleaseApiUrl = urlXray.Replace(Global.GithubUrl, Global.GithubApiUrl),
@@ -215,4 +215,3 @@ namespace ServiceLib.Handler
return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases"; return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases";
} }
} }
}

View File

@@ -1,7 +1,7 @@
using System.Collections.Specialized; using System.Collections.Specialized;
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class BaseFmt public class BaseFmt
{ {
protected static string GetIpv6(string address) protected static string GetIpv6(string address)
@@ -16,12 +16,12 @@ namespace ServiceLib.Handler.Fmt
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery) protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
{ {
if (Utils.IsNotEmpty(item.Flow)) if (item.Flow.IsNotEmpty())
{ {
dicQuery.Add("flow", item.Flow); dicQuery.Add("flow", item.Flow);
} }
if (Utils.IsNotEmpty(item.StreamSecurity)) if (item.StreamSecurity.IsNotEmpty())
{ {
dicQuery.Add("security", item.StreamSecurity); dicQuery.Add("security", item.StreamSecurity);
} }
@@ -32,27 +32,27 @@ namespace ServiceLib.Handler.Fmt
dicQuery.Add("security", securityDef); dicQuery.Add("security", securityDef);
} }
} }
if (Utils.IsNotEmpty(item.Sni)) if (item.Sni.IsNotEmpty())
{ {
dicQuery.Add("sni", item.Sni); dicQuery.Add("sni", item.Sni);
} }
if (Utils.IsNotEmpty(item.Alpn)) if (item.Alpn.IsNotEmpty())
{ {
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn)); dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
} }
if (Utils.IsNotEmpty(item.Fingerprint)) if (item.Fingerprint.IsNotEmpty())
{ {
dicQuery.Add("fp", Utils.UrlEncode(item.Fingerprint)); dicQuery.Add("fp", Utils.UrlEncode(item.Fingerprint));
} }
if (Utils.IsNotEmpty(item.PublicKey)) if (item.PublicKey.IsNotEmpty())
{ {
dicQuery.Add("pbk", Utils.UrlEncode(item.PublicKey)); dicQuery.Add("pbk", Utils.UrlEncode(item.PublicKey));
} }
if (Utils.IsNotEmpty(item.ShortId)) if (item.ShortId.IsNotEmpty())
{ {
dicQuery.Add("sid", Utils.UrlEncode(item.ShortId)); dicQuery.Add("sid", Utils.UrlEncode(item.ShortId));
} }
if (Utils.IsNotEmpty(item.SpiderX)) if (item.SpiderX.IsNotEmpty())
{ {
dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX)); dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX));
} }
@@ -61,21 +61,21 @@ namespace ServiceLib.Handler.Fmt
dicQuery.Add("allowInsecure", "1"); dicQuery.Add("allowInsecure", "1");
} }
dicQuery.Add("type", Utils.IsNotEmpty(item.Network) ? item.Network : nameof(ETransport.tcp)); dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
switch (item.Network) switch (item.Network)
{ {
case nameof(ETransport.tcp): case nameof(ETransport.tcp):
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None); dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (Utils.IsNotEmpty(item.RequestHost)) if (item.RequestHost.IsNotEmpty())
{ {
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
} }
break; break;
case nameof(ETransport.kcp): case nameof(ETransport.kcp):
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None); dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (Utils.IsNotEmpty(item.Path)) if (item.Path.IsNotEmpty())
{ {
dicQuery.Add("seed", Utils.UrlEncode(item.Path)); dicQuery.Add("seed", Utils.UrlEncode(item.Path));
} }
@@ -83,30 +83,30 @@ namespace ServiceLib.Handler.Fmt
case nameof(ETransport.ws): case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade): case nameof(ETransport.httpupgrade):
if (Utils.IsNotEmpty(item.RequestHost)) if (item.RequestHost.IsNotEmpty())
{ {
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
} }
if (Utils.IsNotEmpty(item.Path)) if (item.Path.IsNotEmpty())
{ {
dicQuery.Add("path", Utils.UrlEncode(item.Path)); dicQuery.Add("path", Utils.UrlEncode(item.Path));
} }
break; break;
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
if (Utils.IsNotEmpty(item.RequestHost)) if (item.RequestHost.IsNotEmpty())
{ {
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
} }
if (Utils.IsNotEmpty(item.Path)) if (item.Path.IsNotEmpty())
{ {
dicQuery.Add("path", Utils.UrlEncode(item.Path)); dicQuery.Add("path", Utils.UrlEncode(item.Path));
} }
if (Utils.IsNotEmpty(item.HeaderType) && Global.XhttpMode.Contains(item.HeaderType)) if (item.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(item.HeaderType))
{ {
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType)); dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
} }
if (Utils.IsNotEmpty(item.Extra)) if (item.Extra.IsNotEmpty())
{ {
dicQuery.Add("extra", Utils.UrlEncode(item.Extra)); dicQuery.Add("extra", Utils.UrlEncode(item.Extra));
} }
@@ -115,24 +115,24 @@ namespace ServiceLib.Handler.Fmt
case nameof(ETransport.http): case nameof(ETransport.http):
case nameof(ETransport.h2): case nameof(ETransport.h2):
dicQuery["type"] = nameof(ETransport.http); dicQuery["type"] = nameof(ETransport.http);
if (Utils.IsNotEmpty(item.RequestHost)) if (item.RequestHost.IsNotEmpty())
{ {
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
} }
if (Utils.IsNotEmpty(item.Path)) if (item.Path.IsNotEmpty())
{ {
dicQuery.Add("path", Utils.UrlEncode(item.Path)); dicQuery.Add("path", Utils.UrlEncode(item.Path));
} }
break; break;
case nameof(ETransport.quic): case nameof(ETransport.quic):
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None); dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("key", Utils.UrlEncode(item.Path)); dicQuery.Add("key", Utils.UrlEncode(item.Path));
break; break;
case nameof(ETransport.grpc): case nameof(ETransport.grpc):
if (Utils.IsNotEmpty(item.Path)) if (item.Path.IsNotEmpty())
{ {
dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.Path)); dicQuery.Add("serviceName", Utils.UrlEncode(item.Path));
@@ -215,8 +215,10 @@ namespace ServiceLib.Handler.Fmt
foreach (var item in s) foreach (var item in s)
{ {
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) if (str.Contains(item, StringComparison.OrdinalIgnoreCase))
{
return true; return true;
} }
}
return false; return false;
} }
@@ -237,4 +239,3 @@ namespace ServiceLib.Handler.Fmt
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}"; return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class ClashFmt : BaseFmt public class ClashFmt : BaseFmt
{ {
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
@@ -20,4 +20,3 @@
return null; return null;
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class FmtHandler public class FmtHandler
{ {
private static readonly string _tag = "FmtHandler"; private static readonly string _tag = "FmtHandler";
@@ -37,7 +37,7 @@
try try
{ {
string str = config.TrimEx(); string str = config.TrimEx();
if (Utils.IsNullOrEmpty(str)) if (str.IsNullOrEmpty())
{ {
msg = ResUI.FailedReadConfiguration; msg = ResUI.FailedReadConfiguration;
return null; return null;
@@ -89,4 +89,3 @@
} }
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class Hysteria2Fmt : BaseFmt public class Hysteria2Fmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -24,6 +24,8 @@ namespace ServiceLib.Handler.Fmt
item.Path = Utils.UrlDecode(query["obfs-password"] ?? ""); item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false"; item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
item.Ports = Utils.UrlDecode(query["mport"] ?? "").Replace('-', ':');
return item; return item;
} }
@@ -34,25 +36,29 @@ namespace ServiceLib.Handler.Fmt
string url = string.Empty; string url = string.Empty;
string remark = string.Empty; string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks)) if (item.Remarks.IsNotEmpty())
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
var dicQuery = new Dictionary<string, string>(); var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Sni)) if (item.Sni.IsNotEmpty())
{ {
dicQuery.Add("sni", item.Sni); dicQuery.Add("sni", item.Sni);
} }
if (Utils.IsNotEmpty(item.Alpn)) if (item.Alpn.IsNotEmpty())
{ {
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn)); dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
} }
if (Utils.IsNotEmpty(item.Path)) if (item.Path.IsNotEmpty())
{ {
dicQuery.Add("obfs", "salamander"); dicQuery.Add("obfs", "salamander");
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path)); dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
} }
dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0"); dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0");
if (item.Ports.IsNotEmpty())
{
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
}
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
} }
@@ -93,4 +99,3 @@ namespace ServiceLib.Handler.Fmt
return null; return null;
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class NaiveproxyFmt : BaseFmt public class NaiveproxyFmt : BaseFmt
{ {
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
@@ -20,4 +20,3 @@
return null; return null;
} }
} }
}

View File

@@ -1,7 +1,7 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class ShadowsocksFmt : BaseFmt public class ShadowsocksFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -27,11 +27,11 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item) public static string? ToUri(ProfileItem? item)
{ {
if (item == null) if (item == null)
{
return null; return null;
string url = string.Empty; }
var remark = string.Empty;
string remark = string.Empty; if (item.Remarks.IsNotEmpty())
if (Utils.IsNotEmpty(item.Remarks))
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
@@ -53,12 +53,14 @@ namespace ServiceLib.Handler.Fmt
{ {
var match = UrlFinder.Match(result); var match = UrlFinder.Match(result);
if (!match.Success) if (!match.Success)
{
return null; return null;
}
ProfileItem item = new(); ProfileItem item = new();
var base64 = match.Groups["base64"].Value.TrimEnd('/'); var base64 = match.Groups["base64"].Value.TrimEnd('/');
var tag = match.Groups["tag"].Value; var tag = match.Groups["tag"].Value;
if (Utils.IsNotEmpty(tag)) if (tag.IsNotEmpty())
{ {
item.Remarks = Utils.UrlDecode(tag); item.Remarks = Utils.UrlDecode(tag);
} }
@@ -72,11 +74,13 @@ namespace ServiceLib.Handler.Fmt
return null; return null;
} }
if (!details.Success) if (!details.Success)
{
return null; return null;
}
item.Security = details.Groups["method"].Value; item.Security = details.Groups["method"].Value;
item.Id = details.Groups["password"].Value; item.Id = details.Groups["password"].Value;
item.Address = details.Groups["hostname"].Value; item.Address = details.Groups["hostname"].Value;
item.Port = Utils.ToInt(details.Groups["port"].Value); item.Port = details.Groups["port"].Value.ToInt();
return item; return item;
} }
@@ -84,7 +88,9 @@ namespace ServiceLib.Handler.Fmt
{ {
var parsedUrl = Utils.TryUri(result); var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null) if (parsedUrl == null)
{
return null; return null;
}
ProfileItem item = new() ProfileItem item = new()
{ {
@@ -96,7 +102,7 @@ namespace ServiceLib.Handler.Fmt
//2022-blake3 //2022-blake3
if (rawUserInfo.Contains(':')) if (rawUserInfo.Contains(':'))
{ {
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2) if (userInfoParts.Length != 2)
{ {
return null; return null;
@@ -107,8 +113,8 @@ namespace ServiceLib.Handler.Fmt
else else
{ {
// parse base64 UserInfo // parse base64 UserInfo
string userInfo = Utils.Base64Decode(rawUserInfo); var userInfo = Utils.Base64Decode(rawUserInfo);
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2); var userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2) if (userInfoParts.Length != 2)
{ {
return null; return null;
@@ -122,7 +128,7 @@ namespace ServiceLib.Handler.Fmt
{ {
//obfs-host exists //obfs-host exists
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host")); var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
if (queryParameters["plugin"].Contains("obfs=http") && Utils.IsNotEmpty(obfsHost)) if (queryParameters["plugin"].Contains("obfs=http") && obfsHost.IsNotEmpty())
{ {
obfsHost = obfsHost?.Replace("obfs-host=", ""); obfsHost = obfsHost?.Replace("obfs-host=", "");
item.Network = Global.DefaultNetwork; item.Network = Global.DefaultNetwork;
@@ -162,7 +168,7 @@ namespace ServiceLib.Handler.Fmt
Security = it.method, Security = it.method,
Id = it.password, Id = it.password,
Address = it.server, Address = it.server,
Port = Utils.ToInt(it.server_port) Port = it.server_port.ToInt()
}; };
lst.Add(ssItem); lst.Add(ssItem);
} }
@@ -171,4 +177,3 @@ namespace ServiceLib.Handler.Fmt
return null; return null;
} }
} }
}

View File

@@ -1,44 +1,39 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class SingboxFmt : BaseFmt public class SingboxFmt : BaseFmt
{ {
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks) public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{ {
var configObjects = JsonUtils.Deserialize<Object[]>(strData); var configObjects = JsonUtils.Deserialize<object[]>(strData);
if (configObjects != null && configObjects.Length > 0) if (configObjects is not { Length: > 0 })
{ {
return null;
}
List<ProfileItem> lstResult = []; List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects) foreach (var configObject in configObjects)
{ {
var objectString = JsonUtils.Serialize(configObject); var objectString = JsonUtils.Serialize(configObject);
var singboxCon = JsonUtils.Deserialize<SingboxConfig>(objectString); var profileIt = ResolveFull(objectString, subRemarks);
if (singboxCon?.inbounds?.Count > 0 if (profileIt != null)
&& singboxCon.outbounds?.Count > 0
&& singboxCon.route != null)
{ {
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
CoreType = ECoreType.sing_box,
Address = fileName,
Remarks = subRemarks ?? "singbox_custom",
};
lstResult.Add(profileIt); lstResult.Add(profileIt);
} }
} }
return lstResult; return lstResult;
} }
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(strData); var config = JsonUtils.ParseJson(strData);
if (singboxConfig?.inbounds?.Count > 0 if (config?["inbounds"] == null
&& singboxConfig.outbounds?.Count > 0 || config["outbounds"] == null
&& singboxConfig.route != null) || config["route"] == null
|| config["dns"] == null)
{ {
return null;
}
var fileName = WriteAllText(strData); var fileName = WriteAllText(strData);
var profileItem = new ProfileItem var profileItem = new ProfileItem
{ {
@@ -49,7 +44,4 @@
return profileItem; return profileItem;
} }
return null;
}
}
} }

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class SocksFmt : BaseFmt public class SocksFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -24,11 +24,11 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item) public static string? ToUri(ProfileItem? item)
{ {
if (item == null) if (item == null)
{
return null; return null;
var url = string.Empty; }
var remark = string.Empty; var remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks)) if (item.Remarks.IsNotEmpty())
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
@@ -77,7 +77,7 @@ namespace ServiceLib.Handler.Fmt
return null; return null;
} }
item.Address = arr1[1][..indexPort]; item.Address = arr1[1][..indexPort];
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]); item.Port = arr1[1][(indexPort + 1)..].ToInt();
item.Security = arr21.First(); item.Security = arr21.First();
item.Id = arr21[1]; item.Id = arr21[1];
@@ -88,7 +88,9 @@ namespace ServiceLib.Handler.Fmt
{ {
var parsedUrl = Utils.TryUri(result); var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null) if (parsedUrl == null)
{
return null; return null;
}
ProfileItem item = new() ProfileItem item = new()
{ {
@@ -110,4 +112,3 @@ namespace ServiceLib.Handler.Fmt
return item; return item;
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class TrojanFmt : BaseFmt public class TrojanFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -13,7 +13,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str); var url = Utils.TryUri(str);
if (url == null) if (url == null)
{
return null; return null;
}
item.Address = url.IdnHost; item.Address = url.IdnHost;
item.Port = url.Port; item.Port = url.Port;
@@ -21,7 +23,7 @@ namespace ServiceLib.Handler.Fmt
item.Id = Utils.UrlDecode(url.UserInfo); item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query); var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item); _ = ResolveStdTransport(query, ref item);
return item; return item;
} }
@@ -29,18 +31,17 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item) public static string? ToUri(ProfileItem? item)
{ {
if (item == null) if (item == null)
{
return null; return null;
string url = string.Empty; }
var remark = string.Empty;
string remark = string.Empty; if (item.Remarks.IsNotEmpty())
if (Utils.IsNotEmpty(item.Remarks))
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
var dicQuery = new Dictionary<string, string>(); var dicQuery = new Dictionary<string, string>();
GetStdTransport(item, null, ref dicQuery); _ = GetStdTransport(item, null, ref dicQuery);
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark);
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class TuicFmt : BaseFmt public class TuicFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -13,7 +13,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str); var url = Utils.TryUri(str);
if (url == null) if (url == null)
{
return null; return null;
}
item.Address = url.IdnHost; item.Address = url.IdnHost;
item.Port = url.Port; item.Port = url.Port;
@@ -36,20 +38,21 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item) public static string? ToUri(ProfileItem? item)
{ {
if (item == null) if (item == null)
{
return null; return null;
string url = string.Empty; }
string remark = string.Empty; var remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks)) if (item.Remarks.IsNotEmpty())
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
var dicQuery = new Dictionary<string, string>(); var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Sni)) if (item.Sni.IsNotEmpty())
{ {
dicQuery.Add("sni", item.Sni); dicQuery.Add("sni", item.Sni);
} }
if (Utils.IsNotEmpty(item.Alpn)) if (item.Alpn.IsNotEmpty())
{ {
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn)); dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
} }
@@ -58,4 +61,3 @@ namespace ServiceLib.Handler.Fmt
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark); return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
} }
} }
}

View File

@@ -1,56 +1,48 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class V2rayFmt : BaseFmt public class V2rayFmt : BaseFmt
{ {
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks) public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{ {
var configObjects = JsonUtils.Deserialize<Object[]>(strData); var configObjects = JsonUtils.Deserialize<object[]>(strData);
if (configObjects != null && configObjects.Length > 0) if (configObjects is not { Length: > 0 })
{ {
return null;
}
List<ProfileItem> lstResult = []; List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects) foreach (var configObject in configObjects)
{ {
var objectString = JsonUtils.Serialize(configObject); var objectString = JsonUtils.Serialize(configObject);
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString); var profileIt = ResolveFull(objectString, subRemarks);
if (v2rayCon?.inbounds?.Count > 0 if (profileIt != null)
&& v2rayCon.outbounds?.Count > 0
&& v2rayCon.routing != null)
{ {
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
CoreType = ECoreType.Xray,
Address = fileName,
Remarks = v2rayCon.remarks ?? subRemarks ?? "v2ray_custom",
};
lstResult.Add(profileIt); lstResult.Add(profileIt);
} }
} }
return lstResult; return lstResult;
} }
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(strData); var config = JsonUtils.ParseJson(strData);
if (v2rayConfig?.inbounds?.Count > 0 if (config?["inbounds"] == null
&& v2rayConfig.outbounds?.Count > 0 || config["outbounds"] == null
&& v2rayConfig.routing != null) || config["routing"] == null)
{ {
return null;
}
var fileName = WriteAllText(strData); var fileName = WriteAllText(strData);
var profileItem = new ProfileItem var profileItem = new ProfileItem
{ {
CoreType = ECoreType.Xray, CoreType = ECoreType.Xray,
Address = fileName, Address = fileName,
Remarks = v2rayConfig.remarks ?? subRemarks ?? "v2ray_custom" Remarks = config?["remarks"]?.ToString() ?? subRemarks ?? "v2ray_custom"
}; };
return profileItem; return profileItem;
} }
return null;
}
}
} }

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class VLESSFmt : BaseFmt public class VLESSFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -14,7 +14,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str); var url = Utils.TryUri(str);
if (url == null) if (url == null)
{
return null; return null;
}
item.Address = url.IdnHost; item.Address = url.IdnHost;
item.Port = url.Port; item.Port = url.Port;
@@ -24,7 +26,7 @@ namespace ServiceLib.Handler.Fmt
var query = Utils.ParseQueryString(url.Query); var query = Utils.ParseQueryString(url.Query);
item.Security = query["encryption"] ?? Global.None; item.Security = query["encryption"] ?? Global.None;
item.StreamSecurity = query["security"] ?? ""; item.StreamSecurity = query["security"] ?? "";
ResolveStdTransport(query, ref item); _ = ResolveStdTransport(query, ref item);
return item; return item;
} }
@@ -32,16 +34,17 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item) public static string? ToUri(ProfileItem? item)
{ {
if (item == null) if (item == null)
{
return null; return null;
string url = string.Empty; }
string remark = string.Empty; var remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks)) if (item.Remarks.IsNotEmpty())
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
var dicQuery = new Dictionary<string, string>(); var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Security)) if (item.Security.IsNotEmpty())
{ {
dicQuery.Add("encryption", item.Security); dicQuery.Add("encryption", item.Security);
} }
@@ -49,9 +52,8 @@ namespace ServiceLib.Handler.Fmt
{ {
dicQuery.Add("encryption", Global.None); dicQuery.Add("encryption", Global.None);
} }
GetStdTransport(item, Global.None, ref dicQuery); _ = GetStdTransport(item, Global.None, ref dicQuery);
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark);
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class VmessFmt : BaseFmt public class VmessFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -20,10 +20,10 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item) public static string? ToUri(ProfileItem? item)
{ {
if (item == null) if (item == null)
{
return null; return null;
string url = string.Empty; }
var vmessQRCode = new VmessQRCode
VmessQRCode vmessQRCode = new()
{ {
v = item.ConfigVersion, v = item.ConfigVersion,
ps = item.Remarks.TrimEx(), ps = item.Remarks.TrimEx(),
@@ -42,7 +42,7 @@ namespace ServiceLib.Handler.Fmt
fp = item.Fingerprint fp = item.Fingerprint
}; };
url = JsonUtils.Serialize(vmessQRCode); var url = JsonUtils.Serialize(vmessQRCode);
url = Utils.Base64Encode(url); url = Utils.Base64Encode(url);
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}"; url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
@@ -60,7 +60,7 @@ namespace ServiceLib.Handler.Fmt
result = result[Global.ProtocolShares[EConfigType.VMess].Length..]; result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
result = Utils.Base64Decode(result); result = Utils.Base64Decode(result);
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result); var vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
if (vmessQRCode == null) if (vmessQRCode == null)
{ {
msg = ResUI.FailedConversionConfiguration; msg = ResUI.FailedConversionConfiguration;
@@ -78,12 +78,12 @@ namespace ServiceLib.Handler.Fmt
item.AlterId = vmessQRCode.aid; item.AlterId = vmessQRCode.aid;
item.Security = Utils.ToString(vmessQRCode.scy); item.Security = Utils.ToString(vmessQRCode.scy);
item.Security = Utils.IsNotEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity; item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
if (Utils.IsNotEmpty(vmessQRCode.net)) if (vmessQRCode.net.IsNotEmpty())
{ {
item.Network = vmessQRCode.net; item.Network = vmessQRCode.net;
} }
if (Utils.IsNotEmpty(vmessQRCode.type)) if (vmessQRCode.type.IsNotEmpty())
{ {
item.HeaderType = vmessQRCode.type; item.HeaderType = vmessQRCode.type;
} }
@@ -100,7 +100,7 @@ namespace ServiceLib.Handler.Fmt
public static ProfileItem? ResolveStdVmess(string str) public static ProfileItem? ResolveStdVmess(string str)
{ {
ProfileItem item = new() var item = new ProfileItem
{ {
ConfigType = EConfigType.VMess, ConfigType = EConfigType.VMess,
Security = "auto" Security = "auto"
@@ -108,7 +108,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str); var url = Utils.TryUri(str);
if (url == null) if (url == null)
{
return null; return null;
}
item.Address = url.IdnHost; item.Address = url.IdnHost;
item.Port = url.Port; item.Port = url.Port;
@@ -121,4 +123,3 @@ namespace ServiceLib.Handler.Fmt
return item; return item;
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class WireguardFmt : BaseFmt public class WireguardFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -13,7 +13,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str); var url = Utils.TryUri(str);
if (url == null) if (url == null)
{
return null; return null;
}
item.Address = url.IdnHost; item.Address = url.IdnHost;
item.Port = url.Port; item.Port = url.Port;
@@ -33,33 +35,33 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item) public static string? ToUri(ProfileItem? item)
{ {
if (item == null) if (item == null)
{
return null; return null;
string url = string.Empty; }
string remark = string.Empty; var remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks)) if (item.Remarks.IsNotEmpty())
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
var dicQuery = new Dictionary<string, string>(); var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.PublicKey)) if (item.PublicKey.IsNotEmpty())
{ {
dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey)); dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey));
} }
if (Utils.IsNotEmpty(item.Path)) if (item.Path.IsNotEmpty())
{ {
dicQuery.Add("reserved", Utils.UrlEncode(item.Path)); dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
} }
if (Utils.IsNotEmpty(item.RequestHost)) if (item.RequestHost.IsNotEmpty())
{ {
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
} }
if (Utils.IsNotEmpty(item.ShortId)) if (item.ShortId.IsNotEmpty())
{ {
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId)); dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
} }
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
} }
} }
}

View File

@@ -1,7 +1,7 @@
using ReactiveUI; using ReactiveUI;
namespace ServiceLib.Handler;
namespace ServiceLib.Handler
{
public class NoticeHandler public class NoticeHandler
{ {
private static readonly Lazy<NoticeHandler> _instance = new(() => new()); private static readonly Lazy<NoticeHandler> _instance = new(() => new());
@@ -41,4 +41,3 @@ namespace ServiceLib.Handler
SendMessage(msg); SendMessage(msg);
} }
} }
}

View File

@@ -1,8 +1,8 @@
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public class PacHandler public class PacHandler
{ {
private static string _configPath; private static string _configPath;
@@ -15,7 +15,7 @@ namespace ServiceLib.Handler
public static async Task Start(string configPath, int httpPort, int pacPort) public static async Task Start(string configPath, int httpPort, int pacPort)
{ {
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning); _needRestart = configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning;
_configPath = configPath; _configPath = configPath;
_httpPort = httpPort; _httpPort = httpPort;
@@ -33,6 +33,13 @@ namespace ServiceLib.Handler
private static async Task InitText() private static async Task InitText()
{ {
var path = Path.Combine(_configPath, "pac.txt"); var path = Path.Combine(_configPath, "pac.txt");
// Delete the old pac file
if (File.Exists(path) && Utils.GetFileHash(path).Equals("b590c07280f058ef05d5394aa2f927fe"))
{
File.Delete(path);
}
if (!File.Exists(path)) if (!File.Exists(path))
{ {
var pac = EmbedUtils.GetEmbedText(Global.PacFileName); var pac = EmbedUtils.GetEmbedText(Global.PacFileName);
@@ -70,7 +77,7 @@ namespace ServiceLib.Handler
} }
var client = await _tcpListener.AcceptTcpClientAsync(); var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => { WriteContent(client); }); await Task.Run(() => WriteContent(client));
} }
catch catch
{ {
@@ -90,7 +97,9 @@ namespace ServiceLib.Handler
public static void Stop() public static void Stop()
{ {
if (_tcpListener == null) if (_tcpListener == null)
{
return; return;
}
try try
{ {
_isRunning = false; _isRunning = false;
@@ -103,4 +112,3 @@ namespace ServiceLib.Handler
} }
} }
} }
}

View File

@@ -1,14 +1,14 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
//using System.Reactive.Linq; //using System.Reactive.Linq;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public class ProfileExHandler public class ProfileExHandler
{ {
private static readonly Lazy<ProfileExHandler> _instance = new(() => new()); private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
private ConcurrentBag<ProfileExItem> _lstProfileEx = []; private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
private Queue<string> _queIndexIds = new(); private readonly Queue<string> _queIndexIds = new();
public static ProfileExHandler Instance => _instance.Value; public static ProfileExHandler Instance => _instance.Value;
private static readonly string _tag = "ProfileExHandler"; private static readonly string _tag = "ProfileExHandler";
@@ -20,14 +20,6 @@ namespace ServiceLib.Handler
public async Task Init() public async Task Init()
{ {
await InitData(); await InitData();
_ = Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000 * 600);
await SaveQueueIndexIds();
}
});
} }
public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs() public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs()
@@ -44,7 +36,7 @@ namespace ServiceLib.Handler
private void IndexIdEnqueue(string indexId) private void IndexIdEnqueue(string indexId)
{ {
if (Utils.IsNotEmpty(indexId) && !_queIndexIds.Contains(indexId)) if (indexId.IsNotEmpty() && !_queIndexIds.Contains(indexId))
{ {
_queIndexIds.Enqueue(indexId); _queIndexIds.Enqueue(indexId);
} }
@@ -59,7 +51,7 @@ namespace ServiceLib.Handler
List<ProfileExItem> lstInserts = []; List<ProfileExItem> lstInserts = [];
List<ProfileExItem> lstUpdates = []; List<ProfileExItem> lstUpdates = [];
for (int i = 0; i < cnt; i++) for (var i = 0; i < cnt; i++)
{ {
var id = _queIndexIds.Dequeue(); var id = _queIndexIds.Dequeue();
var item = lstExists.FirstOrDefault(t => t.IndexId == id); var item = lstExists.FirstOrDefault(t => t.IndexId == id);
@@ -78,14 +70,19 @@ namespace ServiceLib.Handler
lstInserts.Add(itemNew); lstInserts.Add(itemNew);
} }
} }
try try
{ {
if (lstInserts.Count() > 0) if (lstInserts.Count > 0)
{
await SQLiteHelper.Instance.InsertAllAsync(lstInserts); await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
}
if (lstUpdates.Count() > 0) if (lstUpdates.Count > 0)
{
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates); await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
} }
}
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
@@ -93,17 +90,24 @@ namespace ServiceLib.Handler
} }
} }
private void AddProfileEx(string indexId, ref ProfileExItem? profileEx) private ProfileExItem AddProfileEx(string indexId)
{ {
profileEx = new() var profileEx = new ProfileExItem()
{ {
IndexId = indexId, IndexId = indexId,
Delay = 0, Delay = 0,
Speed = 0, Speed = 0,
Sort = 0 Sort = 0,
Message = string.Empty
}; };
_lstProfileEx.Add(profileEx); _lstProfileEx.Add(profileEx);
IndexIdEnqueue(indexId); IndexIdEnqueue(indexId);
return profileEx;
}
private ProfileExItem GetProfileExItem(string? indexId)
{
return _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId) ?? AddProfileEx(indexId);
} }
public async Task ClearAll() public async Task ClearAll()
@@ -124,39 +128,34 @@ namespace ServiceLib.Handler
} }
} }
public void SetTestDelay(string indexId, string delayVal) public void SetTestDelay(string indexId, int delay)
{ {
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId); var profileEx = GetProfileExItem(indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
int.TryParse(delayVal, out int delay);
profileEx.Delay = delay; profileEx.Delay = delay;
IndexIdEnqueue(indexId); IndexIdEnqueue(indexId);
} }
public void SetTestSpeed(string indexId, string speedVal) public void SetTestSpeed(string indexId, decimal speed)
{ {
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId); var profileEx = GetProfileExItem(indexId);
if (profileEx == null)
{ profileEx.Speed = speed;
AddProfileEx(indexId, ref profileEx); IndexIdEnqueue(indexId);
} }
decimal.TryParse(speedVal, out decimal speed); public void SetTestMessage(string indexId, string message)
profileEx.Speed = speed; {
var profileEx = GetProfileExItem(indexId);
profileEx.Message = message;
IndexIdEnqueue(indexId); IndexIdEnqueue(indexId);
} }
public void SetSort(string indexId, int sort) public void SetSort(string indexId, int sort)
{ {
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId); var profileEx = GetProfileExItem(indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
profileEx.Sort = sort; profileEx.Sort = sort;
IndexIdEnqueue(indexId); IndexIdEnqueue(indexId);
} }
@@ -180,4 +179,3 @@ namespace ServiceLib.Handler
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort); return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public class StatisticsHandler public class StatisticsHandler
{ {
private static readonly Lazy<StatisticsHandler> instance = new(() => new()); private static readonly Lazy<StatisticsHandler> instance = new(() => new());
@@ -161,4 +161,3 @@
} }
} }
} }
}

View File

@@ -1,202 +1,32 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
{
public class ProxySettingLinux public class ProxySettingLinux
{ {
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
public static async Task SetProxy(string host, int port, string exceptions) public static async Task SetProxy(string host, int port, string exceptions)
{ {
var lstCmd = GetSetCmds(host, port, exceptions); List<string> args = ["manual", host, port.ToString(), exceptions];
await ExecCmd(args);
await ExecCmd(lstCmd);
} }
public static async Task UnsetProxy() public static async Task UnsetProxy()
{ {
var lstCmd = GetUnsetCmds(); List<string> args = ["none"];
await ExecCmd(args);
await ExecCmd(lstCmd);
} }
private static async Task ExecCmd(List<CmdItem> lstCmd) private static async Task ExecCmd(List<string> args)
{ {
foreach (var cmd in lstCmd) var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{ {
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null) var contents = EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName);
{ await File.AppendAllTextAsync(fileName, contents);
continue;
} await Utils.SetLinuxChmod(fileName);
await Task.Delay(10);
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
}
} }
private static List<CmdItem> GetSetCmds(string host, int port, string exceptions) await Utils.GetCliWrapOutput(fileName, args);
{
var isKde = IsKde(out var configDir);
List<string> lstType = ["", "http", "https", "socks", "ftp"];
List<CmdItem> lstCmd = [];
//GNOME
foreach (var type in lstType)
{
lstCmd.AddRange(GetSetCmd4Gnome(type, host, port));
}
if (exceptions.IsNotEmpty())
{
lstCmd.AddRange(GetSetCmd4Gnome("exceptions", exceptions, 0));
}
if (isKde)
{
foreach (var type in lstType)
{
lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir));
}
if (exceptions.IsNotEmpty())
{
lstCmd.AddRange(GetSetCmd4Kde("exceptions", exceptions, 0, configDir));
}
// Notify system to reload
lstCmd.Add(new CmdItem()
{
Cmd = "dbus-send",
Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
});
}
return lstCmd;
}
private static List<CmdItem> GetUnsetCmds()
{
var isKde = IsKde(out var configDir);
List<CmdItem> lstCmd = [];
//GNOME
lstCmd.Add(new CmdItem()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"]
});
if (isKde)
{
lstCmd.Add(new CmdItem()
{
Cmd = GetKdeVersion(),
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]
});
// Notify system to reload
lstCmd.Add(new CmdItem()
{
Cmd = "dbus-send",
Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
});
}
return lstCmd;
}
private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir)
{
List<CmdItem> lstCmd = [];
var cmd = GetKdeVersion();
if (type.IsNullOrEmpty())
{
lstCmd.Add(new()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"]
});
}
else if (type == "exceptions")
{
lstCmd.Add(new()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "NoProxyFor", host]
});
}
else
{
var type2 = type.Equals("https") ? "http" : type;
lstCmd.Add(new CmdItem()
{
Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"]
});
}
return lstCmd;
}
private static List<CmdItem> GetSetCmd4Gnome(string type, string host, int port)
{
List<CmdItem> lstCmd = [];
if (type.IsNullOrEmpty())
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "manual"]
});
}
else if (type == "exceptions")
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy", "ignore-hosts", JsonUtils.Serialize(host.Split(','), false)]
});
}
else
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy.{type}", "host", host]
});
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy.{type}", "port", $"{port}"]
});
}
return lstCmd;
}
private static bool IsKde(out string configDir)
{
configDir = "/home";
var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
var desktop2 = Environment.GetEnvironmentVariable("XDG_SESSION_DESKTOP");
var isKde = string.Equals(desktop, "KDE", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop, "plasma", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop2, "KDE", StringComparison.OrdinalIgnoreCase)
|| string.Equals(desktop2, "plasma", StringComparison.OrdinalIgnoreCase);
if (isKde)
{
var homeDir = Environment.GetEnvironmentVariable("HOME");
if (homeDir != null)
{
configDir = Path.Combine(homeDir, ".config");
}
}
return isKde;
}
private static string GetKdeVersion()
{
var ver = Environment.GetEnvironmentVariable("KDE_SESSION_VERSION") ?? "0";
return ver switch
{
"6" => "kwriteconfig6",
_ => "kwriteconfig5"
};
}
} }
} }

View File

@@ -1,101 +1,37 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
{
public class ProxySettingOSX public class ProxySettingOSX
{ {
/// <summary> private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
/// 应用接口类型
/// </summary>
private static readonly List<string> LstInterface = ["Ethernet", "Wi-Fi", "Thunderbolt Bridge", "USB 10/100/1000 LAN"];
/// <summary>
/// 代理类型,对应 http,https,socks
/// </summary>
private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"];
public static async Task SetProxy(string host, int port, string exceptions) public static async Task SetProxy(string host, int port, string exceptions)
{ {
var lstInterface = await GetListNetworkServices(); List<string> args = ["set", host, port.ToString()];
var lstCmd = GetSetCmds(lstInterface, host, port, exceptions); if (exceptions.IsNotEmpty())
await ExecCmd(lstCmd); {
args.AddRange(exceptions.Split(','));
}
await ExecCmd(args);
} }
public static async Task UnsetProxy() public static async Task UnsetProxy()
{ {
var lstInterface = await GetListNetworkServices(); List<string> args = ["clear"];
var lstCmd = GetUnsetCmds(lstInterface); await ExecCmd(args);
await ExecCmd(lstCmd);
} }
private static async Task ExecCmd(List<CmdItem> lstCmd) private static async Task ExecCmd(List<string> args)
{ {
foreach (var cmd in lstCmd) var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{ {
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null) var contents = EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName);
{ await File.AppendAllTextAsync(fileName, contents);
continue;
await Utils.SetLinuxChmod(fileName);
} }
await Task.Delay(10); await Utils.GetCliWrapOutput(fileName, args);
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
}
}
private static List<CmdItem> GetSetCmds(List<string> lstInterface, string host, int port, string exceptions)
{
List<CmdItem> lstCmd = [];
foreach (var interf in lstInterface)
{
foreach (var type in LstTypes)
{
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = [$"-{type}", interf, host, port.ToString()]
});
}
if (exceptions.IsNotEmpty())
{
List<string> args = [$"-setproxybypassdomains", interf];
args.AddRange(exceptions.Split(','));
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = args
});
}
}
return lstCmd;
}
private static List<CmdItem> GetUnsetCmds(List<string> lstInterface)
{
List<CmdItem> lstCmd = [];
foreach (var interf in lstInterface)
{
foreach (var type in LstTypes)
{
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = [$"-{type}state", interf, "off"]
});
}
}
return lstCmd;
}
public static async Task<List<string>> GetListNetworkServices()
{
var services = await Utils.GetListNetworkServices();
if (services.IsNullOrEmpty())
{
return LstInterface;
}
var lst = services.Split(Environment.NewLine).Where(t => t.Length > 0 && t.Contains('*') == false);
return lst.ToList();
}
} }
} }

View File

@@ -1,8 +1,8 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption; using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
{
public class ProxySettingWindows public class ProxySettingWindows
{ {
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings"; private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
@@ -60,7 +60,7 @@ namespace ServiceLib.Handler.SysProxy
try try
{ {
// set proxy for LAN // set proxy for LAN
bool result = SetConnectionProxy(null, strProxy, exceptions, type); var result = SetConnectionProxy(null, strProxy, exceptions, type);
// set proxy for dial up connections // set proxy for dial up connections
var connections = EnumerateRasEntries(); var connections = EnumerateRasEntries();
foreach (var connection in connections) foreach (var connection in connections)
@@ -71,27 +71,27 @@ namespace ServiceLib.Handler.SysProxy
} }
catch catch
{ {
SetProxyFallback(strProxy, exceptions, type); _ = SetProxyFallback(strProxy, exceptions, type);
return false; return false;
} }
} }
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type) private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
{ {
InternetPerConnOptionList list = new(); var list = new InternetPerConnOptionList();
int optionCount = 1; var optionCount = 1;
if (type == 1) // No proxy if (type == 1) // No proxy
{ {
optionCount = 1; optionCount = 1;
} }
else if (type is 2 or 4) // named proxy or autoproxy script URL else if (type is 2 or 4) // named proxy or autoproxy script URL
{ {
optionCount = string.IsNullOrEmpty(exceptions) ? 2 : 3; optionCount = exceptions.IsNullOrEmpty() ? 2 : 3;
} }
int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT; var m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS; var m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
if (type == 2) // named proxy if (type == 2) // named proxy
{ {
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY); m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
@@ -103,11 +103,9 @@ namespace ServiceLib.Handler.SysProxy
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL; m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
} }
//int optionCount = Utile.IsNullOrEmpty(strProxy) ? 1 : (Utile.IsNullOrEmpty(exceptions) ? 2 : 3); var options = new InternetConnectionOption[optionCount];
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
// USE a proxy server ... // USE a proxy server ...
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS; options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
//options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY));
options[0].m_Value.m_Int = m_Int; options[0].m_Value.m_Int = m_Int;
// use THIS proxy server // use THIS proxy server
if (optionCount > 1) if (optionCount > 1)
@@ -135,20 +133,20 @@ namespace ServiceLib.Handler.SysProxy
list.dwOptionCount = options.Length; list.dwOptionCount = options.Length;
list.dwOptionError = 0; list.dwOptionError = 0;
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption)); var optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ... // make a pointer out of all that ...
nint 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 ...
for (int i = 0; i < options.Length; ++i) for (var i = 0; i < options.Length; ++i)
{ {
if (Environment.Is64BitOperatingSystem) if (Environment.Is64BitOperatingSystem)
{ {
nint opt = new(optionsPtr.ToInt64() + (i * optSize)); var opt = new nint(optionsPtr.ToInt64() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false); Marshal.StructureToPtr(options[i], opt, false);
} }
else else
{ {
nint opt = new(optionsPtr.ToInt32() + (i * optSize)); var opt = new nint(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false); Marshal.StructureToPtr(options[i], opt, false);
} }
} }
@@ -156,14 +154,14 @@ namespace ServiceLib.Handler.SysProxy
list.options = optionsPtr; list.options = optionsPtr;
// and then make a pointer out of the whole list // and then make a pointer out of the whole list
nint ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5 var ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
Marshal.StructureToPtr(list, ipcoListPtr, false); Marshal.StructureToPtr(list, ipcoListPtr, false);
// and finally, call the API method! // and finally, call the API method!
bool isSuccess = NativeMethods.InternetSetOption(nint.Zero, var isSuccess = NativeMethods.InternetSetOption(nint.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION, InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize); ipcoListPtr, list.dwSize);
int returnvalue = 0; // ERROR_SUCCESS var returnvalue = 0; // ERROR_SUCCESS
if (!isSuccess) if (!isSuccess)
{ // get the error codes, they might be helpful { // get the error codes, they might be helpful
returnvalue = Marshal.GetLastPInvokeError(); returnvalue = Marshal.GetLastPInvokeError();
@@ -171,13 +169,15 @@ namespace ServiceLib.Handler.SysProxy
else else
{ {
// Notify the system that the registry settings have been changed and cause them to be refreshed // Notify the system that the registry settings have been changed and cause them to be refreshed
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0); _ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0);
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0); _ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0);
} }
// FREE the data ASAP // FREE the data ASAP
if (list.szConnection != nint.Zero) if (list.szConnection != nint.Zero)
{
Marshal.FreeHGlobal(list.szConnection); // release mem 3 Marshal.FreeHGlobal(list.szConnection); // release mem 3
}
if (optionCount > 1) if (optionCount > 1)
{ {
Marshal.FreeHGlobal(options[1].m_Value.m_StringPtr); // release mem 1 Marshal.FreeHGlobal(options[1].m_Value.m_StringPtr); // release mem 1
@@ -204,18 +204,18 @@ namespace ServiceLib.Handler.SysProxy
/// <exception cref="ApplicationException">Error message with win32 error code</exception> /// <exception cref="ApplicationException">Error message with win32 error code</exception>
private static IEnumerable<string> EnumerateRasEntries() private static IEnumerable<string> EnumerateRasEntries()
{ {
int entries = 0; var entries = 0;
// attempt to query with 1 entry buffer // attempt to query with 1 entry buffer
RASENTRYNAME[] rasEntryNames = new RASENTRYNAME[1]; var rasEntryNames = new RASENTRYNAME[1];
int bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME)); var bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME)); rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
uint 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(typeof(RASENTRYNAME))];
for (int 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(typeof(RASENTRYNAME));
} }
@@ -225,7 +225,7 @@ namespace ServiceLib.Handler.SysProxy
if (result == 0) if (result == 0)
{ {
var entryNames = new List<string>(); var entryNames = new List<string>();
for (int i = 0; i < entries; i++) for (var i = 0; i < entries; i++)
{ {
entryNames.Add(rasEntryNames[i].szEntryName); entryNames.Add(rasEntryNames[i].szEntryName);
} }
@@ -357,4 +357,3 @@ namespace ServiceLib.Handler.SysProxy
); );
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
{
public static class SysProxyHandler public static class SysProxyHandler
{ {
private static readonly string _tag = "SysProxyHandler"; private static readonly string _tag = "SysProxyHandler";
@@ -75,7 +75,7 @@
} }
strProxy = string.Empty; strProxy = string.Empty;
if (Utils.IsNullOrEmpty(config.SystemProxyItem.SystemProxyAdvancedProtocol)) if (config.SystemProxyItem.SystemProxyAdvancedProtocol.IsNullOrEmpty())
{ {
strProxy = $"{Global.Loopback}:{port}"; strProxy = $"{Global.Loopback}:{port}";
} }
@@ -96,4 +96,3 @@
ProxySettingWindows.SetProxy(strProxy, "", 4); ProxySettingWindows.SetProxy(strProxy, "", 4);
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public class TaskHandler public class TaskHandler
{ {
private static readonly Lazy<TaskHandler> _instance = new(() => new()); private static readonly Lazy<TaskHandler> _instance = new(() => new());
@@ -7,66 +7,91 @@
public void RegUpdateTask(Config config, Action<bool, string> updateFunc) public void RegUpdateTask(Config config, Action<bool, string> updateFunc)
{ {
Task.Run(() => UpdateTaskRunSubscription(config, updateFunc)); Task.Run(() => ScheduledTasks(config, updateFunc));
Task.Run(() => UpdateTaskRunGeo(config, updateFunc)); }
private async Task ScheduledTasks(Config config, Action<bool, string> updateFunc)
{
Logging.SaveLog("Setup Scheduled Tasks");
var numOfExecuted = 1;
while (true)
{
//1 minute
await Task.Delay(1000 * 60);
//Execute once 1 minute
await UpdateTaskRunSubscription(config, updateFunc);
//Execute once 20 minute
if (numOfExecuted % 20 == 0)
{
//Logging.SaveLog("Execute save config");
await ConfigHandler.SaveConfig(config);
await ProfileExHandler.Instance.SaveTo();
}
//Execute once 1 hour
if (numOfExecuted % 60 == 0)
{
//Logging.SaveLog("Execute delete expired files");
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
//Check once 1 hour
await UpdateTaskRunGeo(config, numOfExecuted / 60, updateFunc);
}
numOfExecuted++;
}
} }
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc) private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc)
{
await Task.Delay(60000);
Logging.SaveLog("UpdateTaskRunSubscription");
var updateHandle = new UpdateService();
while (true)
{ {
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
var lstSubs = (await AppHandler.Instance.SubItems()) var lstSubs = (await AppHandler.Instance.SubItems())?
.Where(t => t.AutoUpdateInterval > 0) .Where(t => t.AutoUpdateInterval > 0)
.Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60) .Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60)
.ToList(); .ToList();
if (lstSubs is not { Count: > 0 })
{
return;
}
Logging.SaveLog("Execute update subscription");
var updateHandle = new UpdateService();
foreach (var item in lstSubs) foreach (var item in lstSubs)
{ {
await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) => await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) =>
{ {
updateFunc?.Invoke(success, msg); updateFunc?.Invoke(success, msg);
if (success) if (success)
Logging.SaveLog("subscription" + msg); {
Logging.SaveLog($"Update subscription end. {msg}");
}
}); });
item.UpdateTime = updateTime; item.UpdateTime = updateTime;
await ConfigHandler.AddSubItem(config, item); await ConfigHandler.AddSubItem(config, item);
await Task.Delay(1000);
await Task.Delay(5000);
}
await Task.Delay(60000);
} }
} }
private async Task UpdateTaskRunGeo(Config config, Action<bool, string> updateFunc) private async Task UpdateTaskRunGeo(Config config, int hours, Action<bool, string> updateFunc)
{ {
var autoUpdateGeoTime = DateTime.Now; if (config.GuiItem.AutoUpdateInterval > 0 && hours > 0 && hours % config.GuiItem.AutoUpdateInterval == 0)
{
//await Task.Delay(1000 * 120); Logging.SaveLog("Execute update geo files");
Logging.SaveLog("UpdateTaskRunGeo");
var updateHandle = new UpdateService(); var updateHandle = new UpdateService();
while (true)
{
await Task.Delay(1000 * 3600);
var dtNow = DateTime.Now;
if (config.GuiItem.AutoUpdateInterval > 0)
{
if ((dtNow - autoUpdateGeoTime).Hours % config.GuiItem.AutoUpdateInterval == 0)
{
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) => await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
{ {
updateFunc?.Invoke(false, msg); updateFunc?.Invoke(false, msg);
}); });
autoUpdateGeoTime = dtNow;
}
}
}
} }
} }
} }

View File

@@ -1,14 +1,14 @@
using System.Net; using System.Net;
using WebDav; using WebDav;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public sealed class WebDavHandler public sealed class WebDavHandler
{ {
private static readonly Lazy<WebDavHandler> _instance = new(() => new()); private static readonly Lazy<WebDavHandler> _instance = new(() => new());
public static WebDavHandler Instance => _instance.Value; public static WebDavHandler Instance => _instance.Value;
private Config? _config; private readonly Config? _config;
private WebDavClient? _client; private WebDavClient? _client;
private string? _lastDescription; private string? _lastDescription;
private string _webDir = Global.AppName + "_backup"; private string _webDir = Global.AppName + "_backup";
@@ -62,7 +62,9 @@ namespace ServiceLib.Handler
private async Task<bool> TryCreateDir() private async Task<bool> TryCreateDir()
{ {
if (_client is null) if (_client is null)
{
return false; return false;
}
try try
{ {
var result2 = await _client.Mkcol(_webDir); var result2 = await _client.Mkcol(_webDir);
@@ -177,4 +179,3 @@ namespace ServiceLib.Handler
public string GetLastError() => _lastDescription ?? string.Empty; public string GetLastError() => _lastDescription ?? string.Empty;
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
public class CheckUpdateModel public class CheckUpdateModel
{ {
public bool? IsSelected { get; set; } public bool? IsSelected { get; set; }
@@ -8,4 +8,3 @@
public string? FileName { get; set; } public string? FileName { get; set; }
public bool? IsFinished { get; set; } public bool? IsFinished { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
public class ClashConnectionModel public class ClashConnectionModel
{ {
public string? Id { get; set; } public string? Id { get; set; }
@@ -14,4 +14,3 @@
public string? Elapsed { get; set; } public string? Elapsed { get; set; }
public string? Chain { get; set; } public string? Chain { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
public class ClashConnections public class ClashConnections
{ {
public ulong downloadTotal { get; set; } public ulong downloadTotal { get; set; }
@@ -34,4 +34,3 @@
public string? processPath { get; set; } public string? processPath { get; set; }
public string? remoteDestination { get; set; } public string? remoteDestination { get; set; }
} }
}

View File

@@ -1,7 +1,7 @@
using static ServiceLib.Models.ClashProxies; using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
public class ClashProviders public class ClashProviders
{ {
public Dictionary<string, ProvidersItem>? providers { get; set; } public Dictionary<string, ProvidersItem>? providers { get; set; }
@@ -14,4 +14,3 @@ namespace ServiceLib.Models
public string? vehicleType { get; set; } public string? vehicleType { get; set; }
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
public class ClashProxies public class ClashProxies
{ {
public Dictionary<string, ProxiesItem>? proxies { get; set; } public Dictionary<string, ProxiesItem>? proxies { get; set; }
@@ -21,4 +21,3 @@
public int delay { get; set; } public int delay { get; set; }
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class ClashProxyModel public class ClashProxyModel
{ {
@@ -15,4 +15,3 @@
public bool IsActive { get; set; } public bool IsActive { get; set; }
} }
}

View File

@@ -1,8 +1,7 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
public class CmdItem public class CmdItem
{ {
public string? Cmd { get; set; } public string? Cmd { get; set; }
public List<string>? Arguments { get; set; } public List<string>? Arguments { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
public class ComboItem public class ComboItem
{ {
public string? ID public string? ID
@@ -12,4 +12,3 @@
get; set; get; set;
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
/// <summary> /// <summary>
/// 本软件配置文件实体类 /// 本软件配置文件实体类
/// </summary> /// </summary>
@@ -54,4 +54,3 @@
#endregion other entities #endregion other entities
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class CoreBasicItem public class CoreBasicItem
{ {
@@ -156,7 +156,7 @@
public int SpeedTestTimeout { get; set; } public int SpeedTestTimeout { get; set; }
public string SpeedTestUrl { get; set; } public string SpeedTestUrl { get; set; }
public string SpeedPingTestUrl { get; set; } public string SpeedPingTestUrl { get; set; }
public int SpeedTestPageSize { get; set; } public int MixedConcurrencyCount { get; set; }
} }
[Serializable] [Serializable]
@@ -197,6 +197,7 @@
{ {
public int UpMbps { get; set; } public int UpMbps { get; set; }
public int DownMbps { get; set; } public int DownMbps { get; set; }
public int HopInterval { get; set; } = 30;
} }
[Serializable] [Serializable]
@@ -244,4 +245,3 @@
public string? Length { get; set; } public string? Length { get; set; }
public string? Interval { get; set; } public string? Interval { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class CoreInfo public class CoreInfo
{ {
@@ -18,4 +18,3 @@ namespace ServiceLib.Models
public string? VersionArg { get; set; } public string? VersionArg { get; set; }
public bool AbsolutePath { get; set; } public bool AbsolutePath { get; set; }
} }
}

View File

@@ -1,7 +1,7 @@
using SQLite; using SQLite;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
[Serializable] [Serializable]
public class DNSItem public class DNSItem
{ {
@@ -17,4 +17,3 @@ namespace ServiceLib.Models
public string? DomainStrategy4Freedom { get; set; } public string? DomainStrategy4Freedom { get; set; }
public string? DomainDNSAddress { get; set; } public string? DomainDNSAddress { get; set; }
} }
}

View File

@@ -1,7 +1,7 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
public class GitHubReleaseAsset public class GitHubReleaseAsset
{ {
[JsonPropertyName("url")] public string? Url { get; set; } [JsonPropertyName("url")] public string? Url { get; set; }
@@ -65,4 +65,3 @@ namespace ServiceLib.Models
[JsonPropertyName("body")] public string? Body { get; set; } [JsonPropertyName("body")] public string? Body { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
internal class IPAPIInfo internal class IPAPIInfo
{ {
public string? ip { get; set; } public string? ip { get; set; }
@@ -10,4 +10,3 @@
public string? country_name { get; set; } public string? country_name { get; set; }
public string? country_code { get; set; } public string? country_code { get; set; }
} }
}

View File

@@ -1,7 +1,7 @@
using SQLite; using SQLite;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
[Serializable] [Serializable]
public class ProfileExItem public class ProfileExItem
{ {
@@ -11,5 +11,5 @@ namespace ServiceLib.Models
public int Delay { get; set; } public int Delay { get; set; }
public decimal Speed { get; set; } public decimal Speed { get; set; }
public int Sort { get; set; } public int Sort { get; set; }
} public string? Message { get; set; }
} }

View File

@@ -1,7 +1,7 @@
using SQLite; using SQLite;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
[Serializable] [Serializable]
public class ProfileItem public class ProfileItem
{ {
@@ -48,12 +48,12 @@ namespace ServiceLib.Models
public List<string>? GetAlpn() public List<string>? GetAlpn()
{ {
return Utils.IsNullOrEmpty(Alpn) ? null : Utils.String2List(Alpn); return Alpn.IsNullOrEmpty() ? null : Utils.String2List(Alpn);
} }
public string GetNetwork() public string GetNetwork()
{ {
if (Utils.IsNullOrEmpty(Network) || !Global.Networks.Contains(Network)) if (Network.IsNullOrEmpty() || !Global.Networks.Contains(Network))
{ {
return Global.DefaultNetwork; return Global.DefaultNetwork;
} }
@@ -69,6 +69,7 @@ namespace ServiceLib.Models
public int ConfigVersion { get; set; } public int ConfigVersion { get; set; }
public string Address { get; set; } public string Address { get; set; }
public int Port { get; set; } public int Port { get; set; }
public string Ports { get; set; }
public string Id { get; set; } public string Id { get; set; }
public int AlterId { get; set; } public int AlterId { get; set; }
public string Security { get; set; } public string Security { get; set; }
@@ -93,4 +94,3 @@ namespace ServiceLib.Models
public string SpiderX { get; set; } public string SpiderX { get; set; }
public string Extra { get; set; } public string Extra { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class ProfileItemModel : ProfileItem public class ProfileItemModel : ProfileItem
{ {
@@ -15,4 +15,3 @@
public string TotalUp { get; set; } public string TotalUp { get; set; }
public string TotalDown { get; set; } public string TotalDown { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
public class RetResult public class RetResult
{ {
public bool Success { get; set; } public bool Success { get; set; }
@@ -24,4 +24,3 @@
Data = data; Data = data;
} }
} }
}

View File

@@ -1,7 +1,7 @@
using SQLite; using SQLite;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
[Serializable] [Serializable]
public class RoutingItem public class RoutingItem
{ {
@@ -20,4 +20,3 @@ namespace ServiceLib.Models
public string DomainStrategy4Singbox { get; set; } public string DomainStrategy4Singbox { get; set; }
public int Sort { get; set; } public int Sort { get; set; }
} }
}

Some files were not shown because too many files have changed in this diff Show More