Compare commits

..

141 Commits
7.1.0 ... 7.4.2

Author SHA1 Message Date
2dust
fe7314c978 up 7.4.2 2024-12-24 10:55:25 +08:00
2dust
d4eb8e59a6 update build 2024-12-23 17:39:50 +08:00
2dust
8ee00907b7 update build 2024-12-23 17:34:06 +08:00
2dust
25fddc3c71 Code clean for desktop 2024-12-23 14:28:33 +08:00
2dust
c78b733850 Revert "update build macos"
This reverts commit a57c83125e.
2024-12-23 13:59:55 +08:00
2dust
a57c83125e update build macos 2024-12-23 12:12:03 +08:00
2dust
247b59985f update build 2024-12-23 11:31:56 +08:00
2dust
7470b8b6d3 Try to fix
https://github.com/2dust/v2rayN/issues/6073
2024-12-23 10:38:37 +08:00
2dust
776d37b7aa Optimize port display 2024-12-23 10:37:43 +08:00
alphax-hue3682
8f532f8468 Update persian translate (#6325) 2024-12-23 10:00:51 +08:00
2dust
472963fe2d Add a second local listening port
https://github.com/2dust/v2rayN/issues/6321
2024-12-22 20:03:20 +08:00
2dust
6a9b62ab9a Improve Ctrl+V for Windows 2024-12-22 12:02:00 +08:00
2dust
9ac8aa2969 up PackageReference 2024-12-22 12:00:50 +08:00
2dust
838bd2c794 up 7.4.1 2024-12-19 16:18:45 +08:00
2dust
b2bbe432e0 Fix
https://github.com/2dust/v2rayN/issues/6309
2024-12-19 16:07:45 +08:00
2dust
b3d8042452 The lower limit of the font size from 10 to 8 2024-12-19 13:53:40 +08:00
2dust
0c758a7fdc Optimize UI for macos 2024-12-19 13:36:45 +08:00
2dust
85cb1d1c92 Bug fix system proxy for macos 2024-12-19 13:20:12 +08:00
2dust
2cd2e8894d Add system proxy exception function for macos 2024-12-19 11:40:16 +08:00
2dust
abb58379b3 Add IsNonWindows instead of IsLinux or IsOSX 2024-12-19 11:24:52 +08:00
2dust
4f48e8b190 Optimize UI 2024-12-19 10:23:46 +08:00
2dust
2b5cbb5e74 UnauthorizedAccessException to Exception 2024-12-18 15:27:00 +08:00
2dust
23dd140921 up 7.4.0 2024-12-18 14:47:27 +08:00
2dust
8c8d7bda64 StartupPath optional LocalApplicationData for linux 2024-12-18 10:27:58 +08:00
2dust
a6e246948a Linux system proxy adds kde version processing 2024-12-17 11:05:12 +08:00
2dust
c49ba735a0 Optimize code 2024-12-16 21:05:36 +08:00
2dust
1a33c598e8 Disable mux when using xhttp 2024-12-16 21:01:03 +08:00
2dust
a4bbdb49de proxy api.ip.sb
https://github.com/2dust/v2rayN/issues/6280
2024-12-16 19:59:46 +08:00
2dust
cf3846fbfd Update README.md 2024-12-16 19:47:27 +08:00
2dust
d46943eedf Optimize UI 2024-12-16 14:28:22 +08:00
2dust
3bc17bd50a Add host for xray ws
https://github.com/XTLS/Xray-core/pull/4142
2024-12-16 14:22:31 +08:00
2dust
48a159f4c8 Need SkiaSharp V2.88 2024-12-15 16:15:58 +08:00
2dust
3d1bcffdc5 up 7.3.2 2024-12-15 15:14:25 +08:00
2dust
45fa0f94d2 up PackageReference 2024-12-15 15:13:37 +08:00
2dust
ed16b7de4a Fix
https://github.com/2dust/v2rayN/issues/6258
2024-12-15 14:41:48 +08:00
2dust
f2ec03c7ec Bug fix
https://github.com/2dust/v2rayN/issues/6263
2024-12-15 14:41:29 +08:00
2dust
11db87f1e6 Remove unused resources 2024-12-15 14:27:00 +08:00
phoenix6936
a1feaf33e0 Update persian translation (#6245)
* Update persian translation

* Update ResUI.fa-Ir.resx
2024-12-15 11:24:13 +08:00
2dust
53a99e3f79 Remove part System.Reflection 2024-12-08 11:11:06 +08:00
2dust
e7f04f55c2 Remove QueryableExtension 2024-12-08 11:10:15 +08:00
2dust
6653ea12b7 Add Language: Hungarian
https://github.com/2dust/v2rayN/issues/6252
2024-12-07 21:25:53 +08:00
2dust
a28cef5b98 Fixed the issue of save window size when exit
https://github.com/2dust/v2rayN/issues/6251
2024-12-07 21:13:57 +08:00
2dust
78a28fbdb3 up 7.3.1 2024-12-07 16:30:32 +08:00
2dust
bf8bbbdcb0 Code clean 2024-12-07 16:29:58 +08:00
2dust
83ad83b135 Optimize save config files 2024-12-07 14:07:51 +08:00
2dust
dbd4f55981 Add system proxy exception function for linux
https://github.com/2dust/v2rayN/issues/6214
2024-12-06 19:12:32 +08:00
2dust
5f5d7172ee Optimize system proxy exceptions 2024-12-06 16:38:23 +08:00
2dust
fbd4557b44 WebProxy uses socks5 instead of http 2024-12-06 14:01:11 +08:00
phoenix6936
5d55a55754 Update persian translate (#6237)
* Update persian translate

* Update ResUI.fa-Ir.resx
2024-12-06 10:01:11 +08:00
phoenix6936
84032aec33 Update persian translation (#6227)
* Update persian translation

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx

* Update ResUI.fa-Ir.resx
2024-12-06 09:38:56 +08:00
2dust
a372d8902e up 7.3.0 2024-12-05 20:08:23 +08:00
2dust
c38c62e4c3 Add automatic batching during speed test and real ping 2024-12-05 19:48:42 +08:00
2dust
8aceff7480 Enhanced testing function
Separate xray-core and sing-box-core testing
2024-12-05 15:21:16 +08:00
2dust
736c450161 Improve the code 2024-12-05 14:05:12 +08:00
2dust
3b63a3d308 Fix
https://github.com/2dust/v2rayN/issues/6216
2024-12-05 14:04:39 +08:00
2dust
4f56174c8f Bug fix
https://github.com/2dust/v2rayN/issues/6219
2024-12-05 12:16:53 +08:00
2dust
4d2eb324f1 Adjust UI
https://github.com/2dust/v2rayN/issues/6217
2024-12-05 10:39:08 +08:00
2dust
3f2ab8ddcb Try to fix
https://github.com/2dust/v2rayN/issues/6186
2024-12-05 10:12:26 +08:00
2dust
fd8f863c5b Add stream-one for xhttp mode 2024-12-03 18:00:55 +08:00
2dust
246f1d7df0 Update README.md 2024-12-03 17:54:13 +08:00
2dust
ccacda9bf5 Using mixed local listening ports 2024-12-03 15:01:36 +08:00
2dust
5494d63878 Remove v2fly-core UI operation support 2024-12-03 13:58:56 +08:00
2dust
c32b9812a7 up PackageReference 2024-12-02 14:43:07 +08:00
2dust
9eb9898b61 Improve the clash connection 2024-12-02 14:40:09 +08:00
2dust
834e05999f Paste import functionality moved to server list
https://github.com/2dust/v2rayN/issues/6201
2024-12-02 11:01:32 +08:00
2dust
b4c37d9906 Bug fix
https://github.com/2dust/v2rayN/issues/6200
2024-12-02 10:16:07 +08:00
2dust
9326b450d7 Delete .github/workflows/winget-pre-release-publish.yml 2024-12-01 14:39:36 +08:00
2dust
019ee8b1ba Update winget-publish.yml 2024-12-01 14:30:39 +08:00
Merrick Zhang
f043645397 Create winget-publish.yml (#6014)
* Create winget-publish.yml

Publish to winget after release

* Update winget-publish.yml

change to released trigger

* Create winget-pre-release-publish.yml

publish pre-release versions.
2024-12-01 14:29:10 +08:00
2dust
3b173f0b3e Improve code 2024-12-01 11:17:36 +08:00
2dust
f36c06389d Improve the clash connection 2024-12-01 10:18:36 +08:00
2dust
d3a0b44247 Bug fix
https://github.com/2dust/v2rayN/issues/6182
2024-11-30 13:49:26 +08:00
2dust
e4d3a98aa8 Fixed the problem of traffic statistics api url 2024-11-30 10:58:53 +08:00
2dust
f685682214 Code clean 2024-11-29 19:21:09 +08:00
2dust
1c6323315b up 7.2.3 2024-11-29 11:43:53 +08:00
2dust
1ff4839be1 Bug fix
https://github.com/2dust/v2rayN/issues/6160
2024-11-29 11:31:22 +08:00
2dust
558e5bb340 Improve tun for linux 2024-11-29 10:31:57 +08:00
2dust
48a9d208e6 Label text optimization 2024-11-28 14:44:24 +08:00
2dust
f29e1f8c45 up 7.2.2 2024-11-27 18:54:16 +08:00
2dust
5d2bc88bb9 up PackageReference 2024-11-27 18:53:32 +08:00
2dust
298fdb1191 Change IPAPI
https://github.com/2dust/v2rayN/issues/6172
2024-11-27 17:03:16 +08:00
2dust
59b34688ea Improve tun for linux 2024-11-27 16:52:25 +08:00
2dust
5ce0bb6e4a Up 2024-11-27 14:40:01 +08:00
2dust
487b1ab182 Update build-osx 2024-11-27 14:37:39 +08:00
2dust
5c144a8ba3 Create build-osx.yml 2024-11-27 14:29:22 +08:00
2dust
ad344356df Create build-osx.sh 2024-11-27 14:23:16 +08:00
2dust
a55c65374d Update build.ps1 2024-11-27 14:22:42 +08:00
2dust
28447a9d43 Update build.ps1 2024-11-27 13:57:51 +08:00
2dust
0c03550c62 Update build.yml 2024-11-27 12:22:40 +08:00
2dust
6d9a84803f Update build.yml 2024-11-27 12:20:26 +08:00
2dust
03b0e4e2bb Update build.yml 2024-11-27 12:18:15 +08:00
2dust
a00e9a6f5e Update build.ps1 2024-11-27 11:25:37 +08:00
2dust
ba17f8fde9 Improve CoreHandler 2024-11-27 11:05:41 +08:00
2dust
01d35456bd Update AesUtils.cs 2024-11-26 20:07:41 +08:00
cryptochecktool
672b8c48ac AesUtils重新修改 (#6165)
* Create AesUtils.cs

新增AesUtils,替换掉DesUtils,提高安全性。
细节:
1.使用AES代替DES
2.正确使用IV调用方式
3.使用SHA256代替MD5进行密钥派生
4.使用PBKDF进行迭代生成密钥。

* Update AesUtils.cs

1.将默认密钥规则设置为与DES一致
2.经过本地测试
2024-11-26 19:54:46 +08:00
2dust
ac1a357740 Delete AesUtils.cs 2024-11-26 19:52:22 +08:00
2dust
504f8d09a6 Tun enable in macos when opening the app 2024-11-26 16:33:19 +08:00
2dust
89ce7c23c9 Fix tun interface name in macos 2024-11-26 15:32:35 +08:00
2dust
a5d99b1eb5 Improved Tun mode function in Linux version 2024-11-26 15:11:02 +08:00
2dust
800d193acb Bug fix 2024-11-26 14:39:33 +08:00
cryptochecktool
7a1d12be76 Update CoreHandler.cs (#6163)
将Desutils替换为Aesutils,提高安全性
2024-11-26 13:54:12 +08:00
cryptochecktool
1b9c95e801 Update OptionSettingViewModel.cs (#6162)
将Desutils替换为Aesutils,提升安全性。
2024-11-26 13:53:50 +08:00
cryptochecktool
9f44815470 Create AesUtils.cs (#6161)
新增AesUtils,替换掉DesUtils,提高安全性。
细节:
1.使用AES代替DES
2.正确使用IV调用方式
3.使用SHA256代替MD5进行密钥派生
4.使用PBKDF进行迭代生成密钥。
2024-11-26 13:52:37 +08:00
2dust
ca38239bce Add Font setting for macos 2024-11-25 21:00:49 +08:00
2dust
d9c22de6b8 Added core update function for macos 2024-11-25 20:31:12 +08:00
2dust
49a3c84fc5 up 7.2.1 2024-11-24 11:31:45 +08:00
2dust
5e0c28438b Refactor Localization for AmazTool 2024-11-23 14:41:19 +08:00
2dust
a6dc801bc4 Merge pac function into the main program 2024-11-23 14:08:08 +08:00
Space Time
22009d1b71 调整订阅分组设置窗口的布局 (#6132)
* 调整订阅分组设置窗口的布局

* 避免添加配置后出现滚动条
2024-11-23 10:45:16 +08:00
2dust
50f39dc40e Delete splithttp 2024-11-22 20:56:34 +08:00
2dust
05d446ed37 up 7.2.0 2024-11-22 18:04:36 +08:00
2dust
7a0b068642 Improve Follow System Theme for windows 2024-11-22 17:37:30 +08:00
2dust
c0ef8c09af Add Follow System Theme for desktop 2024-11-22 17:35:49 +08:00
fonaix
a2db6dd468 修复:TrayIcon不跟随代理模式变化的问题 (#6126)
* 添加:MacOS 代理配置与清除

* 修复:点击表头排序时,超时服务器排在前面的问题

* 添加:MacOS 打开存储所在位置功能

* 修复:删除全部节点时,UI不更新的问题

* 修复:TrayIcon不跟随代理模式变化的问题
2024-11-21 09:02:35 +08:00
2dust
471bc0f65d Update README.md 2024-11-20 15:33:29 +08:00
2dust
80f840a7c2 Fix
https://github.com/2dust/v2rayN/issues/6119
2024-11-20 15:29:06 +08:00
2dust
a2413fdf23 Remove Routing Basic function 2024-11-20 12:01:04 +08:00
2dust
9cbe2b938c Fixed the inbound issue caused by server configuration error during speed test
https://github.com/2dust/v2rayN/issues/6110
2024-11-20 10:34:51 +08:00
2dust
e915726c52 Improved upgrade
https://github.com/2dust/v2rayN/issues/6102
2024-11-20 10:29:04 +08:00
2dust
b11e68cfd8 Remove StatisticsV2rayService
Remove ProtosLib project
2024-11-19 19:20:12 +08:00
2dust
bee66d06dd Added real IP location display
https://ipapi.co/api/?shell#introduction
2024-11-19 16:43:00 +08:00
2dust
945a0add96 Code clean 2024-11-19 15:52:08 +08:00
2dust
294cabcf05 Update README.md 2024-11-18 17:14:42 +08:00
2dust
22eb993ebf up 7.1.3 2024-11-18 16:51:51 +08:00
2dust
b5e1a297ae Optimize Linux start tun mode
You can choose not to store the password and enter it manually each time
2024-11-18 16:44:12 +08:00
2dust
499a16feae Fix LocalizationHelper for AmazTool 2024-11-18 10:13:49 +08:00
Slnanx
1866a59d12 更新程序内嵌资源文件 (#6101)
* fix

* fix

* 移除自述

* 改用单例模式,避免多次初始化。

* 小修正

* 修正

* 更正自述

* 应该完成了

* 内嵌资源

* Update LocalizationHelper.cs
2024-11-18 09:57:38 +08:00
2dust
10513e0f3b Fixed the problem that the statistics column would not be hidden 2024-11-18 09:56:13 +08:00
Slnanx
2e32de2fbb fix (#6099)
* fix

* fix

* 移除自述

* 改用单例模式,避免多次初始化。

* 小修正

* 修正

* 更正自述

* 应该完成了
2024-11-17 19:42:14 +08:00
2dust
30a838df77 Bug fix
https://github.com/2dust/v2rayN/issues/6092
2024-11-17 16:18:46 +08:00
2dust
7cf9b9f57e up 7.1.2 2024-11-17 14:15:37 +08:00
2dust
736d995d4c up PackageReference 2024-11-17 14:11:51 +08:00
2dust
e335b2c0d6 Fix with AppendQuotes 2024-11-17 14:11:17 +08:00
2dust
96ae5517f6 Improve core msg 2024-11-17 09:58:57 +08:00
Slnanx
fb4b8d923a 可以通过判断系统语言来显示对应语言 (#6083) 2024-11-17 09:42:40 +08:00
2dust
2ade705e51 Bug fix 2024-11-16 20:15:05 +08:00
NagisaEfi
e0086b4b79 Update ResUI.zh-Hant.resx (#6080) 2024-11-16 10:34:55 +08:00
2dust
8f6d443104 Hide to tray when closing the window
https://github.com/2dust/v2rayN/issues/6076
2024-11-15 19:50:37 +08:00
2dust
5dbce16895 up 7.1.1 2024-11-15 13:36:44 +08:00
2dust
1016dcb3d1 Improve PAC 2024-11-15 11:59:22 +08:00
2dust
ba5ad12e13 Linux password encryption storage 2024-11-15 09:42:49 +08:00
146 changed files with 4392 additions and 3059 deletions

34
.github/workflows/build-linux.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: release Linux
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
strategy:
matrix:
configuration: [Release]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: |
cd v2rayN
chmod 755 build-linux.sh
./build-linux.sh
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: v2rayN-linux
path: |
./v2rayN/v2rayN-linux.zip

34
.github/workflows/build-osx.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: release macOS
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
strategy:
matrix:
configuration: [Release]
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: |
cd v2rayN
chmod 755 build-osx.sh
./build-osx.sh
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: v2rayN-osx
path: |
./v2rayN/v2rayN-osx.zip

View File

@@ -1,4 +1,4 @@
name: release name: release Windows
on: on:
push: push:
@@ -18,43 +18,24 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
# - name: 删除工作流运行
# uses: Mattraks/delete-workflow-runs@v2
# with:
# token: ${{ github.token }}
# repository: ${{ github.repository }}
# retain_days: 0
# keep_minimum_runs: 1
- name: Build - name: Build
run: cd v2rayN && run: cd v2rayN &&
.\build.ps1 ./build.ps1
# - name: Package - name: Upload build artifacts
# shell: pwsh uses: actions/upload-artifact@v4
# run: | with:
# 7z a -mx9 ..\v2rayN.7z $env:Wap_Project_Directory name: v2rayN-windows-64
path: |
./v2rayN/v2rayN-windows-64.zip
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: v2rayN name: v2rayN-windows-arm64
path: | path: |
.\v2rayN\v2rayN.zip ./v2rayN/v2rayN-windows-arm64.zip
# - name: Release
# uses: softprops/action-gh-release@v1
# env:
# GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
# with:
# prerelease: ${{ contains(github.ref, '-') }}
# draft: false
# files: |
# .\v2rayN\v2rayN.zip
# body: |
# [![](https://img.shields.io/badge/Telegram-Channel-blue)](https://t.me/netch_channel) [![](https://img.shields.io/badge/Telegram-Group-green)](https://t.me/netch_group)
# ## Changelogs
# * This is an automated deployment of GitHub Actions, the change log should be updated manually soon
# ## 更新日志
# * 这是 GitHub Actions 自动化部署,更新日志应该很快会手动更新

31
.github/workflows/winget-publish.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: WinGet submission on release
# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml
# inspired by https://github.com/microsoft/PowerToys/blob/main/.github/workflows/package-submissions.yml
# Modified by @MerrickZ https://github.com/anpho
on:
workflow_dispatch:
release:
types: [released]
jobs:
winget:
name: Publish winget package
runs-on: windows-latest
steps:
- name: Submit v2ray package to Windows Package Manager Community Repository
run: |
$wingetPackage = "2dust.v2rayN"
$gitToken = "${{ secrets.PT_WINGET }}"
$github = Invoke-RestMethod -uri "https://api.github.com/repos/2dust/v2rayN/releases"
$targetRelease = $github | Where-Object -Property prerelease -match 'False' | Select -First 1
$installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64-With-Core\.zip*' | Select -ExpandProperty browser_download_url
$ver = $targetRelease.tag_name
# getting latest wingetcreate file
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUrl|x64" -t $gitToken

View File

@@ -1,5 +1,5 @@
# v2rayN # v2rayN
A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores) A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) and [sing-box-core](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayN)](https://github.com/2dust/v2rayN/commits/master) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayN)](https://github.com/2dust/v2rayN/commits/master)
@@ -9,13 +9,19 @@ A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/
## How to use ## How to use
- If you are new to this, please download v2rayN-With-Core.zip from [releases](https://github.com/2dust/v2rayN/releases) Check [Release files introduction](https://github.com/2dust/v2rayN/wiki/Release-files-introduction) and select the version you need to download
- Otherwise please download v2rayN.zip (you will also need to download cores in the bin directory) ### Windows
- Run v2rayN.exe - Run `v2rayN.exe`
### Linux
- `chmod +x v2rayN` Run `./v2rayN` under user permissions
```
Debian 9+
Ubuntu 16.04+
Fedora 30+
```
## Requirements ## Requirements
- (6.35 and above)[Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) - [Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
- (6.33 and below)[Microsoft .NET 6.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
- [Supported cores](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores) - [Supported cores](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)

View File

@@ -8,5 +8,20 @@
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright> <Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
<FileVersion>1.3.0</FileVersion> <FileVersion>1.3.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Update="Resx\Resource.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resx\Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project> </Project>

View File

@@ -10,7 +10,7 @@
{ {
if (args.Length == 0) if (args.Length == 0)
{ {
Console.WriteLine("Please run it from the main application.(请从主应用运行)"); Console.WriteLine(Resx.Resource.Guidelines);
Thread.Sleep(5000); Thread.Sleep(5000);
return; return;
} }

171
v2rayN/AmazTool/Resx/Resource.Designer.cs generated Normal file
View File

@@ -0,0 +1,171 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace AmazTool.Resx {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AmazTool.Resx.Resource", typeof(Resource).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找类似 Failed to terminate the v2rayN.Close it manually,or the upgrade may fail. 的本地化字符串。
/// </summary>
internal static string FailedTerminateProcess {
get {
return ResourceManager.GetString("FailedTerminateProcess", resourceCulture);
}
}
/// <summary>
/// 查找类似 Failed to extract the update package. 的本地化字符串。
/// </summary>
internal static string FailedUnzipping {
get {
return ResourceManager.GetString("FailedUnzipping", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upgrade failed. 的本地化字符串。
/// </summary>
internal static string FailedUpgrade {
get {
return ResourceManager.GetString("FailedUpgrade", resourceCulture);
}
}
/// <summary>
/// 查找类似 Please run it from the main application. 的本地化字符串。
/// </summary>
internal static string Guidelines {
get {
return ResourceManager.GetString("Guidelines", resourceCulture);
}
}
/// <summary>
/// 查找类似 Information 的本地化字符串。
/// </summary>
internal static string Information {
get {
return ResourceManager.GetString("Information", resourceCulture);
}
}
/// <summary>
/// 查找类似 In progress, please wait... 的本地化字符串。
/// </summary>
internal static string InProgress {
get {
return ResourceManager.GetString("InProgress", resourceCulture);
}
}
/// <summary>
/// 查找类似 Start v2rayN, please wait... 的本地化字符串。
/// </summary>
internal static string Restartv2rayN {
get {
return ResourceManager.GetString("Restartv2rayN", resourceCulture);
}
}
/// <summary>
/// 查找类似 Start extracting the update package... 的本地化字符串。
/// </summary>
internal static string StartUnzipping {
get {
return ResourceManager.GetString("StartUnzipping", resourceCulture);
}
}
/// <summary>
/// 查找类似 Successfully extracted the update package. 的本地化字符串。
/// </summary>
internal static string SuccessUnzipping {
get {
return ResourceManager.GetString("SuccessUnzipping", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upgrade success. 的本地化字符串。
/// </summary>
internal static string SuccessUpgrade {
get {
return ResourceManager.GetString("SuccessUpgrade", resourceCulture);
}
}
/// <summary>
/// 查找类似 Try to terminate the v2rayN process... 的本地化字符串。
/// </summary>
internal static string TryTerminateProcess {
get {
return ResourceManager.GetString("TryTerminateProcess", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upgrade failed, file not found. 的本地化字符串。
/// </summary>
internal static string UpgradeFileNotFound {
get {
return ResourceManager.GetString("UpgradeFileNotFound", resourceCulture);
}
}
}
}

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>Start v2rayN, please wait...</value>
</data>
<data name="Guidelines" xml:space="preserve">
<value>Please run it from the main application.</value>
</data>
<data name="UpgradeFileNotFound" xml:space="preserve">
<value>Upgrade failed, file not found.</value>
</data>
<data name="InProgress" xml:space="preserve">
<value>In progress, please wait...</value>
</data>
<data name="TryTerminateProcess" xml:space="preserve">
<value>Try to terminate the v2rayN process...</value>
</data>
<data name="FailedTerminateProcess" xml:space="preserve">
<value>Failed to terminate the v2rayN.Close it manually,or the upgrade may fail.</value>
</data>
<data name="StartUnzipping" xml:space="preserve">
<value>Start extracting the update package...</value>
</data>
<data name="SuccessUnzipping" xml:space="preserve">
<value>Successfully extracted the update package.</value>
</data>
<data name="FailedUnzipping" xml:space="preserve">
<value>Failed to extract the update package.</value>
</data>
<data name="FailedUpgrade" xml:space="preserve">
<value>Upgrade failed.</value>
</data>
<data name="SuccessUpgrade" xml:space="preserve">
<value>Upgrade success.</value>
</data>
<data name="Information" xml:space="preserve">
<value>Information</value>
</data>
</root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@@ -117,8 +117,40 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <data name="Restartv2rayN" xml:space="preserve">
<data name="pac" type="System.Resources.ResXFileRef, System.Windows.Forms"> <value>正在重启,请等待...</value>
<value>Resources\pac.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312</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> </data>
</root> </root>

View File

@@ -8,35 +8,37 @@ namespace AmazTool
{ {
public static void Upgrade(string fileName) public static void Upgrade(string fileName)
{ {
Console.WriteLine(fileName); Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}");
Console.WriteLine("In progress, please wait...(正在进行中,请等待)");
Thread.Sleep(9000); Waiting(9);
if (!File.Exists(fileName)) if (!File.Exists(fileName))
{ {
Console.WriteLine("Upgrade Failed, File Not Exist(升级失败,文件不存在)."); Console.WriteLine(Resx.Resource.UpgradeFileNotFound);
return; return;
} }
Console.WriteLine("Try to end the process(尝试结束进程)."); Console.WriteLine(Resx.Resource.TryTerminateProcess);
try try
{ {
var existing = Process.GetProcessesByName(V2rayN); var existing = Process.GetProcessesByName(V2rayN);
foreach (var pp in existing) foreach (var pp in existing)
{ {
pp?.Kill(); var path = pp.MainModule?.FileName ?? "";
pp?.WaitForExit(1000); if (path.StartsWith(GetPath(V2rayN)))
{
pp?.Kill();
pp?.WaitForExit(1000);
}
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
// Access may be denied without admin right. The user may not be an administrator. // Access may be denied without admin right. The user may not be an administrator.
Console.WriteLine("Failed to close v2rayN(关闭v2rayN失败).\n" + Console.WriteLine(Resx.Resource.FailedTerminateProcess + ex.StackTrace);
"Close it manually, or the upgrade may fail.(请手动关闭正在运行的v2rayN否则可能升级失败。\n\n" + ex.StackTrace);
} }
Console.WriteLine("Start extracting files(开始解压文件)."); Console.WriteLine(Resx.Resource.StartUnzipping);
StringBuilder sb = new(); StringBuilder sb = new();
try try
{ {
@@ -79,17 +81,17 @@ namespace AmazTool
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("Upgrade Failed(升级失败)." + ex.StackTrace); Console.WriteLine(Resx.Resource.FailedUpgrade + ex.StackTrace);
//return; //return;
} }
if (sb.Length > 0) if (sb.Length > 0)
{ {
Console.WriteLine("Upgrade Failed(升级失败)." + sb.ToString()); Console.WriteLine(Resx.Resource.FailedUpgrade + sb.ToString());
//return; //return;
} }
Console.WriteLine("Start v2rayN, please wait...(正在重启,请等待)"); Console.WriteLine(Resx.Resource.Restartv2rayN);
Thread.Sleep(9000); Waiting(9);
Process process = new() Process process = new()
{ {
StartInfo = new() StartInfo = new()
@@ -122,6 +124,15 @@ namespace AmazTool
return Path.Combine(startupPath, fileName); return Path.Combine(startupPath, fileName);
} }
private static void Waiting(int second)
{
for (var i = second; i > 0; i--)
{
Console.WriteLine(i);
Thread.Sleep(1000);
}
}
private static string V2rayN => "v2rayN"; private static string V2rayN => "v2rayN";
} }
} }

View File

@@ -1,101 +0,0 @@
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace PacLib;
public class PacHandler
{
private static string _configPath;
private static int _httpPort;
private static int _pacPort;
private static TcpListener? _tcpListener;
private static string _pacText;
private static bool _isRunning;
private static bool _needRestart = true;
public static void Start(string configPath, int httpPort, int pacPort)
{
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
_configPath = configPath;
_httpPort = httpPort;
_pacPort = pacPort;
InitText();
if (_needRestart)
{
Stop();
RunListener();
}
}
private static void InitText()
{
var path = Path.Combine(_configPath, "pac.txt");
if (!File.Exists(path))
{
File.AppendAllText(path, Resources.ResourceManager.GetString("pac"));
}
_pacText = File.ReadAllText(path).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
}
private static void RunListener()
{
_tcpListener = TcpListener.Create(_pacPort);
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(async () =>
{
while (_isRunning)
{
try
{
if (!_tcpListener.Pending())
{
await Task.Delay(10);
continue;
}
var client = _tcpListener.AcceptTcpClient();
await Task.Run(() =>
{
var stream = client.GetStream();
var sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
sb.AppendLine("Connection:close");
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(_pacText));
sb.AppendLine();
sb.Append(_pacText);
var content = Encoding.UTF8.GetBytes(sb.ToString());
stream.Write(content, 0, content.Length);
stream.Flush();
});
}
catch
{
// ignored
}
}
}, TaskCreationOptions.LongRunning);
}
public static void Stop()
{
if (_tcpListener == null) return;
try
{
_isRunning = false;
_tcpListener.Stop();
_tcpListener = null;
}
catch
{
// ignored
}
}
}

View File

@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -1,95 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace PacLib {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PacLib.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找类似 var proxy = &apos;__PROXY__&apos;;
///var rules = [
/// [
/// [],
/// []
/// ],
/// [
/// [
/// &quot;aftygh.gov.tw&quot;,
/// &quot;aide.gov.tw&quot;,
/// &quot;aliyun.com&quot;,
/// &quot;arte.gov.tw&quot;,
/// &quot;baidu.com&quot;,
/// &quot;chinaso.com&quot;,
/// &quot;chinaz.com&quot;,
/// &quot;chukuang.gov.tw&quot;,
/// &quot;cycab.gov.tw&quot;,
/// &quot;dbnsa.gov.tw&quot;,
/// &quot;df.gov.tw&quot;,
/// &quot;eastcoast-nsa.gov.tw&quot;,
/// &quot;erv-nsa.gov.tw&quot;,
/// &quot;grb.gov.tw&quot;,
/// &quot;haosou.com&quot;,
/// [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// </summary>
internal static string pac {
get {
return ResourceManager.GetString("pac", resourceCulture);
}
}
}
}

View File

@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Protobuf Include="Statistics.proto" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.28.3" />
<PackageReference Include="Grpc.Net.Client" Version="2.66.0" />
<PackageReference Include="Grpc.Tools" Version="2.67.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -1,53 +0,0 @@
syntax = "proto3";
package v2ray.core.app.stats.command;
option csharp_namespace = "ProtosLib.Statistics";
message GetStatsRequest {
// Name of the stat counter.
string name = 1;
// Whether or not to reset the counter to fetching its value.
bool reset = 2;
}
message Stat {
string name = 1;
int64 value = 2;
}
message GetStatsResponse {
Stat stat = 1;
}
message QueryStatsRequest {
string pattern = 1;
bool reset = 2;
}
message QueryStatsResponse {
repeated Stat stat = 1;
}
message SysStatsRequest {
}
message SysStatsResponse {
uint32 NumGoroutine = 1;
uint32 NumGC = 2;
uint64 Alloc = 3;
uint64 TotalAlloc = 4;
uint64 Sys = 5;
uint64 Mallocs = 6;
uint64 Frees = 7;
uint64 LiveObjects = 8;
uint64 PauseTotalNs = 9;
uint32 Uptime = 10;
}
service StatsService {
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {}
rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {}
}
message Config {}

View File

@@ -1,13 +0,0 @@
using ProtosLib.Statistics;
namespace ProtosLib
{
public class Tests
{
private StatsService.StatsServiceClient client_;
public Tests()
{
}
}
}

View File

@@ -0,0 +1,101 @@
using System.Security.Cryptography;
using System.Text;
namespace ServiceLib.Common
{
public class AesUtils
{
private const int KeySize = 256; // AES-256
private const int IvSize = 16; // AES block size
private const int Iterations = 10000;
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' ')); // google浏览器默认盐值
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
/// <summary>
/// Encrypt
/// </summary>
/// <param name="text">Plain text</param>
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
/// <returns>Base64 encoded cipher text with IV</returns>
public static string Encrypt(string text, string? password = null)
{
if (string.IsNullOrEmpty(text))
return string.Empty;
var plaintext = Encoding.UTF8.GetBytes(text);
var key = GetKey(password);
var iv = GenerateIv();
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var ms = new MemoryStream();
ms.Write(iv, 0, iv.Length);
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(plaintext, 0, plaintext.Length);
cs.FlushFinalBlock();
}
var cipherTextWithIv = ms.ToArray();
return Convert.ToBase64String(cipherTextWithIv);
}
/// <summary>
/// Decrypt
/// </summary>
/// <param name="cipherTextWithIv">Base64 encoded cipher text with IV</param>
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
/// <returns>Plain text</returns>
public static string Decrypt(string cipherTextWithIv, string? password = null)
{
if (string.IsNullOrEmpty(cipherTextWithIv))
return string.Empty;
var cipherTextWithIvBytes = Convert.FromBase64String(cipherTextWithIv);
var key = GetKey(password);
var iv = new byte[IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, 0, iv, 0, IvSize);
var cipherText = new byte[cipherTextWithIvBytes.Length - IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, IvSize, cipherText, 0, cipherText.Length);
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var ms = new MemoryStream();
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
cs.FlushFinalBlock();
}
var plainText = ms.ToArray();
return Encoding.UTF8.GetString(plainText);
}
private static byte[] GetKey(string? password)
{
if (password.IsNullOrEmpty())
{
password = DefaultPassword;
}
using var pbkdf2 = new Rfc2898DeriveBytes(password, Salt, Iterations, HashAlgorithmName.SHA256);
return pbkdf2.GetBytes(KeySize / 8);
}
private static byte[] GenerateIv()
{
var randomNumber = new byte[IvSize];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomNumber);
return randomNumber;
}
}
}

View File

@@ -0,0 +1,75 @@
using System.Security.Cryptography;
using System.Text;
namespace ServiceLib.Common
{
public class DesUtils
{
/// <summary>
/// Encrypt
/// </summary>
/// <param name="text"></param>
/// /// <param name="key"></param>
/// <returns></returns>
public static string Encrypt(string? text, string? key = null)
{
if (text.IsNullOrEmpty())
{
return string.Empty;
}
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
var dsp = DES.Create();
using var memStream = new MemoryStream();
using var cryStream = new CryptoStream(memStream, dsp.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
using var sWriter = new StreamWriter(cryStream);
sWriter.Write(text);
sWriter.Flush();
cryStream.FlushFinalBlock();
memStream.Flush();
return Convert.ToBase64String(memStream.GetBuffer(), 0, (int)memStream.Length);
}
/// <summary>
/// Decrypt
/// </summary>
/// <param name="encryptText"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string Decrypt(string? encryptText, string? key = null)
{
if (encryptText.IsNullOrEmpty())
{
return string.Empty;
}
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
var dsp = DES.Create();
var buffer = Convert.FromBase64String(encryptText);
using var memStream = new MemoryStream();
using var cryStream = new CryptoStream(memStream, dsp.CreateDecryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
cryStream.Write(buffer, 0, buffer.Length);
cryStream.FlushFinalBlock();
return Encoding.UTF8.GetString(memStream.ToArray());
}
private static void GetKeyIv(string key, out byte[] rgbKey, out byte[] rgbIv)
{
if (key.IsNullOrEmpty())
{
throw new ArgumentNullException("The key cannot be null");
}
if (key.Length <= 8)
{
throw new ArgumentNullException("The key length cannot be less than 8 characters.");
}
rgbKey = Encoding.ASCII.GetBytes(key.Substring(0, 8));
rgbIv = Encoding.ASCII.GetBytes(key.Insert(0, "w").Substring(0, 8));
}
private static string GetDefaultKey()
{
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
}
}
}

View File

@@ -10,7 +10,7 @@ namespace ServiceLib.Common
{ {
private static readonly Lazy<HttpClientHelper> _instance = new(() => private static readonly Lazy<HttpClientHelper> _instance = new(() =>
{ {
HttpClientHandler handler = new() { UseCookies = false }; SocketsHttpHandler handler = new() { UseCookies = false };
HttpClientHelper helper = new(new HttpClient(handler)); HttpClientHelper helper = new(new HttpClient(handler));
return helper; return helper;
}); });

View File

@@ -70,8 +70,9 @@ namespace ServiceLib.Common
/// </summary> /// </summary>
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="indented"></param> /// <param name="indented"></param>
/// <param name="nullValue"></param>
/// <returns></returns> /// <returns></returns>
public static string Serialize(object? obj, bool indented = true) public static string Serialize(object? obj, bool indented = true, bool nullValue = false)
{ {
var result = string.Empty; var result = string.Empty;
try try
@@ -82,8 +83,8 @@ namespace ServiceLib.Common
} }
var options = new JsonSerializerOptions var options = new JsonSerializerOptions
{ {
WriteIndented = indented ? true : false, WriteIndented = indented,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
}; };
result = JsonSerializer.Serialize(obj, options); result = JsonSerializer.Serialize(obj, options);
} }
@@ -100,38 +101,5 @@ namespace ServiceLib.Common
/// <param name="obj"></param> /// <param name="obj"></param>
/// <returns></returns> /// <returns></returns>
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj); public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj);
/// <summary>
/// Save as json file
/// </summary>
/// <param name="obj"></param>
/// <param name="filePath"></param>
/// <param name="nullValue"></param>
/// <returns></returns>
public static int ToFile(object? obj, string? filePath, bool nullValue = true)
{
if (filePath is null)
{
return -1;
}
try
{
using var file = File.Create(filePath);
var options = new JsonSerializerOptions
{
WriteIndented = true,
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
};
JsonSerializer.Serialize(file, obj, options);
return 0;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return -1;
}
}
} }
} }

View File

@@ -1,50 +0,0 @@
using System.Linq.Expressions;
using System.Reflection;
namespace ServiceLib.Common
{
public static class QueryableExtension
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName)
{
return _OrderBy<T>(query, propertyName, false);
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName)
{
return _OrderBy<T>(query, propertyName, true);
}
private static IOrderedQueryable<T> _OrderBy<T>(IQueryable<T> query, string propertyName, bool isDesc)
{
var methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
var memberProp = typeof(T).GetProperty(propertyName);
var method = typeof(QueryableExtension).GetMethod(methodname)
.MakeGenericMethod(typeof(T), memberProp.PropertyType);
return (IOrderedQueryable<T>)method.Invoke(null, new object[] { query, memberProp });
}
public static IOrderedQueryable<T> OrderByInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
{//public
return query.OrderBy(_GetLambda<T, TProp>(memberProperty));
}
public static IOrderedQueryable<T> OrderByDescendingInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
{//public
return query.OrderByDescending(_GetLambda<T, TProp>(memberProperty));
}
private static Expression<Func<T, TProp>> _GetLambda<T, TProp>(PropertyInfo memberProperty)
{
if (memberProperty.PropertyType != typeof(TProp)) throw new Exception();
var thisArg = Expression.Parameter(typeof(T));
var lambda = Expression.Lambda<Func<T, TProp>>(Expression.Property(thisArg, memberProperty), thisArg);
return lambda;
}
}
}

View File

@@ -31,8 +31,8 @@
var parts = this.version.Split('.'); var parts = this.version.Split('.');
if (parts.Length == 2) if (parts.Length == 2)
{ {
this.major = int.Parse(parts[0]); this.major = int.Parse(parts.First());
this.minor = int.Parse(parts[1]); this.minor = int.Parse(parts.Last());
this.patch = 0; this.patch = 0;
} }
else if (parts.Length is 3 or 4) else if (parts.Length is 3 or 4)

View File

@@ -22,7 +22,7 @@ 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()) return false; if (s.IsNullOrEmpty()) return false;
return chars.Contains(s[0]); return chars.Contains(s.First());
} }
private static bool IsWhiteSpace(this string value) private static bool IsWhiteSpace(this string value)
@@ -61,7 +61,7 @@ namespace ServiceLib.Common
return string.Empty; return string.Empty;
} }
return char.ToUpper(value[0]) + value[1..]; return char.ToUpper(value.First()) + value[1..];
} }
public static string AppendQuotes(this string value) public static string AppendQuotes(this string value)

View File

@@ -313,8 +313,8 @@ namespace ServiceLib.Common
continue; continue;
} }
var key = Uri.UnescapeDataString(keyValue[0]); var key = Uri.UnescapeDataString(keyValue.First());
var val = Uri.UnescapeDataString(keyValue[1]); var val = Uri.UnescapeDataString(keyValue.Last());
if (result[key] is null) if (result[key] is null)
{ {
@@ -529,15 +529,9 @@ namespace ServiceLib.Common
{ {
try try
{ {
if (blFull) return blFull
{ ? $"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture} - {StartupPath()}"
return : $"{Global.AppName}/{GetVersionInfo()}";
$"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture} - {File.GetLastWriteTime(GetExePath()):yyyy/MM/dd}";
}
else
{
return $"{Global.AppName}/{GetVersionInfo()}";
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -622,8 +616,8 @@ namespace ServiceLib.Common
{ {
if (host.StartsWith("#")) continue; if (host.StartsWith("#")) continue;
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (hostItem.Length < 2) continue; if (hostItem.Length != 2) continue;
systemHosts.Add(hostItem[1], hostItem[0]); systemHosts.Add(hostItem.Last(), hostItem.First());
} }
} }
} }
@@ -677,6 +671,27 @@ namespace ServiceLib.Common
#region TempPath #region TempPath
public static bool HasWritePermission()
{
try
{
var tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "guiTemps");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
var fileName = Path.Combine(tempPath, GetGuid());
File.Create(fileName).Close();
File.Delete(fileName);
}
catch (Exception)
{
return false;
}
return true;
}
public static string GetPath(string fileName) public static string GetPath(string fileName)
{ {
var startupPath = StartupPath(); var startupPath = StartupPath();
@@ -695,6 +710,11 @@ namespace ServiceLib.Common
public static string StartupPath() public static string StartupPath()
{ {
if (Utils.IsNonWindows() && Environment.GetEnvironmentVariable("V2RAYN_LOCAL_APPLICATION_DATA") == "1")
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "v2rayN");
}
return AppDomain.CurrentDomain.BaseDirectory; return AppDomain.CurrentDomain.BaseDirectory;
} }
@@ -818,6 +838,8 @@ namespace ServiceLib.Common
public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsNonWindows() => !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static string GetExeName(string name) public static string GetExeName(string name)
{ {
return IsWindows() ? $"{name}.exe" : name; return IsWindows() ? $"{name}.exe" : name;
@@ -852,6 +874,7 @@ namespace ServiceLib.Common
public static async Task<string?> SetLinuxChmod(string? fileName) public static async Task<string?> SetLinuxChmod(string? fileName)
{ {
if (fileName.IsNullOrEmpty()) return null; if (fileName.IsNullOrEmpty()) return null;
//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("/bin/bash", arg);
} }

View File

@@ -5,9 +5,7 @@
v2fly = 1, v2fly = 1,
Xray = 2, Xray = 2,
v2fly_v5 = 4, v2fly_v5 = 4,
mihomo = 13, mihomo = 13,
hysteria = 21, hysteria = 21,
naiveproxy = 22, naiveproxy = 22,
tuic = 23, tuic = 23,

View File

@@ -3,12 +3,12 @@
public enum EInboundProtocol public enum EInboundProtocol
{ {
socks = 0, socks = 0,
http,
socks2, socks2,
http2, socks3,
pac, pac,
api, api,
api2, api2,
mixed,
speedtest = 21 speedtest = 21
} }
} }

View File

@@ -6,6 +6,7 @@
SendMsgView, SendMsgView,
SendSnackMsg, SendSnackMsg,
RefreshProfiles, RefreshProfiles,
StopSpeedtest StopSpeedtest,
AppExit
} }
} }

View File

@@ -6,7 +6,6 @@
kcp, kcp,
ws, ws,
httpupgrade, httpupgrade,
splithttp,
xhttp, xhttp,
h2, h2,
http, http,

View File

@@ -20,6 +20,7 @@
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases"; public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"; public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs"; public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
public const string IPAPIUrl = "https://api.ip.sb/geoip";
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
public const string ConfigFileName = "guiNConfig.json"; public const string ConfigFileName = "guiNConfig.json";
@@ -46,6 +47,7 @@
public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml"; public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml";
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 DefaultSecurity = "auto"; public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp"; public const string DefaultNetwork = "tcp";
@@ -66,15 +68,17 @@
public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run"; public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
public const string AutoRunName = "v2rayNAutoRun"; public const string AutoRunName = "v2rayNAutoRun";
public const string CustomIconName = "v2rayN.ico"; public const string CustomIconName = "v2rayN.ico";
public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*"; public const string SystemProxyExceptionsWindows = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
public const string SystemProxyExceptionsLinux = "localhost, 127.0.0.0/8, ::1";
public const string RoutingRuleComma = "<COMMA>"; public const string RoutingRuleComma = "<COMMA>";
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 DelayUnit = "";
public const string SpeedUnit = ""; public const string SpeedUnit = "";
public const int MinFontSize = 10; public const int MinFontSize = 8;
public const string RebootAs = "rebootas"; public const string RebootAs = "rebootas";
public const string AvaAssets = "avares://v2rayN/Assets/";
public static readonly List<string> IEProxyProtocols = new() { public static readonly List<string> IEProxyProtocols = new() {
"{ip}:{http_port}", "{ip}:{http_port}",
@@ -178,26 +182,25 @@
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" }; public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" }; public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" }; public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "splithttp", "h2", "quic", "grpc" }; public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "h2", "quic", "grpc" };
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" }; public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
public static readonly List<string> CoreTypes = new() { "v2fly", "Xray", "sing_box" }; public static readonly List<string> CoreTypes = new() { "Xray", "sing_box" };
public static readonly List<string> CoreTypes4VLESS = new() { "Xray", "sing_box" };
public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" }; public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" }; public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" }; public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" };
public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" }; public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" }; public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up" }; public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up", "stream-one" };
public static readonly List<string> AllowInsecure = new() { "true", "false", "" }; public static readonly List<string> AllowInsecure = new() { "true", "false", "" };
public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" }; public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
public static readonly List<string> SingboxDomainStrategy4Out = new() { "ipv4_only", "prefer_ipv4", "prefer_ipv6", "ipv6_only", "" }; public static readonly List<string> SingboxDomainStrategy4Out = new() { "ipv4_only", "prefer_ipv4", "prefer_ipv6", "ipv6_only", "" };
public static readonly List<string> DomainDNSAddress = ["223.5.5.5", "223.6.6.6", "localhost"]; public static readonly List<string> DomainDNSAddress = ["223.5.5.5", "223.6.6.6", "localhost"];
public static readonly List<string> SingboxDomainDNSAddress = ["223.5.5.5", "223.6.6.6", "dhcp://auto"]; public static readonly List<string> SingboxDomainDNSAddress = ["223.5.5.5", "223.6.6.6", "dhcp://auto"];
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" }; public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru", "hu" };
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" }; public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" };
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" }; public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" }; public static readonly List<string> InboundTags = new() { "socks", "socks2", "socks3" };
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" }; public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" }; public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"]; public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];

View File

@@ -27,7 +27,7 @@
get get
{ {
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2)); _statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
return _statePort2.Value; return _statePort2.Value + (_config.TunModeItem.EnableTun ? 1 : 0);
} }
} }
@@ -46,11 +46,18 @@
public bool InitApp() public bool InitApp()
{ {
_config = ConfigHandler.LoadConfig(); if (Utils.IsNonWindows() && Utils.HasWritePermission() == false)
if (_config == null) {
Environment.SetEnvironmentVariable("V2RAYN_LOCAL_APPLICATION_DATA", "1", EnvironmentVariableTarget.Process);
}
Logging.Setup();
var config = ConfigHandler.LoadConfig();
if (config == null)
{ {
return false; return false;
} }
_config = config;
Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage); Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage);
//Under Win10 //Under Win10
@@ -70,15 +77,21 @@
public bool InitComponents() public bool InitComponents()
{ {
Logging.Setup();
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}"); Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}"); Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
Logging.ClearLogs(); Logging.ClearLogs();
return true; return true;
} }
public bool Reset()
{
_statePort = null;
_statePort2 = null;
return true;
}
#endregion Init #endregion Init
#region Config #region Config
@@ -211,12 +224,12 @@
public async Task<List<RoutingItem>?> RoutingItems() public async Task<List<RoutingItem>?> RoutingItems()
{ {
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().Where(it => it.Locked == false).OrderBy(t => t.Sort).ToListAsync(); return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
} }
public async Task<RoutingItem?> GetRoutingItem(string id) public async Task<RoutingItem?> GetRoutingItem(string id)
{ {
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Locked == false && it.Id == id); return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Id == id);
} }
public async Task<List<DNSItem>?> DNSItems() public async Task<List<DNSItem>?> DNSItems()
@@ -257,16 +270,8 @@
return (ECoreType)profileItem.CoreType; return (ECoreType)profileItem.CoreType;
} }
if (_config.CoreTypeItem == null) var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType);
{ return item?.CoreType ?? ECoreType.Xray;
return ECoreType.Xray;
}
var item = _config.CoreTypeItem.FirstOrDefault(it => it.ConfigType == eConfigType);
if (item == null)
{
return ECoreType.Xray;
}
return item.CoreType;
} }
#endregion Core Type #endregion Core Type

View File

@@ -25,6 +25,10 @@ namespace ServiceLib.Handler
await SetTaskLinux(); await SetTaskLinux();
} }
} }
else if (Utils.IsOSX())
{
//TODO
}
return true; return true;
} }

View File

@@ -9,7 +9,6 @@ namespace ServiceLib.Handler
public class ConfigHandler public class ConfigHandler
{ {
private static readonly string _configRes = Global.ConfigFileName; private static readonly string _configRes = Global.ConfigFileName;
private static readonly object _objLock = new();
#region ConfigHandler #region ConfigHandler
@@ -62,17 +61,14 @@ namespace ServiceLib.Handler
{ {
if (config.Inbound.Count > 0) if (config.Inbound.Count > 0)
{ {
config.Inbound[0].Protocol = EInboundProtocol.socks.ToString(); config.Inbound.First().Protocol = EInboundProtocol.socks.ToString();
} }
} }
config.RoutingBasicItem ??= new()
{
EnableRoutingAdvanced = true
};
config.RoutingBasicItem ??= new();
if (Utils.IsNullOrEmpty(config.RoutingBasicItem.DomainStrategy)) if (Utils.IsNullOrEmpty(config.RoutingBasicItem.DomainStrategy))
{ {
config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies[0];//"IPIfNonMatch"; config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies.First();//"IPIfNonMatch";
} }
config.KcpItem ??= new KcpItem config.KcpItem ??= new KcpItem
@@ -113,7 +109,7 @@ namespace ServiceLib.Handler
{ {
if (Thread.CurrentThread.CurrentCulture.Name.Equals("zh-cn", StringComparison.CurrentCultureIgnoreCase)) if (Thread.CurrentThread.CurrentCulture.Name.Equals("zh-cn", StringComparison.CurrentCultureIgnoreCase))
{ {
config.UiItem.CurrentLanguage = Global.Languages[0]; config.UiItem.CurrentLanguage = Global.Languages.First();
} }
else else
{ {
@@ -122,10 +118,6 @@ namespace ServiceLib.Handler
} }
config.ConstItem ??= new ConstItem(); config.ConstItem ??= new ConstItem();
if (Utils.IsNullOrEmpty(config.ConstItem.DefIEProxyExceptions))
{
config.ConstItem.DefIEProxyExceptions = Global.IEProxyExceptions;
}
config.SpeedTestItem ??= new(); config.SpeedTestItem ??= new();
if (config.SpeedTestItem.SpeedTestTimeout < 10) if (config.SpeedTestItem.SpeedTestTimeout < 10)
@@ -134,7 +126,7 @@ namespace ServiceLib.Handler
} }
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedTestUrl)) if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedTestUrl))
{ {
config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls[0]; config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls.First();
} }
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedPingTestUrl)) if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedPingTestUrl))
{ {
@@ -150,7 +142,7 @@ namespace ServiceLib.Handler
config.Mux4SboxItem ??= new() config.Mux4SboxItem ??= new()
{ {
Protocol = Global.SingboxMuxs[0], Protocol = Global.SingboxMuxs.First(),
MaxConnections = 8 MaxConnections = 8
}; };
@@ -164,6 +156,16 @@ namespace ServiceLib.Handler
config.WebDavItem ??= new(); config.WebDavItem ??= new();
config.CheckUpdateItem ??= new(); config.CheckUpdateItem ??= new();
if (Utils.IsNotEmpty(config.ConstItem.DefIEProxyExceptions))
{
config.SystemProxyItem.SystemProxyExceptions = $"{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}";
config.ConstItem.DefIEProxyExceptions = string.Empty;
}
if (config.SystemProxyItem.SystemProxyExceptions.IsNullOrEmpty())
{
config.SystemProxyItem.SystemProxyExceptions = Utils.IsWindows() ? Global.SystemProxyExceptionsWindows : Global.SystemProxyExceptionsLinux;
}
return config; return config;
} }
@@ -174,30 +176,26 @@ namespace ServiceLib.Handler
/// <returns></returns> /// <returns></returns>
public static async Task<int> SaveConfig(Config config) public static async Task<int> SaveConfig(Config config)
{ {
lock (_objLock) try
{ {
try //save temp file
{ var resPath = Utils.GetConfigPath(_configRes);
//save temp file var tempPath = $"{resPath}_temp";
var resPath = Utils.GetConfigPath(_configRes);
var tempPath = $"{resPath}_temp";
if (JsonUtils.ToFile(config, tempPath) != 0)
{
return -1;
}
if (File.Exists(resPath)) var content = JsonUtils.Serialize(config, true, true);
{ if (content.IsNullOrEmpty())
File.Delete(resPath);
}
//rename
File.Move(tempPath, resPath);
}
catch (Exception ex)
{ {
Logging.SaveLog("ToJsonFile", ex);
return -1; return -1;
} }
await File.WriteAllTextAsync(tempPath, content);
//rename
File.Move(tempPath, resPath, true);
}
catch (Exception ex)
{
Logging.SaveLog("ToJsonFile", ex);
return -1;
} }
return 0; return 0;
@@ -385,7 +383,7 @@ namespace ServiceLib.Handler
} }
var item = await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(t => t.Port > 0); var item = await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(t => t.Port > 0);
return await SetDefaultServerIndex(config, item.IndexId); return await SetDefaultServerIndex(config, item?.IndexId);
} }
public static async Task<ProfileItem?> GetDefaultServer(Config config) public static async Task<ProfileItem?> GetDefaultServer(Config config)
@@ -431,7 +429,7 @@ namespace ServiceLib.Handler
{ {
return 0; return 0;
} }
sort = ProfileExHandler.Instance.GetSort(lstProfile[0].IndexId) - 1; sort = ProfileExHandler.Instance.GetSort(lstProfile.First().IndexId) - 1;
break; break;
} }
@@ -768,69 +766,66 @@ namespace ServiceLib.Handler
}).ToList(); }).ToList();
Enum.TryParse(colName, true, out EServerColName name); Enum.TryParse(colName, true, out EServerColName name);
var propertyName = string.Empty;
switch (name)
{
case EServerColName.ConfigType:
case EServerColName.Remarks:
case EServerColName.Address:
case EServerColName.Port:
case EServerColName.Network:
case EServerColName.StreamSecurity:
propertyName = name.ToString();
break;
case EServerColName.DelayVal:
propertyName = "Delay";
break;
case EServerColName.SpeedVal:
propertyName = "Speed";
break;
case EServerColName.SubRemarks:
propertyName = "Subid";
break;
default:
return -1;
}
var items = lstProfile.AsQueryable();
if (asc) if (asc)
{ {
lstProfile = items.OrderBy(propertyName).ToList(); lstProfile = name switch
{
EServerColName.ConfigType => lstProfile.OrderBy(t => t.ConfigType).ToList(),
EServerColName.Remarks => lstProfile.OrderBy(t => t.Remarks).ToList(),
EServerColName.Address => lstProfile.OrderBy(t => t.Address).ToList(),
EServerColName.Port => lstProfile.OrderBy(t => t.Port).ToList(),
EServerColName.Network => lstProfile.OrderBy(t => t.Network).ToList(),
EServerColName.StreamSecurity => lstProfile.OrderBy(t => t.StreamSecurity).ToList(),
EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(),
EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(),
EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(),
_ => lstProfile
};
} }
else else
{ {
lstProfile = items.OrderByDescending(propertyName).ToList(); lstProfile = name switch
{
EServerColName.ConfigType => lstProfile.OrderByDescending(t => t.ConfigType).ToList(),
EServerColName.Remarks => lstProfile.OrderByDescending(t => t.Remarks).ToList(),
EServerColName.Address => lstProfile.OrderByDescending(t => t.Address).ToList(),
EServerColName.Port => lstProfile.OrderByDescending(t => t.Port).ToList(),
EServerColName.Network => lstProfile.OrderByDescending(t => t.Network).ToList(),
EServerColName.StreamSecurity => lstProfile.OrderByDescending(t => t.StreamSecurity).ToList(),
EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(),
EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(),
EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(),
_ => lstProfile
};
} }
for (int i = 0; i < lstProfile.Count; i++)
for (var i = 0; i < lstProfile.Count; i++)
{ {
ProfileExHandler.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10); ProfileExHandler.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
} }
if (name == EServerColName.DelayVal) switch (name)
{ {
var maxSort = lstProfile.Max(t => t.Sort) + 10; case EServerColName.DelayVal:
foreach (var item in lstProfile)
{
if (item.Delay <= 0)
{ {
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort); var maxSort = lstProfile.Max(t => t.Sort) + 10;
foreach (var item in lstProfile.Where(item => item.Delay <= 0))
{
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort);
}
break;
} }
} case EServerColName.SpeedVal:
}
if (name == EServerColName.SpeedVal)
{
var maxSort = lstProfile.Max(t => t.Sort) + 10;
foreach (var item in lstProfile)
{
if (item.Speed <= 0)
{ {
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort); var maxSort = lstProfile.Max(t => t.Sort) + 10;
foreach (var item in lstProfile.Where(item => item.Speed <= 0))
{
ProfileExHandler.Instance.SetSort(item.IndexId, maxSort);
}
break;
} }
}
} }
return 0; return 0;
@@ -959,7 +954,6 @@ namespace ServiceLib.Handler
&& o.Address == n.Address && o.Address == n.Address
&& o.Port == n.Port && o.Port == n.Port
&& o.Id == n.Id && o.Id == n.Id
&& o.AlterId == n.AlterId
&& o.Security == n.Security && o.Security == n.Security
&& o.Network == n.Network && o.Network == n.Network
&& o.HeaderType == n.HeaderType && o.HeaderType == n.HeaderType
@@ -968,6 +962,10 @@ namespace ServiceLib.Handler
&& (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity) && (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity)
&& o.Flow == n.Flow && o.Flow == n.Flow
&& o.Sni == n.Sni && o.Sni == n.Sni
&& o.Alpn == n.Alpn
&& o.Fingerprint == n.Fingerprint
&& o.PublicKey == n.PublicKey
&& o.ShortId == n.ShortId
&& (!remarks || o.Remarks == n.Remarks); && (!remarks || o.Remarks == n.Remarks);
} }
@@ -1025,6 +1023,35 @@ namespace ServiceLib.Handler
return result; return result;
} }
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
{
ProfileItem? itemSocks = null;
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
{
itemSocks = new ProfileItem()
{
CoreType = ECoreType.sing_box,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
Sni = node.Address, //Tun2SocksAddress
Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
};
}
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
{
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem()
{
CoreType = preCoreType,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
Port = node.PreSocksPort.Value,
};
}
return itemSocks;
}
#endregion Server #endregion Server
#region Batch add servers #region Batch add servers
@@ -1295,6 +1322,20 @@ namespace ServiceLib.Handler
} }
} }
//Keep the last traffic statistics
if (lstOriSub != null)
{
var lstSub = await AppHandler.Instance.ProfileItems(subid);
foreach (var item in lstSub)
{
var existItem = lstOriSub?.FirstOrDefault(t => config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == item.Remarks : CompareProfileItem(t, item, true));
if (existItem != null)
{
await StatisticsHandler.Instance.CloneServerStatItem(existItem.IndexId, item.IndexId);
}
}
}
return counter; return counter;
} }
@@ -1597,7 +1638,7 @@ namespace ServiceLib.Handler
var item = await AppHandler.Instance.GetRoutingItem(config.RoutingBasicItem.RoutingIndexId); var item = await AppHandler.Instance.GetRoutingItem(config.RoutingBasicItem.RoutingIndexId);
if (item is null) if (item is null)
{ {
var item2 = await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(t => t.Locked == false); var item2 = await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync();
await SetDefaultRouting(config, item2); await SetDefaultRouting(config, item2);
return item2; return item2;
} }
@@ -1671,6 +1712,15 @@ namespace ServiceLib.Handler
{ {
var ver = "V3-"; var ver = "V3-";
var items = await AppHandler.Instance.RoutingItems(); var items = await AppHandler.Instance.RoutingItems();
//TODO Temporary code to be removed later
var lockItem = items?.FirstOrDefault(t => t.Locked == true);
if (lockItem != null)
{
await ConfigHandler.RemoveRoutingItem(lockItem);
items = await AppHandler.Instance.RoutingItems();
}
if (!blImportAdvancedRules && items.Where(t => t.Remarks.StartsWith(ver)).ToList().Count > 0) if (!blImportAdvancedRules && items.Where(t => t.Remarks.StartsWith(ver)).ToList().Count > 0)
{ {
return 0; return 0;
@@ -1711,11 +1761,6 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
public static async Task<RoutingItem?> GetLockedRoutingItem(Config config)
{
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Locked == true);
}
public static async Task RemoveRoutingItem(RoutingItem routingItem) public static async Task RemoveRoutingItem(RoutingItem routingItem)
{ {
await SQLiteHelper.Instance.DeleteAsync(routingItem); await SQLiteHelper.Instance.DeleteAsync(routingItem);

View File

@@ -13,6 +13,7 @@ namespace ServiceLib.Handler
private Config _config; private Config _config;
private Process? _process; private Process? _process;
private Process? _processPre; private Process? _processPre;
private int _linuxSudoPid = -1;
private Action<bool, string>? _updateFunc; private Action<bool, string>? _updateFunc;
public async Task Init(Config config, Action<bool, string> updateFunc) public async Task Init(Config config, Action<bool, string> updateFunc)
@@ -23,7 +24,7 @@ namespace ServiceLib.Handler
Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
if (Utils.IsLinux()) if (Utils.IsNonWindows())
{ {
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo) foreach (var it in coreInfo)
@@ -59,50 +60,35 @@ namespace ServiceLib.Handler
var fileName = Utils.GetConfigPath(Global.CoreConfigFileName); var fileName = Utils.GetConfigPath(Global.CoreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName); var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
ShowMsg(true, result.Msg);
if (result.Success != true) if (result.Success != true)
{ {
ShowMsg(true, result.Msg);
return; return;
} }
else
{
ShowMsg(true, $"{node.GetSummary()}");
await CoreStop();
await Task.Delay(100);
await CoreStart(node);
//In tun mode, do a delay check and restart the core ShowMsg(true, $"{node.GetSummary()}");
//if (_config.tunModeItem.enableTun) ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
//{ ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
// Observable.Range(1, 1) await CoreStop();
// .Delay(TimeSpan.FromSeconds(15)) await Task.Delay(100);
// .Subscribe(x => await CoreStart(node);
// { await CoreStartPreService(node);
// {
// if (_process == null || _process.HasExited)
// {
// CoreStart(node);
// ShowMsg(false, "Tun mode restart the core once");
// Logging.SaveLog("Tun mode restart the core once");
// }
// }
// });
//}
}
} }
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds) public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{ {
var pid = -1;
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 configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
ShowMsg(false, result.Msg); ShowMsg(false, result.Msg);
if (result.Success) if (result.Success != true)
{ {
pid = await CoreStartSpeedtest(configPath, coreType); return -1;
} }
return pid;
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
ShowMsg(false, configPath);
return await CoreStartSpeedtest(configPath, coreType);
} }
public async Task CoreStop() public async Task CoreStop()
@@ -111,17 +97,19 @@ namespace ServiceLib.Handler
{ {
if (_process != null) if (_process != null)
{ {
await KillProcess(_process); _process = await KillProcess(_process);
_process.Dispose();
_process = null;
} }
if (_processPre != null) if (_processPre != null)
{ {
await KillProcess(_processPre); _processPre = await KillProcess(_processPre);
_processPre.Dispose();
_processPre = null;
} }
if (_linuxSudoPid > 0)
{
await KillProcessAsLinuxSudo();
}
_linuxSudoPid = -1;
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -133,8 +121,7 @@ namespace ServiceLib.Handler
{ {
try try
{ {
var _p = Process.GetProcessById(pid); await KillProcess(Process.GetProcessById(pid));
await KillProcess(_p);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -167,11 +154,7 @@ namespace ServiceLib.Handler
private async Task CoreStart(ProfileItem node) private async Task CoreStart(ProfileItem node)
{ {
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}"); var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
_config.RunningCoreType = coreType;
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog; var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
@@ -181,47 +164,28 @@ namespace ServiceLib.Handler
return; return;
} }
_process = proc; _process = proc;
}
//start a pre service private async Task CoreStartPreService(ProfileItem node)
{
if (_process != null && !_process.HasExited) if (_process != null && !_process.HasExited)
{ {
ProfileItem? itemSocks = null; var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var preCoreType = ECoreType.sing_box; var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && _config.TunModeItem.EnableTun)
{
itemSocks = new ProfileItem()
{
CoreType = preCoreType,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
Sni = node.Address, //Tun2SocksAddress
Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
};
}
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
{
preCoreType = _config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem()
{
CoreType = preCoreType,
ConfigType = EConfigType.SOCKS,
Address = Global.Loopback,
Port = node.PreSocksPort.Value,
};
_config.RunningCoreType = preCoreType;
}
if (itemSocks != null) if (itemSocks != null)
{ {
var fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName); var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2); var fileName = Utils.GetConfigPath(Global.CorePreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
if (result.Success) if (result.Success)
{ {
var coreInfo2 = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
var proc2 = await RunProcess(coreInfo2, Global.CorePreConfigFileName, true, true); var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
if (proc2 is not null) if (proc is null)
{ {
_processPre = proc2; return;
} }
_processPre = proc;
} }
} }
} }
@@ -229,9 +193,6 @@ namespace ServiceLib.Handler
private async Task<int> CoreStartSpeedtest(string configPath, ECoreType coreType) private async Task<int> CoreStartSpeedtest(string configPath, ECoreType coreType)
{ {
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
ShowMsg(false, configPath);
try try
{ {
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
@@ -260,8 +221,8 @@ namespace ServiceLib.Handler
{ {
return _config.TunModeItem.EnableTun return _config.TunModeItem.EnableTun
&& eCoreType == ECoreType.sing_box && eCoreType == ECoreType.sing_box
&& Utils.IsLinux() && (Utils.IsNonWindows())
&& _config.TunModeItem.LinuxSudoPassword.IsNotEmpty() //&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
; ;
} }
@@ -277,7 +238,6 @@ namespace ServiceLib.Handler
return null; return null;
} }
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
try try
{ {
Process proc = new() Process proc = new()
@@ -296,12 +256,10 @@ namespace ServiceLib.Handler
} }
}; };
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
if (isNeedSudo) if (isNeedSudo)
{ {
proc.StartInfo.FileName = $"/bin/sudo"; await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
proc.StartInfo.Arguments = $"-S {fileName} {string.Format(coreInfo.Arguments, configPath)}";
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.RedirectStandardInput = true;
} }
var startUpErrorMessage = new StringBuilder(); var startUpErrorMessage = new StringBuilder();
@@ -326,13 +284,15 @@ namespace ServiceLib.Handler
} }
proc.Start(); proc.Start();
if (isNeedSudo) if (isNeedSudo && _config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{ {
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10); await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(_config.TunModeItem.LinuxSudoPassword); await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10); await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(_config.TunModeItem.LinuxSudoPassword); await proc.StandardInput.WriteLineAsync(pwd);
} }
if (isNeedSudo) _linuxSudoPid = proc.Id;
if (displayLog) if (displayLog)
{ {
@@ -361,34 +321,104 @@ namespace ServiceLib.Handler
} }
} }
private async Task KillProcess(Process? proc) private async Task<Process?> KillProcess(Process? proc)
{ {
if (proc is null) if (proc is null)
{ {
return; return null;
}
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1));
try
{
await proc.WaitForExitAsync(timeout.Token);
}
catch (OperationCanceledException)
{
proc.Kill();
}
if (!proc.HasExited)
{
try
{
await proc.WaitForExitAsync(timeout.Token);
}
catch (Exception)
{
proc.Kill();
}
} }
try { proc?.Kill(true); } catch { }
try { proc?.Close(); } catch { }
try { proc?.Dispose(); } catch { }
await Task.Delay(100);
return null;
} }
#endregion Process #endregion Process
#region Linux
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 shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
proc.StartInfo.FileName = shFilePath;
proc.StartInfo.Arguments = "";
proc.StartInfo.WorkingDirectory = "";
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.RedirectStandardInput = true;
}
}
private async Task KillProcessAsLinuxSudo()
{
var cmdLine = $"kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
Process proc = new()
{
StartInfo = new()
{
FileName = shFilePath,
UseShellExecute = false,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
RedirectStandardInput = true
}
};
proc.Start();
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
try
{
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
}
catch (Exception)
{
// ignored
}
}
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await proc.WaitForExitAsync(timeout.Token);
await Task.Delay(3000);
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
//Shell scripts
var shFilePath = Utils.GetBinPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (AppHandler.Instance.IsAdministrator)
{
sb.AppendLine($"{cmdLine}");
}
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
{
sb.AppendLine($"pkexec {cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
Logging.SaveLog(shFilePath);
return shFilePath;
}
#endregion Linux
} }
} }

View File

@@ -42,6 +42,8 @@
DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip", DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip",
DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip", DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip",
DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip", DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip",
DownloadUrlOSX64 = Global.NUrl + "/download/{0}/v2rayN-macos-64.zip",
DownloadUrlOSXArm64 = Global.NUrl + "/download/{0}/v2rayN-macos-arm64.zip",
}); });
_coreInfo.Add(new CoreInfo _coreInfo.Add(new CoreInfo
@@ -79,6 +81,8 @@
DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip", DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip",
DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip", DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip",
DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip", DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip",
DownloadUrlOSX64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-64.zip",
DownloadUrlOSXArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-arm64-v8a.zip",
Match = "Xray", Match = "Xray",
VersionArg = "-version", VersionArg = "-version",
RedirectInfo = true, RedirectInfo = true,
@@ -95,6 +99,8 @@
DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip", DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz", DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz", DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz",
DownloadUrlOSX64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-amd64-compatible-{0}.gz",
DownloadUrlOSXArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-arm64-{0}.gz",
Match = "Mihomo", Match = "Mihomo",
VersionArg = "-v", VersionArg = "-v",
RedirectInfo = true, RedirectInfo = true,
@@ -140,6 +146,8 @@
DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip", DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz", DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz", DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
DownloadUrlOSX64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz",
DownloadUrlOSXArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz",
Match = "sing-box", Match = "sing-box",
VersionArg = "version", VersionArg = "version",
}); });
@@ -165,7 +173,7 @@
private string PortableMode() private string PortableMode()
{ {
return $" -d \"{Utils.GetBinPath("")}\""; return $" -d {Utils.GetBinPath("").AppendQuotes()}";
} }
} }
} }

View File

@@ -93,7 +93,6 @@ namespace ServiceLib.Handler.Fmt
} }
break; break;
case nameof(ETransport.splithttp):
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
if (Utils.IsNotEmpty(item.RequestHost)) if (Utils.IsNotEmpty(item.RequestHost))
{ {
@@ -179,7 +178,6 @@ namespace ServiceLib.Handler.Fmt
item.Path = Utils.UrlDecode(query["path"] ?? "/"); item.Path = Utils.UrlDecode(query["path"] ?? "/");
break; break;
case nameof(ETransport.splithttp):
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/"); item.Path = Utils.UrlDecode(query["path"] ?? "/");

View File

@@ -99,8 +99,8 @@ namespace ServiceLib.Handler.Fmt
{ {
return null; return null;
} }
item.Security = userInfoParts[0]; item.Security = userInfoParts.First();
item.Id = Utils.UrlDecode(userInfoParts[1]); item.Id = Utils.UrlDecode(userInfoParts.Last());
} }
else else
{ {
@@ -111,8 +111,8 @@ namespace ServiceLib.Handler.Fmt
{ {
return null; return null;
} }
item.Security = userInfoParts[0]; item.Security = userInfoParts.First();
item.Id = userInfoParts[1]; item.Id = userInfoParts.Last();
} }
var queryParameters = Utils.ParseQueryString(parsedUrl.Query); var queryParameters = Utils.ParseQueryString(parsedUrl.Query);

View File

@@ -5,9 +5,8 @@
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
{ {
msg = ResUI.ConfigurationFormatIncorrect; msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
item = ResolveSocksNew(str) ?? ResolveSocks(str); var item = ResolveSocksNew(str) ?? ResolveSocks(str);
if (item == null) if (item == null)
{ {
return null; return null;
@@ -25,19 +24,13 @@
public static string? ToUri(ProfileItem? item) public static string? ToUri(ProfileItem? item)
{ {
if (item == null) return null; if (item == null) return null;
string url = string.Empty; var url = string.Empty;
string remark = string.Empty; var remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks)) if (Utils.IsNotEmpty(item.Remarks))
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
//url = string.Format("{0}:{1}@{2}:{3}",
// item.security,
// item.id,
// item.address,
// item.port);
//url = Utile.Base64Encode(url);
//new //new
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}"); var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark); return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
@@ -51,7 +44,7 @@
}; };
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..]; result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
//remark //remark
int indexRemark = result.IndexOf("#"); var indexRemark = result.IndexOf("#");
if (indexRemark > 0) if (indexRemark > 0)
{ {
try try
@@ -62,7 +55,7 @@
result = result[..indexRemark]; result = result[..indexRemark];
} }
//part decode //part decode
int indexS = result.IndexOf("@"); var indexS = result.IndexOf("@");
if (indexS > 0) if (indexS > 0)
{ {
} }
@@ -71,21 +64,20 @@
result = Utils.Base64Decode(result); result = Utils.Base64Decode(result);
} }
string[] arr1 = result.Split('@'); var arr1 = result.Split('@');
if (arr1.Length != 2) if (arr1.Length != 2)
{ {
return null; return null;
} }
string[] arr21 = arr1[0].Split(':'); var arr21 = arr1.First().Split(':');
//string[] arr22 = arr1[1].Split(':'); var indexPort = arr1.Last().LastIndexOf(":");
int indexPort = arr1[1].LastIndexOf(":");
if (arr21.Length != 2 || indexPort < 0) if (arr21.Length != 2 || indexPort < 0)
{ {
return null; return null;
} }
item.Address = arr1[1][..indexPort]; item.Address = arr1[1][..indexPort];
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]); item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
item.Security = arr21[0]; item.Security = arr21.First();
item.Id = arr21[1]; item.Id = arr21[1];
return item; return item;
@@ -106,10 +98,10 @@
// parse base64 UserInfo // parse base64 UserInfo
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
var userInfo = Utils.Base64Decode(rawUserInfo); var userInfo = Utils.Base64Decode(rawUserInfo);
var userInfoParts = userInfo.Split(new[] { ':' }, 2); var userInfoParts = userInfo.Split([':'], 2);
if (userInfoParts.Length == 2) if (userInfoParts.Length == 2)
{ {
item.Security = userInfoParts[0]; item.Security = userInfoParts.First();
item.Id = userInfoParts[1]; item.Id = userInfoParts[1];
} }

View File

@@ -21,8 +21,8 @@
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length == 2) if (userInfoParts.Length == 2)
{ {
item.Id = userInfoParts[0]; item.Id = userInfoParts.First();
item.Security = userInfoParts[1]; item.Security = userInfoParts.Last();
} }
var query = Utils.ParseQueryString(url.Query); var query = Utils.ParseQueryString(url.Query);

View File

@@ -0,0 +1,105 @@
using System.Net.Sockets;
using System.Text;
namespace ServiceLib.Handler
{
public class PacHandler
{
private static string _configPath;
private static int _httpPort;
private static int _pacPort;
private static TcpListener? _tcpListener;
private static byte[] _writeContent;
private static bool _isRunning;
private static bool _needRestart = true;
public static async Task Start(string configPath, int httpPort, int pacPort)
{
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
_configPath = configPath;
_httpPort = httpPort;
_pacPort = pacPort;
await InitText();
if (_needRestart)
{
Stop();
RunListener();
}
}
private static async Task InitText()
{
var path = Path.Combine(_configPath, "pac.txt");
if (!File.Exists(path))
{
var pac = Utils.GetEmbedText(Global.PacFileName);
await File.AppendAllTextAsync(path, pac);
}
var pacText =
(await File.ReadAllTextAsync(path)).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
var sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
sb.AppendLine("Connection:close");
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
sb.AppendLine();
sb.Append(pacText);
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
}
private static void RunListener()
{
_tcpListener = TcpListener.Create(_pacPort);
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(async () =>
{
while (_isRunning)
{
try
{
if (!_tcpListener.Pending())
{
await Task.Delay(10);
continue;
}
var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => { WriteContent(client); });
}
catch
{
// ignored
}
}
}, TaskCreationOptions.LongRunning);
}
private static void WriteContent(TcpClient client)
{
var stream = client.GetStream();
stream.Write(_writeContent, 0, _writeContent.Length);
stream.Flush();
}
public static void Stop()
{
if (_tcpListener == null) return;
try
{
_isRunning = false;
_tcpListener.Stop();
_tcpListener = null;
}
catch
{
// ignored
}
}
}
}

View File

@@ -9,7 +9,7 @@
private ServerStatItem? _serverStatItem; private ServerStatItem? _serverStatItem;
private List<ServerStatItem> _lstServerStat; private List<ServerStatItem> _lstServerStat;
private Action<ServerSpeedItem>? _updateFunc; private Action<ServerSpeedItem>? _updateFunc;
//private StatisticsV2rayService? _statisticsV2Ray;
private StatisticsXrayService? _statisticsXray; private StatisticsXrayService? _statisticsXray;
private StatisticsSingboxService? _statisticsSingbox; private StatisticsSingboxService? _statisticsSingbox;
@@ -26,7 +26,6 @@
await InitData(); await InitData();
//_statisticsV2Ray = new StatisticsV2rayService(config, UpdateServerStatHandler);
_statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler); _statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler);
_statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler); _statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler);
} }
@@ -35,7 +34,6 @@
{ {
try try
{ {
//_statisticsV2Ray?.Close();
_statisticsXray?.Close(); _statisticsXray?.Close();
_statisticsSingbox?.Close(); _statisticsSingbox?.Close();
} }
@@ -67,6 +65,25 @@
} }
} }
public async Task CloneServerStatItem(string indexId, string toIndexId)
{
if (_lstServerStat == null)
{
return;
}
var stat = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
if (stat == null)
{
return;
}
var toStat = JsonUtils.DeepCopy(stat);
toStat.IndexId = toIndexId;
await SQLiteHelper.Instance.ReplaceAsync(toStat);
_lstServerStat.Add(toStat);
}
private async Task InitData() private async Task InitData()
{ {
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )"); await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");

View File

@@ -2,9 +2,9 @@
{ {
public class ProxySettingLinux public class ProxySettingLinux
{ {
public static async Task SetProxy(string host, int port) public static async Task SetProxy(string host, int port, string exceptions)
{ {
var lstCmd = GetSetCmds(host, port); var lstCmd = GetSetCmds(host, port, exceptions);
await ExecCmd(lstCmd); await ExecCmd(lstCmd);
} }
@@ -29,25 +29,39 @@
} }
} }
private static List<CmdItem> GetSetCmds(string host, int port) private static List<CmdItem> GetSetCmds(string host, int port, string exceptions)
{ {
var isKde = IsKde(out var configDir); var isKde = IsKde(out var configDir);
List<string> lstType = ["", "http", "https", "socks", "ftp"]; List<string> lstType = ["", "http", "https", "socks", "ftp"];
List<CmdItem> lstCmd = []; 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) if (isKde)
{ {
foreach (var type in lstType) foreach (var type in lstType)
{ {
lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir)); lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir));
} }
} if (exceptions.IsNotEmpty())
else
{
foreach (var type in lstType)
{ {
lstCmd.AddRange(GetSetCmd4Gnome(type, host, port)); 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; return lstCmd;
} }
@@ -57,44 +71,58 @@
var isKde = IsKde(out var configDir); var isKde = IsKde(out var configDir);
List<CmdItem> lstCmd = []; List<CmdItem> lstCmd = [];
//GNOME
lstCmd.Add(new CmdItem()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"]
});
if (isKde) if (isKde)
{ {
lstCmd.Add(new CmdItem() lstCmd.Add(new CmdItem()
{ {
Cmd = "kwriteconfig5", Cmd = GetKdeVersion(),
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"] Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]
}); });
}
else // Notify system to reload
{
lstCmd.Add(new CmdItem() lstCmd.Add(new CmdItem()
{ {
Cmd = "gsettings", Cmd = "dbus-send",
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"] Arguments = ["--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''"]
}); });
} }
return lstCmd; return lstCmd;
} }
private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir) private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir)
{ {
List<CmdItem> lstCmd = []; List<CmdItem> lstCmd = [];
var cmd = GetKdeVersion();
if (type.IsNullOrEmpty()) if (type.IsNullOrEmpty())
{ {
lstCmd.Add(new() lstCmd.Add(new()
{ {
Cmd = "kwriteconfig5", Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"] 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 else
{ {
var type2 = type.Equals("https") ? "http" : type; var type2 = type.Equals("https") ? "http" : type;
lstCmd.Add(new CmdItem() lstCmd.Add(new CmdItem()
{ {
Cmd = "kwriteconfig5", Cmd = cmd,
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"] Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"]
}); });
} }
@@ -114,6 +142,14 @@
Arguments = ["set", "org.gnome.system.proxy", "mode", "manual"] 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 else
{ {
lstCmd.Add(new() lstCmd.Add(new()
@@ -136,7 +172,11 @@
{ {
configDir = "/home"; configDir = "/home";
var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP"); var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
var isKde = string.Equals(desktop, "KDE", StringComparison.OrdinalIgnoreCase); 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) if (isKde)
{ {
var homeDir = Environment.GetEnvironmentVariable("HOME"); var homeDir = Environment.GetEnvironmentVariable("HOME");
@@ -148,5 +188,15 @@
return isKde; return isKde;
} }
private static string GetKdeVersion()
{
var ver = Environment.GetEnvironmentVariable("KDE_SESSION_VERSION") ?? "0";
return ver switch
{
"6" => "kwriteconfig6",
_ => "kwriteconfig5"
};
}
} }
} }

View File

@@ -5,6 +5,7 @@
/* /*
* 仅测试了MacOS 13.7.1 x86 版本,其他版本有待确认 * 仅测试了MacOS 13.7.1 x86 版本,其他版本有待确认
*/ */
/// <summary> /// <summary>
/// 应用接口类型 /// 应用接口类型
/// </summary> /// </summary>
@@ -15,20 +16,18 @@
/// </summary> /// </summary>
private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"]; private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"];
public static async Task SetProxy(string host, int port) public static async Task SetProxy(string host, int port, string exceptions)
{ {
var lstCmd = GetSetCmds(host, port); var lstCmd = GetSetCmds(host, port, exceptions);
await ExecCmd(lstCmd); await ExecCmd(lstCmd);
} }
public static async Task UnsetProxy() public static async Task UnsetProxy()
{ {
var lstCmd = GetUnsetCmds(); var lstCmd = GetUnsetCmds();
await ExecCmd(lstCmd); await ExecCmd(lstCmd);
} }
private static async Task ExecCmd(List<CmdItem> lstCmd) private static async Task ExecCmd(List<CmdItem> lstCmd)
{ {
foreach (var cmd in lstCmd) foreach (var cmd in lstCmd)
@@ -43,7 +42,7 @@
} }
} }
private static List<CmdItem> GetSetCmds(string host, int port) private static List<CmdItem> GetSetCmds(string host, int port, string exceptions)
{ {
List<CmdItem> lstCmd = []; List<CmdItem> lstCmd = [];
foreach (var interf in LstInterface) foreach (var interf in LstInterface)
@@ -53,7 +52,17 @@
lstCmd.Add(new CmdItem() lstCmd.Add(new CmdItem()
{ {
Cmd = "networksetup", Cmd = "networksetup",
Arguments = [$"-{type}", interf, host, (type.Contains("socks") ? (port - 1) : port).ToString()] 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
}); });
} }
} }

View File

@@ -1,6 +1,4 @@
using PacLib; namespace ServiceLib.Handler.SysProxy
namespace ServiceLib.Handler.SysProxy
{ {
public static class SysProxyHandler public static class SysProxyHandler
{ {
@@ -15,8 +13,8 @@ namespace ServiceLib.Handler.SysProxy
try try
{ {
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.http); var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
var portSocks = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); var exceptions = config.SystemProxyItem.SystemProxyExceptions;
if (port <= 0) if (port <= 0)
{ {
return false; return false;
@@ -25,23 +23,18 @@ namespace ServiceLib.Handler.SysProxy
{ {
case ESysProxyType.ForcedChange when Utils.IsWindows(): case ESysProxyType.ForcedChange when Utils.IsWindows():
{ {
GetWindowsProxyString(config, port, portSocks, out var strProxy, out var strExceptions); GetWindowsProxyString(config, port, out var strProxy, out var strExceptions);
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2); ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
break; break;
} }
case ESysProxyType.ForcedChange when Utils.IsLinux(): case ESysProxyType.ForcedChange when Utils.IsLinux():
await ProxySettingLinux.SetProxy(Global.Loopback, port); await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions);
break; break;
case ESysProxyType.ForcedChange: case ESysProxyType.ForcedChange when Utils.IsOSX():
{ await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions);
if (Utils.IsOSX()) break;
{
await ProxySettingOSX.SetProxy(Global.Loopback, port);
}
break;
}
case ESysProxyType.ForcedClear when Utils.IsWindows(): case ESysProxyType.ForcedClear when Utils.IsWindows():
ProxySettingWindows.UnsetProxy(); ProxySettingWindows.UnsetProxy();
break; break;
@@ -50,23 +43,13 @@ namespace ServiceLib.Handler.SysProxy
await ProxySettingLinux.UnsetProxy(); await ProxySettingLinux.UnsetProxy();
break; break;
case ESysProxyType.ForcedClear: case ESysProxyType.ForcedClear when Utils.IsOSX():
{ await ProxySettingOSX.UnsetProxy();
if (Utils.IsOSX()) break;
{
await ProxySettingOSX.UnsetProxy();
}
break;
}
case ESysProxyType.Pac when Utils.IsWindows(): case ESysProxyType.Pac when Utils.IsWindows():
{ await SetWindowsProxyPac(port);
var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac); break;
PacHandler.Start(Utils.GetConfigPath(), port, portPac);
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
ProxySettingWindows.SetProxy(strProxy, "", 4);
break;
}
} }
if (type != ESysProxyType.Pac && Utils.IsWindows()) if (type != ESysProxyType.Pac && Utils.IsWindows())
@@ -81,12 +64,12 @@ namespace ServiceLib.Handler.SysProxy
return true; return true;
} }
private static void GetWindowsProxyString(Config config, int port, int portSocks, out string strProxy, out string strExceptions) private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions)
{ {
strExceptions = ""; strExceptions = config.SystemProxyItem.SystemProxyExceptions;
if (config.SystemProxyItem.NotProxyLocalAddress) if (config.SystemProxyItem.NotProxyLocalAddress)
{ {
strExceptions = $"<local>;{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}"; strExceptions = $"<local>;{strExceptions}";
} }
strProxy = string.Empty; strProxy = string.Empty;
@@ -99,8 +82,16 @@ namespace ServiceLib.Handler.SysProxy
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
.Replace("{ip}", Global.Loopback) .Replace("{ip}", Global.Loopback)
.Replace("{http_port}", port.ToString()) .Replace("{http_port}", port.ToString())
.Replace("{socks_port}", portSocks.ToString()); .Replace("{socks_port}", port.ToString());
} }
} }
private static async Task SetWindowsProxyPac(int port)
{
var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac);
await PacHandler.Start(Utils.GetConfigPath(), port, portPac);
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
ProxySettingWindows.SetProxy(strProxy, "", 4);
}
} }
} }

View File

@@ -20,6 +20,7 @@
case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5: case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5:
case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo: case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo:
return true; return true;
default: default:
return false; return false;
} }

View File

@@ -24,21 +24,16 @@
public class InItem public class InItem
{ {
public int LocalPort { get; set; } public int LocalPort { get; set; }
public string Protocol { get; set; } public string Protocol { get; set; }
public bool UdpEnabled { get; set; } public bool UdpEnabled { get; set; }
public bool SniffingEnabled { get; set; } = true; public bool SniffingEnabled { get; set; } = true;
public List<string>? DestOverride { get; set; } = ["http", "tls"]; public List<string>? DestOverride { get; set; } = ["http", "tls"];
public bool RouteOnly { get; set; } public bool RouteOnly { get; set; }
public bool AllowLANConn { get; set; } public bool AllowLANConn { get; set; }
public bool NewPort4LAN { get; set; } public bool NewPort4LAN { get; set; }
public string User { get; set; } public string User { get; set; }
public string Pass { get; set; } public string Pass { get; set; }
public bool SecondLocalPortEnabled { get; set; }
} }
[Serializable] [Serializable]
@@ -80,7 +75,7 @@
public bool IgnoreGeoUpdateCore { get; set; } = true; public bool IgnoreGeoUpdateCore { get; set; } = true;
public int AutoUpdateInterval { get; set; } public int AutoUpdateInterval { get; set; }
public bool EnableSecurityProtocolTls13 { get; set; } public bool EnableSecurityProtocolTls13 { get; set; }
public int TrayMenuServersLimit { get; set; } = 20; public int TrayMenuServersLimit { get; set; } = 20;
@@ -116,6 +111,7 @@
public bool EnableDragDropSort { get; set; } public bool EnableDragDropSort { get; set; }
public bool DoubleClick2Activate { get; set; } public bool DoubleClick2Activate { get; set; }
public bool AutoHideStartup { get; set; } public bool AutoHideStartup { get; set; }
public bool Hide2TrayWhenClose { get; set; }
public List<ColumnItem> MainColumnItem { get; set; } public List<ColumnItem> MainColumnItem { get; set; }
public bool ShowInTaskbar { get; set; } public bool ShowInTaskbar { get; set; }
} }
@@ -161,7 +157,7 @@
public int Mtu { get; set; } public int Mtu { get; set; }
public bool EnableExInbound { get; set; } public bool EnableExInbound { get; set; }
public bool EnableIPv6Address { get; set; } public bool EnableIPv6Address { get; set; }
public string? LinuxSudoPassword { get; set; } public string? LinuxSudoPwd { get; set; }
} }
[Serializable] [Serializable]
@@ -170,6 +166,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; }
} }
[Serializable] [Serializable]
@@ -179,7 +176,6 @@
public string DomainStrategy4Singbox { get; set; } public string DomainStrategy4Singbox { get; set; }
public string DomainMatcher { get; set; } public string DomainMatcher { get; set; }
public string RoutingIndexId { get; set; } public string RoutingIndexId { get; set; }
public bool EnableRoutingAdvanced { get; set; }
} }
[Serializable] [Serializable]
@@ -222,7 +218,6 @@
public int ProxiesSorting { get; set; } public int ProxiesSorting { get; set; }
public bool ProxiesAutoRefresh { get; set; } public bool ProxiesAutoRefresh { get; set; }
public int ProxiesAutoDelayTestInterval { get; set; } = 10; public int ProxiesAutoDelayTestInterval { get; set; } = 10;
public int ConnectionsSorting { get; set; }
public bool ConnectionsAutoRefresh { get; set; } public bool ConnectionsAutoRefresh { get; set; }
public int ConnectionsRefreshInterval { get; set; } = 2; public int ConnectionsRefreshInterval { get; set; } = 2;
} }

View File

@@ -12,6 +12,8 @@
public string? DownloadUrlWinArm64 { get; set; } public string? DownloadUrlWinArm64 { get; set; }
public string? DownloadUrlLinux64 { get; set; } public string? DownloadUrlLinux64 { get; set; }
public string? DownloadUrlLinuxArm64 { get; set; } public string? DownloadUrlLinuxArm64 { get; set; }
public string? DownloadUrlOSX64 { get; set; }
public string? DownloadUrlOSXArm64 { get; set; }
public string? Match { get; set; } public string? Match { get; set; }
public string? VersionArg { get; set; } public string? VersionArg { get; set; }
public bool RedirectInfo { get; set; } public bool RedirectInfo { get; set; }

View File

@@ -0,0 +1,13 @@
namespace ServiceLib.Models
{
internal class IPAPIInfo
{
public string? ip { get; set; }
public string? city { get; set; }
public string? region { get; set; }
public string? region_code { get; set; }
public string? country { get; set; }
public string? country_name { get; set; }
public string? country_code { get; set; }
}
}

View File

@@ -324,7 +324,8 @@ namespace ServiceLib.Models
public class WsSettings4Ray public class WsSettings4Ray
{ {
public string path { get; set; } public string? path { get; set; }
public string? host { get; set; }
public Headers4Ray headers { get; set; } public Headers4Ray headers { get; set; }
} }
@@ -349,21 +350,9 @@ namespace ServiceLib.Models
public string? path { get; set; } public string? path { get; set; }
public string? host { get; set; } public string? host { get; set; }
public string? mode { get; set; } public string? mode { get; set; }
public string? scMaxEachPostBytes { get; set; }
public string? scMaxConcurrentPosts { get; set; }
public string? scMinPostsIntervalMs { get; set; }
public Xmux4Ray? xmux { get; set; }
public object? extra { get; set; } public object? extra { get; set; }
} }
public class Xmux4Ray
{
public int? maxConcurrency { get; set; }
public int? maxConnections { get; set; }
public int? cMaxReuseTimes { get; set; }
public int? cMaxLifetimeMs { get; set; }
}
public class HttpSettings4Ray public class HttpSettings4Ray
{ {
public string? path { get; set; } public string? path { get; set; }

View File

@@ -1365,24 +1365,6 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Basic Function 的本地化字符串。
/// </summary>
public static string menuRoutingBasic {
get {
return ResourceManager.GetString("menuRoutingBasic", resourceCulture);
}
}
/// <summary>
/// 查找类似 Import Basic Rules 的本地化字符串。
/// </summary>
public static string menuRoutingBasicImportRules {
get {
return ResourceManager.GetString("menuRoutingBasicImportRules", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 RoutingRuleDetailsSetting 的本地化字符串。 /// 查找类似 RoutingRuleDetailsSetting 的本地化字符串。
/// </summary> /// </summary>
@@ -2617,6 +2599,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。
/// </summary>
public static string TbRoutingInboundTagTips {
get {
return ResourceManager.GetString("TbRoutingInboundTagTips", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Domain 的本地化字符串。 /// 查找类似 Domain 的本地化字符串。
/// </summary> /// </summary>
@@ -2932,6 +2923,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Sniffing type 的本地化字符串。
/// </summary>
public static string TbSettingsDestOverride {
get {
return ResourceManager.GetString("TbSettingsDestOverride", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Outbound DNS address 的本地化字符串。 /// 查找类似 Outbound DNS address 的本地化字符串。
/// </summary> /// </summary>
@@ -3076,6 +3076,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Exception. Do not use proxy server for addresses,with a comma (,) 的本地化字符串。
/// </summary>
public static string TbSettingsExceptionTip2 {
get {
return ResourceManager.GetString("TbSettingsExceptionTip2", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Follow System Theme 的本地化字符串。 /// 查找类似 Follow System Theme 的本地化字符串。
/// </summary> /// </summary>
@@ -3103,6 +3112,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Hide to tray when closing the window 的本地化字符串。
/// </summary>
public static string TbSettingsHide2TrayWhenClose {
get {
return ResourceManager.GetString("TbSettingsHide2TrayWhenClose", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 HTTP Port 的本地化字符串。 /// 查找类似 HTTP Port 的本地化字符串。
/// </summary> /// </summary>
@@ -3149,7 +3167,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Linux system sudo password 的本地化字符串。 /// 查找类似 System sudo password 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsLinuxSudoPassword { public static string TbSettingsLinuxSudoPassword {
get { get {
@@ -3176,7 +3194,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 The password will only be stored in the local file. 的本地化字符串。 /// 查找类似 The password is encrypted and stored only in local files. 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsLinuxSudoPasswordTip { public static string TbSettingsLinuxSudoPasswordTip {
get { get {
@@ -3301,6 +3319,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Enable second mixed port 的本地化字符串。
/// </summary>
public static string TbSettingsSecondLocalPortEnabled {
get {
return ResourceManager.GetString("TbSettingsSecondLocalPortEnabled", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Set Win10 UWP Loopback 的本地化字符串。 /// 查找类似 Set Win10 UWP Loopback 的本地化字符串。
/// </summary> /// </summary>
@@ -3320,7 +3347,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 SOCKS Port 的本地化字符串。 /// 查找类似 Mixed Port 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsSocksPort { public static string TbSettingsSocksPort {
get { get {
@@ -3329,7 +3356,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6; 的本地化字符串。 /// 查找类似 Pac port = +3; Xray API port = +4; mihomo API port = +5; 的本地化字符串。
/// </summary> /// </summary>
public static string TbSettingsSocksPortTip { public static string TbSettingsSocksPortTip {
get { get {
@@ -3346,6 +3373,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Number per time for auto batch during speedtest(max 1000) 的本地化字符串。
/// </summary>
public static string TbSettingsSpeedTestPageSize {
get {
return ResourceManager.GetString("TbSettingsSpeedTestPageSize", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。 /// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。
/// </summary> /// </summary>
@@ -3635,7 +3671,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 The ping of current service: {0} ms 的本地化字符串。 /// 查找类似 The delay : {0} ms, {1} 的本地化字符串。
/// </summary> /// </summary>
public static string TestMeOutput { public static string TestMeOutput {
get { get {

View File

@@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="BatchExportURLSuccessfully" xml:space="preserve"> <data name="BatchExportURLSuccessfully" xml:space="preserve">
<value>Batch export share URL to clipboard successfully</value> <value>ارسال دسته ای آدرس اینترنتی اشتراک گذاری به کلیپ بورد با موفقیت.</value>
</data> </data>
<data name="CheckServerSettings" xml:space="preserve"> <data name="CheckServerSettings" xml:space="preserve">
<value>لطفا ابتدا تنظیمات سرور را بررسی کنید</value> <value>لطفا ابتدا تنظیمات سرور را بررسی کنید</value>
@@ -136,7 +136,7 @@
<value>دانلود</value> <value>دانلود</value>
</data> </data>
<data name="DownloadYesNo" xml:space="preserve"> <data name="DownloadYesNo" xml:space="preserve">
<value>Whether to download? {0}</value> <value>آیا برای دانلود؟ {0}</value>
</data> </data>
<data name="FailedConversionConfiguration" xml:space="preserve"> <data name="FailedConversionConfiguration" xml:space="preserve">
<value>تبدیل فایل پیکربندی انجام نشد</value> <value>تبدیل فایل پیکربندی انجام نشد</value>
@@ -226,7 +226,7 @@
<value>هیچ اشتراک معتبری تنظیم نشده است</value> <value>هیچ اشتراک معتبری تنظیم نشده است</value>
</data> </data>
<data name="MsgParsingSuccessfully" xml:space="preserve"> <data name="MsgParsingSuccessfully" xml:space="preserve">
<value>Resolve {0} successfully</value> <value>حل {0} با موفقیت</value>
</data> </data>
<data name="MsgStartGettingSubscriptions" xml:space="preserve"> <data name="MsgStartGettingSubscriptions" xml:space="preserve">
<value>شروع به دریافت اشتراک شد</value> <value>شروع به دریافت اشتراک شد</value>
@@ -253,19 +253,19 @@
<value>هسته با موفقیت بروزرسانی شد! راه اندازی مجدد سرویس...</value> <value>هسته با موفقیت بروزرسانی شد! راه اندازی مجدد سرویس...</value>
</data> </data>
<data name="NonvmessOrssProtocol" xml:space="preserve"> <data name="NonvmessOrssProtocol" xml:space="preserve">
<value>Non-VMess or ss protocol</value> <value>پروتکل غیر VMess یا ss</value>
</data> </data>
<data name="NotFoundCore" xml:space="preserve"> <data name="NotFoundCore" xml:space="preserve">
<value>The Core file (file name: {1}) was not found under the folder ({0}), please download and put it in the folder, download address: {2}</value> <value>فایل Core (نام فایل: {1}) در زیر پوشه ({0}) یافت نشد، لطفاً آن را دانلود کرده و در پوشه قرار دهید، آدرس دانلود: {2}</value>
</data> </data>
<data name="NoValidQRcodeFound" xml:space="preserve"> <data name="NoValidQRcodeFound" xml:space="preserve">
<value>Scan completed, no valid QR code found</value> <value>اسکن کامل شد، QRcode معتبری یافت نشد</value>
</data> </data>
<data name="OperationFailed" xml:space="preserve"> <data name="OperationFailed" xml:space="preserve">
<value> عملیات انجام نشد، لطفا بررسی کنید و دوباره امتحان کنید</value> <value> عملیات انجام نشد، لطفا بررسی کنید و دوباره امتحان کنید</value>
</data> </data>
<data name="PleaseFillRemarks" xml:space="preserve"> <data name="PleaseFillRemarks" xml:space="preserve">
<value>Please Fill Remarks</value> <value>لطفا ملاحظات را پر کنید</value>
</data> </data>
<data name="PleaseSelectEncryption" xml:space="preserve"> <data name="PleaseSelectEncryption" xml:space="preserve">
<value>لطفاً روش رمزگذاری را انتخاب کنید</value> <value>لطفاً روش رمزگذاری را انتخاب کنید</value>
@@ -277,16 +277,16 @@
<value>لطفا ابتدا سرور را انتخاب کنید</value> <value>لطفا ابتدا سرور را انتخاب کنید</value>
</data> </data>
<data name="RemoveDuplicateServerResult" xml:space="preserve"> <data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>Servers deduplication completed. Old: {0}, New: {1}.</value> <value>حذف مجدد سرورها تکمیل شد. قدیمی: {0}، جدید: {1}.</value>
</data> </data>
<data name="RemoveServer" xml:space="preserve"> <data name="RemoveServer" xml:space="preserve">
<value>آیا مطمئن هستید که سرور را حذف می کنید؟</value> <value>آیا مطمئن هستید که سرور را حذف می کنید؟</value>
</data> </data>
<data name="SaveClientConfigurationIn" xml:space="preserve"> <data name="SaveClientConfigurationIn" xml:space="preserve">
<value>The client configuration file is saved at: {0}</value> <value>فایل پیکربندی کلاینت در این آدرس ذخیره می شود: {0}</value>
</data> </data>
<data name="StartService" xml:space="preserve"> <data name="StartService" xml:space="preserve">
<value>Start service ({0})...</value> <value>شروع سرویس ({0})...</value>
</data> </data>
<data name="SuccessfulConfiguration" xml:space="preserve"> <data name="SuccessfulConfiguration" xml:space="preserve">
<value>پیکربندی با موفقیت انجام شد <value>پیکربندی با موفقیت انجام شد
@@ -302,7 +302,7 @@
<value>اسکن URL وارد کردن با موفقیت</value> <value>اسکن URL وارد کردن با موفقیت</value>
</data> </data>
<data name="TestMeOutput" xml:space="preserve"> <data name="TestMeOutput" xml:space="preserve">
<value>پینگ سرویس فعلی: {0} ms</value> <value>پینگ سرویس فعلی: {0} ms, {1}</value>
</data> </data>
<data name="OperationSuccess" xml:space="preserve"> <data name="OperationSuccess" xml:space="preserve">
<value>موفقیت عملیات</value> <value>موفقیت عملیات</value>
@@ -317,19 +317,19 @@
<value>{0},یکی از مورد نیاز.</value> <value>{0},یکی از مورد نیاز.</value>
</data> </data>
<data name="LvRemarks" xml:space="preserve"> <data name="LvRemarks" xml:space="preserve">
<value>Remarks</value> <value>ملاحظات</value>
</data> </data>
<data name="LvUrl" xml:space="preserve"> <data name="LvUrl" xml:space="preserve">
<value>Url(Optional)</value> <value>آدرس اینترنتی (اختیاری)</value>
</data> </data>
<data name="LvCount" xml:space="preserve"> <data name="LvCount" xml:space="preserve">
<value>Count</value> <value>شمارش</value>
</data> </data>
<data name="MsgNeedUrl" xml:space="preserve"> <data name="MsgNeedUrl" xml:space="preserve">
<value>Please fill in the address (Url)</value> <value>لطفا آدرس (آدرس اینترنتی) را وارد کنید</value>
</data> </data>
<data name="AddBatchRoutingRulesYesNo" xml:space="preserve"> <data name="AddBatchRoutingRulesYesNo" xml:space="preserve">
<value>Do you want to append rules? Choose yes to append, choose otherwise to replace</value> <value>آیا می خواهید قوانین را اضافه کنید؟ برای ضمیمه بله را انتخاب کنید، در غیر این صورت جایگزین کنید</value>
</data> </data>
<data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve"> <data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve">
<value>دانلود GeoFile: {0} با موفقیت</value> <value>دانلود GeoFile: {0} با موفقیت</value>
@@ -386,10 +386,10 @@
<value>*Kcp seed</value> <value>*Kcp seed</value>
</data> </data>
<data name="RegisterGlobalHotkeyFailed" xml:space="preserve"> <data name="RegisterGlobalHotkeyFailed" xml:space="preserve">
<value>Global hotkey {0} registered failed, reason {1}</value> <value>کلید میانبر جهانی {0} ثبت نشد، دلیل کلید میانبر جهانی {0} ثبت ناموفق بود، دلیل {1}{1}</value>
</data> </data>
<data name="RegisterGlobalHotkeySuccessfully" xml:space="preserve"> <data name="RegisterGlobalHotkeySuccessfully" xml:space="preserve">
<value>Global hotkey {0} registered successfully</value> <value>کلید میانبر جهانی {0} با موفقیت ثبت شد</value>
</data> </data>
<data name="UngroupedServers" xml:space="preserve"> <data name="UngroupedServers" xml:space="preserve">
<value>گروه بندی نشده</value> <value>گروه بندی نشده</value>
@@ -398,7 +398,7 @@
<value>همه سرورها</value> <value>همه سرورها</value>
</data> </data>
<data name="FillServerAddressCustom" xml:space="preserve"> <data name="FillServerAddressCustom" xml:space="preserve">
<value>Please browse to import server configuration</value> <value>لطفاً برای وارد کردن پیکربندی سرور مرور کنید</value>
</data> </data>
<data name="Speedtesting" xml:space="preserve"> <data name="Speedtesting" xml:space="preserve">
<value>درحال تست کردن...</value> <value>درحال تست کردن...</value>
@@ -593,7 +593,7 @@
<value>آدرس</value> <value>آدرس</value>
</data> </data>
<data name="TbAllowInsecure" xml:space="preserve"> <data name="TbAllowInsecure" xml:space="preserve">
<value>اجازه ناامن</value> <value>اعطای مجوز ناامن</value>
</data> </data>
<data name="TbAlpn" xml:space="preserve"> <data name="TbAlpn" xml:space="preserve">
<value>Alpn</value> <value>Alpn</value>
@@ -602,10 +602,10 @@
<value>AlterId</value> <value>AlterId</value>
</data> </data>
<data name="TbFingerprint" xml:space="preserve"> <data name="TbFingerprint" xml:space="preserve">
<value>Fingerprint</value> <value>اثرانگشت</value>
</data> </data>
<data name="TbHeaderType" xml:space="preserve"> <data name="TbHeaderType" xml:space="preserve">
<value>Camouflage type</value> <value>نوع Camouflage</value>
</data> </data>
<data name="TbId" xml:space="preserve"> <data name="TbId" xml:space="preserve">
<value>UUID(id)</value> <value>UUID(id)</value>
@@ -620,13 +620,13 @@
<value>پورت</value> <value>پورت</value>
</data> </data>
<data name="TbRemarks" xml:space="preserve"> <data name="TbRemarks" xml:space="preserve">
<value>Alias (remarks)</value> <value>نام مستعار (ملاحظات)</value>
</data> </data>
<data name="TbRequestHost" xml:space="preserve"> <data name="TbRequestHost" xml:space="preserve">
<value>Camouflage domain(host)</value> <value>Camouflage domain(host)</value>
</data> </data>
<data name="TbSecurity" xml:space="preserve"> <data name="TbSecurity" xml:space="preserve">
<value>Encryption method (security)</value> <value>روش رمزگذاری (امنیتی)</value>
</data> </data>
<data name="TbSNI" xml:space="preserve"> <data name="TbSNI" xml:space="preserve">
<value>SNI</value> <value>SNI</value>
@@ -635,7 +635,7 @@
<value>TLS</value> <value>TLS</value>
</data> </data>
<data name="TipNetwork" xml:space="preserve"> <data name="TipNetwork" xml:space="preserve">
<value>*Default value tcp</value> <value>*مقدار پیش فرض tcp</value>
</data> </data>
<data name="TbCoreType" xml:space="preserve"> <data name="TbCoreType" xml:space="preserve">
<value>نوع هسته</value> <value>نوع هسته</value>
@@ -644,7 +644,7 @@
<value>جریان</value> <value>جریان</value>
</data> </data>
<data name="TbGUID" xml:space="preserve"> <data name="TbGUID" xml:space="preserve">
<value>Generate</value> <value>ساختن</value>
</data> </data>
<data name="TbId3" xml:space="preserve"> <data name="TbId3" xml:space="preserve">
<value>رمزعبور</value> <value>رمزعبور</value>
@@ -668,10 +668,10 @@
<value>txtPreSocksPort</value> <value>txtPreSocksPort</value>
</data> </data>
<data name="TipPreSocksPort" xml:space="preserve"> <data name="TipPreSocksPort" xml:space="preserve">
<value>* After setting this value, an socks service will be started using Xray/sing-box(Tun) to provide functions such as speed display</value> <value>* پس از تنظیم این مقدار، یک سرویس جوراب با استفاده از Xray/sing-box (Tun) برای ارائه عملکردهایی مانند نمایش سرعت شروع می شود.</value>
</data> </data>
<data name="TbBrowse" xml:space="preserve"> <data name="TbBrowse" xml:space="preserve">
<value>Browse</value> <value>مرور کردن</value>
</data> </data>
<data name="TbEdit" xml:space="preserve"> <data name="TbEdit" xml:space="preserve">
<value>ویرایش</value> <value>ویرایش</value>
@@ -683,7 +683,7 @@
<value>اتصالات از LAN را مجاز کنید</value> <value>اتصالات از LAN را مجاز کنید</value>
</data> </data>
<data name="TbSettingsAutoHideStartup" xml:space="preserve"> <data name="TbSettingsAutoHideStartup" xml:space="preserve">
<value>Auto hide startup</value> <value>راه اندازی مخفی کردن خودکار</value>
</data> </data>
<data name="TbSettingsAutoUpdateInterval" xml:space="preserve"> <data name="TbSettingsAutoUpdateInterval" xml:space="preserve">
<value>فاصله به روز رسانی خودکار و Geo (ساعت)</value> <value>فاصله به روز رسانی خودکار و Geo (ساعت)</value>
@@ -692,7 +692,7 @@
<value>هسته: تنظیمات اولیه</value> <value>هسته: تنظیمات اولیه</value>
</data> </data>
<data name="TbSettingsCoreDns" xml:space="preserve"> <data name="TbSettingsCoreDns" xml:space="preserve">
<value>V2ray DNS settings</value> <value>تنظیمات V2ray DNS</value>
</data> </data>
<data name="TbSettingsCoreKcp" xml:space="preserve"> <data name="TbSettingsCoreKcp" xml:space="preserve">
<value>هسته: تنظیمات KCP</value> <value>هسته: تنظیمات KCP</value>
@@ -701,7 +701,7 @@
<value>تنظیمات CoreType</value> <value>تنظیمات CoreType</value>
</data> </data>
<data name="TbSettingsDefAllowInsecure" xml:space="preserve"> <data name="TbSettingsDefAllowInsecure" xml:space="preserve">
<value>اجازه ناامن</value> <value>اعطای مجوز ناامن</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Freedom" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Freedom" xml:space="preserve">
<value>Outbound Freedom domainStrategy</value> <value>Outbound Freedom domainStrategy</value>
@@ -716,7 +716,7 @@
<value>استثنا</value> <value>استثنا</value>
</data> </data>
<data name="TbSettingsExceptionTip" xml:space="preserve"> <data name="TbSettingsExceptionTip" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses beginning with,Use semicolon (;)</value> <value>استثنا: از سرور پروکسی برای آدرس هایی که با شروع می شوند استفاده نکنید، از نقطه ویرگول (;) استفاده کنید.</value>
</data> </data>
<data name="TbSettingsHttpPort" xml:space="preserve"> <data name="TbSettingsHttpPort" xml:space="preserve">
<value>پورت Http</value> <value>پورت Http</value>
@@ -725,7 +725,7 @@
<value>هنگام به‌روزرسانی هسته، فایل‌های Geo را نادیده بگیرید</value> <value>هنگام به‌روزرسانی هسته، فایل‌های Geo را نادیده بگیرید</value>
</data> </data>
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve"> <data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
<value>Keep older when deduplication</value> <value>هنگام کپی برداری، نگه داری قدیمی تر ها</value>
</data> </data>
<data name="TbSettingsLogEnabled" xml:space="preserve"> <data name="TbSettingsLogEnabled" xml:space="preserve">
<value>ثبت گزارش های محلی</value> <value>ثبت گزارش های محلی</value>
@@ -734,25 +734,25 @@
<value>سطح ثبت رویداد</value> <value>سطح ثبت رویداد</value>
</data> </data>
<data name="TbSettingsMuxEnabled" xml:space="preserve"> <data name="TbSettingsMuxEnabled" xml:space="preserve">
<value>Turn on Mux Multiplexing</value> <value>فعال کردن Mux Multiplexing</value>
</data> </data>
<data name="TbSettingsN" xml:space="preserve"> <data name="TbSettingsN" xml:space="preserve">
<value>تنظیمات v2rayN</value> <value>تنظیمات v2rayN</value>
</data> </data>
<data name="TbSettingsPass" xml:space="preserve"> <data name="TbSettingsPass" xml:space="preserve">
<value>Auth pass</value> <value>مجوز احراز هویت</value>
</data> </data>
<data name="TbSettingsRemoteDNS" xml:space="preserve"> <data name="TbSettingsRemoteDNS" xml:space="preserve">
<value>سفارشی DNS (multiple, separated by commas (,))</value> <value>سفارشی DNS (multiple, separated by commas (,))</value>
</data> </data>
<data name="TbSettingsSetUWP" xml:space="preserve"> <data name="TbSettingsSetUWP" xml:space="preserve">
<value>Set Win10 UWP Loopback</value> <value>تنظیم کردن Win10 UWP Loopback</value>
</data> </data>
<data name="TbSettingsSniffingEnabled" xml:space="preserve"> <data name="TbSettingsSniffingEnabled" xml:space="preserve">
<value>Turn on Sniffing</value> <value>فعال کردن Sniffing</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>ساکس Port</value> <value>پورت Mixed</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>درهنگام راه ائدازی شروع شود</value> <value>درهنگام راه ائدازی شروع شود</value>
@@ -761,22 +761,22 @@
<value>فعال کردن آمار (نیاز به راه اندازی مجدد)</value> <value>فعال کردن آمار (نیاز به راه اندازی مجدد)</value>
</data> </data>
<data name="TbSettingsSubConvert" xml:space="preserve"> <data name="TbSettingsSubConvert" xml:space="preserve">
<value>Subscription conversion Url</value> <value>آدرس اینترنتی تبدیل اشتراک</value>
</data> </data>
<data name="TbSettingsSystemproxy" xml:space="preserve"> <data name="TbSettingsSystemproxy" xml:space="preserve">
<value>تنظیمات پراکسی سیستم</value> <value>تنظیمات پراکسی سیستم</value>
</data> </data>
<data name="TbSettingsTLS13" xml:space="preserve"> <data name="TbSettingsTLS13" xml:space="preserve">
<value>Enable Security Protocol TLS v1.3 (subscription/update)</value> <value>فعال کردن پروتکل امنیتی TLS نسخه 1.3 (اشتراک/به‌روزرسانی)</value>
</data> </data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve"> <data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Tray right-click menu servers display limit</value> <value>محدودیت نمایش سرورهای منوی سینی کلیک راست</value>
</data> </data>
<data name="TbSettingsUdpEnabled" xml:space="preserve"> <data name="TbSettingsUdpEnabled" xml:space="preserve">
<value>UDP را فعال شود</value> <value>فعال سازی UDP</value>
</data> </data>
<data name="TbSettingsUser" xml:space="preserve"> <data name="TbSettingsUser" xml:space="preserve">
<value>Auth user</value> <value>تایید کاربر</value>
</data> </data>
<data name="TbClearSystemProxy" xml:space="preserve"> <data name="TbClearSystemProxy" xml:space="preserve">
<value>پاک کردن پروکسی سیستم</value> <value>پاک کردن پروکسی سیستم</value>
@@ -821,16 +821,16 @@
<value>پایین (D)</value> <value>پایین (D)</value>
</data> </data>
<data name="menuMoveTop" xml:space="preserve"> <data name="menuMoveTop" xml:space="preserve">
<value>Move to top (T)</value> <value>حرکت به بالا (T)</value>
</data> </data>
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>Up (U)</value> <value>بالا (U)</value>
</data> </data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>Filter, support regular</value> <value>فیلتر، پشتیبانی منظم</value>
</data> </data>
<data name="menuWebsiteItem" xml:space="preserve"> <data name="menuWebsiteItem" xml:space="preserve">
<value>{0} Website</value> <value>{0} وب سایت</value>
</data> </data>
<data name="menuRoutingAdvanced" xml:space="preserve"> <data name="menuRoutingAdvanced" xml:space="preserve">
<value>عملکرد پیشرفته</value> <value>عملکرد پیشرفته</value>
@@ -845,13 +845,7 @@
<value>حذف انتخاب شده</value> <value>حذف انتخاب شده</value>
</data> </data>
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>Set as active rule</value> <value>تنظیم کردن به عنوان قانون فعال</value>
</data>
<data name="menuRoutingBasic" xml:space="preserve">
<value>عملکرد پایه</value>
</data>
<data name="menuRoutingBasicImportRules" xml:space="preserve">
<value>واردات قوانین اساسی</value>
</data> </data>
<data name="TbdomainMatcher" xml:space="preserve"> <data name="TbdomainMatcher" xml:space="preserve">
<value>تطبیق دامنه</value> <value>تطبیق دامنه</value>
@@ -863,19 +857,19 @@
<value>فعال کردن عملکرد پیشرفته</value> <value>فعال کردن عملکرد پیشرفته</value>
</data> </data>
<data name="TbRoutingTabBlock" xml:space="preserve"> <data name="TbRoutingTabBlock" xml:space="preserve">
<value>3.Block Domain or IP</value> <value>3. مسدود کردن دامنه یا آیپی</value>
</data> </data>
<data name="TbRoutingTabDirect" xml:space="preserve"> <data name="TbRoutingTabDirect" xml:space="preserve">
<value>2.Direct Domain or IP</value> <value>2. دایرکت کردن دامنه یا IP</value>
</data> </data>
<data name="TbRoutingTabProxy" xml:space="preserve"> <data name="TbRoutingTabProxy" xml:space="preserve">
<value>1.Proxy Domain or IP</value> <value>1. پروکسی کردن دامنه یا IP</value>
</data> </data>
<data name="TbRoutingTabRuleList" xml:space="preserve"> <data name="TbRoutingTabRuleList" xml:space="preserve">
<value>لیست مجموعه قوانین از پیش تعریف شده</value> <value>لیست مجموعه قوانین از پیش تعریف شده</value>
</data> </data>
<data name="TbRoutingTips" xml:space="preserve"> <data name="TbRoutingTips" xml:space="preserve">
<value>*Set the rules, separated by commas (,); The comma in the regular is replaced by &lt;COMMA&gt;</value> <value>*قوانین را تنظیم کنید که با کاما از هم جدا شده اند (,); کاما در حالت عادی با &lt;COMMA&gt;</value>
</data> </data>
<data name="menuImportRulesFromClipboard" xml:space="preserve"> <data name="menuImportRulesFromClipboard" xml:space="preserve">
<value>وارد کردن قوانین از کلیپ بورد</value> <value>وارد کردن قوانین از کلیپ بورد</value>
@@ -884,7 +878,7 @@
<value>وارد کردن قوانین از فایل</value> <value>وارد کردن قوانین از فایل</value>
</data> </data>
<data name="menuImportRulesFromUrl" xml:space="preserve"> <data name="menuImportRulesFromUrl" xml:space="preserve">
<value>وارد کردن قوانین از Sub Url</value> <value>وارد کردن قوانین از آدرس اینترنتی Sub</value>
</data> </data>
<data name="menuRoutingRuleSetting" xml:space="preserve"> <data name="menuRoutingRuleSetting" xml:space="preserve">
<value>تنظیم قانون</value> <value>تنظیم قانون</value>
@@ -908,7 +902,7 @@
<value>دامنه و آی پی در هنگام ذخیره به طور خودکار مرتب می شوند</value> <value>دامنه و آی پی در هنگام ذخیره به طور خودکار مرتب می شوند</value>
</data> </data>
<data name="TbRuleobjectDoc" xml:space="preserve"> <data name="TbRuleobjectDoc" xml:space="preserve">
<value>Ruleobject Doc</value> <value>مستندات شی قانون</value>
</data> </data>
<data name="TbDnsObjectDoc" xml:space="preserve"> <data name="TbDnsObjectDoc" xml:space="preserve">
<value>پشتیبانی از DnsObject</value> <value>پشتیبانی از DnsObject</value>
@@ -926,16 +920,16 @@
<value>فقط مسیر</value> <value>فقط مسیر</value>
</data> </data>
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve"> <data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
<value>يەرلىك (Intranet) ئادرېسلارغا ۋاكالەتچى مۇلازىمېتىر ئىشلەتمەڭ</value> <value>از سرورهای پروکسی برای آدرس های محلی (اینترانت) استفاده نکنید</value>
</data> </data>
<data name="menuMixedTestServer" xml:space="preserve"> <data name="menuMixedTestServer" xml:space="preserve">
<value>One-click test Latency and speed (Ctrl+E)</value> <value>تاخیر و سرعت تست با یک کلیک (Ctrl+E)</value>
</data> </data>
<data name="LvTestDelay" xml:space="preserve"> <data name="LvTestDelay" xml:space="preserve">
<value>تاخیر (میلی‌ثانیه)</value> <value>تاخیر (میلی‌ثانیه)</value>
</data> </data>
<data name="LvTestSpeed" xml:space="preserve"> <data name="LvTestSpeed" xml:space="preserve">
<value>Speed(M/s)</value> <value>سرعت (M/s)</value>
</data> </data>
<data name="FailedToRunCore" xml:space="preserve"> <data name="FailedToRunCore" xml:space="preserve">
<value>Core اجرا نشد، لطفاً گزارش را ببینید</value> <value>Core اجرا نشد، لطفاً گزارش را ببینید</value>
@@ -950,7 +944,7 @@
<value>پیکربندی قدیمی guiNConfig را وارد شود</value> <value>پیکربندی قدیمی guiNConfig را وارد شود</value>
</data> </data>
<data name="TbEnableTunAs" xml:space="preserve"> <data name="TbEnableTunAs" xml:space="preserve">
<value>Tun را فعال شود</value> <value>فعال سازی Tun</value>
</data> </data>
<data name="TbSettingsNewPort4LAN" xml:space="preserve"> <data name="TbSettingsNewPort4LAN" xml:space="preserve">
<value>پورت جدید برای LAN</value> <value>پورت جدید برای LAN</value>
@@ -962,10 +956,10 @@
<value>User-Agent</value> <value>User-Agent</value>
</data> </data>
<data name="TbSettingsDefUserAgentTips" xml:space="preserve"> <data name="TbSettingsDefUserAgentTips" xml:space="preserve">
<value>This parameter is valid only for tcp/http and ws</value> <value>این پارامتر فقط برای tcp/http و ws معتبر است</value>
</data> </data>
<data name="TbSettingsEnableHWA" xml:space="preserve"> <data name="TbSettingsEnableHWA" xml:space="preserve">
<value>فعال‌سازی شتاب‌دهنده سخت‌افزاری (نیاز به راه‌اندازی مجدد)</value> <value>فعال‌ سازی شتاب‌ دهنده سخت‌افزاری (نیاز به راه‌اندازی مجدد)</value>
</data> </data>
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve"> <data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
<value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value> <value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value>
@@ -974,7 +968,7 @@
<value>مرتب سازی</value> <value>مرتب سازی</value>
</data> </data>
<data name="TbSortingDefault" xml:space="preserve"> <data name="TbSortingDefault" xml:space="preserve">
<value>Default</value> <value>پیش فرض</value>
</data> </data>
<data name="TbSortingDelay" xml:space="preserve"> <data name="TbSortingDelay" xml:space="preserve">
<value>تاخیر</value> <value>تاخیر</value>
@@ -1013,10 +1007,10 @@
<value>نوع قانون</value> <value>نوع قانون</value>
</data> </data>
<data name="menuModeDirect" xml:space="preserve"> <data name="menuModeDirect" xml:space="preserve">
<value>Direct</value> <value>مستقیم</value>
</data> </data>
<data name="menuModeGlobal" xml:space="preserve"> <data name="menuModeGlobal" xml:space="preserve">
<value>Global</value> <value>جهانی</value>
</data> </data>
<data name="menuModeNothing" xml:space="preserve"> <data name="menuModeNothing" xml:space="preserve">
<value>تغییر نده</value> <value>تغییر نده</value>
@@ -1025,193 +1019,193 @@
<value>قانون</value> <value>قانون</value>
</data> </data>
<data name="menuProxiesDelaytest" xml:space="preserve"> <data name="menuProxiesDelaytest" xml:space="preserve">
<value>Latency Test</value> <value>تست تأخیر</value>
</data> </data>
<data name="menuProxiesDelaytestPart" xml:space="preserve"> <data name="menuProxiesDelaytestPart" xml:space="preserve">
<value>Part Node Latency Test</value> <value>تست تاخیر قسمت گره (نقطه اتصال)</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>Select active node (Enter)</value> <value>انتخاب گره فعال (Enter)</value>
</data> </data>
<data name="menuRemoteBackup" xml:space="preserve"> <data name="menuRemoteBackup" xml:space="preserve">
<value>Backup to remote (WebDAV)</value> <value>پشتیبان گیری از راه دور (WebDAV)</value>
</data> </data>
<data name="menuRemoteRestore" xml:space="preserve"> <data name="menuRemoteRestore" xml:space="preserve">
<value>Restore from remote (WebDAV)</value> <value>بازیابی از راه دور (WebDAV)</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Default domain strategy for outbound</value> <value>استراتژی دامنه پیش فرض برای خروجی</value>
</data> </data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve"> <data name="menuSetDefaultMultipleServer" xml:space="preserve">
<value>Multi-Server lowest latency</value> <value>کمترین تأخیر چند سروره</value>
</data> </data>
<data name="TbSettingsMainGirdOrientation" xml:space="preserve"> <data name="TbSettingsMainGirdOrientation" xml:space="preserve">
<value>Main layout orientation(Require restart)</value> <value>جهت چیدمان اصلی (نیاز به راه اندازی مجدد)</value>
</data> </data>
<data name="menuSetDefaultLoadBalanceServer" xml:space="preserve"> <data name="menuSetDefaultLoadBalanceServer" xml:space="preserve">
<value>Multi-server load balancing</value> <value>تعادل بار چند سروره</value>
</data> </data>
<data name="TbSettingsDomainDNSAddress" xml:space="preserve"> <data name="TbSettingsDomainDNSAddress" xml:space="preserve">
<value>Outbound DNS address</value> <value>آدرس DNS خروجی</value>
</data> </data>
<data name="menuProfileAutofitColumnWidth" xml:space="preserve"> <data name="menuProfileAutofitColumnWidth" xml:space="preserve">
<value>Auto column width adjustment</value> <value>تنظیم خودکار عرض ستون</value>
</data> </data>
<data name="menuExport2ShareUrlBase64" xml:space="preserve"> <data name="menuExport2ShareUrlBase64" xml:space="preserve">
<value>Export Base64-encoded Share Links to Clipboard</value> <value>صادر کردن پیوندهای اشتراک گذاری کدگذاری شده با Base64 به کلیپ بورد</value>
</data> </data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve"> <data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>Export selected server for complete configuration to clipboard</value> <value>صادر کردن سرور انتخاب شده برای پیکربندی کامل به کلیپ بورد</value>
</data> </data>
<data name="menuShowOrHideMainWindow" xml:space="preserve"> <data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>Show or hide the main window</value> <value>نمایش یا پنهان کردن پنجره اصلی</value>
</data> </data>
<data name="UpdateStandalonePackageTip" xml:space="preserve"> <data name="UpdateStandalonePackageTip" xml:space="preserve">
<value>You are currently running a standalone package, please manually download the SelfContained.7z file to unzip and overwrite it!</value> <value>شما در حال حاضر در حال اجرای یک بسته مستقل هستید، لطفاً فایل SelfContained.7z را به صورت دستی دانلود کنید تا آن را از حالت فشرده خارج کرده و بازنویسی کنید!</value>
</data> </data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>Custom config socks port</value> <value>پیکربندی سفارشی ساکس پورت</value>
</data> </data>
<data name="menuBackupAndRestore" xml:space="preserve"> <data name="menuBackupAndRestore" xml:space="preserve">
<value>Backup and Restore</value> <value>پشتیبان گیری و بازیابی</value>
</data> </data>
<data name="menuLocalBackup" xml:space="preserve"> <data name="menuLocalBackup" xml:space="preserve">
<value>Backup to local</value> <value>پشتیبان گیری به محلی</value>
</data> </data>
<data name="menuLocalRestore" xml:space="preserve"> <data name="menuLocalRestore" xml:space="preserve">
<value>Restore from local</value> <value>بازیابی از محلی</value>
</data> </data>
<data name="menuLocalBackupAndRestore" xml:space="preserve"> <data name="menuLocalBackupAndRestore" xml:space="preserve">
<value>Local</value> <value>محلی</value>
</data> </data>
<data name="menuRemoteBackupAndRestore" xml:space="preserve"> <data name="menuRemoteBackupAndRestore" xml:space="preserve">
<value>Remote (WebDAV)</value> <value>از راه دور (WebDAV)</value>
</data> </data>
<data name="LvWebDavUrl" xml:space="preserve"> <data name="LvWebDavUrl" xml:space="preserve">
<value>WebDav Url</value> <value>آدرس اینترنتی WebDav</value>
</data> </data>
<data name="LvWebDavUserName" xml:space="preserve"> <data name="LvWebDavUserName" xml:space="preserve">
<value>WebDav User Name</value> <value>نام کاربری WebDav</value>
</data> </data>
<data name="LvWebDavPassword" xml:space="preserve"> <data name="LvWebDavPassword" xml:space="preserve">
<value>WebDav Password</value> <value>پسورد WebDav</value>
</data> </data>
<data name="LvWebDavCheck" xml:space="preserve"> <data name="LvWebDavCheck" xml:space="preserve">
<value>WebDav Check</value> <value>چک کردن WebDav</value>
</data> </data>
<data name="LvWebDavDirName" xml:space="preserve"> <data name="LvWebDavDirName" xml:space="preserve">
<value>Remote folder name (optional)</value> <value>نام پوشه راه دور (اختیاری)</value>
</data> </data>
<data name="LocalRestoreInvalidZipTips" xml:space="preserve"> <data name="LocalRestoreInvalidZipTips" xml:space="preserve">
<value>Invalid backup file</value> <value>فایل پشتیبان نامعتبر است</value>
</data> </data>
<data name="ConnectionsHostFilterTitle" xml:space="preserve"> <data name="ConnectionsHostFilterTitle" xml:space="preserve">
<value>Host filter</value> <value>Host filter</value>
</data> </data>
<data name="TipActiveServer" xml:space="preserve"> <data name="TipActiveServer" xml:space="preserve">
<value>Active</value> <value>فعال سازی</value>
</data> </data>
<data name="menuStorageUI" xml:space="preserve"> <data name="menuStorageUI" xml:space="preserve">
<value>Save Interface Layout</value> <value>ذخیره طرح رابط</value>
</data> </data>
<data name="TbSettingsGeoFilesSource" xml:space="preserve"> <data name="TbSettingsGeoFilesSource" xml:space="preserve">
<value>Geo files source (optional)</value> <value>منبع فایل های جغرافیایی (اختیاری)</value>
</data> </data>
<data name="TbSettingsSrsFilesSource" xml:space="preserve"> <data name="TbSettingsSrsFilesSource" xml:space="preserve">
<value>sing-box ruleset files source (optional)</value> <value>منبع فایل های مجموعه قوانین sing-box (اختیاری)</value>
</data> </data>
<data name="UpgradeAppNotExistTip" xml:space="preserve"> <data name="UpgradeAppNotExistTip" xml:space="preserve">
<value>UpgradeApp does not exist</value> <value>برنامه ارتقا وجود ندارد</value>
</data> </data>
<data name="TbSettingsRoutingRulesSource" xml:space="preserve"> <data name="TbSettingsRoutingRulesSource" xml:space="preserve">
<value>Routing rules source (optional)</value> <value>منبع قوانین مسیریابی (اختیاری)</value>
</data> </data>
<data name="menuRegionalPresets" xml:space="preserve"> <data name="menuRegionalPresets" xml:space="preserve">
<value>Regional presets setting</value> <value>تنظیمات از پیش تعیین شده منطقه ای</value>
</data> </data>
<data name="menuRegionalPresetsDefault" xml:space="preserve"> <data name="menuRegionalPresetsDefault" xml:space="preserve">
<value>Default</value> <value>پیش فرض</value>
</data> </data>
<data name="menuRegionalPresetsRussia" xml:space="preserve"> <data name="menuRegionalPresetsRussia" xml:space="preserve">
<value>Russia</value> <value>روسیه</value>
</data> </data>
<data name="TbSettingsChinaUserTip" xml:space="preserve"> <data name="TbSettingsChinaUserTip" xml:space="preserve">
<value>Users in China region can ignore this item</value> <value>کاربران در منطقه چین می توانند این مورد را نادیده بگیرند</value>
</data> </data>
<data name="menuAddServerViaImage" xml:space="preserve"> <data name="menuAddServerViaImage" xml:space="preserve">
<value>Scan QR code in the image</value> <value>اسکن کردن QRcode موجود در تصویر</value>
</data> </data>
<data name="InvalidUrlTip" xml:space="preserve"> <data name="InvalidUrlTip" xml:space="preserve">
<value>Invalid address (Url)</value> <value>آدرس نامعتبر (آدرس اینترنتی)</value>
</data> </data>
<data name="InsecureUrlProtocol" xml:space="preserve"> <data name="InsecureUrlProtocol" xml:space="preserve">
<value>Please do not use the insecure HTTP protocol subscription address</value> <value>لطفاً از آدرس اشتراک پروتکل HTTP ناامن استفاده نکنید</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>Install the font to the system and restart the settings</value> <value>فونت را روی سیستم نصب کنید و تنظیمات را مجددا راه اندازی کنید</value>
</data> </data>
<data name="menuExitTips" xml:space="preserve"> <data name="menuExitTips" xml:space="preserve">
<value>Are you sure to exit?</value> <value>آیا مطمئن هستید که خارج می شوید؟</value>
</data> </data>
<data name="LvMemo" xml:space="preserve"> <data name="LvMemo" xml:space="preserve">
<value>Remarks Memo</value> <value>یادداشت ملاحظات</value>
</data> </data>
<data name="TbSettingsLogEnabledToFile" xml:space="preserve"> <data name="TbSettingsLogEnabledToFile" xml:space="preserve">
<value>Enable logging to file</value> <value>فعال کردن ورود به فایل</value>
</data> </data>
<data name="MsgSkipSubscriptionUpdate" xml:space="preserve"> <data name="MsgSkipSubscriptionUpdate" xml:space="preserve">
<value>Updates are not enabled, skip this subscription</value> <value>به روز رسانی ها فعال نیستند، از این اشتراک رد شوید</value>
</data> </data>
<data name="menuRebootAsAdmin" xml:space="preserve"> <data name="menuRebootAsAdmin" xml:space="preserve">
<value>Restart as Administrator</value> <value>به عنوان مدیر راه اندازی مجدد</value>
</data> </data>
<data name="LvMoreUrl" xml:space="preserve"> <data name="LvMoreUrl" xml:space="preserve">
<value>More URLs, separated by commas; Subscription conversion will be invalid</value> <value>نشانی‌های وب بیشتر که با کاما از هم جدا شده‌اند. تبدیل اشتراک نامعتبر خواهد بود</value>
</data> </data>
<data name="SpeedDisplayText" xml:space="preserve"> <data name="SpeedDisplayText" xml:space="preserve">
<value>{0} : {1}/s↑ | {2}/s↓</value> <value>{0} : {1}/s↑ | {2}/s↓</value>
</data> </data>
<data name="LvAutoUpdateInterval" xml:space="preserve"> <data name="LvAutoUpdateInterval" xml:space="preserve">
<value>Automatic update interval (minutes)</value> <value>فاصله به روز رسانی خودکار (دقیقه)</value>
</data> </data>
<data name="LvConvertTarget" xml:space="preserve"> <data name="LvConvertTarget" xml:space="preserve">
<value>Convert target type</value> <value>تبدیل نوع هدف</value>
</data> </data>
<data name="LvConvertTargetTip" xml:space="preserve"> <data name="LvConvertTargetTip" xml:space="preserve">
<value>Please leave blank if no conversion is required</value> <value>اگر نیازی به تبدیل نیست، لطفاً خالی بگذارید</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>Please turn off when there is an abnormal disconnection</value> <value>لطفاً در صورت قطع غیرعادی آن را خاموش کنید</value>
</data> </data>
<data name="menuDNSSetting" xml:space="preserve"> <data name="menuDNSSetting" xml:space="preserve">
<value>DNS Settings</value> <value>تنظیمات DNS</value>
</data> </data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve"> <data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Please fill in DNS Structure, Click to view the document</value> <value>لطفا ساختار DNS را پر کنید، برای مشاهده سند کلیک کنید</value>
</data> </data>
<data name="TbSettingDnsImportDefConfig" xml:space="preserve"> <data name="TbSettingDnsImportDefConfig" xml:space="preserve">
<value>Click to import default DNS config</value> <value>برای وارد کردن تنظیمات پیش‌فرض DNS کلیک کنید</value>
</data> </data>
<data name="TbdomainStrategy4Singbox" xml:space="preserve"> <data name="TbdomainStrategy4Singbox" xml:space="preserve">
<value>sing-box domain strategy</value> <value>استراتژی دامنه sing-box</value>
</data> </data>
<data name="TbSettingsMux4SboxProtocol" xml:space="preserve"> <data name="TbSettingsMux4SboxProtocol" xml:space="preserve">
<value>sing-box Mux Protocol</value> <value>پروتکل sing-box Mux</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Full process name (Tun mode)</value> <value>نام کامل فرانید (حالت Tun)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP or IP CIDR</value> <value>IP or IP CIDR</value>
</data> </data>
<data name="TbRoutingRuleDomain" xml:space="preserve"> <data name="TbRoutingRuleDomain" xml:space="preserve">
<value>Domain</value> <value>دامنه</value>
</data> </data>
<data name="TbSettingsCoreDnsSingbox" xml:space="preserve"> <data name="TbSettingsCoreDnsSingbox" xml:space="preserve">
<value>sing-box DNS settings</value> <value>تنظیمات DNS sing-box</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>Waiting for testing (press ESC to terminate)...</value> <value>در انتظار آزمایش (برای پایان دادن به ESC فشار دهید)...</value>
</data> </data>
<data name="TbSpiderX" xml:space="preserve"> <data name="TbSpiderX" xml:space="preserve">
<value>SpiderX</value> <value>SpiderX</value>
@@ -1220,52 +1214,52 @@
<value>ShortId</value> <value>ShortId</value>
</data> </data>
<data name="menuMoveToGroup" xml:space="preserve"> <data name="menuMoveToGroup" xml:space="preserve">
<value>Move to group</value> <value>انتقال به گروه</value>
</data> </data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve"> <data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>Enable Server Drag Drop Sort(Require restart)</value> <value>فعال کردن مرتب سازی با کشیدن سرور (نیاز به راه اندازی مجدد)</value>
</data> </data>
<data name="TbAutoRefresh" xml:space="preserve"> <data name="TbAutoRefresh" xml:space="preserve">
<value>AutoRefresh</value> <value>بازخوانی خودکار</value>
</data> </data>
<data name="SpeedtestingSkip" xml:space="preserve"> <data name="SpeedtestingSkip" xml:space="preserve">
<value>Skip test</value> <value>رد شدن از آزمون</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>Edit Server (Ctrl+D)</value> <value>ویرایش سرور (Ctrl+D)</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>Double-click server make active</value> <value>روی server make active دوبار کلیک کنید</value>
</data> </data>
<data name="SpeedtestingCompleted" xml:space="preserve"> <data name="SpeedtestingCompleted" xml:space="preserve">
<value>Test completed</value> <value>تست تکمیل شد</value>
</data> </data>
<data name="TbSettingsDefFingerprint" xml:space="preserve"> <data name="TbSettingsDefFingerprint" xml:space="preserve">
<value>Default TLS fingerprint</value> <value>اثر انگشت tls پیش فرض</value>
</data> </data>
<data name="TbSettingsCurrentFontFamily" xml:space="preserve"> <data name="TbSettingsCurrentFontFamily" xml:space="preserve">
<value>FontFamily(Require restart)</value> <value>FontFamily (نیاز به راه اندازی مجدد)</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value> <value>فایل TTF/TTC فونت را در دایرکتوری guiFonts کپی کنید، تنظیمات را مجددا راه اندازی کنید</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value> <value>پورت Pac = +3; پورت Xray API = +4; پورت mihomo API = +5;</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>Set this with admin privileges, get admin privileges after startup</value> <value>این را با امتیازات ادمین تنظیم کنید، پس از راه اندازی، امتیازات مدیر را دریافت کنید</value>
</data> </data>
<data name="TbSettingsFontSize" xml:space="preserve"> <data name="TbSettingsFontSize" xml:space="preserve">
<value>Font Size</value> <value>اندازه فونت</value>
</data> </data>
<data name="TbSettingsSpeedTestTimeout" xml:space="preserve"> <data name="TbSettingsSpeedTestTimeout" xml:space="preserve">
<value>SpeedTest Single Timeout Value</value> <value>یمقدار تاخیر تست سرعت منفرد</value>
</data> </data>
<data name="TbSettingsSpeedTestUrl" xml:space="preserve"> <data name="TbSettingsSpeedTestUrl" xml:space="preserve">
<value>SpeedTest URL</value> <value>/آدرس اینترنتی SpeedTest</value>
</data> </data>
<data name="menuMoveTo" xml:space="preserve"> <data name="menuMoveTo" xml:space="preserve">
<value>Move up and down</value> <value>بالا و پایین حرکت کنید</value>
</data> </data>
<data name="TbPublicKey" xml:space="preserve"> <data name="TbPublicKey" xml:space="preserve">
<value>PublicKey</value> <value>PublicKey</value>
@@ -1274,46 +1268,46 @@
<value>Add [Hysteria2] server</value> <value>Add [Hysteria2] server</value>
</data> </data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve"> <data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria Max bandwidth (Up/Dw)</value> <value>حداکتر پهنای باند هیستریا (آپلود/دانلود)</value>
</data> </data>
<data name="TbSettingsFollowSystemTheme" xml:space="preserve"> <data name="TbSettingsFollowSystemTheme" xml:space="preserve">
<value>Follow System Theme</value> <value>دنبال کردن تم سیستم</value>
</data> </data>
<data name="menuAddTuicServer" xml:space="preserve"> <data name="menuAddTuicServer" xml:space="preserve">
<value>Add [TUIC] server</value> <value>افزودن سرور [TUIC]</value>
</data> </data>
<data name="TbSettingsEnableUpdateSubOnlyRemarksExist" xml:space="preserve"> <data name="TbSettingsEnableUpdateSubOnlyRemarksExist" xml:space="preserve">
<value>Updating subscription, only determine remarks exists</value> <value>اشتراک در حال به‌روزرسانی، فقط مشخص کنید که ملاحظاتی وجود دارد</value>
</data> </data>
<data name="menuProxiesReload" xml:space="preserve"> <data name="menuProxiesReload" xml:space="preserve">
<value>Refresh Proxies</value> <value>تازه سازی پروکسی ها</value>
</data> </data>
<data name="TbSortingNetwork" xml:space="preserve"> <data name="TbSortingNetwork" xml:space="preserve">
<value>Network</value> <value>شبکه</value>
</data> </data>
<data name="TbSortingType" xml:space="preserve"> <data name="TbSortingType" xml:space="preserve">
<value>Type</value> <value>نوع</value>
</data> </data>
<data name="TransportRequestHostTip5" xml:space="preserve"> <data name="TransportRequestHostTip5" xml:space="preserve">
<value>*grpc Authority</value> <value>*grpc Authority</value>
</data> </data>
<data name="menuAddHttpServer" xml:space="preserve"> <data name="menuAddHttpServer" xml:space="preserve">
<value>Add [HTTP] server</value> <value>افزودن سرور [HTTP]</value>
</data> </data>
<data name="TbSettingsSpeedPingTestUrl" xml:space="preserve"> <data name="TbSettingsSpeedPingTestUrl" xml:space="preserve">
<value>Speed Ping Test URL</value> <value>آدرس اینترنتی تست پینگ سرعت</value>
</data> </data>
<data name="TbSettingsEnableFragmentTips" xml:space="preserve"> <data name="TbSettingsEnableFragmentTips" xml:space="preserve">
<value>Use Xray and enable non-Tun mode, which conflicts with the group previous proxy</value> <value>از Xray استفاده کنید و حالت non-Tun را فعال کنید، که با پراکسی قبلی گروه در تضاد است</value>
</data> </data>
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve"> <data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
<value>Custom the rule-set of sing-box</value> <value>سفارش سازی مجموعه قوانین از sing box</value>
</data> </data>
<data name="NeedRebootTips" xml:space="preserve"> <data name="NeedRebootTips" xml:space="preserve">
<value>Successful operation. Click the settings menu to reboot the app.</value> <value>عملکرد موفقیت آمیز بود، روی منوی تنظیمات کلیک کنید تا برنامه راه اندازی مجدد شود.</value>
</data> </data>
<data name="menuOpenTheFileLocation" xml:space="preserve"> <data name="menuOpenTheFileLocation" xml:space="preserve">
<value>Open the storage location</value> <value>باز کردن محل ذخیره سازی</value>
</data> </data>
<data name="TbSortingChain" xml:space="preserve"> <data name="TbSortingChain" xml:space="preserve">
<value>Chain</value> <value>Chain</value>
@@ -1322,69 +1316,87 @@
<value>Host</value> <value>Host</value>
</data> </data>
<data name="TbSettingsUseSystemHosts" xml:space="preserve"> <data name="TbSettingsUseSystemHosts" xml:space="preserve">
<value>Use System Hosts</value> <value>استفاده کردن از System Hosts</value>
</data> </data>
<data name="TbSettingsEnableFragment" xml:space="preserve"> <data name="TbSettingsEnableFragment" xml:space="preserve">
<value>Enable fragment</value> <value>فعال کردن فرگمنت</value>
</data> </data>
<data name="TbAutoScrollToEnd" xml:space="preserve"> <data name="TbAutoScrollToEnd" xml:space="preserve">
<value>Auto ScrollToEnd</value> <value>خودکار ScrollToEnd</value>
</data> </data>
<data name="SpeedtestingStop" xml:space="preserve"> <data name="SpeedtestingStop" xml:space="preserve">
<value>Test terminating...</value> <value>پایان تست...</value>
</data> </data>
<data name="LvNextProfile" xml:space="preserve"> <data name="LvNextProfile" xml:space="preserve">
<value>Next proxy remarks</value> <value>Next proxy remarks</value>
</data> </data>
<data name="TbPath7" xml:space="preserve"> <data name="TbPath7" xml:space="preserve">
<value>obfs password</value> <value>پسورد obfs</value>
</data> </data>
<data name="TbHeaderType8" xml:space="preserve"> <data name="TbHeaderType8" xml:space="preserve">
<value>Congestion control</value> <value>کنترل تراکم</value>
</data> </data>
<data name="LvPrevProfile" xml:space="preserve"> <data name="LvPrevProfile" xml:space="preserve">
<value>Previous proxy remarks</value> <value>Previous proxy remarks</value>
</data> </data>
<data name="TbLocalAddress" xml:space="preserve"> <data name="TbLocalAddress" xml:space="preserve">
<value>Address(Ip,Ipv6)</value> <value>آدرس (IP, IPv6)</value>
</data> </data>
<data name="TbReserved" xml:space="preserve"> <data name="TbReserved" xml:space="preserve">
<value>Reserved(2,3,4)</value> <value>Reserved(2,3,4)</value>
</data> </data>
<data name="TbPrivateKey" xml:space="preserve"> <data name="TbPrivateKey" xml:space="preserve">
<value>PrivateKey</value> <value>کلید خصوصی</value>
</data> </data>
<data name="menuAddWireguardServer" xml:space="preserve"> <data name="menuAddWireguardServer" xml:space="preserve">
<value>Add [WireGuard] server</value> <value>افزودن سرور [WireGuard]</value>
</data> </data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve"> <data name="TbSettingsEnableIPv6Address" xml:space="preserve">
<value>Enable IPv6 Address</value> <value>فعال سازی آدرس IPv6</value>
</data> </data>
<data name="TbSettingsEnableExInbound" xml:space="preserve"> <data name="TbSettingsEnableExInbound" xml:space="preserve">
<value>Enable additional Inbound</value> <value>فعال سازی additional Inbound</value>
</data> </data>
<data name="LvPrevProfileTip" xml:space="preserve"> <data name="LvPrevProfileTip" xml:space="preserve">
<value>Please make sure the remarks exists and is unique</value> <value>لطفاً مطمئن شوید که ملاحظات وجود دارد و منحصر به فرد است</value>
</data> </data>
<data name="TbRuleMatchingTips" xml:space="preserve"> <data name="TbRuleMatchingTips" xml:space="preserve">
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value> <value>(Domain or IP or ProcName) and Port and Protocol and InboundTag =&gt; OutboundTag</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux system sudo password</value> <value>رمز عبور sudo سیستم</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password will only be stored in the local file.</value> <value>رمز عبور رمزگذاری شده و فقط در فایل های محلی ذخیره می شود.</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value> <value>لطفاً ابتدا رمز عبور sudo را در تنظیمات حالت Tun تنظیم کنید</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
<value>Please do not run this app with sudo</value> <value>لطفا این برنامه را با sudo اجرا نکنید</value>
</data> </data>
<data name="TransportHeaderTypeTip5" xml:space="preserve"> <data name="TransportHeaderTypeTip5" xml:space="preserve">
<value>*xhttp mode</value> <value>*حالت xhttp</value>
</data> </data>
<data name="TransportExtraTip" xml:space="preserve"> <data name="TransportExtraTip" xml:space="preserve">
<value>XHTTP Extra raw JSON, format: { XHTTPObject }</value> <value>جیسون خام XHTTP Extra, فرمت: { XHTTPObject }</value>
</data> </data>
</root> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>هنگام بستن پنجره در سینی پنهان شوید</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>تعداد در هر زمان برای دسته خودکار در طول تست سرعت (حداکثر 1000)</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>استثنا:از سرور پروکسی برای آدرس ها، با کاما (،) استفاده نکنید</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>نوع Sniffing</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>فعال کردن دومین پورت ترکیبی</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks:پورت محلی، socks2: پورت دوم محلی، socks3: پورت LAN</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

View File

@@ -302,7 +302,7 @@
<value>Scan import the shared link successfully</value> <value>Scan import the shared link successfully</value>
</data> </data>
<data name="TestMeOutput" xml:space="preserve"> <data name="TestMeOutput" xml:space="preserve">
<value>The ping of current service: {0} ms</value> <value>The delay : {0} ms, {1}</value>
</data> </data>
<data name="OperationSuccess" xml:space="preserve"> <data name="OperationSuccess" xml:space="preserve">
<value>Operation success</value> <value>Operation success</value>
@@ -755,7 +755,7 @@
<value>Turn on Sniffing</value> <value>Turn on Sniffing</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>SOCKS Port</value> <value>Mixed Port</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>Start on boot</value> <value>Start on boot</value>
@@ -850,12 +850,6 @@
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>Set as active rule(Enter)</value> <value>Set as active rule(Enter)</value>
</data> </data>
<data name="menuRoutingBasic" xml:space="preserve">
<value>Basic Function</value>
</data>
<data name="menuRoutingBasicImportRules" xml:space="preserve">
<value>Import Basic Rules</value>
</data>
<data name="TbdomainMatcher" xml:space="preserve"> <data name="TbdomainMatcher" xml:space="preserve">
<value>Domain Matcher</value> <value>Domain Matcher</value>
</data> </data>
@@ -998,7 +992,7 @@
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value> <value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value> <value>Pac port = +3; Xray API port = +4; mihomo API port = +5;</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>Set this with admin privileges, get admin privileges after startup</value> <value>Set this with admin privileges, get admin privileges after startup</value>
@@ -1370,10 +1364,10 @@
<value>Remarks Memo</value> <value>Remarks Memo</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux system sudo password</value> <value>System sudo password</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password will only be stored in the local file.</value> <value>The password is encrypted and stored only in local files.</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value> <value>Please set the sudo password in Tun mode settings first</value>
@@ -1387,4 +1381,22 @@
<data name="TransportExtraTip" xml:space="preserve"> <data name="TransportExtraTip" xml:space="preserve">
<value>XHTTP Extra raw JSON, format: { XHTTPObject }</value> <value>XHTTP Extra raw JSON, format: { XHTTPObject }</value>
</data> </data>
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Hide to tray when closing the window</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>Number per time for auto batch during speedtest(max 1000)</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses,with a comma (,)</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>Sniffing type</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>Enable second mixed port</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks: local port, socks2: second local port, socks3: LAN port</value>
</data>
</root> </root>

View File

@@ -302,7 +302,7 @@
<value>Сканирование URL-адреса импорта успешна.</value> <value>Сканирование URL-адреса импорта успешна.</value>
</data> </data>
<data name="TestMeOutput" xml:space="preserve"> <data name="TestMeOutput" xml:space="preserve">
<value>Задержка текущего сервера: {0} мс</value> <value>Задержка текущего сервера: {0} мс, {1}</value>
</data> </data>
<data name="OperationSuccess" xml:space="preserve"> <data name="OperationSuccess" xml:space="preserve">
<value>Операция успешна</value> <value>Операция успешна</value>
@@ -761,7 +761,7 @@
<value>Включить сниффинг</value> <value>Включить сниффинг</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>Порт Socks</value> <value>Mixed Port</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>Автозапуск</value> <value>Автозапуск</value>
@@ -856,12 +856,6 @@
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>Установить как активное правило</value> <value>Установить как активное правило</value>
</data> </data>
<data name="menuRoutingBasic" xml:space="preserve">
<value>Основные функции</value>
</data>
<data name="menuRoutingBasicImportRules" xml:space="preserve">
<value>Импорт основных правил</value>
</data>
<data name="TbdomainMatcher" xml:space="preserve"> <data name="TbdomainMatcher" xml:space="preserve">
<value>Сопоставитель доменов</value> <value>Сопоставитель доменов</value>
</data> </data>
@@ -1316,7 +1310,7 @@
<value>Move up and down</value> <value>Move up and down</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value> <value>Pac port = +3; Xray API port = +4; mihomo API port = +5;</value>
</data> </data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve"> <data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>Install the font to the system and restart the settings</value> <value>Install the font to the system and restart the settings</value>
@@ -1370,10 +1364,10 @@
<value>Remote (WebDAV)</value> <value>Remote (WebDAV)</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux system sudo password</value> <value>System sudo password</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>The password will only be stored in the local file.</value> <value>The password is encrypted and stored only in local files.</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>Please set the sudo password in Tun mode settings first</value> <value>Please set the sudo password in Tun mode settings first</value>
@@ -1387,4 +1381,22 @@
<data name="TransportExtraTip" xml:space="preserve"> <data name="TransportExtraTip" xml:space="preserve">
<value>XHTTP Extra raw JSON, format: { XHTTPObject }</value> <value>XHTTP Extra raw JSON, format: { XHTTPObject }</value>
</data> </data>
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Hide to tray when closing the window</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>Number per time for auto batch during speedtest(max 1000)</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>Exception. Do not use proxy server for addresses,with a comma (,)</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>Sniffing type</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>Enable second mixed port</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks: local port, socks2: second local port, socks3: LAN port</value>
</data>
</root> </root>

View File

@@ -302,7 +302,7 @@
<value>扫描导入分享链接成功</value> <value>扫描导入分享链接成功</value>
</data> </data>
<data name="TestMeOutput" xml:space="preserve"> <data name="TestMeOutput" xml:space="preserve">
<value>当前服务的真连接延迟: {0} ms</value> <value>当前延迟: {0} ms{1}</value>
</data> </data>
<data name="OperationSuccess" xml:space="preserve"> <data name="OperationSuccess" xml:space="preserve">
<value>操作成功</value> <value>操作成功</value>
@@ -755,7 +755,7 @@
<value>开启流量探测</value> <value>开启流量探测</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>本地socks监听端口</value> <value>本地混合监听端口</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>开机启动(可能会不成功)</value> <value>开机启动(可能会不成功)</value>
@@ -850,12 +850,6 @@
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>设为活动规则 (Enter)</value> <value>设为活动规则 (Enter)</value>
</data> </data>
<data name="menuRoutingBasic" xml:space="preserve">
<value>基础功能</value>
</data>
<data name="menuRoutingBasicImportRules" xml:space="preserve">
<value>一键导入基础规则</value>
</data>
<data name="TbdomainMatcher" xml:space="preserve"> <data name="TbdomainMatcher" xml:space="preserve">
<value>域名匹配算法</value> <value>域名匹配算法</value>
</data> </data>
@@ -998,7 +992,7 @@
<value>拷贝字体TTF/TTC文件到目录guiFonts重启设置</value> <value>拷贝字体TTF/TTC文件到目录guiFonts重启设置</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>http端口= +1Pac端口= +4*ray API端口= +5mihomo API端口= +6</value> <value>Pac端口= +3Xray API端口= +4mihomo API端口= +5</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>以管理员权限设置此项,在启动后获得管理员权限</value> <value>以管理员权限设置此项,在启动后获得管理员权限</value>
@@ -1367,10 +1361,10 @@
<value>备注备忘</value> <value>备注备忘</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux系统的sudo密码</value> <value>系统的sudo密码</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>密码只存储在本地文件中,没有密码无法开启Tun</value> <value>密码已加密且只存储在本地文件中,无密码则每次都要输入</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>请先在Tun模式设置中设置sudo密码</value> <value>请先在Tun模式设置中设置sudo密码</value>
@@ -1384,4 +1378,22 @@
<data name="TransportExtraTip" xml:space="preserve"> <data name="TransportExtraTip" xml:space="preserve">
<value>XHTTP Extra 原始 JSON格式 { XHTTPObject }</value> <value>XHTTP Extra 原始 JSON格式 { XHTTPObject }</value>
</data> </data>
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>关闭窗口时隐藏至托盘</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>测试时自动分批的每批数量最大1000</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外. 对于下列地址不使用代理配置文件:使用逗号(,)分隔</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>流量探测类型</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>开启第二个本地监听端口</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks本地端口socks2第二个本地端口socks3局域网端口</value>
</data>
</root> </root>

View File

@@ -127,7 +127,7 @@
<value>設定格式不正確</value> <value>設定格式不正確</value>
</data> </data>
<data name="CustomServerTips" xml:space="preserve"> <data name="CustomServerTips" xml:space="preserve">
<value>注意,自訂設定完全依賴您自己的設定,不能使用所有設定功能。如需使用系統代理請手動修改聽埠。</value> <value>注意,自訂設定完全依賴您自己的設定,不能使用所有設定功能。如需使用系統代理請手動修改聽埠。</value>
</data> </data>
<data name="Downloading" xml:space="preserve"> <data name="Downloading" xml:space="preserve">
<value>下載開始...</value> <value>下載開始...</value>
@@ -157,7 +157,7 @@
<value>請填寫正確格式伺服器埠</value> <value>請填寫正確格式伺服器埠</value>
</data> </data>
<data name="FillLocalListeningPort" xml:space="preserve"> <data name="FillLocalListeningPort" xml:space="preserve">
<value>請填寫本機聽埠</value> <value>請填寫本機聽埠</value>
</data> </data>
<data name="FillPassword" xml:space="preserve"> <data name="FillPassword" xml:space="preserve">
<value>請填寫密碼</value> <value>請填寫密碼</value>
@@ -301,7 +301,7 @@
<value>掃描匯入分享链接成功</value> <value>掃描匯入分享链接成功</value>
</data> </data>
<data name="TestMeOutput" xml:space="preserve"> <data name="TestMeOutput" xml:space="preserve">
<value>目前服務的真連線延遲: {0} ms</value> <value>目前延遲: {0} ms{1}</value>
</data> </data>
<data name="OperationSuccess" xml:space="preserve"> <data name="OperationSuccess" xml:space="preserve">
<value>操作成功</value> <value>操作成功</value>
@@ -722,7 +722,7 @@
<value>例外. 對於下列字元開頭的位址不使用代理設定檔:使用分號(;)分隔</value> <value>例外. 對於下列字元開頭的位址不使用代理設定檔:使用分號(;)分隔</value>
</data> </data>
<data name="TbSettingsHttpPort" xml:space="preserve"> <data name="TbSettingsHttpPort" xml:space="preserve">
<value>本機HTTP聽埠</value> <value>本機HTTP聽埠</value>
</data> </data>
<data name="TbSettingsIgnoreGeoUpdateCore" xml:space="preserve"> <data name="TbSettingsIgnoreGeoUpdateCore" xml:space="preserve">
<value>更新Core時忽略Geo檔案</value> <value>更新Core時忽略Geo檔案</value>
@@ -755,7 +755,7 @@
<value>開啟流量探測</value> <value>開啟流量探測</value>
</data> </data>
<data name="TbSettingsSocksPort" xml:space="preserve"> <data name="TbSettingsSocksPort" xml:space="preserve">
<value>本機SOCKS監聽埠</value> <value>本機混合偵聽埠</value>
</data> </data>
<data name="TbSettingsStartBoot" xml:space="preserve"> <data name="TbSettingsStartBoot" xml:space="preserve">
<value>開機啟動(可能會不成功)</value> <value>開機啟動(可能會不成功)</value>
@@ -850,12 +850,6 @@
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>設為活動規則 (Enter)</value> <value>設為活動規則 (Enter)</value>
</data> </data>
<data name="menuRoutingBasic" xml:space="preserve">
<value>基礎功能</value>
</data>
<data name="menuRoutingBasicImportRules" xml:space="preserve">
<value>一鍵匯入基礎規則</value>
</data>
<data name="TbdomainMatcher" xml:space="preserve"> <data name="TbdomainMatcher" xml:space="preserve">
<value>域名匹配演算法</value> <value>域名匹配演算法</value>
</data> </data>
@@ -998,7 +992,7 @@
<value>複製字型TTF/TTC檔案到目錄guiFonts重啟設定</value> <value>複製字型TTF/TTC檔案到目錄guiFonts重啟設定</value>
</data> </data>
<data name="TbSettingsSocksPortTip" xml:space="preserve"> <data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>http端口= +1Pac端口= +4*ray API端口= +5mihomo API端口= +6</value> <value>Pac連接埠= +3Xray API連接埠= +4mihomo API連接埠= +5</value>
</data> </data>
<data name="TbSettingsStartBootTip" xml:space="preserve"> <data name="TbSettingsStartBootTip" xml:space="preserve">
<value>以管理員權限設定此項,在啟動後獲得管理員權限</value> <value>以管理員權限設定此項,在啟動後獲得管理員權限</value>
@@ -1094,7 +1088,7 @@
<value>請確保別名存在並且唯一</value> <value>請確保別名存在並且唯一</value>
</data> </data>
<data name="TbSettingsEnableExInbound" xml:space="preserve"> <data name="TbSettingsEnableExInbound" xml:space="preserve">
<value>啟用額外監聽端口</value> <value>啟用額外偵聽連接埠</value>
</data> </data>
<data name="TbSettingsEnableIPv6Address" xml:space="preserve"> <data name="TbSettingsEnableIPv6Address" xml:space="preserve">
<value>啟用IPv6</value> <value>啟用IPv6</value>
@@ -1154,7 +1148,7 @@
<value>您目前運行的是獨立包,請手動下載 SelfContained.7z檔案解壓縮覆蓋!</value> <value>您目前運行的是獨立包,請手動下載 SelfContained.7z檔案解壓縮覆蓋!</value>
</data> </data>
<data name="TbPreSocksPort4Sub" xml:space="preserve"> <data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>自訂設定的Socks端口</value> <value>自訂設定的Socks連接埠</value>
</data> </data>
<data name="menuBackupAndRestore" xml:space="preserve"> <data name="menuBackupAndRestore" xml:space="preserve">
<value>備份和還原</value> <value>備份和還原</value>
@@ -1367,10 +1361,10 @@
<value>混淆密碼(obfs password)</value> <value>混淆密碼(obfs password)</value>
</data> </data>
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve"> <data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
<value>Linux系統的sudo密碼</value> <value>系統的sudo密碼</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
<value>密碼只儲存在本機檔案中,沒有密碼無法開啟Tun</value> <value>密碼已加密且只儲存在本機檔案中,無密碼則每次都要輸入</value>
</data> </data>
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve"> <data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
<value>請先在Tun模式設定中設定sudo密碼</value> <value>請先在Tun模式設定中設定sudo密碼</value>
@@ -1384,4 +1378,22 @@
<data name="TransportExtraTip" xml:space="preserve"> <data name="TransportExtraTip" xml:space="preserve">
<value>XHTTP Extra 原始 JSON格式 { XHTTPObject }</value> <value>XHTTP Extra 原始 JSON格式 { XHTTPObject }</value>
</data> </data>
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>關閉視窗時隱藏至托盤</value>
</data>
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
<value>測試時自動分批的每批數量最大1000</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外. 對於下列位址不使用代理設定檔:使用逗號(,)分隔</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>流量探測類型</value>
</data>
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
<value>開啟第二個本機監聽埠</value>
</data>
<data name="TbRoutingInboundTagTips" xml:space="preserve">
<value>socks本地端口socks2第二個本地端口socks3區域網路端口</value>
</data>
</root> </root>

View File

@@ -1,149 +1,156 @@
[ [
{ {
"remarks": "绕过bittorrent", "remarks": "绕过bittorrent",
"outboundTag": "direct", "outboundTag": "direct",
"protocol": [ "protocol": [
"bittorrent" "bittorrent"
] ]
}, },
{ {
"remarks": "Google cn", "remarks": "api.ip.sb",
"outboundTag": "proxy", "outboundTag": "proxy",
"domain": [ "domain": [
"domain:googleapis.cn", "api.ip.sb"
"domain:gstatic.com" ]
] },
}, {
{ "remarks": "Google cn",
"remarks": "阻断udp443", "outboundTag": "proxy",
"outboundTag": "block", "domain": [
"port": "443", "domain:googleapis.cn",
"network": "udp" "domain:gstatic.com"
}, ]
{ },
"remarks": "阻断广告", {
"outboundTag": "block", "remarks": "阻断udp443",
"domain": [ "outboundTag": "block",
"geosite:category-ads-all" "port": "443",
] "network": "udp"
}, },
{ {
"remarks": "绕过局域网IP", "remarks": "阻断广告",
"outboundTag": "direct", "outboundTag": "block",
"ip": [ "domain": [
"geoip:private" "geosite:category-ads-all"
] ]
}, },
{ {
"remarks": "绕过局域网域名", "remarks": "绕过局域网IP",
"outboundTag": "direct", "outboundTag": "direct",
"domain": [ "ip": [
"geosite:private" "geoip:private"
] ]
}, },
{ {
"remarks": "代理海外公共DNSIP", "remarks": "绕过局域网域名",
"outboundTag": "proxy", "outboundTag": "direct",
"ip": [ "domain": [
"1.1.1.1", "geosite:private"
"1.0.0.1", ]
"2606:4700:4700::1111", },
"2606:4700:4700::1001", {
"1.1.1.2", "remarks": "代理海外公共DNSIP",
"1.0.0.2", "outboundTag": "proxy",
"2606:4700:4700::1112", "ip": [
"2606:4700:4700::1002", "1.1.1.1",
"1.1.1.3", "1.0.0.1",
"1.0.0.3", "2606:4700:4700::1111",
"2606:4700:4700::1113", "2606:4700:4700::1001",
"2606:4700:4700::1003", "1.1.1.2",
"8.8.8.8", "1.0.0.2",
"8.8.4.4", "2606:4700:4700::1112",
"2001:4860:4860::8888", "2606:4700:4700::1002",
"2001:4860:4860::8844", "1.1.1.3",
"94.140.14.14", "1.0.0.3",
"94.140.15.15", "2606:4700:4700::1113",
"2a10:50c0::ad1:ff", "2606:4700:4700::1003",
"2a10:50c0::ad2:ff", "8.8.8.8",
"94.140.14.15", "8.8.4.4",
"94.140.15.16", "2001:4860:4860::8888",
"2a10:50c0::bad1:ff", "2001:4860:4860::8844",
"2a10:50c0::bad2:ff", "94.140.14.14",
"94.140.14.140", "94.140.15.15",
"94.140.14.141", "2a10:50c0::ad1:ff",
"2a10:50c0::1:ff", "2a10:50c0::ad2:ff",
"2a10:50c0::2:ff", "94.140.14.15",
"208.67.222.222", "94.140.15.16",
"208.67.220.220", "2a10:50c0::bad1:ff",
"2620:119:35::35", "2a10:50c0::bad2:ff",
"2620:119:53::53", "94.140.14.140",
"208.67.222.123", "94.140.14.141",
"208.67.220.123", "2a10:50c0::1:ff",
"2620:119:35::123", "2a10:50c0::2:ff",
"2620:119:53::123", "208.67.222.222",
"9.9.9.9", "208.67.220.220",
"149.112.112.112", "2620:119:35::35",
"2620:fe::9", "2620:119:53::53",
"2620:fe::fe", "208.67.222.123",
"9.9.9.11", "208.67.220.123",
"149.112.112.11", "2620:119:35::123",
"2620:fe::11", "2620:119:53::123",
"2620:fe::fe:11", "9.9.9.9",
"9.9.9.10", "149.112.112.112",
"149.112.112.10", "2620:fe::9",
"2620:fe::10", "2620:fe::fe",
"2620:fe::fe:10", "9.9.9.11",
"77.88.8.8", "149.112.112.11",
"77.88.8.1", "2620:fe::11",
"2a02:6b8::feed:0ff", "2620:fe::fe:11",
"2a02:6b8:0:1::feed:0ff", "9.9.9.10",
"77.88.8.88", "149.112.112.10",
"77.88.8.2", "2620:fe::10",
"2a02:6b8::feed:bad", "2620:fe::fe:10",
"2a02:6b8:0:1::feed:bad", "77.88.8.8",
"77.88.8.7", "77.88.8.1",
"77.88.8.3", "2a02:6b8::feed:0ff",
"2a02:6b8::feed:a11", "2a02:6b8:0:1::feed:0ff",
"2a02:6b8:0:1::feed:a11" "77.88.8.88",
] "77.88.8.2",
}, "2a02:6b8::feed:bad",
{ "2a02:6b8:0:1::feed:bad",
"remarks": "代理海外公共DNS域名", "77.88.8.7",
"outboundTag": "proxy", "77.88.8.3",
"domain": [ "2a02:6b8::feed:a11",
"domain:cloudflare-dns.com", "2a02:6b8:0:1::feed:a11"
"domain:one.one.one.one", ]
"domain:dns.google", },
"domain:adguard-dns.com", {
"domain:opendns.com", "remarks": "代理海外公共DNS域名",
"domain:umbrella.com", "outboundTag": "proxy",
"domain:quad9.net", "domain": [
"domain:yandex.net" "domain:cloudflare-dns.com",
] "domain:one.one.one.one",
}, "domain:dns.google",
{ "domain:adguard-dns.com",
"remarks": "代理IP", "domain:opendns.com",
"outboundTag": "proxy", "domain:umbrella.com",
"ip": [ "domain:quad9.net",
"geoip:facebook", "domain:yandex.net"
"geoip:fastly", ]
"geoip:google", },
"geoip:netflix", {
"geoip:telegram", "remarks": "代理IP",
"geoip:twitter" "outboundTag": "proxy",
] "ip": [
}, "geoip:facebook",
{ "geoip:fastly",
"remarks": "代理GFW", "geoip:google",
"outboundTag": "proxy", "geoip:netflix",
"domain": [ "geoip:telegram",
"geosite:gfw", "geoip:twitter"
"geosite:greatfire" ]
] },
}, {
{ "remarks": "代理GFW",
"remarks": "最终直连", "outboundTag": "proxy",
"port": "0-65535", "domain": [
"outboundTag": "direct" "geosite:gfw",
} "geosite:greatfire"
]
},
{
"remarks": "最终直连",
"port": "0-65535",
"outboundTag": "direct"
}
] ]

View File

@@ -1,21 +0,0 @@
[
{
"domain": [
"geosite:google"
],
"outboundTag": "proxy"
},
{
"outboundTag": "direct",
"domain": [
"domain:example-example.com",
"domain:example-example2.com"
]
},
{
"outboundTag": "block",
"domain": [
"geosite:category-ads-all"
]
}
]

View File

@@ -1,29 +0,0 @@
[
{
"remarks": "block",
"outboundTag": "block",
"domain": [
"geosite:category-ads-all"
]
},
{
"remarks": "direct",
"outboundTag": "direct",
"domain": [
"geosite:cn"
]
},
{
"remarks": "direct",
"outboundTag": "direct",
"ip": [
"geoip:private",
"geoip:cn"
]
},
{
"remarks": "proxy",
"port": "0-65535",
"outboundTag": "proxy"
}
]

View File

@@ -4,19 +4,19 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Version>7.1.0</Version> <Version>7.4.2</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Downloader" Version="3.2.1" /> <PackageReference Include="Downloader" Version="3.3.1" />
<PackageReference Include="ReactiveUI" Version="20.1.63" /> <PackageReference Include="ReactiveUI" Version="20.1.63" />
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" /> <PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
<PackageReference Include="Splat.NLog" Version="15.2.22" /> <PackageReference Include="Splat.NLog" Version="15.2.22" />
<PackageReference Include="WebDav.Client" Version="2.8.0" /> <PackageReference Include="WebDav.Client" Version="2.8.0" />
<PackageReference Include="YamlDotNet" Version="16.2.0" /> <PackageReference Include="YamlDotNet" Version="16.2.1" />
<PackageReference Include="QRCoder" Version="1.6.0" /> <PackageReference Include="QRCoder" Version="1.6.0" />
<PackageReference Include="CliWrap" Version="3.6.7" /> <PackageReference Include="CliWrap" Version="3.7.0" />
<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" /> <PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
<PackageReference Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" /> <PackageReference Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
<PackageReference Include="TaskScheduler" Version="2.11.0" /> <PackageReference Include="TaskScheduler" Version="2.11.0" />
@@ -28,11 +28,10 @@
<EmbeddedResource Include="Sample\clash_tun_yaml" /> <EmbeddedResource Include="Sample\clash_tun_yaml" />
<EmbeddedResource Include="Sample\custom_routing_black" /> <EmbeddedResource Include="Sample\custom_routing_black" />
<EmbeddedResource Include="Sample\custom_routing_global" /> <EmbeddedResource Include="Sample\custom_routing_global" />
<EmbeddedResource Include="Sample\custom_routing_locked" />
<EmbeddedResource Include="Sample\custom_routing_rules" />
<EmbeddedResource Include="Sample\custom_routing_white" /> <EmbeddedResource Include="Sample\custom_routing_white" />
<EmbeddedResource Include="Sample\dns_singbox_normal" /> <EmbeddedResource Include="Sample\dns_singbox_normal" />
<EmbeddedResource Include="Sample\dns_v2ray_normal" /> <EmbeddedResource Include="Sample\dns_v2ray_normal" />
<EmbeddedResource Include="Sample\pac" />
<EmbeddedResource Include="Sample\SampleClientConfig" /> <EmbeddedResource Include="Sample\SampleClientConfig" />
<EmbeddedResource Include="Sample\SampleHttpRequest" /> <EmbeddedResource Include="Sample\SampleHttpRequest" />
<EmbeddedResource Include="Sample\SampleHttpResponse" /> <EmbeddedResource Include="Sample\SampleHttpResponse" />
@@ -46,12 +45,6 @@
<EmbeddedResource Include="Sample\linux_autostart_config" /> <EmbeddedResource Include="Sample\linux_autostart_config" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PacLib\PacLib.csproj" />
<ProjectReference Include="..\ProtosLib\ProtosLib.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Resx\ResUI.Designer.cs"> <Compile Update="Resx\ResUI.Designer.cs">
<DependentUpon>ResUI.resx</DependentUpon> <DependentUpon>ResUI.resx</DependentUpon>
@@ -65,6 +58,9 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.hu.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.resx"> <EmbeddedResource Update="Resx\ResUI.resx">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<LastGenOutput>ResUI.Designer.cs</LastGenOutput> <LastGenOutput>ResUI.Designer.cs</LastGenOutput>

View File

@@ -78,17 +78,15 @@ namespace ServiceLib.Services.CoreConfig
return ret; return ret;
} }
//port //mixed-port
fileContent["port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.http); fileContent["mixed-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
//socks-port
fileContent["socks-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
//log-level //log-level
fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel); fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel);
//external-controller //external-controller
fileContent["external-controller"] = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}"; fileContent["external-controller"] = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}";
//allow-lan //allow-lan
if (_config.Inbound[0].AllowLANConn) if (_config.Inbound.First().AllowLANConn)
{ {
fileContent["allow-lan"] = "true"; fileContent["allow-lan"] = "true";
fileContent["bind-address"] = "*"; fileContent["bind-address"] = "*";

View File

@@ -26,7 +26,7 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.CheckServerSettings; ret.Msg = ResUI.CheckServerSettings;
return ret; return ret;
} }
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.splithttp) or nameof(ETransport.xhttp)) if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
{ {
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
return ret; return ret;
@@ -52,7 +52,7 @@ namespace ServiceLib.Services.CoreConfig
await GenInbounds(singboxConfig); await GenInbounds(singboxConfig);
await GenOutbound(node, singboxConfig.outbounds[0]); await GenOutbound(node, singboxConfig.outbounds.First());
await GenMoreOutbounds(node, singboxConfig); await GenMoreOutbounds(node, singboxConfig);
@@ -90,8 +90,8 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.InitialConfiguration; ret.Msg = ResUI.InitialConfiguration;
string result = Utils.GetEmbedText(Global.SingboxSampleClient); var result = Utils.GetEmbedText(Global.SingboxSampleClient);
string txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound); var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty()) if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
{ {
ret.Msg = ResUI.FailedGetDefaultConfiguration; ret.Msg = ResUI.FailedGetDefaultConfiguration;
@@ -119,10 +119,10 @@ namespace ServiceLib.Services.CoreConfig
await GenLog(singboxConfig); await GenLog(singboxConfig);
//GenDns(new(), singboxConfig); //GenDns(new(), singboxConfig);
singboxConfig.inbounds.Clear(); // Remove "proxy" service for speedtest, avoiding port conflicts. singboxConfig.inbounds.Clear();
singboxConfig.outbounds.RemoveAt(0); singboxConfig.outbounds.RemoveAt(0);
int httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
@@ -144,8 +144,8 @@ namespace ServiceLib.Services.CoreConfig
} }
//find unused port //find unused port
var port = httpPort; var port = initPort;
for (int k = httpPort; k < Global.MaxPort; k++) for (int k = initPort; k < Global.MaxPort; k++)
{ {
if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0)
{ {
@@ -157,7 +157,7 @@ namespace ServiceLib.Services.CoreConfig
} }
//found //found
port = k; port = k;
httpPort = port + 1; initPort = port + 1;
break; break;
} }
@@ -174,7 +174,7 @@ namespace ServiceLib.Services.CoreConfig
{ {
listen = Global.Loopback, listen = Global.Loopback,
listen_port = port, listen_port = port,
type = EInboundProtocol.http.ToString(), type = EInboundProtocol.mixed.ToString(),
}; };
inbound.tag = inbound.type + inbound.listen_port.ToString(); inbound.tag = inbound.type + inbound.listen_port.ToString();
singboxConfig.inbounds.Add(inbound); singboxConfig.inbounds.Add(inbound);
@@ -409,7 +409,9 @@ namespace ServiceLib.Services.CoreConfig
{ {
await GenInbounds(singboxConfig); await GenInbounds(singboxConfig);
await GenExperimental(singboxConfig); await GenExperimental(singboxConfig);
JsonUtils.ToFile(singboxConfig, fileName, false);
var content = JsonUtils.Serialize(singboxConfig, true);
await File.WriteAllTextAsync(fileName, content);
} }
} }
else else
@@ -440,7 +442,7 @@ namespace ServiceLib.Services.CoreConfig
#region private gen function #region private gen function
public async Task<int> GenLog(SingboxConfig singboxConfig) private async Task<int> GenLog(SingboxConfig singboxConfig)
{ {
try try
{ {
@@ -488,53 +490,46 @@ namespace ServiceLib.Services.CoreConfig
{ {
var inbound = new Inbound4Sbox() var inbound = new Inbound4Sbox()
{ {
type = EInboundProtocol.socks.ToString(), type = EInboundProtocol.mixed.ToString(),
tag = EInboundProtocol.socks.ToString(), tag = EInboundProtocol.socks.ToString(),
listen = Global.Loopback, listen = Global.Loopback,
}; };
singboxConfig.inbounds.Add(inbound); singboxConfig.inbounds.Add(inbound);
inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
inbound.sniff = _config.Inbound[0].SniffingEnabled; inbound.sniff = _config.Inbound.First().SniffingEnabled;
inbound.sniff_override_destination = _config.Inbound[0].RouteOnly ? false : _config.Inbound[0].SniffingEnabled; inbound.sniff_override_destination = _config.Inbound.First().RouteOnly ? false : _config.Inbound.First().SniffingEnabled;
inbound.domain_strategy = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainStrategy4Singbox) ? null : _config.RoutingBasicItem.DomainStrategy4Singbox; inbound.domain_strategy = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainStrategy4Singbox) ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
if (_config.RoutingBasicItem.EnableRoutingAdvanced) var routing = await ConfigHandler.GetDefaultRouting(_config);
if (Utils.IsNotEmpty(routing.DomainStrategy4Singbox))
{ {
var routing = await ConfigHandler.GetDefaultRouting(_config); inbound.domain_strategy = routing.DomainStrategy4Singbox;
if (Utils.IsNotEmpty(routing.DomainStrategy4Singbox))
{
inbound.domain_strategy = routing.DomainStrategy4Singbox;
}
} }
//http if (_config.Inbound.First().SecondLocalPortEnabled)
var inbound2 = GetInbound(inbound, EInboundProtocol.http, false);
singboxConfig.inbounds.Add(inbound2);
if (_config.Inbound[0].AllowLANConn)
{ {
if (_config.Inbound[0].NewPort4LAN) var inbound2 = GetInbound(inbound, EInboundProtocol.socks2, true);
singboxConfig.inbounds.Add(inbound2);
}
if (_config.Inbound.First().AllowLANConn)
{
if (_config.Inbound.First().NewPort4LAN)
{ {
var inbound3 = GetInbound(inbound, EInboundProtocol.socks2, true); var inbound3 = GetInbound(inbound, EInboundProtocol.socks3, true);
inbound3.listen = listen; inbound3.listen = listen;
singboxConfig.inbounds.Add(inbound3); singboxConfig.inbounds.Add(inbound3);
var inbound4 = GetInbound(inbound, EInboundProtocol.http2, false);
inbound4.listen = listen;
singboxConfig.inbounds.Add(inbound4);
//auth //auth
if (Utils.IsNotEmpty(_config.Inbound[0].User) && Utils.IsNotEmpty(_config.Inbound[0].Pass)) if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
{ {
inbound3.users = new() { new() { username = _config.Inbound[0].User, password = _config.Inbound[0].Pass } }; inbound3.users = new() { new() { username = _config.Inbound.First().User, password = _config.Inbound.First().Pass } };
inbound4.users = new() { new() { username = _config.Inbound[0].User, password = _config.Inbound[0].Pass } };
} }
} }
else else
{ {
inbound.listen = listen; inbound.listen = listen;
inbound2.listen = listen;
} }
} }
} }
@@ -543,19 +538,20 @@ namespace ServiceLib.Services.CoreConfig
{ {
if (_config.TunModeItem.Mtu <= 0) if (_config.TunModeItem.Mtu <= 0)
{ {
_config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus[0]); _config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus.First());
} }
if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack)) if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack))
{ {
_config.TunModeItem.Stack = Global.TunStacks[0]; _config.TunModeItem.Stack = Global.TunStacks.First();
} }
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(Utils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { }; var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(Utils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun";
tunInbound.mtu = _config.TunModeItem.Mtu; tunInbound.mtu = _config.TunModeItem.Mtu;
tunInbound.strict_route = _config.TunModeItem.StrictRoute; tunInbound.strict_route = _config.TunModeItem.StrictRoute;
tunInbound.stack = _config.TunModeItem.Stack; tunInbound.stack = _config.TunModeItem.Stack;
tunInbound.sniff = _config.Inbound[0].SniffingEnabled; tunInbound.sniff = _config.Inbound.First().SniffingEnabled;
//tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled; //tunInbound.sniff_override_destination = _config.inbound.First().routeOnly ? false : _config.inbound.First().sniffingEnabled;
if (_config.TunModeItem.EnableIPv6Address == false) if (_config.TunModeItem.EnableIPv6Address == false)
{ {
tunInbound.address = ["172.18.0.1/30"]; tunInbound.address = ["172.18.0.1/30"];
@@ -576,11 +572,11 @@ namespace ServiceLib.Services.CoreConfig
var inbound = JsonUtils.DeepCopy(inItem); var inbound = JsonUtils.DeepCopy(inItem);
inbound.tag = protocol.ToString(); inbound.tag = protocol.ToString();
inbound.listen_port = inItem.listen_port + (int)protocol; inbound.listen_port = inItem.listen_port + (int)protocol;
inbound.type = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString(); inbound.type = EInboundProtocol.mixed.ToString();
return inbound; return inbound;
} }
public async Task<int> GenOutbound(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutbound(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
{ {
@@ -704,7 +700,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
{ {
@@ -727,7 +723,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenOutboundTls(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutboundTls(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
{ {
@@ -777,7 +773,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenOutboundTransport(ProfileItem node, Outbound4Sbox outbound) private async Task<int> GenOutboundTransport(ProfileItem node, Outbound4Sbox outbound)
{ {
try try
{ {
@@ -869,7 +865,7 @@ namespace ServiceLib.Services.CoreConfig
} }
//current proxy //current proxy
var outbound = singboxConfig.outbounds[0]; var outbound = singboxConfig.outbounds.First();
var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound); var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
//Previous proxy //Previous proxy
@@ -912,7 +908,7 @@ namespace ServiceLib.Services.CoreConfig
try try
{ {
var dnsOutbound = "dns_out"; var dnsOutbound = "dns_out";
if (!_config.Inbound[0].SniffingEnabled) if (!_config.Inbound.First().SniffingEnabled)
{ {
singboxConfig.route.rules.Add(new() singboxConfig.route.rules.Add(new()
{ {
@@ -958,28 +954,13 @@ namespace ServiceLib.Services.CoreConfig
}); });
} }
if (_config.RoutingBasicItem.EnableRoutingAdvanced) var routing = await ConfigHandler.GetDefaultRouting(_config);
if (routing != null)
{ {
var routing = await ConfigHandler.GetDefaultRouting(_config); var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
if (routing != null) foreach (var item in rules ?? [])
{ {
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet); if (item.Enabled)
foreach (var item in rules ?? [])
{
if (item.Enabled)
{
await GenRoutingUserRule(item, singboxConfig.route.rules);
}
}
}
}
else
{
var lockedItem = await ConfigHandler.GetLockedRoutingItem(_config);
if (lockedItem != null)
{
var rules = JsonUtils.Deserialize<List<RulesItem>>(lockedItem.RuleSet);
foreach (var item in rules ?? [])
{ {
await GenRoutingUserRule(item, singboxConfig.route.rules); await GenRoutingUserRule(item, singboxConfig.route.rules);
} }
@@ -1019,7 +1000,7 @@ namespace ServiceLib.Services.CoreConfig
} }
} }
public async Task<int> GenRoutingUserRule(RulesItem item, List<Rule4Sbox> rules) private async Task<int> GenRoutingUserRule(RulesItem item, List<Rule4Sbox> rules)
{ {
try try
{ {
@@ -1177,7 +1158,7 @@ namespace ServiceLib.Services.CoreConfig
return true; return true;
} }
public async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig) private async Task<int> GenDns(ProfileItem? node, SingboxConfig singboxConfig)
{ {
try try
{ {
@@ -1208,7 +1189,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenDnsDomains(ProfileItem? node, SingboxConfig singboxConfig, DNSItem? dNSItem) private async Task<int> GenDnsDomains(ProfileItem? node, SingboxConfig singboxConfig, DNSItem? dNSItem)
{ {
var dns4Sbox = singboxConfig.dns ?? new(); var dns4Sbox = singboxConfig.dns ?? new();
dns4Sbox.servers ??= []; dns4Sbox.servers ??= [];
@@ -1261,7 +1242,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenExperimental(SingboxConfig singboxConfig) private async Task<int> GenExperimental(SingboxConfig singboxConfig)
{ {
//if (_config.guiItem.enableStatistics) //if (_config.guiItem.enableStatistics)
{ {
@@ -1334,20 +1315,18 @@ namespace ServiceLib.Services.CoreConfig
//load custom ruleset file //load custom ruleset file
List<Ruleset4Sbox> customRulesets = []; List<Ruleset4Sbox> customRulesets = [];
if (_config.RoutingBasicItem.EnableRoutingAdvanced)
var routing = await ConfigHandler.GetDefaultRouting(_config);
if (Utils.IsNotEmpty(routing.CustomRulesetPath4Singbox))
{ {
var routing = await ConfigHandler.GetDefaultRouting(_config); var result = Utils.LoadResource(routing.CustomRulesetPath4Singbox);
if (Utils.IsNotEmpty(routing.CustomRulesetPath4Singbox)) if (Utils.IsNotEmpty(result))
{ {
var result = Utils.LoadResource(routing.CustomRulesetPath4Singbox); customRulesets = (JsonUtils.Deserialize<List<Ruleset4Sbox>>(result) ?? [])
if (Utils.IsNotEmpty(result)) .Where(t => t.tag != null)
{ .Where(t => t.type != null)
customRulesets = (JsonUtils.Deserialize<List<Ruleset4Sbox>>(result) ?? []) .Where(t => t.format != null)
.Where(t => t.tag != null) .ToList();
.Where(t => t.type != null)
.Where(t => t.format != null)
.ToList();
}
} }
} }

View File

@@ -55,7 +55,7 @@ namespace ServiceLib.Services.CoreConfig
await GenRouting(v2rayConfig); await GenRouting(v2rayConfig);
await GenOutbound(node, v2rayConfig.outbounds[0]); await GenOutbound(node, v2rayConfig.outbounds.First());
await GenMoreOutbounds(node, v2rayConfig); await GenMoreOutbounds(node, v2rayConfig);
@@ -216,8 +216,8 @@ namespace ServiceLib.Services.CoreConfig
ret.Msg = ResUI.InitialConfiguration; ret.Msg = ResUI.InitialConfiguration;
string result = Utils.GetEmbedText(Global.V2raySampleClient); var result = Utils.GetEmbedText(Global.V2raySampleClient);
string txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound); var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty()) if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
{ {
ret.Msg = ResUI.FailedGetDefaultConfiguration; ret.Msg = ResUI.FailedGetDefaultConfiguration;
@@ -244,10 +244,11 @@ namespace ServiceLib.Services.CoreConfig
} }
await GenLog(v2rayConfig); await GenLog(v2rayConfig);
v2rayConfig.inbounds.Clear(); // Remove "proxy" service for speedtest, avoiding port conflicts. v2rayConfig.inbounds.Clear();
v2rayConfig.outbounds.RemoveAt(0); v2rayConfig.outbounds.Clear();
v2rayConfig.routing.rules.Clear();
int httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
@@ -269,8 +270,8 @@ namespace ServiceLib.Services.CoreConfig
} }
//find unused port //find unused port
var port = httpPort; var port = initPort;
for (int k = httpPort; k < Global.MaxPort; k++) for (var k = initPort; k < Global.MaxPort; k++)
{ {
if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0)
{ {
@@ -282,7 +283,7 @@ namespace ServiceLib.Services.CoreConfig
} }
//found //found
port = k; port = k;
httpPort = port + 1; initPort = port + 1;
break; break;
} }
@@ -294,16 +295,6 @@ namespace ServiceLib.Services.CoreConfig
it.Port = port; it.Port = port;
it.AllowTest = true; it.AllowTest = true;
//inbound
Inbounds4Ray inbound = new()
{
listen = Global.Loopback,
port = port,
protocol = EInboundProtocol.http.ToString(),
};
inbound.tag = inbound.protocol + inbound.port.ToString();
v2rayConfig.inbounds.Add(inbound);
//outbound //outbound
if (item is null) if (item is null)
{ {
@@ -326,6 +317,16 @@ namespace ServiceLib.Services.CoreConfig
continue; continue;
} }
//inbound
Inbounds4Ray inbound = new()
{
listen = Global.Loopback,
port = port,
protocol = EInboundProtocol.socks.ToString(),
};
inbound.tag = inbound.protocol + inbound.port.ToString();
v2rayConfig.inbounds.Add(inbound);
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
await GenOutbound(item, outbound); await GenOutbound(item, outbound);
outbound.tag = Global.ProxyTag + inbound.port.ToString(); outbound.tag = Global.ProxyTag + inbound.port.ToString();
@@ -358,7 +359,7 @@ namespace ServiceLib.Services.CoreConfig
#region private gen function #region private gen function
public async Task<int> GenLog(V2rayConfig v2rayConfig) private async Task<int> GenLog(V2rayConfig v2rayConfig)
{ {
try try
{ {
@@ -383,46 +384,40 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenInbounds(V2rayConfig v2rayConfig) private async Task<int> GenInbounds(V2rayConfig v2rayConfig)
{ {
try try
{ {
var listen = "0.0.0.0"; var listen = "0.0.0.0";
v2rayConfig.inbounds = []; v2rayConfig.inbounds = [];
Inbounds4Ray? inbound = GetInbound(_config.Inbound[0], EInboundProtocol.socks, true); var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
v2rayConfig.inbounds.Add(inbound); v2rayConfig.inbounds.Add(inbound);
//http if (_config.Inbound.First().SecondLocalPortEnabled)
Inbounds4Ray? inbound2 = GetInbound(_config.Inbound[0], EInboundProtocol.http, false);
v2rayConfig.inbounds.Add(inbound2);
if (_config.Inbound[0].AllowLANConn)
{ {
if (_config.Inbound[0].NewPort4LAN) var inbound2 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
v2rayConfig.inbounds.Add(inbound2);
}
if (_config.Inbound.First().AllowLANConn)
{
if (_config.Inbound.First().NewPort4LAN)
{ {
var inbound3 = GetInbound(_config.Inbound[0], EInboundProtocol.socks2, true); var inbound3 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks3, true);
inbound3.listen = listen; inbound3.listen = listen;
v2rayConfig.inbounds.Add(inbound3); v2rayConfig.inbounds.Add(inbound3);
var inbound4 = GetInbound(_config.Inbound[0], EInboundProtocol.http2, false);
inbound4.listen = listen;
v2rayConfig.inbounds.Add(inbound4);
//auth //auth
if (Utils.IsNotEmpty(_config.Inbound[0].User) && Utils.IsNotEmpty(_config.Inbound[0].Pass)) if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
{ {
inbound3.settings.auth = "password"; inbound3.settings.auth = "password";
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound[0].User, pass = _config.Inbound[0].Pass } }; inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
inbound4.settings.auth = "password";
inbound4.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound[0].User, pass = _config.Inbound[0].Pass } };
} }
} }
else else
{ {
inbound.listen = listen; inbound.listen = listen;
inbound2.listen = listen;
} }
} }
} }
@@ -448,7 +443,7 @@ namespace ServiceLib.Services.CoreConfig
} }
inbound.tag = protocol.ToString(); inbound.tag = protocol.ToString();
inbound.port = inItem.LocalPort + (int)protocol; inbound.port = inItem.LocalPort + (int)protocol;
inbound.protocol = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString(); inbound.protocol = EInboundProtocol.socks.ToString();
inbound.settings.udp = inItem.UdpEnabled; inbound.settings.udp = inItem.UdpEnabled;
inbound.sniffing.enabled = inItem.SniffingEnabled; inbound.sniffing.enabled = inItem.SniffingEnabled;
inbound.sniffing.destOverride = inItem.DestOverride; inbound.sniffing.destOverride = inItem.DestOverride;
@@ -466,33 +461,17 @@ namespace ServiceLib.Services.CoreConfig
v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy; v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy;
v2rayConfig.routing.domainMatcher = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainMatcher) ? null : _config.RoutingBasicItem.DomainMatcher; v2rayConfig.routing.domainMatcher = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainMatcher) ? null : _config.RoutingBasicItem.DomainMatcher;
if (_config.RoutingBasicItem.EnableRoutingAdvanced) var routing = await ConfigHandler.GetDefaultRouting(_config);
if (routing != null)
{ {
var routing = await ConfigHandler.GetDefaultRouting(_config); if (Utils.IsNotEmpty(routing.DomainStrategy))
if (routing != null)
{ {
if (Utils.IsNotEmpty(routing.DomainStrategy)) v2rayConfig.routing.domainStrategy = routing.DomainStrategy;
{
v2rayConfig.routing.domainStrategy = routing.DomainStrategy;
}
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
foreach (var item in rules)
{
if (item.Enabled)
{
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
await GenRoutingUserRule(item2, v2rayConfig);
}
}
} }
} var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
else foreach (var item in rules)
{
var lockedItem = await ConfigHandler.GetLockedRoutingItem(_config);
if (lockedItem != null)
{ {
var rules = JsonUtils.Deserialize<List<RulesItem>>(lockedItem.RuleSet); if (item.Enabled)
foreach (var item in rules)
{ {
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item)); var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
await GenRoutingUserRule(item2, v2rayConfig); await GenRoutingUserRule(item2, v2rayConfig);
@@ -508,7 +487,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig) private async Task<int> GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig)
{ {
try try
{ {
@@ -586,7 +565,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenOutbound(ProfileItem node, Outbounds4Ray outbound) private async Task<int> GenOutbound(ProfileItem node, Outbounds4Ray outbound)
{ {
try try
{ {
@@ -602,7 +581,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
vnextItem = outbound.settings.vnext[0]; vnextItem = outbound.settings.vnext.First();
} }
vnextItem.address = node.Address; vnextItem.address = node.Address;
vnextItem.port = node.Port; vnextItem.port = node.Port;
@@ -615,7 +594,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
usersItem = vnextItem.users[0]; usersItem = vnextItem.users.First();
} }
//远程服务器用户ID //远程服务器用户ID
usersItem.id = node.Id; usersItem.id = node.Id;
@@ -645,7 +624,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
serversItem = outbound.settings.servers[0]; serversItem = outbound.settings.servers.First();
} }
serversItem.address = node.Address; serversItem.address = node.Address;
serversItem.port = node.Port; serversItem.port = node.Port;
@@ -671,7 +650,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
serversItem = outbound.settings.servers[0]; serversItem = outbound.settings.servers.First();
} }
serversItem.address = node.Address; serversItem.address = node.Address;
serversItem.port = node.Port; serversItem.port = node.Port;
@@ -706,7 +685,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
vnextItem = outbound.settings.vnext[0]; vnextItem = outbound.settings.vnext.First();
} }
vnextItem.address = node.Address; vnextItem.address = node.Address;
vnextItem.port = node.Port; vnextItem.port = node.Port;
@@ -719,7 +698,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
usersItem = vnextItem.users[0]; usersItem = vnextItem.users.First();
} }
usersItem.id = node.Id; usersItem.id = node.Id;
usersItem.email = Global.UserEMail; usersItem.email = Global.UserEMail;
@@ -727,8 +706,7 @@ namespace ServiceLib.Services.CoreConfig
await GenOutboundMux(node, outbound, _config.CoreBasicItem.MuxEnabled); await GenOutboundMux(node, outbound, _config.CoreBasicItem.MuxEnabled);
if (node.StreamSecurity == Global.StreamSecurityReality if (node.StreamSecurity == Global.StreamSecurityReality || node.StreamSecurity == Global.StreamSecurity)
|| node.StreamSecurity == Global.StreamSecurity)
{ {
if (Utils.IsNotEmpty(node.Flow)) if (Utils.IsNotEmpty(node.Flow))
{ {
@@ -755,7 +733,7 @@ namespace ServiceLib.Services.CoreConfig
} }
else else
{ {
serversItem = outbound.settings.servers[0]; serversItem = outbound.settings.servers.First();
} }
serversItem.address = node.Address; serversItem.address = node.Address;
serversItem.port = node.Port; serversItem.port = node.Port;
@@ -772,7 +750,7 @@ namespace ServiceLib.Services.CoreConfig
} }
outbound.protocol = Global.ProtocolTypes[node.ConfigType]; outbound.protocol = Global.ProtocolTypes[node.ConfigType];
await GenBoundStreamSettings(node, outbound.streamSettings); await GenBoundStreamSettings(node, outbound);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -781,7 +759,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenOutboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabled) private async Task<int> GenOutboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabled)
{ {
try try
{ {
@@ -805,14 +783,16 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenBoundStreamSettings(ProfileItem node, StreamSettings4Ray streamSettings) private async Task<int> GenBoundStreamSettings(ProfileItem node, Outbounds4Ray outbound)
{ {
try try
{ {
var streamSettings = outbound.streamSettings;
streamSettings.network = node.GetNetwork(); streamSettings.network = node.GetNetwork();
string host = node.RequestHost.TrimEx(); var host = node.RequestHost.TrimEx();
string sni = node.Sni; var path = node.Path.TrimEx();
string useragent = ""; var sni = node.Sni.TrimEx();
var useragent = "";
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty()) if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
{ {
try try
@@ -885,9 +865,9 @@ namespace ServiceLib.Services.CoreConfig
{ {
type = node.HeaderType type = node.HeaderType
}; };
if (Utils.IsNotEmpty(node.Path)) if (Utils.IsNotEmpty(path))
{ {
kcpSettings.seed = node.Path; kcpSettings.seed = path;
} }
streamSettings.kcpSettings = kcpSettings; streamSettings.kcpSettings = kcpSettings;
break; break;
@@ -895,9 +875,10 @@ namespace ServiceLib.Services.CoreConfig
case nameof(ETransport.ws): case nameof(ETransport.ws):
WsSettings4Ray wsSettings = new(); WsSettings4Ray wsSettings = new();
wsSettings.headers = new Headers4Ray(); wsSettings.headers = new Headers4Ray();
string path = node.Path;
if (Utils.IsNotEmpty(host)) if (Utils.IsNotEmpty(host))
{ {
wsSettings.host = host;
wsSettings.headers.Host = host; wsSettings.headers.Host = host;
} }
if (Utils.IsNotEmpty(path)) if (Utils.IsNotEmpty(path))
@@ -915,9 +896,9 @@ namespace ServiceLib.Services.CoreConfig
case nameof(ETransport.httpupgrade): case nameof(ETransport.httpupgrade):
HttpupgradeSettings4Ray httpupgradeSettings = new(); HttpupgradeSettings4Ray httpupgradeSettings = new();
if (Utils.IsNotEmpty(node.Path)) if (Utils.IsNotEmpty(path))
{ {
httpupgradeSettings.path = node.Path; httpupgradeSettings.path = path;
} }
if (Utils.IsNotEmpty(host)) if (Utils.IsNotEmpty(host))
{ {
@@ -926,20 +907,14 @@ namespace ServiceLib.Services.CoreConfig
streamSettings.httpupgradeSettings = httpupgradeSettings; streamSettings.httpupgradeSettings = httpupgradeSettings;
break; break;
//splithttp //xhttp //xhttp
case nameof(ETransport.splithttp):
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
streamSettings.network = ETransport.xhttp.ToString(); streamSettings.network = ETransport.xhttp.ToString();
XhttpSettings4Ray xhttpSettings = new() XhttpSettings4Ray xhttpSettings = new();
{
scMaxEachPostBytes = "500000-1000000",
scMaxConcurrentPosts = "50-100",
scMinPostsIntervalMs = "30-50"
};
if (Utils.IsNotEmpty(node.Path)) if (Utils.IsNotEmpty(path))
{ {
xhttpSettings.path = node.Path; xhttpSettings.path = path;
} }
if (Utils.IsNotEmpty(host)) if (Utils.IsNotEmpty(host))
{ {
@@ -955,6 +930,7 @@ namespace ServiceLib.Services.CoreConfig
} }
streamSettings.xhttpSettings = xhttpSettings; streamSettings.xhttpSettings = xhttpSettings;
await GenOutboundMux(node, outbound, false);
break; break;
//h2 //h2
@@ -965,7 +941,7 @@ namespace ServiceLib.Services.CoreConfig
{ {
httpSettings.host = Utils.String2List(host); httpSettings.host = Utils.String2List(host);
} }
httpSettings.path = node.Path; httpSettings.path = path;
streamSettings.httpSettings = httpSettings; streamSettings.httpSettings = httpSettings;
@@ -975,7 +951,7 @@ namespace ServiceLib.Services.CoreConfig
QuicSettings4Ray quicsettings = new() QuicSettings4Ray quicsettings = new()
{ {
security = host, security = host,
key = node.Path, key = path,
header = new Header4Ray header = new Header4Ray
{ {
type = node.HeaderType type = node.HeaderType
@@ -999,7 +975,7 @@ namespace ServiceLib.Services.CoreConfig
GrpcSettings4Ray grpcSettings = new() GrpcSettings4Ray grpcSettings = new()
{ {
authority = Utils.IsNullOrEmpty(host) ? null : host, authority = Utils.IsNullOrEmpty(host) ? null : host,
serviceName = node.Path, serviceName = path,
multiMode = node.HeaderType == Global.GrpcMultiMode, multiMode = node.HeaderType == Global.GrpcMultiMode,
idle_timeout = _config.GrpcItem.IdleTimeout, idle_timeout = _config.GrpcItem.IdleTimeout,
health_check_timeout = _config.GrpcItem.HealthCheckTimeout, health_check_timeout = _config.GrpcItem.HealthCheckTimeout,
@@ -1024,18 +1000,17 @@ namespace ServiceLib.Services.CoreConfig
//request Host //request Host
string request = Utils.GetEmbedText(Global.V2raySampleHttpRequestFileName); string request = Utils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
string[] arrHost = host.Split(','); string[] arrHost = host.Split(',');
string host2 = string.Join("\",\"", arrHost); string host2 = string.Join(",".AppendQuotes(), arrHost);
request = request.Replace("$requestHost$", $"\"{host2}\""); request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
//request = request.Replace("$requestHost$", string.Format("\"{0}\"", config.requestHost())); request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
request = request.Replace("$requestUserAgent$", $"\"{useragent}\"");
//Path //Path
string pathHttp = @"/"; string pathHttp = @"/";
if (Utils.IsNotEmpty(node.Path)) if (Utils.IsNotEmpty(path))
{ {
string[] arrPath = node.Path.Split(','); string[] arrPath = path.Split(',');
pathHttp = string.Join("\",\"", arrPath); pathHttp = string.Join(",".AppendQuotes(), arrPath);
} }
request = request.Replace("$requestPath$", $"\"{pathHttp}\""); request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
tcpSettings.header.request = JsonUtils.Deserialize<object>(request); tcpSettings.header.request = JsonUtils.Deserialize<object>(request);
streamSettings.tcpSettings = tcpSettings; streamSettings.tcpSettings = tcpSettings;
@@ -1050,7 +1025,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenDns(ProfileItem? node, V2rayConfig v2rayConfig) private async Task<int> GenDns(ProfileItem? node, V2rayConfig v2rayConfig)
{ {
try try
{ {
@@ -1113,7 +1088,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenDnsDomains(ProfileItem? node, JsonNode dns, DNSItem? dNSItem) private async Task<int> GenDnsDomains(ProfileItem? node, JsonNode dns, DNSItem? dNSItem)
{ {
if (node == null) if (node == null)
{ return 0; } { return 0; }
@@ -1133,7 +1108,7 @@ namespace ServiceLib.Services.CoreConfig
return 0; return 0;
} }
public async Task<int> GenStatistic(V2rayConfig v2rayConfig) private async Task<int> GenStatistic(V2rayConfig v2rayConfig)
{ {
if (_config.GuiItem.EnableStatistics) if (_config.GuiItem.EnableStatistics)
{ {
@@ -1184,7 +1159,7 @@ namespace ServiceLib.Services.CoreConfig
{ {
//fragment proxy //fragment proxy
if (_config.CoreBasicItem.EnableFragment if (_config.CoreBasicItem.EnableFragment
&& Utils.IsNotEmpty(v2rayConfig.outbounds[0].streamSettings?.security)) && Utils.IsNotEmpty(v2rayConfig.outbounds.First().streamSettings?.security))
{ {
var fragmentOutbound = new Outbounds4Ray var fragmentOutbound = new Outbounds4Ray
{ {
@@ -1202,7 +1177,7 @@ namespace ServiceLib.Services.CoreConfig
}; };
v2rayConfig.outbounds.Add(fragmentOutbound); v2rayConfig.outbounds.Add(fragmentOutbound);
v2rayConfig.outbounds[0].streamSettings.sockopt = new() v2rayConfig.outbounds.First().streamSettings.sockopt = new()
{ {
dialerProxy = fragmentOutbound.tag dialerProxy = fragmentOutbound.tag
}; };
@@ -1222,7 +1197,7 @@ namespace ServiceLib.Services.CoreConfig
} }
//current proxy //current proxy
var outbound = v2rayConfig.outbounds[0]; var outbound = v2rayConfig.outbounds.First();
var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound); var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
//Previous proxy //Previous proxy

View File

@@ -280,13 +280,13 @@ namespace ServiceLib.Services
{ {
return null; return null;
} }
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.http); var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
if (await SocketCheck(Global.Loopback, httpPort) == false) if (await SocketCheck(Global.Loopback, port) == false)
{ {
return null; return null;
} }
return new WebProxy(Global.Loopback, httpPort); return new WebProxy($"socks5://{Global.Loopback}:{port}");
} }
private async Task<bool> SocketCheck(string ip, int port) private async Task<bool> SocketCheck(string ip, int port)

View File

@@ -8,19 +8,16 @@ namespace ServiceLib.Services
public class SpeedtestService public class SpeedtestService
{ {
private Config? _config; private Config? _config;
private List<ServerTestItem> _selecteds;
private ESpeedActionType _actionType;
private Action<SpeedTestResult>? _updateFunc; private Action<SpeedTestResult>? _updateFunc;
private bool _exitLoop = false; private bool _exitLoop = false;
public SpeedtestService(Config config, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> updateFunc) public SpeedtestService(Config config, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> updateFunc)
{ {
_config = config; _config = config;
_actionType = actionType;
_updateFunc = updateFunc; _updateFunc = updateFunc;
_selecteds = new List<ServerTestItem>(); var lstSelected = new List<ServerTestItem>();
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
if (it.ConfigType == EConfigType.Custom) if (it.ConfigType == EConfigType.Custom)
@@ -31,7 +28,7 @@ namespace ServiceLib.Services
{ {
continue; continue;
} }
_selecteds.Add(new ServerTestItem() lstSelected.Add(new ServerTestItem()
{ {
IndexId = it.IndexId, IndexId = it.IndexId,
Address = it.Address, Address = it.Address,
@@ -39,8 +36,9 @@ namespace ServiceLib.Services
ConfigType = it.ConfigType ConfigType = it.ConfigType
}); });
} }
//clear test result //clear test result
foreach (var it in _selecteds) foreach (var it in lstSelected)
{ {
switch (actionType) switch (actionType)
{ {
@@ -63,25 +61,59 @@ namespace ServiceLib.Services
} }
} }
switch (actionType)
{
case ESpeedActionType.Tcping:
Task.Run(RunTcping);
break;
case ESpeedActionType.Realping:
Task.Run(RunRealPing);
break;
case ESpeedActionType.Speedtest:
Task.Run(RunSpeedTestAsync);
break;
case ESpeedActionType.Mixedtest:
Task.Run(RunMixedtestAsync);
break;
}
MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop); MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop);
Task.Run(async () => { await RunAsync(actionType, lstSelected); });
}
private async Task RunAsync(ESpeedActionType actionType, List<ServerTestItem> lstSelected)
{
if (actionType == ESpeedActionType.Tcping)
{
await RunTcpingAsync(lstSelected);
return;
}
var pageSize = _config.SpeedTestItem.SpeedTestPageSize;
if (pageSize is <= 0 or > 1000)
{
pageSize = 1000;
}
List<List<ServerTestItem>> lstTest = new();
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)).ToList();
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard).ToList();
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
}
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
{
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
}
foreach (var lst in lstTest)
{
switch (actionType)
{
case ESpeedActionType.Realping:
await RunRealPingAsync(lst);
break;
case ESpeedActionType.Speedtest:
await RunSpeedTestAsync(lst);
break;
case ESpeedActionType.Mixedtest:
await RunMixedTestAsync(lst);
break;
}
await Task.Delay(100);
}
UpdateFunc("", ResUI.SpeedtestingCompleted);
} }
private void ExitLoop(string x) private void ExitLoop(string x)
@@ -91,12 +123,12 @@ namespace ServiceLib.Services
UpdateFunc("", ResUI.SpeedtestingStop); UpdateFunc("", ResUI.SpeedtestingStop);
} }
private async Task RunTcping() private async Task RunTcpingAsync(List<ServerTestItem> selecteds)
{ {
try try
{ {
List<Task> tasks = []; List<Task> tasks = [];
foreach (var it in _selecteds) foreach (var it in selecteds)
{ {
if (it.ConfigType == EConfigType.Custom) if (it.ConfigType == EConfigType.Custom)
{ {
@@ -106,7 +138,7 @@ namespace ServiceLib.Services
{ {
try try
{ {
int time = await GetTcpingTime(it.Address, it.Port); var time = await GetTcpingTime(it.Address, it.Port);
var output = FormatOut(time, Global.DelayUnit); var output = FormatOut(time, Global.DelayUnit);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output); ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
@@ -130,24 +162,22 @@ namespace ServiceLib.Services
} }
} }
private async Task RunRealPing() private async Task RunRealPingAsync(List<ServerTestItem> selecteds)
{ {
int pid = -1; var pid = -1;
try try
{ {
string msg = string.Empty; pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
if (pid < 0) if (pid < 0)
{ {
UpdateFunc("", ResUI.FailedToRunCore); UpdateFunc("", ResUI.FailedToRunCore);
return; return;
} }
DownloadService downloadHandle = new DownloadService(); var downloadHandle = new DownloadService();
List<Task> tasks = new(); List<Task> tasks = new();
foreach (var it in _selecteds) foreach (var it in selecteds)
{ {
if (!it.AllowTest) if (!it.AllowTest)
{ {
@@ -161,12 +191,12 @@ namespace ServiceLib.Services
{ {
try try
{ {
WebProxy webProxy = new(Global.Loopback, it.Port); var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
string output = await GetRealPingTime(downloadHandle, webProxy); var output = await GetRealPingTime(downloadHandle, webProxy);
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output); ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
UpdateFunc(it.IndexId, output); UpdateFunc(it.IndexId, output);
int.TryParse(output, out int delay); int.TryParse(output, out var delay);
it.Delay = delay; it.Delay = delay;
} }
catch (Exception ex) catch (Exception ex)
@@ -185,33 +215,28 @@ namespace ServiceLib.Services
{ {
if (pid > 0) if (pid > 0)
{ {
CoreHandler.Instance.CoreStopPid(pid); await CoreHandler.Instance.CoreStopPid(pid);
} }
await ProfileExHandler.Instance.SaveTo(); await ProfileExHandler.Instance.SaveTo();
} }
} }
private async Task RunSpeedTestAsync() private async Task RunSpeedTestAsync(List<ServerTestItem> selecteds)
{ {
int pid = -1; var pid = -1;
//if (_actionType == ESpeedActionType.Mixedtest) pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
//{
// _selecteds = _selecteds.OrderBy(t => t.delay).ToList();
//}
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
if (pid < 0) if (pid < 0)
{ {
UpdateFunc("", ResUI.FailedToRunCore); UpdateFunc("", ResUI.FailedToRunCore);
return; return;
} }
string url = _config.SpeedTestItem.SpeedTestUrl; var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout; var timeout = _config.SpeedTestItem.SpeedTestTimeout;
DownloadService downloadHandle = new(); DownloadService downloadHandle = new();
foreach (var it in _selecteds) foreach (var it in selecteds)
{ {
if (_exitLoop) if (_exitLoop)
{ {
@@ -237,11 +262,11 @@ namespace ServiceLib.Services
var item = await AppHandler.Instance.GetProfileItem(it.IndexId); var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
if (item is null) continue; if (item is null) continue;
WebProxy webProxy = new(Global.Loopback, it.Port); var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) => await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
{ {
decimal.TryParse(msg, out decimal dec); decimal.TryParse(msg, out var dec);
if (dec > 0) if (dec > 0)
{ {
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg); ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
@@ -252,28 +277,27 @@ namespace ServiceLib.Services
if (pid > 0) if (pid > 0)
{ {
CoreHandler.Instance.CoreStopPid(pid); await CoreHandler.Instance.CoreStopPid(pid);
} }
UpdateFunc("", ResUI.SpeedtestingCompleted);
await ProfileExHandler.Instance.SaveTo(); await ProfileExHandler.Instance.SaveTo();
} }
private async Task RunSpeedTestMulti() private async Task RunSpeedTestMulti(List<ServerTestItem> selecteds)
{ {
int pid = -1; var pid = -1;
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds); pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
if (pid < 0) if (pid < 0)
{ {
UpdateFunc("", ResUI.FailedToRunCore); UpdateFunc("", ResUI.FailedToRunCore);
return; return;
} }
string url = _config.SpeedTestItem.SpeedTestUrl; var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout; var timeout = _config.SpeedTestItem.SpeedTestTimeout;
DownloadService downloadHandle = new(); DownloadService downloadHandle = new();
foreach (var it in _selecteds) foreach (var it in selecteds)
{ {
if (_exitLoop) if (_exitLoop)
{ {
@@ -300,10 +324,10 @@ namespace ServiceLib.Services
var item = await AppHandler.Instance.GetProfileItem(it.IndexId); var item = await AppHandler.Instance.GetProfileItem(it.IndexId);
if (item is null) continue; if (item is null) continue;
WebProxy webProxy = new(Global.Loopback, it.Port); var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) => _ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
{ {
decimal.TryParse(msg, out decimal dec); decimal.TryParse(msg, out var dec);
if (dec > 0) if (dec > 0)
{ {
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg); ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
@@ -317,38 +341,37 @@ namespace ServiceLib.Services
if (pid > 0) if (pid > 0)
{ {
CoreHandler.Instance.CoreStopPid(pid); await CoreHandler.Instance.CoreStopPid(pid);
} }
UpdateFunc("", ResUI.SpeedtestingCompleted);
await ProfileExHandler.Instance.SaveTo(); await ProfileExHandler.Instance.SaveTo();
} }
private async Task RunMixedtestAsync() private async Task RunMixedTestAsync(List<ServerTestItem> selecteds)
{ {
await RunRealPing(); await RunRealPingAsync(selecteds);
await Task.Delay(1000); await Task.Delay(1000);
await RunSpeedTestMulti(); await RunSpeedTestMulti(selecteds);
} }
private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy) private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy)
{ {
int responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10); var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
//string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status; //string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
return FormatOut(responseTime, Global.DelayUnit); return FormatOut(responseTime, Global.DelayUnit);
} }
private async Task<int> GetTcpingTime(string url, int port) private async Task<int> GetTcpingTime(string url, int port)
{ {
int responseTime = -1; var responseTime = -1;
try try
{ {
if (!IPAddress.TryParse(url, out IPAddress? ipAddress)) if (!IPAddress.TryParse(url, out var ipAddress))
{ {
IPHostEntry ipHostInfo = Dns.GetHostEntry(url); var ipHostInfo = await Dns.GetHostEntryAsync(url);
ipAddress = ipHostInfo.AddressList[0]; ipAddress = ipHostInfo.AddressList.First();
} }
var timer = Stopwatch.StartNew(); var timer = Stopwatch.StartNew();

View File

@@ -8,8 +8,8 @@ namespace ServiceLib.Services.Statistics
private Config _config; private Config _config;
private bool _exitFlag; private bool _exitFlag;
private ClientWebSocket? webSocket; private ClientWebSocket? webSocket;
private string url = string.Empty;
private Action<ServerSpeedItem>? _updateFunc; private Action<ServerSpeedItem>? _updateFunc;
private string Url => $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic";
public StatisticsSingboxService(Config config, Action<ServerSpeedItem> updateFunc) public StatisticsSingboxService(Config config, Action<ServerSpeedItem> updateFunc)
{ {
@@ -26,12 +26,10 @@ namespace ServiceLib.Services.Statistics
try try
{ {
url = $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic";
if (webSocket == null) if (webSocket == null)
{ {
webSocket = new ClientWebSocket(); webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(url), CancellationToken.None); await webSocket.ConnectAsync(new Uri(Url), CancellationToken.None);
} }
} }
catch { } catch { }

View File

@@ -1,135 +0,0 @@
using Grpc.Core;
using Grpc.Net.Client;
using ProtosLib.Statistics;
namespace ServiceLib.Services.Statistics
{
public class StatisticsV2rayService
{
private Models.Config _config;
private GrpcChannel? _channel;
private StatsService.StatsServiceClient? _client;
private bool _exitFlag;
private Action<ServerSpeedItem>? _updateFunc;
public StatisticsV2rayService(Models.Config config, Action<ServerSpeedItem> updateFunc)
{
_config = config;
_updateFunc = updateFunc;
_exitFlag = false;
GrpcInit();
Task.Run(Run);
}
private void GrpcInit()
{
if (_channel is null)
{
try
{
_channel = GrpcChannel.ForAddress($"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}");
_client = new StatsService.StatsServiceClient(_channel);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
}
public void Close()
{
_exitFlag = true;
}
private async void Run()
{
while (!_exitFlag)
{
await Task.Delay(1000);
try
{
if (!_config.IsRunningCore(ECoreType.Xray))
{
continue;
}
if (_channel?.State == ConnectivityState.Ready)
{
QueryStatsResponse? res = null;
try
{
if (_client != null)
res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true });
}
catch
{
}
if (res != null)
{
ParseOutput(res.Stat, out ServerSpeedItem server);
_updateFunc?.Invoke(server);
}
}
if (_channel != null)
await _channel.ConnectAsync();
}
catch
{
}
}
}
private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> source, out ServerSpeedItem server)
{
server = new();
long aggregateProxyUp = 0;
long aggregateProxyDown = 0;
try
{
foreach (Stat stat in source)
{
string name = stat.Name;
long value = stat.Value / 1024; //KByte
string[] nStr = name.Split(">>>".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
string type = "";
name = name.Trim();
name = nStr[1];
type = nStr[3];
if (name.StartsWith(Global.ProxyTag))
{
if (type == "uplink")
{
aggregateProxyUp += value;
}
else if (type == "downlink")
{
aggregateProxyDown += value;
}
}
else if (name == Global.DirectTag)
{
if (type == "uplink")
{
server.DirectUp = value;
}
else if (type == "downlink")
{
server.DirectDown = value;
}
}
}
server.ProxyUp = aggregateProxyUp;
server.ProxyDown = aggregateProxyDown;
}
catch
{
}
}
}
}

View File

@@ -3,18 +3,17 @@
public class StatisticsXrayService public class StatisticsXrayService
{ {
private const long linkBase = 1024; private const long linkBase = 1024;
private string _url;
private ServerSpeedItem _serverSpeedItem = new(); private ServerSpeedItem _serverSpeedItem = new();
private Config _config; private Config _config;
private bool _exitFlag; private bool _exitFlag;
private Action<ServerSpeedItem>? _updateFunc; private Action<ServerSpeedItem>? _updateFunc;
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
public StatisticsXrayService(Config config, Action<ServerSpeedItem> updateFunc) public StatisticsXrayService(Config config, Action<ServerSpeedItem> updateFunc)
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
_exitFlag = false; _exitFlag = false;
_url = $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
Task.Run(Run); Task.Run(Run);
} }
@@ -36,7 +35,7 @@
continue; continue;
} }
var result = await HttpClientHelper.Instance.TryGetAsync(_url); var result = await HttpClientHelper.Instance.TryGetAsync(Url);
if (result != null) if (result != null)
{ {
var server = ParseOutput(result) ?? new ServerSpeedItem(); var server = ParseOutput(result) ?? new ServerSpeedItem();

View File

@@ -244,8 +244,17 @@ namespace ServiceLib.Services
public async Task RunAvailabilityCheck(Action<bool, string> updateFunc) public async Task RunAvailabilityCheck(Action<bool, string> updateFunc)
{ {
var time = await new DownloadService().RunAvailabilityCheck(null); var downloadHandle = new DownloadService();
updateFunc?.Invoke(false, string.Format(ResUI.TestMeOutput, time)); var time = await downloadHandle.RunAvailabilityCheck(null);
var ip = Global.None;
if (time > 0)
{
var result = await downloadHandle.TryDownloadString(Global.IPAPIUrl, true, Global.IPAPIUrl);
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
ip = $"({ipInfo?.country_code}) {ipInfo?.ip}";
}
updateFunc?.Invoke(false, string.Format(ResUI.TestMeOutput, time, ip));
} }
#region CheckUpdate private #region CheckUpdate private
@@ -441,6 +450,15 @@ namespace ServiceLib.Services
_ => null, _ => null,
}; };
} }
else if (Utils.IsOSX())
{
return RuntimeInformation.ProcessArchitecture switch
{
Architecture.Arm64 => coreInfo?.DownloadUrlOSXArm64,
Architecture.X64 => coreInfo?.DownloadUrlOSX64,
_ => null,
};
}
return null; return null;
} }

View File

@@ -160,9 +160,9 @@ namespace ServiceLib.ViewModels
var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}"); var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}");
var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs); var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs);
await Task.Run(() => FileManager.CopyDirectory(configDir, configDirTemp, false, "cache.db")); FileManager.CopyDirectory(configDir, configDirTemp, false, "cache.db");
var ret = await Task.Run(() => FileManager.CreateFromDirectory(configDirZipTemp, fileName)); var ret = FileManager.CreateFromDirectory(configDirZipTemp, fileName);
await Task.Run(() => Directory.Delete(configDirZipTemp, true)); Directory.Delete(configDirZipTemp, true);
return ret; return ret;
} }
} }

View File

@@ -262,12 +262,12 @@ namespace ServiceLib.ViewModels
FileManager.ZipExtractToFile(fileName, toPath, _config.GuiItem.IgnoreGeoUpdateCore ? "geo" : ""); FileManager.ZipExtractToFile(fileName, toPath, _config.GuiItem.IgnoreGeoUpdateCore ? "geo" : "");
} }
if (Utils.IsLinux()) if (Utils.IsNonWindows())
{ {
var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList(); var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList();
foreach (var file in filesList) foreach (var file in filesList)
{ {
await Utils.SetLinuxChmod(Path.Combine(toPath, item.CoreType)); await Utils.SetLinuxChmod(Path.Combine(toPath, item.CoreType.ToLower()));
} }
} }

View File

@@ -21,9 +21,6 @@ namespace ServiceLib.ViewModels
[Reactive] [Reactive]
public string HostFilter { get; set; } public string HostFilter { get; set; }
[Reactive]
public int SortingSelected { get; set; }
[Reactive] [Reactive]
public bool AutoRefresh { get; set; } public bool AutoRefresh { get; set; }
@@ -31,18 +28,12 @@ namespace ServiceLib.ViewModels
{ {
_config = AppHandler.Instance.Config; _config = AppHandler.Instance.Config;
_updateView = updateView; _updateView = updateView;
SortingSelected = _config.ClashUIItem.ConnectionsSorting;
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh; AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
var canEditRemove = this.WhenAnyValue( var canEditRemove = this.WhenAnyValue(
x => x.SelectedSource, x => x.SelectedSource,
selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.Id)); selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.Id));
this.WhenAnyValue(
x => x.SortingSelected,
y => y >= 0)
.Subscribe(async c => await DoSortingSelected(c));
this.WhenAnyValue( this.WhenAnyValue(
x => x.AutoRefresh, x => x.AutoRefresh,
y => y == true) y => y == true)
@@ -84,20 +75,6 @@ namespace ServiceLib.ViewModels
}); });
} }
private async Task DoSortingSelected(bool c)
{
if (!c)
{
return;
}
if (SortingSelected != _config.ClashUIItem.ConnectionsSorting)
{
_config.ClashUIItem.ConnectionsSorting = SortingSelected;
}
await GetClashConnections();
}
private async Task GetClashConnections() private async Task GetClashConnections()
{ {
var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync(_config); var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync(_config);
@@ -115,7 +92,7 @@ namespace ServiceLib.ViewModels
var dtNow = DateTime.Now; var dtNow = DateTime.Now;
var lstModel = new List<ClashConnectionModel>(); var lstModel = new List<ClashConnectionModel>();
foreach (var item in connections ?? []) foreach (var item in connections ?? new())
{ {
var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}"; var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter)) if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
@@ -131,45 +108,14 @@ namespace ServiceLib.ViewModels
model.Host = host; model.Host = host;
var sp = (dtNow - item.start); var sp = (dtNow - item.start);
model.Time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds; model.Time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
model.Upload = item.upload;
model.Download = item.download;
model.UploadTraffic = $"{Utils.HumanFy((long)item.upload)}";
model.DownloadTraffic = $"{Utils.HumanFy((long)item.download)}";
model.Elapsed = sp.ToString(@"hh\:mm\:ss"); model.Elapsed = sp.ToString(@"hh\:mm\:ss");
model.Chain = item.chains?.Count > 0 ? item.chains[0] : string.Empty; item.chains?.Reverse();
model.Chain = $"{item.rule} , {string.Join("->", item.chains ?? new())}";
lstModel.Add(model); lstModel.Add(model);
} }
if (lstModel.Count <= 0) { return; } if (lstModel.Count <= 0) { return; }
//sort
switch (SortingSelected)
{
case 0:
lstModel = lstModel.OrderBy(t => t.Upload / t.Time).ToList();
break;
case 1:
lstModel = lstModel.OrderBy(t => t.Download / t.Time).ToList();
break;
case 2:
lstModel = lstModel.OrderBy(t => t.Upload).ToList();
break;
case 3:
lstModel = lstModel.OrderBy(t => t.Download).ToList();
break;
case 4:
lstModel = lstModel.OrderBy(t => t.Time).ToList();
break;
case 5:
lstModel = lstModel.OrderBy(t => t.Host).ToList();
break;
}
_connectionItems.AddRange(lstModel); _connectionItems.AddRange(lstModel);
} }

View File

@@ -239,7 +239,7 @@ namespace ServiceLib.ViewModels
} }
else else
{ {
SelectedGroup = _proxyGroups[0]; SelectedGroup = _proxyGroups.First();
} }
} }
else else

View File

@@ -282,22 +282,25 @@ namespace ServiceLib.ViewModels
{ {
try try
{ {
Logging.SaveLog("MyAppExit Begin"); Logging.SaveLog("MyAppExitAsync Begin");
//if (blWindowsShutDown) MessageBus.Current.SendMessage("", EMsgCommand.AppExit.ToString());
await SysProxyHandler.UpdateSysProxy(_config, true);
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
await SysProxyHandler.UpdateSysProxy(_config, true);
await ProfileExHandler.Instance.SaveTo(); await ProfileExHandler.Instance.SaveTo();
await StatisticsHandler.Instance.SaveTo(); await StatisticsHandler.Instance.SaveTo();
StatisticsHandler.Instance.Close(); StatisticsHandler.Instance.Close();
await CoreHandler.Instance.CoreStop(); await CoreHandler.Instance.CoreStop();
Logging.SaveLog("MyAppExit End"); Logging.SaveLog("MyAppExitAsync End");
} }
catch { } catch { }
finally finally
{ {
_updateView?.Invoke(EViewAction.Shutdown, null); if (!blWindowsShutDown)
{
_updateView?.Invoke(EViewAction.Shutdown, null);
}
} }
} }
@@ -324,6 +327,7 @@ namespace ServiceLib.ViewModels
if (process.Id > 0) if (process.Id > 0)
{ {
await MyAppExitAsync(false); await MyAppExitAsync(false);
await MyAppExitAsync(false);
} }
} }

View File

@@ -9,7 +9,6 @@ namespace ServiceLib.ViewModels
{ {
private ConcurrentQueue<string> _queueMsg = new(); private ConcurrentQueue<string> _queueMsg = new();
private int _numMaxMsg = 500; private int _numMaxMsg = 500;
private string _lastMsgFilter = string.Empty;
private bool _lastMsgFilterNotAvailable; private bool _lastMsgFilterNotAvailable;
private bool _blLockShow = false; private bool _blLockShow = false;
@@ -28,7 +27,7 @@ namespace ServiceLib.ViewModels
this.WhenAnyValue( this.WhenAnyValue(
x => x.MsgFilter) x => x.MsgFilter)
.Subscribe(c => _config.MsgUIItem.MainMsgFilter = MsgFilter); .Subscribe(c => DoMsgFilter());
this.WhenAnyValue( this.WhenAnyValue(
x => x.AutoRefresh, x => x.AutoRefresh,
@@ -77,8 +76,7 @@ namespace ServiceLib.ViewModels
private async Task EnqueueQueueMsg(string msg) private async Task EnqueueQueueMsg(string msg)
{ {
//filter msg //filter msg
if (MsgFilter != _lastMsgFilter) _lastMsgFilterNotAvailable = false; if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
if (Utils.IsNotEmpty(MsgFilter) && !_lastMsgFilterNotAvailable)
{ {
try try
{ {
@@ -87,12 +85,12 @@ namespace ServiceLib.ViewModels
return; return;
} }
} }
catch (Exception) catch (Exception ex)
{ {
_queueMsg.Enqueue(ex.Message);
_lastMsgFilterNotAvailable = true; _lastMsgFilterNotAvailable = true;
} }
} }
_lastMsgFilter = MsgFilter;
//Enqueue //Enqueue
if (_queueMsg.Count > _numMaxMsg) if (_queueMsg.Count > _numMaxMsg)
@@ -113,5 +111,11 @@ namespace ServiceLib.ViewModels
{ {
_queueMsg.Clear(); _queueMsg.Clear();
} }
private void DoMsgFilter()
{
_config.MsgUIItem.MainMsgFilter = MsgFilter;
_lastMsgFilterNotAvailable = false;
}
} }
} }

View File

@@ -9,6 +9,7 @@ namespace ServiceLib.ViewModels
#region Core #region Core
[Reactive] public int localPort { get; set; } [Reactive] public int localPort { get; set; }
[Reactive] public bool SecondLocalPortEnabled { get; set; }
[Reactive] public bool udpEnabled { get; set; } [Reactive] public bool udpEnabled { get; set; }
[Reactive] public bool sniffingEnabled { get; set; } [Reactive] public bool sniffingEnabled { get; set; }
public IList<string> destOverride { get; set; } public IList<string> destOverride { get; set; }
@@ -53,6 +54,7 @@ namespace ServiceLib.ViewModels
[Reactive] public bool EnableUpdateSubOnlyRemarksExist { get; set; } [Reactive] public bool EnableUpdateSubOnlyRemarksExist { get; set; }
[Reactive] public bool EnableSecurityProtocolTls13 { get; set; } [Reactive] public bool EnableSecurityProtocolTls13 { get; set; }
[Reactive] public bool AutoHideStartup { get; set; } [Reactive] public bool AutoHideStartup { get; set; }
[Reactive] public bool Hide2TrayWhenClose { get; set; }
[Reactive] public bool EnableDragDropSort { get; set; } [Reactive] public bool EnableDragDropSort { get; set; }
[Reactive] public bool DoubleClick2Activate { get; set; } [Reactive] public bool DoubleClick2Activate { get; set; }
[Reactive] public int AutoUpdateInterval { get; set; } [Reactive] public int AutoUpdateInterval { get; set; }
@@ -61,6 +63,7 @@ namespace ServiceLib.ViewModels
[Reactive] public int SpeedTestTimeout { get; set; } [Reactive] public int SpeedTestTimeout { get; set; }
[Reactive] public string SpeedTestUrl { get; set; } [Reactive] public string SpeedTestUrl { get; set; }
[Reactive] public string SpeedPingTestUrl { get; set; } [Reactive] public string SpeedPingTestUrl { get; set; }
[Reactive] public int SpeedTestPageSize { get; set; }
[Reactive] public bool EnableHWA { get; set; } [Reactive] public bool EnableHWA { get; set; }
[Reactive] public string SubConvertUrl { get; set; } [Reactive] public string SubConvertUrl { get; set; }
[Reactive] public int MainGirdOrientation { get; set; } [Reactive] public int MainGirdOrientation { get; set; }
@@ -121,8 +124,9 @@ namespace ServiceLib.ViewModels
#region Core #region Core
var inbound = _config.Inbound[0]; var inbound = _config.Inbound.First();
localPort = inbound.LocalPort; localPort = inbound.LocalPort;
SecondLocalPortEnabled = inbound.SecondLocalPortEnabled;
udpEnabled = inbound.UdpEnabled; udpEnabled = inbound.UdpEnabled;
sniffingEnabled = inbound.SniffingEnabled; sniffingEnabled = inbound.SniffingEnabled;
routeOnly = inbound.RouteOnly; routeOnly = inbound.RouteOnly;
@@ -166,6 +170,7 @@ namespace ServiceLib.ViewModels
EnableUpdateSubOnlyRemarksExist = _config.UiItem.EnableUpdateSubOnlyRemarksExist; EnableUpdateSubOnlyRemarksExist = _config.UiItem.EnableUpdateSubOnlyRemarksExist;
EnableSecurityProtocolTls13 = _config.GuiItem.EnableSecurityProtocolTls13; EnableSecurityProtocolTls13 = _config.GuiItem.EnableSecurityProtocolTls13;
AutoHideStartup = _config.UiItem.AutoHideStartup; AutoHideStartup = _config.UiItem.AutoHideStartup;
Hide2TrayWhenClose = _config.UiItem.Hide2TrayWhenClose;
EnableDragDropSort = _config.UiItem.EnableDragDropSort; EnableDragDropSort = _config.UiItem.EnableDragDropSort;
DoubleClick2Activate = _config.UiItem.DoubleClick2Activate; DoubleClick2Activate = _config.UiItem.DoubleClick2Activate;
AutoUpdateInterval = _config.GuiItem.AutoUpdateInterval; AutoUpdateInterval = _config.GuiItem.AutoUpdateInterval;
@@ -173,6 +178,7 @@ namespace ServiceLib.ViewModels
CurrentFontFamily = _config.UiItem.CurrentFontFamily; CurrentFontFamily = _config.UiItem.CurrentFontFamily;
SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout; SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout;
SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl; SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl;
SpeedTestPageSize = _config.SpeedTestItem.SpeedTestPageSize;
SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl; SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl;
EnableHWA = _config.GuiItem.EnableHWA; EnableHWA = _config.GuiItem.EnableHWA;
SubConvertUrl = _config.ConstItem.SubConvertUrl; SubConvertUrl = _config.ConstItem.SubConvertUrl;
@@ -198,7 +204,7 @@ namespace ServiceLib.ViewModels
TunMtu = _config.TunModeItem.Mtu; TunMtu = _config.TunModeItem.Mtu;
TunEnableExInbound = _config.TunModeItem.EnableExInbound; TunEnableExInbound = _config.TunModeItem.EnableExInbound;
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address; TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
TunLinuxSudoPassword = _config.TunModeItem.LinuxSudoPassword; TunLinuxSudoPassword = _config.TunModeItem.LinuxSudoPwd;
#endregion Tun mode #endregion Tun mode
@@ -283,15 +289,16 @@ namespace ServiceLib.ViewModels
//} //}
//Core //Core
_config.Inbound[0].LocalPort = localPort; _config.Inbound.First().LocalPort = localPort;
_config.Inbound[0].UdpEnabled = udpEnabled; _config.Inbound.First().SecondLocalPortEnabled = SecondLocalPortEnabled;
_config.Inbound[0].SniffingEnabled = sniffingEnabled; _config.Inbound.First().UdpEnabled = udpEnabled;
_config.Inbound[0].DestOverride = destOverride?.ToList(); _config.Inbound.First().SniffingEnabled = sniffingEnabled;
_config.Inbound[0].RouteOnly = routeOnly; _config.Inbound.First().DestOverride = destOverride?.ToList();
_config.Inbound[0].AllowLANConn = allowLANConn; _config.Inbound.First().RouteOnly = routeOnly;
_config.Inbound[0].NewPort4LAN = newPort4LAN; _config.Inbound.First().AllowLANConn = allowLANConn;
_config.Inbound[0].User = user; _config.Inbound.First().NewPort4LAN = newPort4LAN;
_config.Inbound[0].Pass = pass; _config.Inbound.First().User = user;
_config.Inbound.First().Pass = pass;
if (_config.Inbound.Count > 1) if (_config.Inbound.Count > 1)
{ {
_config.Inbound.RemoveAt(1); _config.Inbound.RemoveAt(1);
@@ -316,12 +323,14 @@ namespace ServiceLib.ViewModels
_config.UiItem.EnableUpdateSubOnlyRemarksExist = EnableUpdateSubOnlyRemarksExist; _config.UiItem.EnableUpdateSubOnlyRemarksExist = EnableUpdateSubOnlyRemarksExist;
_config.GuiItem.EnableSecurityProtocolTls13 = EnableSecurityProtocolTls13; _config.GuiItem.EnableSecurityProtocolTls13 = EnableSecurityProtocolTls13;
_config.UiItem.AutoHideStartup = AutoHideStartup; _config.UiItem.AutoHideStartup = AutoHideStartup;
_config.UiItem.Hide2TrayWhenClose = Hide2TrayWhenClose;
_config.GuiItem.AutoUpdateInterval = AutoUpdateInterval; _config.GuiItem.AutoUpdateInterval = AutoUpdateInterval;
_config.UiItem.EnableDragDropSort = EnableDragDropSort; _config.UiItem.EnableDragDropSort = EnableDragDropSort;
_config.UiItem.DoubleClick2Activate = DoubleClick2Activate; _config.UiItem.DoubleClick2Activate = DoubleClick2Activate;
_config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit; _config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit;
_config.UiItem.CurrentFontFamily = CurrentFontFamily; _config.UiItem.CurrentFontFamily = CurrentFontFamily;
_config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout; _config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout;
_config.SpeedTestItem.SpeedTestPageSize = SpeedTestPageSize;
_config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl; _config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl;
_config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl; _config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl;
_config.GuiItem.EnableHWA = EnableHWA; _config.GuiItem.EnableHWA = EnableHWA;
@@ -342,7 +351,10 @@ namespace ServiceLib.ViewModels
_config.TunModeItem.Mtu = TunMtu; _config.TunModeItem.Mtu = TunMtu;
_config.TunModeItem.EnableExInbound = TunEnableExInbound; _config.TunModeItem.EnableExInbound = TunEnableExInbound;
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address; _config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
_config.TunModeItem.LinuxSudoPassword = TunLinuxSudoPassword; if (TunLinuxSudoPassword != _config.TunModeItem.LinuxSudoPwd)
{
_config.TunModeItem.LinuxSudoPwd = DesUtils.Encrypt(TunLinuxSudoPassword);
}
//coreType //coreType
await SaveCoreType(); await SaveCoreType();
@@ -350,6 +362,7 @@ namespace ServiceLib.ViewModels
if (await ConfigHandler.SaveConfig(_config) == 0) if (await ConfigHandler.SaveConfig(_config) == 0)
{ {
await AutoStartupHandler.UpdateTask(_config); await AutoStartupHandler.UpdateTask(_config);
AppHandler.Instance.Reset();
NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess); NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
_updateView?.Invoke(EViewAction.CloseWindow, null); _updateView?.Invoke(EViewAction.CloseWindow, null);

View File

@@ -374,7 +374,7 @@ namespace ServiceLib.ViewModels
} }
else else
{ {
SelectedProfile = lstModel[0]; SelectedProfile = lstModel.First();
} }
} }
} }
@@ -395,7 +395,7 @@ namespace ServiceLib.ViewModels
} }
else else
{ {
SelectedSub = _subItems[0]; SelectedSub = _subItems.First();
} }
} }
@@ -639,6 +639,7 @@ namespace ServiceLib.ViewModels
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
RefreshServers(); RefreshServers();
SelectedMoveToGroup = null;
SelectedMoveToGroup = new(); SelectedMoveToGroup = new();
//Reload(); //Reload();
} }
@@ -729,7 +730,7 @@ namespace ServiceLib.ViewModels
{ {
return; return;
} }
var result = await CoreConfigHandler.GenerateClientConfig(item, null); var result = await CoreConfigHandler.GenerateClientConfig(item, fileName);
if (result.Success != true) if (result.Success != true)
{ {
NoticeHandler.Instance.Enqueue(result.Msg); NoticeHandler.Instance.Enqueue(result.Msg);

View File

@@ -7,9 +7,6 @@ namespace ServiceLib.ViewModels
{ {
public class RoutingSettingViewModel : MyReactiveObject public class RoutingSettingViewModel : MyReactiveObject
{ {
private RoutingItem _lockedItem;
private List<RulesItem> _lockedRules;
#region Reactive #region Reactive
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>(); private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>();
@@ -20,12 +17,6 @@ namespace ServiceLib.ViewModels
public IList<RoutingItemModel> SelectedSources { get; set; } public IList<RoutingItemModel> SelectedSources { get; set; }
[Reactive]
public bool enableRoutingAdvanced { get; set; }
[Reactive]
public bool enableRoutingBasic { get; set; }
[Reactive] [Reactive]
public string domainStrategy { get; set; } public string domainStrategy { get; set; }
@@ -35,25 +26,6 @@ namespace ServiceLib.ViewModels
[Reactive] [Reactive]
public string domainStrategy4Singbox { get; set; } public string domainStrategy4Singbox { get; set; }
[Reactive]
public string ProxyDomain { get; set; }
[Reactive]
public string ProxyIP { get; set; }
[Reactive]
public string DirectDomain { get; set; }
[Reactive]
public string DirectIP { get; set; }
[Reactive]
public string BlockDomain { get; set; }
[Reactive]
public string BlockIP { get; set; }
public ReactiveCommand<Unit, Unit> RoutingBasicImportRulesCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; } public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; } public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; } public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
@@ -73,15 +45,6 @@ namespace ServiceLib.ViewModels
x => x.SelectedSource, x => x.SelectedSource,
selectedSource => selectedSource != null && !selectedSource.Remarks.IsNullOrEmpty()); selectedSource => selectedSource != null && !selectedSource.Remarks.IsNullOrEmpty());
this.WhenAnyValue(
x => x.enableRoutingAdvanced)
.Subscribe(c => enableRoutingBasic = !enableRoutingAdvanced);
RoutingBasicImportRulesCmd = ReactiveCommand.CreateFromTask(async () =>
{
await RoutingBasicImportRules();
});
RoutingAdvancedAddCmd = ReactiveCommand.CreateFromTask(async () => RoutingAdvancedAddCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await RoutingAdvancedEditAsync(true); await RoutingAdvancedEditAsync(true);
@@ -111,67 +74,14 @@ namespace ServiceLib.ViewModels
{ {
SelectedSource = new(); SelectedSource = new();
enableRoutingAdvanced = true;//TODO _config.RoutingBasicItem.EnableRoutingAdvanced;
domainStrategy = _config.RoutingBasicItem.DomainStrategy; domainStrategy = _config.RoutingBasicItem.DomainStrategy;
domainMatcher = _config.RoutingBasicItem.DomainMatcher; domainMatcher = _config.RoutingBasicItem.DomainMatcher;
domainStrategy4Singbox = _config.RoutingBasicItem.DomainStrategy4Singbox; domainStrategy4Singbox = _config.RoutingBasicItem.DomainStrategy4Singbox;
await ConfigHandler.InitBuiltinRouting(_config); await ConfigHandler.InitBuiltinRouting(_config);
await RefreshRoutingItems(); await RefreshRoutingItems();
await BindingLockedData();
} }
#region locked
private async Task BindingLockedData()
{
_lockedItem = await ConfigHandler.GetLockedRoutingItem(_config);
if (_lockedItem == null)
{
_lockedItem = new RoutingItem()
{
Remarks = "locked",
Url = string.Empty,
Locked = true,
};
await ConfigHandler.AddBatchRoutingRules(_lockedItem, Utils.GetEmbedText(Global.CustomRoutingFileName + "locked"));
}
if (_lockedItem != null)
{
_lockedRules = JsonUtils.Deserialize<List<RulesItem>>(_lockedItem.RuleSet);
ProxyDomain = Utils.List2String(_lockedRules[0].Domain, true);
ProxyIP = Utils.List2String(_lockedRules[0].Ip, true);
DirectDomain = Utils.List2String(_lockedRules[1].Domain, true);
DirectIP = Utils.List2String(_lockedRules[1].Ip, true);
BlockDomain = Utils.List2String(_lockedRules[2].Domain, true);
BlockIP = Utils.List2String(_lockedRules[2].Ip, true);
}
}
private async Task EndBindingLockedData()
{
if (_lockedItem != null)
{
_lockedRules[0].Domain = Utils.String2List(Utils.Convert2Comma(ProxyDomain.TrimEx()));
_lockedRules[0].Ip = Utils.String2List(Utils.Convert2Comma(ProxyIP.TrimEx()));
_lockedRules[1].Domain = Utils.String2List(Utils.Convert2Comma(DirectDomain.TrimEx()));
_lockedRules[1].Ip = Utils.String2List(Utils.Convert2Comma(DirectIP.TrimEx()));
_lockedRules[2].Domain = Utils.String2List(Utils.Convert2Comma(BlockDomain.TrimEx()));
_lockedRules[2].Ip = Utils.String2List(Utils.Convert2Comma(BlockIP.TrimEx()));
_lockedItem.RuleSet = JsonUtils.Serialize(_lockedRules, false);
await ConfigHandler.SaveRoutingItem(_config, _lockedItem);
}
}
#endregion locked
#region Refresh Save #region Refresh Save
public async Task RefreshRoutingItems() public async Task RefreshRoutingItems()
@@ -205,12 +115,9 @@ namespace ServiceLib.ViewModels
private async Task SaveRoutingAsync() private async Task SaveRoutingAsync()
{ {
_config.RoutingBasicItem.DomainStrategy = domainStrategy; _config.RoutingBasicItem.DomainStrategy = domainStrategy;
_config.RoutingBasicItem.EnableRoutingAdvanced = enableRoutingAdvanced;
_config.RoutingBasicItem.DomainMatcher = domainMatcher; _config.RoutingBasicItem.DomainMatcher = domainMatcher;
_config.RoutingBasicItem.DomainStrategy4Singbox = domainStrategy4Singbox; _config.RoutingBasicItem.DomainStrategy4Singbox = domainStrategy4Singbox;
await EndBindingLockedData();
if (await ConfigHandler.SaveConfig(_config) == 0) if (await ConfigHandler.SaveConfig(_config) == 0)
{ {
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
@@ -224,18 +131,6 @@ namespace ServiceLib.ViewModels
#endregion Refresh Save #endregion Refresh Save
private async Task RoutingBasicImportRules()
{
//Extra to bypass the mainland
ProxyDomain = "geosite:google";
DirectDomain = "geosite:cn";
DirectIP = "geoip:private,geoip:cn";
BlockDomain = "geosite:category-ads-all";
//NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
}
public async Task RoutingAdvancedEditAsync(bool blNew) public async Task RoutingAdvancedEditAsync(bool blNew)
{ {
RoutingItem item; RoutingItem item;

View File

@@ -347,11 +347,6 @@ namespace ServiceLib.ViewModels
public async Task RefreshRoutingsMenu() public async Task RefreshRoutingsMenu()
{ {
_routingItems.Clear(); _routingItems.Clear();
if (!_config.RoutingBasicItem.EnableRoutingAdvanced)
{
BlRouting = false;
return;
}
BlRouting = true; BlRouting = true;
var routings = await AppHandler.Instance.RoutingItems(); var routings = await AppHandler.Instance.RoutingItems();
@@ -416,16 +411,16 @@ namespace ServiceLib.ViewModels
// When running as a non-administrator, reboot to administrator mode // When running as a non-administrator, reboot to administrator mode
if (EnableTun && AllowEnableTun() == false) if (EnableTun && AllowEnableTun() == false)
{ {
_config.TunModeItem.EnableTun = false;
if (Utils.IsWindows()) if (Utils.IsWindows())
{ {
_config.TunModeItem.EnableTun = false;
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin(); Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
return;
} }
else if (Utils.IsLinux()) //else if (Utils.IsLinux())
{ //{
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty); // NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty);
} //}
return;
} }
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
Locator.Current.GetService<MainWindowViewModel>()?.Reload(); Locator.Current.GetService<MainWindowViewModel>()?.Reload();
@@ -440,7 +435,11 @@ namespace ServiceLib.ViewModels
} }
else if (Utils.IsLinux()) else if (Utils.IsLinux())
{ {
return _config.TunModeItem.LinuxSudoPassword.IsNotEmpty(); return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
}
else if (Utils.IsOSX())
{
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
} }
return false; return false;
} }
@@ -452,29 +451,24 @@ namespace ServiceLib.ViewModels
public async Task InboundDisplayStatus() public async Task InboundDisplayStatus()
{ {
StringBuilder sb = new(); StringBuilder sb = new();
sb.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]"); sb.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}");
sb.Append(" | "); if (_config.Inbound.First().SecondLocalPortEnabled)
sb.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http)}]"); {
sb.Append($",{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}");
}
sb.Append(']');
InboundDisplay = $"{ResUI.LabLocal}:{sb}"; InboundDisplay = $"{ResUI.LabLocal}:{sb}";
if (_config.Inbound[0].AllowLANConn) if (_config.Inbound.First().AllowLANConn)
{ {
if (_config.Inbound[0].NewPort4LAN) var lan = _config.Inbound.First().NewPort4LAN
{ ? $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks3)}]"
StringBuilder sb2 = new(); : $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]";
sb2.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}]"); InboundLanDisplay = $"{ResUI.LabLAN}:{lan}";
sb2.Append(" | ");
sb2.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http2)}]");
InboundLanDisplay = $"{ResUI.LabLAN}:{sb2}";
}
else
{
InboundLanDisplay = $"{ResUI.LabLAN}:{sb}";
}
} }
else else
{ {
InboundLanDisplay = $"{ResUI.LabLAN}:None"; InboundLanDisplay = $"{ResUI.LabLAN}:{Global.None}";
} }
} }

18
v2rayN/build-linux.sh Normal file
View File

@@ -0,0 +1,18 @@
#!/bin/sh
echo 'Building Linux'
OutputPath='./bin/v2rayN'
OutputPath64="${OutputPath}/linux-x64"
OutputPathArm64="${OutputPath}/linux-arm64"
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-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64
rm -rf "$OutputPath64/*.pdb"
rm -rf "$OutputPathArm64/*.pdb"
echo 'Build done'
7z a v2rayN-linux.zip $OutputPath
exit 0

18
v2rayN/build-osx.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/sh
echo 'Building macOS'
OutputPath='./bin/v2rayN'
OutputPath64="${OutputPath}/osx-x64"
OutputPathArm64="${OutputPath}/osx-arm64"
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-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o $OutputPathArm64
rm -rf "$OutputPath64/*.pdb"
rm -rf "$OutputPathArm64/*.pdb"
echo 'Build done'
7z a v2rayN-osx.zip $OutputPath
exit 0

View File

@@ -2,41 +2,41 @@ param (
[Parameter()] [Parameter()]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[string] [string]
$OutputPath = '.\bin\v2rayN' $OutputPathWin64 = './bin/v2rayN/win-x64',
$OutputPathWinArm64 = './bin/v2rayN/win-arm64'
) )
Write-Host 'Building' Write-Host 'Building Windows'
dotnet publish ` dotnet publish `
.\v2rayN\v2rayN.csproj ` ./v2rayN/v2rayN.csproj `
-c Release ` -c Release `
-r win-x64 ` -r win-x64 `
--self-contained false ` --self-contained false `
-p:PublishReadyToRun=false ` -p:PublishReadyToRun=false `
-p:PublishSingleFile=true ` -p:PublishSingleFile=true `
-o "$OutputPath\win-x64" -o $OutputPathWin64
dotnet publish ` dotnet publish `
.\v2rayN.Desktop\v2rayN.Desktop.csproj ` ./v2rayN/v2rayN.csproj `
-c Release ` -c Release `
-r linux-x64 ` -r win-arm64 `
--self-contained true ` --self-contained false `
-p:PublishReadyToRun=false ` -p:PublishReadyToRun=false `
-p:PublishSingleFile=true ` -p:PublishSingleFile=true `
-o "$OutputPath\linux-x64" -o $OutputPathWinArm64
if ( -Not $? ) { if ( -Not $? ) {
exit $lastExitCode exit $lastExitCode
} }
if ( Test-Path -Path .\bin\v2rayN ) { if ( Test-Path -Path ./bin/v2rayN ) {
rm -Force "$OutputPath\win-x64\*.pdb" rm -Force "$OutputPathWin64/*.pdb"
rm -Force "$OutputPath\linux-x64\*.pdb" rm -Force "$OutputPathWinArm64/*.pdb"
} }
Write-Host 'Build done' Write-Host 'Build done'
ls $OutputPath 7z a v2rayN-windows-64.zip $OutputPathWin64
7z a v2rayN.zip $OutputPath 7z a v2rayN-windows-arm64.zip $OutputPathWinArm64
exit 0 exit 0

View File

@@ -56,13 +56,13 @@ public partial class App : Application
{ {
} }
private void MenuAddServerViaClipboardClick(object? sender, EventArgs e) private async void MenuAddServerViaClipboardClick(object? sender, EventArgs e)
{ {
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
if (desktop.MainWindow != null) if (desktop.MainWindow != null)
{ {
var clipboardData = AvaUtils.GetClipboardData(desktop.MainWindow).Result; var clipboardData = await AvaUtils.GetClipboardData(desktop.MainWindow);
var service = Locator.Current.GetService<MainWindowViewModel>(); var service = Locator.Current.GetService<MainWindowViewModel>();
if (service != null) _ = service.AddServerViaClipboardAsync(clipboardData); if (service != null) _ = service.AddServerViaClipboardAsync(clipboardData);
} }

View File

@@ -6,18 +6,18 @@
</Design.PreviewWith> </Design.PreviewWith>
<Style Selector="TextBlock.Margin8"> <Style Selector="TextBlock.Margin8">
<Setter Property="Margin" Value="10" /> <Setter Property="Margin" Value="8" />
</Style> </Style>
<Style Selector="StackPanel.Margin8"> <Style Selector="StackPanel.Margin8">
<Setter Property="Margin" Value="10" /> <Setter Property="Margin" Value="8" />
</Style> </Style>
<Style Selector="DockPanel.Margin8"> <Style Selector="DockPanel.Margin8">
<Setter Property="Margin" Value="10" /> <Setter Property="Margin" Value="8" />
</Style> </Style>
<Style Selector="WrapPanel.Margin8"> <Style Selector="WrapPanel.Margin8">
<Setter Property="Margin" Value="10" /> <Setter Property="Margin" Value="8" />
</Style> </Style>
<Style Selector="Grid.Margin8"> <Style Selector="Grid.Margin8">
<Setter Property="Margin" Value="10" /> <Setter Property="Margin" Value="8" />
</Style> </Style>
</Styles> </Styles>

View File

@@ -1,6 +1,5 @@
using Avalonia; using Avalonia;
using Avalonia.Media; using Avalonia.Media;
using System.Reflection;
namespace v2rayN.Desktop.Common namespace v2rayN.Desktop.Common
{ {
@@ -8,7 +7,7 @@ namespace v2rayN.Desktop.Common
{ {
public static AppBuilder WithFontByDefault(this AppBuilder appBuilder) public static AppBuilder WithFontByDefault(this AppBuilder appBuilder)
{ {
var uri = $"avares://{Assembly.GetExecutingAssembly().GetName().Name}/Assets/Fonts#Noto Sans SC"; var uri = Path.Combine(Global.AvaAssets, "Fonts#Noto Sans SC");
return appBuilder.With(new FontManagerOptions() return appBuilder.With(new FontManagerOptions()
{ {
DefaultFamilyName = uri, DefaultFamilyName = uri,

View File

@@ -3,7 +3,6 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using System.Reflection;
namespace v2rayN.Desktop.Common namespace v2rayN.Desktop.Common
{ {
@@ -41,7 +40,7 @@ namespace v2rayN.Desktop.Common
public static WindowIcon GetAppIcon(ESysProxyType sysProxyType) public static WindowIcon GetAppIcon(ESysProxyType sysProxyType)
{ {
var index = (int)sysProxyType + 1; var index = (int)sysProxyType + 1;
var uri = new Uri($"avares://{Assembly.GetExecutingAssembly().GetName().Name}/Assets/NotifyIcon{index}.ico"); var uri = new Uri(Path.Combine(Global.AvaAssets, $"NotifyIcon{index}.ico"));
using var bitmap = new Bitmap(AssetLoader.Open(uri)); using var bitmap = new Bitmap(AssetLoader.Open(uri));
return new(bitmap); return new(bitmap);
} }

View File

@@ -13,6 +13,7 @@ namespace v2rayN.Desktop.ViewModels
public class ThemeSettingViewModel : MyReactiveObject public class ThemeSettingViewModel : MyReactiveObject
{ {
[Reactive] public bool ColorModeDark { get; set; } [Reactive] public bool ColorModeDark { get; set; }
[Reactive] public bool FollowSystemTheme { get; set; }
[Reactive] public int CurrentFontSize { get; set; } [Reactive] public int CurrentFontSize { get; set; }
@@ -28,13 +29,14 @@ namespace v2rayN.Desktop.ViewModels
private void RestoreUI() private void RestoreUI()
{ {
ModifyTheme(_config.UiItem.ColorModeDark); ModifyTheme();
ModifyFontFamily(); ModifyFontFamily();
} }
private void BindingUI() private void BindingUI()
{ {
ColorModeDark = _config.UiItem.ColorModeDark; ColorModeDark = _config.UiItem.ColorModeDark;
FollowSystemTheme = _config.UiItem.FollowSystemTheme;
CurrentFontSize = _config.UiItem.CurrentFontSize; CurrentFontSize = _config.UiItem.CurrentFontSize;
CurrentLanguage = _config.UiItem.CurrentLanguage; CurrentLanguage = _config.UiItem.CurrentLanguage;
@@ -44,7 +46,18 @@ namespace v2rayN.Desktop.ViewModels
if (_config.UiItem.ColorModeDark != ColorModeDark) if (_config.UiItem.ColorModeDark != ColorModeDark)
{ {
_config.UiItem.ColorModeDark = ColorModeDark; _config.UiItem.ColorModeDark = ColorModeDark;
ModifyTheme(ColorModeDark); ModifyTheme();
ConfigHandler.SaveConfig(_config);
}
});
this.WhenAnyValue(x => x.FollowSystemTheme,
y => y == true)
.Subscribe(c =>
{
if (_config.UiItem.FollowSystemTheme != FollowSystemTheme)
{
_config.UiItem.FollowSystemTheme = FollowSystemTheme;
ModifyTheme();
ConfigHandler.SaveConfig(_config); ConfigHandler.SaveConfig(_config);
} }
}); });
@@ -59,7 +72,6 @@ namespace v2rayN.Desktop.ViewModels
_config.UiItem.CurrentFontSize = CurrentFontSize; _config.UiItem.CurrentFontSize = CurrentFontSize;
double size = CurrentFontSize; double size = CurrentFontSize;
ModifyFontSize(size); ModifyFontSize(size);
ConfigHandler.SaveConfig(_config); ConfigHandler.SaveConfig(_config);
} }
}); });
@@ -79,12 +91,12 @@ namespace v2rayN.Desktop.ViewModels
}); });
} }
private void ModifyTheme(bool isDarkTheme) private void ModifyTheme()
{ {
var app = Application.Current; var app = Application.Current;
if (app is not null) if (app is not null)
{ {
app.RequestedThemeVariant = isDarkTheme ? ThemeVariant.Dark : ThemeVariant.Light; app.RequestedThemeVariant = FollowSystemTheme ? ThemeVariant.Default : (ColorModeDark ? ThemeVariant.Dark : ThemeVariant.Light);
} }
} }
@@ -97,7 +109,8 @@ namespace v2rayN.Desktop.ViewModels
x.OfType<Menu>(), x.OfType<Menu>(),
x.OfType<ContextMenu>(), x.OfType<ContextMenu>(),
x.OfType<DataGridRow>(), x.OfType<DataGridRow>(),
x.OfType<ListBoxItem>() x.OfType<ListBoxItem>(),
x.OfType<HeaderedContentControl>()
)); ));
style.Add(new Setter() style.Add(new Setter()
{ {
@@ -125,6 +138,7 @@ namespace v2rayN.Desktop.ViewModels
x.OfType<ContextMenu>(), x.OfType<ContextMenu>(),
x.OfType<DataGridRow>(), x.OfType<DataGridRow>(),
x.OfType<ListBoxItem>(), x.OfType<ListBoxItem>(),
x.OfType<HeaderedContentControl>(),
x.OfType<WindowNotificationManager>() x.OfType<WindowNotificationManager>()
)); ));
style.Add(new Setter() style.Add(new Setter()

View File

@@ -34,147 +34,126 @@
IsCancel="True" /> IsCancel="True" />
</StackPanel> </StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid> <Grid ColumnDefinitions="Auto,Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
Classes="Margin8" Classes="Margin8"
Text="{x:Static resx:ResUI.menuServers}" /> Text="{x:Static resx:ResUI.menuServers}" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Classes="Margin8"
Text="{x:Static resx:ResUI.TbRemarks}" /> Text="{x:Static resx:ResUI.TbRemarks}" />
<TextBox <TextBox
x:Name="txtRemarks" x:Name="txtRemarks"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" /> Classes="Margin8" />
<TextBlock <TextBlock
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Classes="Margin8"
Text="{x:Static resx:ResUI.TbAddress}" /> Text="{x:Static resx:ResUI.TbAddress}" />
<TextBox <TextBox
x:Name="txtAddress" x:Name="txtAddress"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Classes="Margin8"
IsReadOnly="True" /> IsReadOnly="True" />
<StackPanel <StackPanel
Grid.Row="2" Grid.Row="2"
Grid.Column="2" Grid.Column="2"
VerticalAlignment="Center" VerticalAlignment="Center"
Orientation="Horizontal"> Orientation="Horizontal">
<Button <Button
x:Name="btnBrowse" x:Name="btnBrowse"
Margin="2,0" Margin="2,0"
Content="{x:Static resx:ResUI.TbBrowse}" /> Content="{x:Static resx:ResUI.TbBrowse}" />
<Button <Button
x:Name="btnEdit" x:Name="btnEdit"
Margin="2,0" Margin="2,0"
Content="{x:Static resx:ResUI.TbEdit}" /> Content="{x:Static resx:ResUI.TbEdit}" />
</StackPanel> </StackPanel>
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Classes="Margin8"
Text="{x:Static resx:ResUI.TbCoreType}" /> Text="{x:Static resx:ResUI.TbCoreType}" />
<ComboBox <ComboBox
x:Name="cmbCoreType" x:Name="cmbCoreType"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" Classes="Margin8"
MaxDropDownHeight="1000" /> MaxDropDownHeight="1000" />
<TextBlock <TextBlock
Grid.Row="4" Grid.Row="4"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Classes="Margin8"
Text="{x:Static resx:ResUI.TbDisplayLog}" /> Text="{x:Static resx:ResUI.TbDisplayLog}" />
<StackPanel <StackPanel
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Classes="Margin8" Classes="Margin8"
Orientation="Horizontal"> Orientation="Horizontal">
<ToggleSwitch <ToggleSwitch
x:Name="togDisplayLog" x:Name="togDisplayLog"
HorizontalAlignment="Left"
Classes="Margin8" />
<TextBlock
Margin="8,0"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TipDisplayLog}" />
</StackPanel>
<TextBlock
Grid.Row="5"
Grid.Column="0"
VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbPreSocksPort}" />
<TextBox
x:Name="txtPreSocksPort"
Grid.Row="5"
Grid.Column="1"
Width="200"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" /> Classes="Margin8" />
<StackPanel <TextBlock
Grid.Row="6" Margin="8,0"
Grid.Column="1" VerticalAlignment="Center"
Grid.ColumnSpan="2" Text="{x:Static resx:ResUI.TipDisplayLog}" />
Classes="Margin8"> </StackPanel>
<TextBlock
Width="500" <TextBlock
VerticalAlignment="Center" Grid.Row="5"
Text="{x:Static resx:ResUI.TipPreSocksPort}" Grid.Column="0"
TextWrapping="Wrap" /> VerticalAlignment="Center"
<TextBlock Classes="Margin8"
Width="500" Text="{x:Static resx:ResUI.TbPreSocksPort}" />
Margin="8,0" <TextBox
VerticalAlignment="Center" x:Name="txtPreSocksPort"
Text="{x:Static resx:ResUI.CustomServerTips}" Grid.Row="5"
TextWrapping="Wrap" /> Grid.Column="1"
</StackPanel> Width="200"
</Grid> HorizontalAlignment="Left"
Classes="Margin8" />
<StackPanel
Grid.Row="6"
Grid.Column="1"
Grid.ColumnSpan="2"
Classes="Margin8">
<TextBlock
Width="500"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TipPreSocksPort}"
TextWrapping="Wrap" />
<TextBlock
Width="500"
Margin="8,0"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.CustomServerTips}"
TextWrapping="Wrap" />
</StackPanel>
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</DockPanel> </DockPanel>

View File

@@ -8,7 +8,7 @@
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuServers}" Title="{x:Static resx:ResUI.menuServers}"
Width="900" Width="900"
Height="700" Height="600"
x:DataType="vms:AddServerViewModel" x:DataType="vms:AddServerViewModel"
ShowInTaskbar="False" ShowInTaskbar="False"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
@@ -34,31 +34,12 @@
IsCancel="True" /> IsCancel="True" />
</StackPanel> </StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid> <Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0"> <Grid
<Grid.RowDefinitions> Grid.Row="0"
<RowDefinition Height="Auto" /> ColumnDefinitions="180,Auto,Auto"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
@@ -122,18 +103,9 @@
<Grid <Grid
x:Name="gridVMess" x:Name="gridVMess"
Grid.Row="2" Grid.Row="2"
IsVisible="False"> ColumnDefinitions="180,Auto,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@@ -184,17 +156,9 @@
<Grid <Grid
x:Name="gridSs" x:Name="gridSs"
Grid.Row="2" Grid.Row="2"
IsVisible="False"> ColumnDefinitions="180,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@@ -225,17 +189,9 @@
<Grid <Grid
x:Name="gridSocks" x:Name="gridSocks"
Grid.Row="2" Grid.Row="2"
IsVisible="False"> ColumnDefinitions="180,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@@ -266,18 +222,9 @@
<Grid <Grid
x:Name="gridVLESS" x:Name="gridVLESS"
Grid.Row="2" Grid.Row="2"
IsVisible="False"> ColumnDefinitions="180,Auto,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@@ -328,17 +275,9 @@
<Grid <Grid
x:Name="gridTrojan" x:Name="gridTrojan"
Grid.Row="2" Grid.Row="2"
IsVisible="False"> ColumnDefinitions="180,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@@ -369,17 +308,9 @@
<Grid <Grid
x:Name="gridHysteria2" x:Name="gridHysteria2"
Grid.Row="2" Grid.Row="2"
IsVisible="False"> ColumnDefinitions="180,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@@ -410,17 +341,9 @@
<Grid <Grid
x:Name="gridTuic" x:Name="gridTuic"
Grid.Row="2" Grid.Row="2"
IsVisible="False"> ColumnDefinitions="180,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@@ -464,19 +387,9 @@
<Grid <Grid
x:Name="gridWireguard" x:Name="gridWireguard"
Grid.Row="2" Grid.Row="2"
IsVisible="False"> ColumnDefinitions="180,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@@ -551,22 +464,11 @@
Grid.Row="3" Grid.Row="3"
Margin="0,10" /> Margin="0,10" />
<Grid x:Name="gridTransport" Grid.Row="4"> <Grid
<Grid.RowDefinitions> x:Name="gridTransport"
<RowDefinition Height="Auto" /> Grid.Row="4"
<RowDefinition Height="Auto" /> ColumnDefinitions="180,Auto,Auto"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
@@ -695,18 +597,11 @@
<Separator Grid.Row="5" Margin="0,10" /> <Separator Grid.Row="5" Margin="0,10" />
<Grid x:Name="gridTls" Grid.Row="6"> <Grid
<Grid.RowDefinitions> x:Name="gridTls"
<RowDefinition Height="Auto" /> Grid.Row="6"
<RowDefinition Height="Auto" /> ColumnDefinitions="180,Auto"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
@@ -724,18 +619,9 @@
<Grid <Grid
x:Name="gridTlsMore" x:Name="gridTlsMore"
Grid.Row="7" Grid.Row="7"
IsVisible="False"> ColumnDefinitions="180,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
@@ -793,18 +679,9 @@
<Grid <Grid
x:Name="gridRealityMore" x:Name="gridRealityMore"
Grid.Row="7" Grid.Row="7"
IsVisible="False"> ColumnDefinitions="180,Auto"
<Grid.RowDefinitions> IsVisible="False"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"

View File

@@ -26,20 +26,10 @@ namespace v2rayN.Desktop.Views
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler); ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
if (profileItem.ConfigType == EConfigType.VLESS) Global.CoreTypes.ForEach(it =>
{ {
Global.CoreTypes4VLESS.ForEach(it => cmbCoreType.Items.Add(it);
{ });
cmbCoreType.Items.Add(it);
});
}
else
{
Global.CoreTypes.ForEach(it =>
{
cmbCoreType.Items.Add(it);
});
}
cmbCoreType.Items.Add(string.Empty); cmbCoreType.Items.Add(string.Empty);
cmbStreamSecurity.Items.Add(string.Empty); cmbStreamSecurity.Items.Add(string.Empty);
@@ -209,7 +199,7 @@ namespace v2rayN.Desktop.Views
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.SelectedValue).DisposeWith(disposables);
@@ -300,7 +290,7 @@ namespace v2rayN.Desktop.Views
cmbHeaderType.Items.Add(it); cmbHeaderType.Items.Add(it);
}); });
} }
else if (network is nameof(ETransport.splithttp) or nameof(ETransport.xhttp)) else if (network is nameof(ETransport.xhttp))
{ {
Global.XhttpMode.ForEach(it => Global.XhttpMode.ForEach(it =>
{ {
@@ -350,7 +340,6 @@ namespace v2rayN.Desktop.Views
tipPath.Text = ResUI.TransportPathTip1; tipPath.Text = ResUI.TransportPathTip1;
break; break;
case nameof(ETransport.splithttp):
case nameof(ETransport.xhttp): case nameof(ETransport.xhttp):
tipRequestHost.Text = ResUI.TransportRequestHostTip2; tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1; tipPath.Text = ResUI.TransportPathTip1;

View File

@@ -25,17 +25,10 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Theme="{StaticResource CardBorder}"> Theme="{StaticResource CardBorder}">
<Grid Classes="Margin8"> <Grid
<Grid.RowDefinitions> Classes="Margin8"
<RowDefinition Height="Auto" /> ColumnDefinitions="300,200"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
@@ -77,18 +70,10 @@
Margin="4" Margin="4"
VerticalAlignment="Center" VerticalAlignment="Center"
Theme="{StaticResource CardBorder}"> Theme="{StaticResource CardBorder}">
<Grid Classes="Margin8"> <Grid
<Grid.RowDefinitions> Classes="Margin8"
<RowDefinition Height="Auto" /> ColumnDefinitions="300,200"
<RowDefinition Height="Auto" /> RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
@@ -111,18 +96,7 @@
<Button.Flyout> <Button.Flyout>
<Flyout> <Flyout>
<StackPanel> <StackPanel>
<Grid> <Grid ColumnDefinitions="Auto,300" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="300" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"

View File

@@ -50,18 +50,10 @@
<Border <Border
Width="500" Width="500"
Height="80" Height="80"
Margin="0" Margin="-8"
VerticalAlignment="Center" VerticalAlignment="Center"
Theme="{StaticResource CardBorder}"> Theme="{StaticResource CardBorder}">
<Grid> <Grid ColumnDefinitions="1*,1*,3*" RowDefinitions="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<ToggleSwitch <ToggleSwitch
x:Name="togAutoRefresh" x:Name="togAutoRefresh"
Grid.Column="0" Grid.Column="0"

View File

@@ -25,22 +25,6 @@
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
Watermark="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" /> Watermark="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" />
<TextBlock
Margin="8,0"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSorting}" />
<ComboBox
x:Name="cmbSorting"
Width="100"
Margin="8,0">
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpSpeed}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownSpeed}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpTraffic}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownTraffic}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingTime}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingHost}" />
</ComboBox>
<Button <Button
x:Name="btnConnectionCloseAll" x:Name="btnConnectionCloseAll"
Width="30" Width="30"
@@ -58,6 +42,23 @@
</Button.Content> </Button.Content>
</Button> </Button>
<Button
x:Name="btnAutofitColumnWidth"
Width="30"
Height="30"
Margin="8,0"
Classes="Success"
Theme="{DynamicResource BorderlessButton}"
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
<Button.Content>
<PathIcon
Width="20"
Height="20"
Data="{StaticResource building_fit}"
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Button.Content>
</Button>
<TextBlock <TextBlock
Margin="8,0" Margin="8,0"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -85,11 +86,11 @@
</DataGrid.ContextMenu> </DataGrid.ContextMenu>
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn <DataGridTextColumn
Width="240" Width="300"
Binding="{Binding Host}" Binding="{Binding Host}"
Header="{x:Static resx:ResUI.TbSortingHost}" /> Header="{x:Static resx:ResUI.TbSortingHost}" />
<DataGridTextColumn <DataGridTextColumn
Width="160" Width="500"
Binding="{Binding Chain}" Binding="{Binding Chain}"
Header="{x:Static resx:ResUI.TbSortingChain}" /> Header="{x:Static resx:ResUI.TbSortingChain}" />
<DataGridTextColumn <DataGridTextColumn
@@ -97,17 +98,9 @@
Binding="{Binding Network}" Binding="{Binding Network}"
Header="{x:Static resx:ResUI.TbSortingNetwork}" /> Header="{x:Static resx:ResUI.TbSortingNetwork}" />
<DataGridTextColumn <DataGridTextColumn
Width="100" Width="160"
Binding="{Binding Type}" Binding="{Binding Type}"
Header="{x:Static resx:ResUI.TbSortingType}" /> Header="{x:Static resx:ResUI.TbSortingType}" />
<DataGridTextColumn
Width="100"
Binding="{Binding UploadTraffic}"
Header="{x:Static resx:ResUI.TbSortingUpTraffic}" />
<DataGridTextColumn
Width="100"
Binding="{Binding DownloadTraffic}"
Header="{x:Static resx:ResUI.TbSortingDownTraffic}" />
<DataGridTextColumn <DataGridTextColumn
Width="100" Width="100"
Binding="{Binding Elapsed}" Binding="{Binding Elapsed}"

View File

@@ -1,3 +1,4 @@
using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading; using Avalonia.Threading;
@@ -12,6 +13,7 @@ namespace v2rayN.Desktop.Views
{ {
InitializeComponent(); InitializeComponent();
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler); ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {
@@ -22,7 +24,6 @@ namespace v2rayN.Desktop.Views
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
}); });
@@ -43,6 +44,19 @@ namespace v2rayN.Desktop.Views
return await Task.FromResult(true); return await Task.FromResult(true);
} }
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
{
AutofitColumnWidth();
}
private void AutofitColumnWidth()
{
foreach (var it in lstConnections.Columns)
{
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}
private void btnClose_Click(object? sender, RoutedEventArgs e) private void btnClose_Click(object? sender, RoutedEventArgs e)
{ {
ViewModel?.ClashConnectionClose(false); ViewModel?.ClashConnectionClose(false);

View File

@@ -105,16 +105,11 @@
<DataTemplate> <DataTemplate>
<Border <Border
Width="160" Width="160"
Margin="0" Margin="-6"
Padding="0" Padding="0"
Theme="{StaticResource CardBorder}"> Theme="{StaticResource CardBorder}">
<DockPanel> <DockPanel>
<Grid Grid.Column="0" Classes="Margin8"> <Grid Classes="Margin8" RowDefinitions="1*,8,1*">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="8" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<DockPanel Grid.Row="0"> <DockPanel Grid.Row="0">
<TextBlock DockPanel.Dock="Right" Text="{Binding Type}" /> <TextBlock DockPanel.Dock="Right" Text="{Binding Type}" />
<TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Name}" />
@@ -143,24 +138,18 @@
<DataTemplate> <DataTemplate>
<Border <Border
Width="160" Width="160"
Margin="0" Margin="-6"
Padding="0" Padding="0"
Theme="{StaticResource CardBorder}"> Theme="{StaticResource CardBorder}">
<DockPanel> <DockPanel>
<Border <Border
Width="5" Width="5"
Height="30"
Margin="0,1" Margin="0,1"
Background="YellowGreen" Background="YellowGreen"
CornerRadius="4" CornerRadius="4"
DockPanel.Dock="Left" DockPanel.Dock="Left"
IsVisible="{Binding IsActive}" /> IsVisible="{Binding IsActive}" />
<Grid Classes="Margin8"> <Grid Classes="Margin8" RowDefinitions="1*,8,1*">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="8" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}" /> <TextBlock Grid.Row="0" Text="{Binding Name}" />
<DockPanel Grid.Row="2"> <DockPanel Grid.Row="2">
<TextBlock <TextBlock

View File

@@ -7,8 +7,8 @@
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="{x:Static resx:ResUI.menuDNSSetting}" Title="{x:Static resx:ResUI.menuDNSSetting}"
Width="1000" Width="900"
Height="700" Height="600"
x:DataType="vms:DNSSettingViewModel" x:DataType="vms:DNSSettingViewModel"
ShowInTaskbar="False" ShowInTaskbar="False"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
@@ -141,12 +141,7 @@
</StackPanel> </StackPanel>
</WrapPanel> </WrapPanel>
<Grid Classes="Margin8"> <Grid Classes="Margin8" ColumnDefinitions="*,10,*">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox <TextBox
x:Name="txtnormalDNS2" x:Name="txtnormalDNS2"

View File

@@ -39,25 +39,12 @@
IsCancel="True" /> IsCancel="True" />
</StackPanel> </StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid> <Grid RowDefinitions="Auto,Auto,Auto">
<Grid.RowDefinitions> <Grid
<RowDefinition Height="Auto" /> x:Name="gridText"
<RowDefinition Height="Auto" /> Grid.Row="0"
<RowDefinition Height="Auto" /> ColumnDefinitions="Auto,400"
</Grid.RowDefinitions> RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
<Grid x:Name="gridText" Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="400" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"

View File

@@ -10,7 +10,7 @@
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="v2rayN" Title="v2rayN"
Width="900" Width="900"
Height="700" Height="600"
MinWidth="900" MinWidth="900"
x:DataType="vms:MainWindowViewModel" x:DataType="vms:MainWindowViewModel"
Icon="/Assets/NotifyIcon1.ico" Icon="/Assets/NotifyIcon1.ico"
@@ -124,12 +124,10 @@
<view:StatusBarView DockPanel.Dock="Bottom" /> <view:StatusBarView DockPanel.Dock="Bottom" />
<Grid> <Grid>
<Grid x:Name="gridMain" IsVisible="False"> <Grid
<Grid.ColumnDefinitions> x:Name="gridMain"
<ColumnDefinition Width="1*" /> ColumnDefinitions="1*,10,1*"
<ColumnDefinition Width="10" /> IsVisible="False">
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<ContentControl x:Name="tabProfiles" Grid.Column="0" /> <ContentControl x:Name="tabProfiles" Grid.Column="0" />
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" /> <GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
<TabControl <TabControl
@@ -141,12 +139,10 @@
<TabItem x:Name="tabClashConnections" Header="{x:Static resx:ResUI.TbConnections}" /> <TabItem x:Name="tabClashConnections" Header="{x:Static resx:ResUI.TbConnections}" />
</TabControl> </TabControl>
</Grid> </Grid>
<Grid x:Name="gridMain1" IsVisible="False"> <Grid
<Grid.RowDefinitions> x:Name="gridMain1"
<RowDefinition Height="1*" /> IsVisible="False"
<RowDefinition Height="10" /> RowDefinitions="1*,10,1*">
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<ContentControl x:Name="tabProfiles1" Grid.Row="0" /> <ContentControl x:Name="tabProfiles1" Grid.Row="0" />
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" /> <GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" />
<TabControl <TabControl

View File

@@ -21,6 +21,7 @@ namespace v2rayN.Desktop.Views
private WindowNotificationManager? _manager; private WindowNotificationManager? _manager;
private CheckUpdateView? _checkUpdateView; private CheckUpdateView? _checkUpdateView;
private BackupAndRestoreView? _backupAndRestoreView; private BackupAndRestoreView? _backupAndRestoreView;
private bool _blCloseByUser = false;
public MainWindow() public MainWindow()
{ {
@@ -40,7 +41,34 @@ namespace v2rayN.Desktop.Views
ViewModel = new MainWindowViewModel(UpdateViewHandler); ViewModel = new MainWindowViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel)); Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
//WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null); switch (_config.UiItem.MainGirdOrientation)
{
case EGirdOrientation.Horizontal:
tabProfiles.Content ??= new ProfilesView(this);
tabMsgView.Content ??= new MsgView();
tabClashProxies.Content ??= new ClashProxiesView();
tabClashConnections.Content ??= new ClashConnectionsView();
gridMain.IsVisible = true;
break;
case EGirdOrientation.Vertical:
tabProfiles1.Content ??= new ProfilesView(this);
tabMsgView1.Content ??= new MsgView();
tabClashProxies1.Content ??= new ClashProxiesView();
tabClashConnections1.Content ??= new ClashConnectionsView();
gridMain1.IsVisible = true;
break;
case EGirdOrientation.Tab:
default:
tabProfiles2.Content ??= new ProfilesView(this);
tabMsgView2.Content ??= new MsgView();
tabClashProxies2.Content ??= new ClashProxiesView();
tabClashConnections2.Content ??= new ClashConnectionsView();
gridMain2.IsVisible = true;
break;
}
conTheme.Content ??= new ThemeSettingView();
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {
@@ -83,7 +111,6 @@ namespace v2rayN.Desktop.Views
switch (_config.UiItem.MainGirdOrientation) switch (_config.UiItem.MainGirdOrientation)
{ {
case EGirdOrientation.Horizontal: case EGirdOrientation.Horizontal:
gridMain.IsVisible = true;
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.IsVisible).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.IsVisible).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.IsVisible).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.IsVisible).DisposeWith(disposables);
@@ -91,7 +118,6 @@ namespace v2rayN.Desktop.Views
break; break;
case EGirdOrientation.Vertical: case EGirdOrientation.Vertical:
gridMain1.IsVisible = true;
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.IsVisible).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.IsVisible).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.IsVisible).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.IsVisible).DisposeWith(disposables);
@@ -100,7 +126,6 @@ namespace v2rayN.Desktop.Views
case EGirdOrientation.Tab: case EGirdOrientation.Tab:
default: default:
gridMain2.IsVisible = true;
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.IsVisible).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.IsVisible).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.IsVisible).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
@@ -128,34 +153,10 @@ namespace v2rayN.Desktop.Views
} }
menuAddServerViaScan.IsVisible = false; menuAddServerViaScan.IsVisible = false;
switch (_config.UiItem.MainGirdOrientation)
{
case EGirdOrientation.Horizontal:
tabProfiles.Content ??= new ProfilesView(this);
tabMsgView.Content ??= new MsgView();
tabClashProxies.Content ??= new ClashProxiesView();
tabClashConnections.Content ??= new ClashConnectionsView();
break;
case EGirdOrientation.Vertical:
tabProfiles1.Content ??= new ProfilesView(this);
tabMsgView1.Content ??= new MsgView();
tabClashProxies1.Content ??= new ClashProxiesView();
tabClashConnections1.Content ??= new ClashConnectionsView();
break;
case EGirdOrientation.Tab:
default:
tabProfiles2.Content ??= new ProfilesView(this);
tabMsgView2.Content ??= new MsgView();
tabClashProxies2.Content ??= new ClashProxiesView();
tabClashConnections2.Content ??= new ClashConnectionsView();
break;
}
conTheme.Content ??= new ThemeSettingView();
RestoreUI(); RestoreUI();
AddHelpMenuItem(); AddHelpMenuItem();
//WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
} }
#region Event #region Event
@@ -279,6 +280,11 @@ namespace v2rayN.Desktop.Views
protected override async void OnClosing(WindowClosingEventArgs e) protected override async void OnClosing(WindowClosingEventArgs e)
{ {
if (_blCloseByUser)
{
return;
}
Logging.SaveLog("OnClosing -> " + e.CloseReason.ToString()); Logging.SaveLog("OnClosing -> " + e.CloseReason.ToString());
switch (e.CloseReason) switch (e.CloseReason)
@@ -374,6 +380,8 @@ namespace v2rayN.Desktop.Views
{ {
return; return;
} }
_blCloseByUser = true;
StorageUI(); StorageUI();
await ViewModel?.MyAppExitAsync(false); await ViewModel?.MyAppExitAsync(false);
@@ -398,7 +406,7 @@ namespace v2rayN.Desktop.Views
} }
else else
{ {
if (Utils.IsWindows()) if (_config.UiItem.Hide2TrayWhenClose)
{ {
this.Hide(); this.Hide();
} }
@@ -434,7 +442,7 @@ namespace v2rayN.Desktop.Views
} }
} }
private void StorageUI() private void StorageUI(string? n = null)
{ {
_config.UiItem.MainWidth = Utils.ToInt(this.Width); _config.UiItem.MainWidth = Utils.ToInt(this.Width);
_config.UiItem.MainHeight = Utils.ToInt(this.Height); _config.UiItem.MainHeight = Utils.ToInt(this.Height);
@@ -449,7 +457,6 @@ namespace v2rayN.Desktop.Views
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain1.RowDefinitions[0].ActualHeight + 0.1); _config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain1.RowDefinitions[0].ActualHeight + 0.1);
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain1.RowDefinitions[2].ActualHeight + 0.1); _config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain1.RowDefinitions[2].ActualHeight + 0.1);
} }
ConfigHandler.SaveConfig(_config);
} }
private void AddHelpMenuItem() private void AddHelpMenuItem()

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