Compare commits

..

89 Commits

Author SHA1 Message Date
2dust
2ab1b9068f up 7.12.4 2025-05-21 20:00:41 +08:00
DHR60
b9613875ce Determine .exe suffix based on OS in GenRoutingDirectExe (#7322)
* Determine .exe suffix based on OS in GenRoutingDirectExe

* Uses Utils.GetExeName
2025-05-21 19:57:23 +08:00
2dust
5d2aea6b4f Change the inbound of the xray configuration file from socks to mixed 2025-05-17 14:51:18 +08:00
Pk-web6936
5824e18ed6 Update Persian translate (#7273) 2025-05-15 19:21:53 +08:00
2dust
4f8648cbc9 Fix
https://github.com/2dust/v2rayN/issues/7270
2025-05-12 20:29:34 +08:00
2dust
01b021b2c3 Bug fix
https://github.com/2dust/v2rayN/issues/7279
2025-05-12 20:28:28 +08:00
2dust
331e8ce960 up 7.12.3 2025-05-11 19:19:26 +08:00
2dust
a2cfe6fa51 Added the current connection information test url option
https://github.com/2dust/v2rayN/discussions/7268
2025-05-11 16:59:00 +08:00
2dust
8381fefb78 up 7.12.2 2025-05-11 10:39:02 +08:00
2dust
d3b95d781a Removed the function of displaying the current connection IP
https://github.com/2dust/v2rayN/discussions/7268
2025-05-11 10:38:48 +08:00
2dust
3a4a96f87a Fix
https://github.com/2dust/v2rayN/issues/7258
2025-05-09 14:33:41 +08:00
2dust
3d462c4be3 Fix
https://github.com/2dust/v2rayN/issues/7247
2025-05-08 15:56:52 +08:00
2dust
82b366cd9b Fix
https://github.com/2dust/v2rayN/issues/7244
2025-05-07 14:28:55 +08:00
DHR60
897a4e5635 Move exe direct rule before clash_mode (#7236) 2025-05-05 13:59:14 +08:00
2dust
8ea76fd318 up 7.12.1 2025-05-04 17:44:36 +08:00
2dust
693a96fff2 Update Directory.Packages.props 2025-05-04 17:44:04 +08:00
DHR60
8b4e2f8f23 Fix DNS (#7233)
* Fix DNS

* Removes expectIPs from remote DNS
2025-05-04 17:28:57 +08:00
2dust
13b164acac Enhanced sorting function, can sort by statistics 2025-05-03 11:29:36 +08:00
2dust
e590547b30 Revert "Bug fix"
This reverts commit 5a0fdd971a.
2025-05-02 10:44:20 +08:00
2dust
5a0fdd971a Bug fix
https://github.com/2dust/v2rayN/issues/7211
2025-04-30 14:12:02 +08:00
2dust
514dce960a up 7.12.0 2025-04-28 15:57:09 +08:00
Reza Bakhshi Laktasaraei
6ee6fb1706 Improve Accessibility in StatusBarView and ProfilesView, Update Package Versions (#7199)
* Fix Tab navigation in ToolBar by setting KeyboardNavigation to Continue

* Improve accessibility for ComboBoxes in StatusBarView.xaml

Added ItemContainerStyle to cmbRoutings2 and cmbRoutings to bind AutomationProperties.Name to Remarks, ensuring screen readers announce the correct values instead of the default object type.

* Improve accessibility for cmbServers in StatusBarView.xaml

Added ItemContainerStyle to cmbServers to bind AutomationProperties.Name to Text, ensuring screen readers announce the correct values instead of the default object type.

* Update package versions and fix accessibility in ProfilesView.xaml

- Updated package versions in Directory.Packages.props:
  - Semi.Avalonia and Semi.Avalonia.DataGrid from 11.2.1.6 to 11.2.1.7.
  - ZXing.Net.Bindings.SkiaSharp from 0.16.14 to 0.16.21.
- Fixed MC3024 error in ProfilesView.xaml by creating AccessibleMyChipListBoxItem style:
  - Added AccessibleMyChipListBoxItem style based on MyChipListBoxItem to set AutomationProperties.Name.
  - Replaced ItemContainerStyle with AccessibleMyChipListBoxItem to preserve original appearance.
  - Updated AutomationProperties.Name to use resx:ResUI.menuSubscription for better localization.
  - Removed duplicate AutomationProperties.Name from TextBlock as it's now handled by the style.

* Update Directory.Packages.props

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-04-28 15:25:57 +08:00
2dust
6985328653 Optimize and improve code 2025-04-28 15:16:58 +08:00
DHR60
41c406b84d Revert "Fix xray wireguard chained proxies not working (2dust#7113)" (#7194) 2025-04-27 09:37:46 +08:00
2dust
30f7f2c563 Update Directory.Packages.props 2025-04-26 10:32:20 +08:00
Reza Bakhshi Laktasaraei
be3dbfb8e3 Fix Tab navigation in ToolBar by setting KeyboardNavigation to Continue (#7185) 2025-04-26 10:31:00 +08:00
Pk-web6936
9b92259e80 Update Persian translation (#7191)
0032a3d27a
2025-04-26 10:30:05 +08:00
2dust
1c04144573 Code clean 2025-04-26 09:53:19 +08:00
2dust
adf3b955d6 Refactor Linux to run Tun as sudo 2025-04-26 09:50:31 +08:00
2dust
0032a3d27a Fix kill process for linux 2025-04-25 16:36:28 +08:00
2dust
c6d347d49a Refactor the Linux version to not store sudo password 2025-04-25 15:19:49 +08:00
2dust
ea42246d1b Improved AmazTool 2025-04-25 14:38:33 +08:00
2dust
3f19958c75 Bug fix
https://github.com/2dust/v2rayN/issues/7179
2025-04-24 11:50:24 +08:00
2dust
35788158bc up 7.11.3 2025-04-21 10:17:37 +08:00
2dust
4fd494ded4 Update Directory.Packages.props 2025-04-21 10:15:49 +08:00
2dust
23eeb8ff55 Try to fix
https://github.com/2dust/v2rayN/issues/7128
2025-04-18 12:31:46 +08:00
2dust
456ffb200a up 7.11.2 2025-04-15 10:54:55 +08:00
2dust
18e0bb194e Enhance Accessibility in MsgView 2025-04-15 09:57:04 +08:00
2dust
392f6111dd Update Directory.Packages.props 2025-04-14 11:17:57 +08:00
DHR60
ce6572af3d Fix xray wireguard chained proxies not working (#7113)
* Fix chained proxies not working

* Add domain field for WireGuard exit node
2025-04-12 19:03:34 +08:00
DHR60
cf59137481 fix dns leak (#7110) 2025-04-12 10:00:18 +08:00
DHR60
519e588124 fix #7105 (#7109) 2025-04-12 09:52:20 +08:00
Reza Bakhshi Laktasaraei
666c874998 Add accessibility labels to improve screen reader support (#7105)
* Add accessibility with AutomationProperties.Name to menus

* Add accessibility labels to StatusBarView using ResUI resources

* Add accessibility labels to ProfilesView using ResUI resources
2025-04-11 09:28:34 +08:00
DHR60
5f9f677467 Adjust menu items (#7100)
* Adjust menu items

* fix #7089
2025-04-11 09:27:34 +08:00
2dust
b06b5779dd up 7.11.1 2025-04-09 17:39:34 +08:00
2dust
e3a3b9c201 Improved language res 2025-04-09 17:39:23 +08:00
2dust
321ec30f39 Internationalized code comments 2025-04-09 16:48:38 +08:00
2dust
5adae2dd2a In Chinese and English, the keyword server is changed to Configuration 2025-04-09 16:21:25 +08:00
2dust
be5e15dfb6 Fix
https://github.com/2dust/v2rayN/pull/7089
2025-04-09 15:25:42 +08:00
DHR60
15d3418c79 add xray wireguard support (#7089)
* add xray wireguard support

* add wireguard core type settings

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

* Refactor multi-server configuration UI logic

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

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

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

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

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

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

* Refine default fallback load balancing rule based on domain strategy
2025-03-12 18:36:51 +08:00
2dust
2df412476a If it is a Windows WinGet installation, the configuration file is stored in the user directory
https://github.com/2dust/v2rayN/issues/6803
2025-03-09 19:04:49 +08:00
2dust
e6011cfede AI-optimized code 2025-03-07 12:11:19 +08:00
2dust
bcf43e2928 Enhanced subscription customization configuration
https://github.com/2dust/v2rayN/issues/6875
2025-03-07 11:37:01 +08:00
230 changed files with 26205 additions and 48990 deletions

View File

@@ -32,7 +32,7 @@ jobs:
fetch-depth: '0' fetch-depth: '0'
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.1
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1 uses: actions/upload-artifact@v4.6.2
with: with:
name: v2rayN-linux name: v2rayN-linux
path: | path: |

View File

@@ -32,7 +32,7 @@ jobs:
fetch-depth: '0' fetch-depth: '0'
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.1
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1 uses: actions/upload-artifact@v4.6.2
with: with:
name: v2rayN-macos name: v2rayN-macos
path: | path: |

View File

@@ -32,7 +32,7 @@ jobs:
fetch-depth: '0' fetch-depth: '0'
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.1
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1 uses: actions/upload-artifact@v4.6.2
with: with:
name: v2rayN-windows-desktop name: v2rayN-windows-desktop
path: | path: |

View File

@@ -30,7 +30,7 @@ jobs:
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
- name: Setup - name: Setup
uses: actions/setup-dotnet@v4.3.0 uses: actions/setup-dotnet@v4.3.1
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
@@ -46,7 +46,7 @@ jobs:
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1 uses: actions/upload-artifact@v4.6.2
with: with:
name: v2rayN-windows name: v2rayN-windows
path: | path: |

View File

@@ -1,26 +1,87 @@
namespace AmazTool namespace AmazTool;
{
internal static class Program internal static class Program
{ {
[STAThread] [STAThread]
private static void Main(string[] args) private static void Main(string[] args)
{ {
try
{
// If no arguments are provided, display usage guidelines and exit
if (args.Length == 0) if (args.Length == 0)
{ {
Console.WriteLine(Resx.Resource.Guidelines); ShowHelp();
Thread.Sleep(5000);
return; return;
} }
var argData = Uri.UnescapeDataString(string.Join(" ", args)); // Log all arguments for debugging purposes
if (argData.Equals("rebootas")) foreach (var arg in args)
{ {
Console.WriteLine(arg);
}
// Parse command based on first argument
switch (args[0].ToLowerInvariant())
{
case "rebootas":
// Handle application restart
HandleRebootAsync();
break;
case "help":
case "--help":
case "-h":
case "/?":
// Display help information
ShowHelp();
break;
default:
// Default behavior: handle as upgrade data
// Maintain backward compatibility with existing usage pattern
var argData = Uri.UnescapeDataString(string.Join(" ", args));
HandleUpgrade(argData);
break;
}
}
catch (Exception ex)
{
// Global exception handling
Console.WriteLine($"An error occurred: {ex.Message}");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
/// <summary>
/// Display help information and usage guidelines
/// </summary>
private static void ShowHelp()
{
Console.WriteLine(Resx.Resource.Guidelines);
Console.WriteLine("Available commands:");
Console.WriteLine(" rebootas - Restart the application");
Console.WriteLine(" help - Display this help information");
Thread.Sleep(5000);
}
/// <summary>
/// Handle application restart
/// </summary>
private static void HandleRebootAsync()
{
Console.WriteLine("Restarting application...");
Thread.Sleep(1000); Thread.Sleep(1000);
Utils.StartV2RayN(); Utils.StartV2RayN();
return;
} }
UpgradeApp.Upgrade(argData); /// <summary>
} /// Handle application upgrade with the provided data
/// </summary>
/// <param name="upgradeData">Data for the upgrade process</param>
private static void HandleUpgrade(string upgradeData)
{
Console.WriteLine("Upgrading application...");
UpgradeApp.Upgrade(upgradeData);
} }
} }

View File

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

View File

@@ -2,8 +2,8 @@ using System.Diagnostics;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
namespace AmazTool namespace AmazTool;
{
internal class UpgradeApp internal class UpgradeApp
{ {
public static void Upgrade(string fileName) public static void Upgrade(string fileName)
@@ -114,4 +114,3 @@ namespace AmazTool
Utils.StartV2RayN(); Utils.StartV2RayN();
} }
} }
}

View File

@@ -1,7 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace AmazTool namespace AmazTool;
{
internal class Utils internal class Utils
{ {
public static string GetExePath() public static string GetExePath()
@@ -49,4 +49,3 @@ namespace AmazTool
} }
} }
} }
}

View File

@@ -1,14 +1,14 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.10.4</Version> <Version>7.12.4</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<NoWarn>CA1031;CS1591;NU1507;CA1416</NoWarn> <NoWarn>CA1031;CS1591;NU1507;CA1416;IDE0058</NoWarn>
<Nullable>annotations</Nullable> <Nullable>annotations</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Authors>2dust</Authors> <Authors>2dust</Authors>

View File

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

View File

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

View File

@@ -1,14 +1,14 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class AesUtils public class AesUtils
{ {
private const int KeySize = 256; // AES-256 private const int KeySize = 256; // AES-256
private const int IvSize = 16; // AES block size private const int IvSize = 16; // AES block size
private const int Iterations = 10000; private const int Iterations = 10000;
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' ')); // google浏览器默认盐值 private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' '));
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils"); private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
/// <summary> /// <summary>
@@ -98,4 +98,3 @@ namespace ServiceLib.Common
return randomNumber; return randomNumber;
} }
} }
}

View File

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

View File

@@ -1,8 +1,8 @@
using System.Net; using System.Net;
using Downloader; using Downloader;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class DownloaderHelper public class DownloaderHelper
{ {
private static readonly Lazy<DownloaderHelper> _instance = new(() => new()); private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
@@ -178,4 +178,3 @@ namespace ServiceLib.Common
downloadOpt = null; downloadOpt = null;
} }
} }
}

View File

@@ -2,8 +2,8 @@ using System.Formats.Tar;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public static class FileManager public static class FileManager
{ {
private static readonly string _tag = "FileManager"; private static readonly string _tag = "FileManager";
@@ -224,4 +224,3 @@ namespace ServiceLib.Common
} }
} }
} }
}

View File

@@ -2,8 +2,8 @@ using System.Net.Http.Headers;
using System.Net.Mime; using System.Net.Mime;
using System.Text; using System.Text;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
/// <summary> /// <summary>
/// </summary> /// </summary>
public class HttpClientHelper public class HttpClientHelper
@@ -203,4 +203,3 @@ namespace ServiceLib.Common
} while (isMoreToRead); } while (isMoreToRead);
} }
} }
}

View File

@@ -1,8 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
/* /*
* See: * See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net * http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
@@ -178,4 +177,4 @@ namespace ServiceLib.Common
} }
#endregion Helper classes #endregion Helper classes
}

View File

@@ -1,9 +1,10 @@
using System.Text.Json; using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class JsonUtils public class JsonUtils
{ {
private static readonly string _tag = "JsonUtils"; private static readonly string _tag = "JsonUtils";
@@ -86,7 +87,8 @@ namespace ServiceLib.Common
var options = new JsonSerializerOptions var options = new JsonSerializerOptions
{ {
WriteIndented = indented, WriteIndented = indented,
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
}; };
result = JsonSerializer.Serialize(obj, options); result = JsonSerializer.Serialize(obj, options);
} }
@@ -128,4 +130,3 @@ namespace ServiceLib.Common
/// <returns></returns> /// <returns></returns>
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj); public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj);
} }
}

View File

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

View File

@@ -1,9 +1,9 @@
using QRCoder; using QRCoder;
using SkiaSharp; using SkiaSharp;
using ZXing.SkiaSharp; using ZXing.SkiaSharp;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class QRCodeHelper public class QRCodeHelper
{ {
public static byte[]? GenQRCode(string? url) public static byte[]? GenQRCode(string? url)
@@ -87,4 +87,3 @@ namespace ServiceLib.Common
return flipped; return flipped;
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class SemanticVersion public class SemanticVersion
{ {
private readonly int major; private readonly int major;
@@ -184,4 +184,3 @@ namespace ServiceLib.Common
#endregion Private #endregion Private
} }
}

View File

@@ -1,8 +1,8 @@
using System.Collections; using System.Collections;
using SQLite; using SQLite;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public sealed class SQLiteHelper public sealed class SQLiteHelper
{ {
private static readonly Lazy<SQLiteHelper> _instance = new(() => new()); private static readonly Lazy<SQLiteHelper> _instance = new(() => new());
@@ -88,4 +88,3 @@ namespace ServiceLib.Common
}); });
} }
} }
}

View File

@@ -1,7 +1,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public static class StringEx public static class StringEx
{ {
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
@@ -80,4 +80,3 @@ namespace ServiceLib.Common
return int.TryParse(value, out var result) ? result : defaultValue; return int.TryParse(value, out var result) ? result : defaultValue;
} }
} }
}

View File

@@ -11,8 +11,8 @@ using System.Text;
using CliWrap; using CliWrap;
using CliWrap.Buffered; using CliWrap.Buffered;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class Utils public class Utils
{ {
private static readonly string _tag = "Utils"; private static readonly string _tag = "Utils";
@@ -224,6 +224,13 @@ namespace ServiceLib.Common
} }
public static string GetMd5(string str) public static string GetMd5(string str)
{
if (string.IsNullOrEmpty(str))
{
return string.Empty;
}
try
{ {
var byteOld = Encoding.UTF8.GetBytes(str); var byteOld = Encoding.UTF8.GetBytes(str);
var byteNew = MD5.HashData(byteOld); var byteNew = MD5.HashData(byteOld);
@@ -235,6 +242,38 @@ namespace ServiceLib.Common
return sb.ToString(); return sb.ToString();
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return string.Empty;
}
}
public static string GetFileHash(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return string.Empty;
}
if (!File.Exists(filePath))
{
return string.Empty;
}
try
{
using var md5 = MD5.Create();
using var stream = File.OpenRead(filePath);
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return string.Empty;
}
}
/// <summary> /// <summary>
/// idn to idc /// idn to idc
@@ -564,13 +603,20 @@ namespace ServiceLib.Common
{ {
try try
{ {
var basePath = GetBaseDirectory();
//When this file exists, it is equivalent to having no permission to read and write //When this file exists, it is equivalent to having no permission to read and write
if (File.Exists(Path.Combine(GetBaseDirectory(), "NotStoreConfigHere.txt"))) if (File.Exists(Path.Combine(basePath, "NotStoreConfigHere.txt")))
{ {
return false; return false;
} }
var tempPath = Path.Combine(GetBaseDirectory(), "guiTemps"); //Check if it is installed by Windows WinGet
if (IsWindows() && basePath.Contains("Users") && basePath.Contains("WinGet"))
{
return false;
}
var tempPath = Path.Combine(basePath, "guiTemps");
if (!Directory.Exists(tempPath)) if (!Directory.Exists(tempPath))
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
@@ -817,4 +863,3 @@ namespace ServiceLib.Common
#endregion Platform #endregion Platform
} }
}

View File

@@ -2,8 +2,8 @@ using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.Win32; using Microsoft.Win32;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
internal static class WindowsUtils internal static class WindowsUtils
{ {
private static readonly string _tag = "WindowsUtils"; private static readonly string _tag = "WindowsUtils";
@@ -71,4 +71,3 @@ namespace ServiceLib.Common
} }
} }
} }
}

View File

@@ -2,8 +2,8 @@ using YamlDotNet.Core;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NamingConventions;
namespace ServiceLib.Common namespace ServiceLib.Common;
{
public class YamlUtils public class YamlUtils
{ {
private static readonly string _tag = "YamlUtils"; private static readonly string _tag = "YamlUtils";
@@ -11,7 +11,7 @@ namespace ServiceLib.Common
#region YAML #region YAML
/// <summary> /// <summary>
/// 反序列化成对象 /// Deserialize
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="str"></param> /// <param name="str"></param>
@@ -34,7 +34,7 @@ namespace ServiceLib.Common
} }
/// <summary> /// <summary>
/// 序列化 /// Serialize
/// </summary> /// </summary>
/// <param name="obj"></param> /// <param name="obj"></param>
/// <returns></returns> /// <returns></returns>
@@ -77,4 +77,3 @@ namespace ServiceLib.Common
#endregion YAML #endregion YAML
} }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EMsgCommand public enum EMsgCommand
{ {
ClearMsg, ClearMsg,
@@ -8,4 +8,3 @@ namespace ServiceLib.Enums
RefreshProfiles, RefreshProfiles,
AppExit AppExit
} }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
{
public enum EViewAction public enum EViewAction
{ {
CloseWindow, CloseWindow,
@@ -20,6 +20,7 @@
BrowseServer, BrowseServer,
ImportRulesFromFile, ImportRulesFromFile,
InitSettingFont, InitSettingFont,
PasswordInput,
SubEditWindow, SubEditWindow,
RoutingRuleSettingWindow, RoutingRuleSettingWindow,
RoutingRuleDetailsWindow, RoutingRuleDetailsWindow,
@@ -43,4 +44,3 @@
DispatcherCheckUpdateFinished, DispatcherCheckUpdateFinished,
DispatcherShowMsg, DispatcherShowMsg,
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib namespace ServiceLib;
{
public class Global public class Global
{ {
#region const #region const
@@ -8,9 +8,7 @@ namespace ServiceLib
public const string GithubUrl = "https://github.com"; public const string GithubUrl = "https://github.com";
public const string GithubApiUrl = "https://api.github.com/repos"; public const string GithubApiUrl = "https://api.github.com/repos";
public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat"; public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
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";
@@ -72,6 +70,7 @@ namespace ServiceLib
public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2"; public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2";
public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET"; public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET";
public const string XrayLocalAsset = "XRAY_LOCATION_ASSET"; public const string XrayLocalAsset = "XRAY_LOCATION_ASSET";
public const string XrayLocalCert = "XRAY_LOCATION_CERT";
public const int SpeedTestPageSize = 1000; public const int SpeedTestPageSize = 1000;
public const string LinuxBash = "/bin/bash"; public const string LinuxBash = "/bin/bash";
@@ -124,7 +123,7 @@ namespace ServiceLib
[ [
"", "",
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat", @"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/{0}.dat" @"https://github.com/Chocolate4U/Iran-v2ray-rules/releases/latest/download/{0}.dat"
]; ];
public static readonly List<string> SingboxRulesetSources = public static readonly List<string> SingboxRulesetSources =
@@ -518,6 +517,15 @@ namespace ServiceLib
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb" @"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
]; ];
public static readonly List<string> IPAPIUrls =
[
@"https://speed.cloudflare.com/meta",
@"https://api.ip.sb/geoip",
@"https://api-ipv4.ip.sb/geoip",
@"https://api-ipv6.ip.sb/geoip",
@"https://api.ipapi.is",
@""
];
#endregion const #endregion const
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public sealed class AppHandler public sealed class AppHandler
{ {
#region Property #region Property
@@ -9,7 +9,6 @@ namespace ServiceLib.Handler
private int? _statePort; private int? _statePort;
private int? _statePort2; private int? _statePort2;
private Job? _processJob; private Job? _processJob;
private bool? _isAdministrator;
public static AppHandler Instance => _instance.Value; public static AppHandler Instance => _instance.Value;
public Config Config => _config; public Config Config => _config;
@@ -31,14 +30,7 @@ namespace ServiceLib.Handler
} }
} }
public bool IsAdministrator public string LinuxSudoPwd { get; set; }
{
get
{
_isAdministrator ??= Utils.IsAdministrator();
return _isAdministrator.Value;
}
}
#endregion Property #endregion Property
@@ -80,6 +72,10 @@ namespace ServiceLib.Handler
Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}"); Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}");
Logging.LoggingEnabled(_config.GuiItem.EnableLog); Logging.LoggingEnabled(_config.GuiItem.EnableLog);
//First determine the port value
_ = StatePort;
_ = StatePort2;
return true; return true;
} }
@@ -241,4 +237,3 @@ namespace ServiceLib.Handler
#endregion Core Type #endregion Core Type
} }
}

View File

@@ -1,8 +1,8 @@
using System.Security.Principal; using System.Security.Principal;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public static class AutoStartupHandler public static class AutoStartupHandler
{ {
private static readonly string _tag = "AutoStartupHandler"; private static readonly string _tag = "AutoStartupHandler";
@@ -239,4 +239,3 @@ namespace ServiceLib.Handler
#endregion macOS #endregion macOS
} }
}

View File

@@ -1,7 +1,7 @@
using static ServiceLib.Models.ClashProxies; using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public sealed class ClashApiHandler public sealed class ClashApiHandler
{ {
private static readonly Lazy<ClashApiHandler> instance = new(() => new()); private static readonly Lazy<ClashApiHandler> instance = new(() => new());
@@ -11,9 +11,9 @@ namespace ServiceLib.Handler
private Dictionary<string, ProxiesItem>? _proxies; private Dictionary<string, ProxiesItem>? _proxies;
public Dictionary<string, object> ProfileContent { get; set; } public Dictionary<string, object> ProfileContent { get; set; }
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config) public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync()
{ {
for (var i = 0; i < 5; i++) for (var i = 0; i < 3; i++)
{ {
var url = $"{GetApiUrl()}/proxies"; var url = $"{GetApiUrl()}/proxies";
var result = await HttpClientHelper.Instance.TryGetAsync(url); var result = await HttpClientHelper.Instance.TryGetAsync(url);
@@ -41,34 +41,26 @@ namespace ServiceLib.Handler
{ {
if (blAll) if (blAll)
{ {
for (var i = 0; i < 5; i++)
{
if (_proxies != null)
{
break;
}
await Task.Delay(5000);
}
if (_proxies == null) if (_proxies == null)
{ {
return; await GetClashProxiesAsync();
} }
lstProxy = new List<ClashProxyModel>(); lstProxy = new List<ClashProxyModel>();
foreach (var kv in _proxies) foreach (var kv in _proxies ?? [])
{ {
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower())) if (Global.notAllowTestType.Contains(kv.Value.type?.ToLower()))
{ {
continue; continue;
} }
lstProxy.Add(new ClashProxyModel() lstProxy.Add(new ClashProxyModel()
{ {
Name = kv.Value.name, Name = kv.Value.name,
Type = kv.Value.type.ToLower(), Type = kv.Value.type?.ToLower(),
}); });
} }
} }
if (lstProxy == null) if (lstProxy is not { Count: > 0 })
{ {
return; return;
} }
@@ -157,7 +149,7 @@ namespace ServiceLib.Handler
} }
} }
public async Task<ClashConnections?> GetClashConnectionsAsync(Config config) public async Task<ClashConnections?> GetClashConnectionsAsync()
{ {
try try
{ {
@@ -193,4 +185,3 @@ namespace ServiceLib.Handler
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}"; return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
} }
} }
}

View File

@@ -1,11 +1,8 @@
using System.Data; using System.Data;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
/// <summary>
/// 本软件配置文件处理类
/// </summary>
public class ConfigHandler public class ConfigHandler
{ {
private static readonly string _configRes = Global.ConfigFileName; private static readonly string _configRes = Global.ConfigFileName;
@@ -14,10 +11,12 @@ namespace ServiceLib.Handler
#region ConfigHandler #region ConfigHandler
/// <summary> /// <summary>
/// 载入配置文件 /// Load the application configuration file
/// If the file exists, deserialize it from JSON
/// If not found, create a new Config object with default settings
/// Initialize default values for missing configuration sections
/// </summary> /// </summary>
/// <param name="config"></param> /// <returns>Config object containing application settings or null if there's an error</returns>
/// <returns></returns>
public static Config? LoadConfig() public static Config? LoadConfig()
{ {
Config? config = null; Config? config = null;
@@ -123,7 +122,7 @@ namespace ServiceLib.Handler
} }
if (config.SpeedTestItem.SpeedPingTestUrl.IsNullOrEmpty()) if (config.SpeedTestItem.SpeedPingTestUrl.IsNullOrEmpty())
{ {
config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl; config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrls.First();
} }
if (config.SpeedTestItem.MixedConcurrencyCount < 1) if (config.SpeedTestItem.MixedConcurrencyCount < 1)
{ {
@@ -169,10 +168,11 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// 保参数 /// Save the configuration to a file
/// First writes to a temporary file, then replaces the original file
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Configuration object to be saved</param>
/// <returns></returns> /// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> SaveConfig(Config config) public static async Task<int> SaveConfig(Config config)
{ {
try try
@@ -204,6 +204,13 @@ namespace ServiceLib.Handler
#region Server #region Server
/// <summary>
/// Add a server profile to the configuration
/// Dispatches the request to the appropriate method based on the config type
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="profileItem">Server profile to add</param>
/// <returns>Result of the operation (0 if successful, -1 if failed)</returns>
public static async Task<int> AddServer(Config config, ProfileItem profileItem) public static async Task<int> AddServer(Config config, ProfileItem profileItem)
{ {
var item = await AppHandler.Instance.GetProfileItem(profileItem.IndexId); var item = await AppHandler.Instance.GetProfileItem(profileItem.IndexId);
@@ -258,11 +265,13 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Add or edit a VMess server
/// Validates and processes VMess-specific settings
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">VMess profile to add</param>
/// <returns></returns> /// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddVMessServer(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddVMessServer(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigType = EConfigType.VMess; profileItem.ConfigType = EConfigType.VMess;
@@ -291,11 +300,11 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// 移除服务器 /// Remove multiple servers from the configuration
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="indexes"></param> /// <param name="indexes">List of server profiles to remove</param>
/// <returns></returns> /// <returns>0 if successful</returns>
public static async Task<int> RemoveServers(Config config, List<ProfileItem> indexes) public static async Task<int> RemoveServers(Config config, List<ProfileItem> indexes)
{ {
var subid = "TempRemoveSubId"; var subid = "TempRemoveSubId";
@@ -311,11 +320,12 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// 克隆服务器 /// Clone server profiles
/// Creates copies of the specified server profiles with "-clone" appended to the remarks
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="index"></param> /// <param name="indexes">List of server profiles to clone</param>
/// <returns></returns> /// <returns>0 if successful</returns>
public static async Task<int> CopyServer(Config config, List<ProfileItem> indexes) public static async Task<int> CopyServer(Config config, List<ProfileItem> indexes)
{ {
foreach (var it in indexes) foreach (var it in indexes)
@@ -347,11 +357,12 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// 设置活动服务器 /// Set the default server by its index ID
/// Updates the configuration to use the specified server as default
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="item"></param> /// <param name="indexId">Index ID of the server to set as default</param>
/// <returns></returns> /// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> SetDefaultServerIndex(Config config, string? indexId) public static async Task<int> SetDefaultServerIndex(Config config, string? indexId)
{ {
if (indexId.IsNullOrEmpty()) if (indexId.IsNullOrEmpty())
@@ -366,6 +377,13 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Set a default server from the provided list of profiles
/// Ensures there's always a valid default server selected
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="lstProfile">List of profile models to choose from</param>
/// <returns>Result of SetDefaultServerIndex operation</returns>
public static async Task<int> SetDefaultServer(Config config, List<ProfileItemModel> lstProfile) public static async Task<int> SetDefaultServer(Config config, List<ProfileItemModel> lstProfile)
{ {
if (lstProfile.Exists(t => t.IndexId == config.IndexId)) if (lstProfile.Exists(t => t.IndexId == config.IndexId))
@@ -386,6 +404,12 @@ namespace ServiceLib.Handler
return await SetDefaultServerIndex(config, item?.IndexId); return await SetDefaultServerIndex(config, item?.IndexId);
} }
/// <summary>
/// Get the current default server profile
/// If the current default is invalid, selects a new default
/// </summary>
/// <param name="config">Current configuration</param>
/// <returns>The default profile item or null if none exists</returns>
public static async Task<ProfileItem?> GetDefaultServer(Config config) public static async Task<ProfileItem?> GetDefaultServer(Config config)
{ {
var item = await AppHandler.Instance.GetProfileItem(config.IndexId); var item = await AppHandler.Instance.GetProfileItem(config.IndexId);
@@ -400,13 +424,15 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// 移动服务器 /// Move a server in the list to a different position
/// Supports moving to top, up, down, bottom or specific position
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="lstProfile"></param> /// <param name="lstProfile">List of server profiles</param>
/// <param name="index"></param> /// <param name="index">Index of the server to move</param>
/// <param name="eMove"></param> /// <param name="eMove">Direction to move the server</param>
/// <returns></returns> /// <param name="pos">Target position when using EMove.Position</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> MoveServer(Config config, List<ProfileItem> lstProfile, int index, EMove eMove, int pos = -1) public static async Task<int> MoveServer(Config config, List<ProfileItem> lstProfile, int index, EMove eMove, int pos = -1)
{ {
int count = lstProfile.Count; int count = lstProfile.Count;
@@ -474,11 +500,13 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// 添加自定义服务器 /// Add a custom server configuration from a file
/// Copies the configuration file to the app's config directory
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">Profile item with the file path in Address</param>
/// <returns></returns> /// <param name="blDelete">Whether to delete the source file after copying</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddCustomServer(Config config, ProfileItem profileItem, bool blDelete) public static async Task<int> AddCustomServer(Config config, ProfileItem profileItem, bool blDelete)
{ {
var fileName = profileItem.Address; var fileName = profileItem.Address;
@@ -517,11 +545,12 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Edit an existing custom server configuration
/// Updates the server's properties without changing the file
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">Profile item with updated properties</param>
/// <returns></returns> /// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> EditCustomServer(Config config, ProfileItem profileItem) public static async Task<int> EditCustomServer(Config config, ProfileItem profileItem)
{ {
var item = await AppHandler.Instance.GetProfileItem(profileItem.IndexId); var item = await AppHandler.Instance.GetProfileItem(profileItem.IndexId);
@@ -551,11 +580,13 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Add or edit a Shadowsocks server
/// Validates and processes Shadowsocks-specific settings
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">Shadowsocks profile to add</param>
/// <returns></returns> /// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddShadowsocksServer(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddShadowsocksServer(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigType = EConfigType.Shadowsocks; profileItem.ConfigType = EConfigType.Shadowsocks;
@@ -579,11 +610,13 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Add or edit a SOCKS server
/// Processes SOCKS-specific settings
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">SOCKS profile to add</param>
/// <returns></returns> /// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddSocksServer(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddSocksServer(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigType = EConfigType.SOCKS; profileItem.ConfigType = EConfigType.SOCKS;
@@ -596,11 +629,13 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Add or edit an HTTP server
/// Processes HTTP-specific settings
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">HTTP profile to add</param>
/// <returns></returns> /// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddHttpServer(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddHttpServer(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigType = EConfigType.HTTP; profileItem.ConfigType = EConfigType.HTTP;
@@ -613,11 +648,13 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Add or edit a Trojan server
/// Validates and processes Trojan-specific settings
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">Trojan profile to add</param>
/// <returns></returns> /// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddTrojanServer(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddTrojanServer(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigType = EConfigType.Trojan; profileItem.ConfigType = EConfigType.Trojan;
@@ -639,11 +676,14 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Add or edit a Hysteria2 server
/// Validates and processes Hysteria2-specific settings
/// Sets the core type to sing_box as required by Hysteria2
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">Hysteria2 profile to add</param>
/// <returns></returns> /// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddHysteria2Server(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddHysteria2Server(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigType = EConfigType.Hysteria2; profileItem.ConfigType = EConfigType.Hysteria2;
@@ -669,11 +709,14 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Add or edit a TUIC server
/// Validates and processes TUIC-specific settings
/// Sets the core type to sing_box as required by TUIC
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">TUIC profile to add</param>
/// <returns></returns> /// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddTuicServer(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddTuicServer(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigType = EConfigType.TUIC; profileItem.ConfigType = EConfigType.TUIC;
@@ -708,15 +751,16 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Add or edit a WireGuard server
/// Validates and processes WireGuard-specific settings
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">WireGuard profile to add</param>
/// <returns></returns> /// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddWireguardServer(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddWireguardServer(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigType = EConfigType.WireGuard; profileItem.ConfigType = EConfigType.WireGuard;
profileItem.CoreType = ECoreType.sing_box;
profileItem.Address = profileItem.Address.TrimEx(); profileItem.Address = profileItem.Address.TrimEx();
profileItem.Id = profileItem.Id.TrimEx(); profileItem.Id = profileItem.Id.TrimEx();
@@ -739,6 +783,15 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Sort the server list by the specified column
/// Updates the sort order in the profile extension data
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="subId">Subscription ID to filter servers</param>
/// <param name="colName">Column name to sort by</param>
/// <param name="asc">Sort in ascending order if true, descending if false</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> SortServers(Config config, string subId, string colName, bool asc) public static async Task<int> SortServers(Config config, string subId, string colName, bool asc)
{ {
var lstModel = await AppHandler.Instance.ProfileItems(subId, ""); var lstModel = await AppHandler.Instance.ProfileItems(subId, "");
@@ -746,8 +799,11 @@ namespace ServiceLib.Handler
{ {
return -1; return -1;
} }
var lstServerStat = (config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs(); var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
var lstProfile = (from t in lstModel var lstProfile = (from t in lstModel
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
from t22 in t2b.DefaultIfEmpty()
join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b
from t33 in t3b.DefaultIfEmpty() from t33 in t3b.DefaultIfEmpty()
select new ProfileItemModel select new ProfileItemModel
@@ -762,7 +818,11 @@ namespace ServiceLib.Handler
StreamSecurity = t.StreamSecurity, StreamSecurity = t.StreamSecurity,
Delay = t33?.Delay ?? 0, Delay = t33?.Delay ?? 0,
Speed = t33?.Speed ?? 0, Speed = t33?.Speed ?? 0,
Sort = t33?.Sort ?? 0 Sort = t33?.Sort ?? 0,
TodayDown = (t22?.TodayDown ?? 0).ToString("D16"),
TodayUp = (t22?.TodayUp ?? 0).ToString("D16"),
TotalDown = (t22?.TotalDown ?? 0).ToString("D16"),
TotalUp = (t22?.TotalUp ?? 0).ToString("D16"),
}).ToList(); }).ToList();
Enum.TryParse(colName, true, out EServerColName name); Enum.TryParse(colName, true, out EServerColName name);
@@ -780,6 +840,10 @@ namespace ServiceLib.Handler
EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(), EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(),
EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(), EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(),
EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(), EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(),
EServerColName.TodayDown => lstProfile.OrderBy(t => t.TodayDown).ToList(),
EServerColName.TodayUp => lstProfile.OrderBy(t => t.TodayUp).ToList(),
EServerColName.TotalDown => lstProfile.OrderBy(t => t.TotalDown).ToList(),
EServerColName.TotalUp => lstProfile.OrderBy(t => t.TotalUp).ToList(),
_ => lstProfile _ => lstProfile
}; };
} }
@@ -796,6 +860,10 @@ namespace ServiceLib.Handler
EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(), EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(),
EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(), EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(),
EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(), EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(),
EServerColName.TodayDown => lstProfile.OrderByDescending(t => t.TodayDown).ToList(),
EServerColName.TodayUp => lstProfile.OrderByDescending(t => t.TodayUp).ToList(),
EServerColName.TotalDown => lstProfile.OrderByDescending(t => t.TotalDown).ToList(),
EServerColName.TotalUp => lstProfile.OrderByDescending(t => t.TotalUp).ToList(),
_ => lstProfile _ => lstProfile
}; };
} }
@@ -832,11 +900,13 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// Add or edit server /// Add or edit a VLESS server
/// Validates and processes VLESS-specific settings
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="profileItem"></param> /// <param name="profileItem">VLESS profile to add</param>
/// <returns></returns> /// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddVlessServer(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddVlessServer(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigType = EConfigType.VLESS; profileItem.ConfigType = EConfigType.VLESS;
@@ -868,16 +938,29 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Remove duplicate servers from a subscription
/// Compares servers based on their properties rather than just names
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="subId">Subscription ID to deduplicate</param>
/// <returns>Tuple with total count and remaining count after deduplication</returns>
public static async Task<Tuple<int, int>> DedupServerList(Config config, string subId) public static async Task<Tuple<int, int>> DedupServerList(Config config, string subId)
{ {
var lstProfile = await AppHandler.Instance.ProfileItems(subId); var lstProfile = await AppHandler.Instance.ProfileItems(subId);
if (lstProfile == null)
{
return new Tuple<int, int>(0, 0);
}
List<ProfileItem> lstKeep = new(); List<ProfileItem> lstKeep = new();
List<ProfileItem> lstRemove = new(); List<ProfileItem> lstRemove = new();
if (!config.GuiItem.KeepOlderDedupl) if (!config.GuiItem.KeepOlderDedupl)
{
lstProfile.Reverse(); lstProfile.Reverse();
}
foreach (ProfileItem item in lstProfile) foreach (var item in lstProfile)
{ {
if (!lstKeep.Exists(i => CompareProfileItem(i, item, false))) if (!lstKeep.Exists(i => CompareProfileItem(i, item, false)))
{ {
@@ -893,6 +976,14 @@ namespace ServiceLib.Handler
return new Tuple<int, int>(lstProfile.Count, lstKeep.Count); return new Tuple<int, int>(lstProfile.Count, lstKeep.Count);
} }
/// <summary>
/// Common server addition logic used by all server types
/// Sets common properties and handles sorting and persistence
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="profileItem">Profile item to add</param>
/// <param name="toFile">Whether to save to database</param>
/// <returns>0 if successful</returns>
public static async Task<int> AddServerCommon(Config config, ProfileItem profileItem, bool toFile = true) public static async Task<int> AddServerCommon(Config config, ProfileItem profileItem, bool toFile = true)
{ {
profileItem.ConfigVersion = 2; profileItem.ConfigVersion = 2;
@@ -944,7 +1035,15 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
private static bool CompareProfileItem(ProfileItem o, ProfileItem n, bool remarks) /// <summary>
/// Compare two profile items to determine if they represent the same server
/// Used for deduplication and server matching
/// </summary>
/// <param name="o">First profile item</param>
/// <param name="n">Second profile item</param>
/// <param name="remarks">Whether to compare remarks</param>
/// <returns>True if the profiles match, false otherwise</returns>
private static bool CompareProfileItem(ProfileItem? o, ProfileItem? n, bool remarks)
{ {
if (o == null || n == null) if (o == null || n == null)
{ {
@@ -952,24 +1051,36 @@ namespace ServiceLib.Handler
} }
return o.ConfigType == n.ConfigType return o.ConfigType == n.ConfigType
&& o.Address == n.Address && AreEqual(o.Address, n.Address)
&& o.Port == n.Port && o.Port == n.Port
&& o.Id == n.Id && AreEqual(o.Id, n.Id)
&& o.Security == n.Security && AreEqual(o.Security, n.Security)
&& o.Network == n.Network && AreEqual(o.Network, n.Network)
&& o.HeaderType == n.HeaderType && AreEqual(o.HeaderType, n.HeaderType)
&& o.RequestHost == n.RequestHost && AreEqual(o.RequestHost, n.RequestHost)
&& o.Path == n.Path && AreEqual(o.Path, n.Path)
&& (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity) && (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity)
&& o.Flow == n.Flow && AreEqual(o.Flow, n.Flow)
&& o.Sni == n.Sni && AreEqual(o.Sni, n.Sni)
&& o.Alpn == n.Alpn && AreEqual(o.Alpn, n.Alpn)
&& o.Fingerprint == n.Fingerprint && AreEqual(o.Fingerprint, n.Fingerprint)
&& o.PublicKey == n.PublicKey && AreEqual(o.PublicKey, n.PublicKey)
&& o.ShortId == n.ShortId && AreEqual(o.ShortId, n.ShortId)
&& (!remarks || o.Remarks == n.Remarks); && (!remarks || o.Remarks == n.Remarks);
static bool AreEqual(string? a, string? b)
{
return string.Equals(a, b) || (string.IsNullOrEmpty(a) && string.IsNullOrEmpty(b));
}
} }
/// <summary>
/// Remove a single server profile by its index ID
/// Deletes the configuration file if it's a custom config
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="indexId">Index ID of the profile to remove</param>
/// <returns>0 if successful</returns>
private static async Task<int> RemoveProfileItem(Config config, string indexId) private static async Task<int> RemoveProfileItem(Config config, string indexId)
{ {
try try
@@ -994,26 +1105,48 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
public static async Task<RetResult> AddCustomServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType) /// <summary>
/// Create a custom server that combines multiple servers for load balancing
/// Generates a configuration file that references multiple servers
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="selecteds">Selected servers to combine</param>
/// <param name="coreType">Core type to use (Xray or sing_box)</param>
/// <param name="multipleLoad">Load balancing algorithm</param>
/// <returns>Result object with success state and data</returns>
public static async Task<RetResult> AddCustomServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
{ {
var indexId = Utils.GetMd5(Global.CoreMultipleLoadConfigFileName); var indexId = Utils.GetMd5(Global.CoreMultipleLoadConfigFileName);
var configPath = Utils.GetConfigPath(Global.CoreMultipleLoadConfigFileName); var configPath = Utils.GetConfigPath(Global.CoreMultipleLoadConfigFileName);
var result = await CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, coreType); var result = await CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, coreType, multipleLoad);
if (result.Success != true) if (result.Success != true)
{ {
return result; return result;
} }
var fileName = configPath; if (!File.Exists(configPath))
if (!File.Exists(fileName))
{ {
return result; return result;
} }
var profileItem = await AppHandler.Instance.GetProfileItem(indexId) ?? new(); var profileItem = await AppHandler.Instance.GetProfileItem(indexId) ?? new();
profileItem.IndexId = indexId; profileItem.IndexId = indexId;
profileItem.Remarks = coreType == ECoreType.sing_box ? ResUI.menuSetDefaultMultipleServer : ResUI.menuSetDefaultLoadBalanceServer; if (coreType == ECoreType.Xray)
{
profileItem.Remarks = multipleLoad switch
{
EMultipleLoad.Random => ResUI.menuSetDefaultMultipleServerXrayRandom,
EMultipleLoad.RoundRobin => ResUI.menuSetDefaultMultipleServerXrayRoundRobin,
EMultipleLoad.LeastPing => ResUI.menuSetDefaultMultipleServerXrayLeastPing,
EMultipleLoad.LeastLoad => ResUI.menuSetDefaultMultipleServerXrayLeastLoad,
_ => ResUI.menuSetDefaultMultipleServerXrayRoundRobin,
};
}
else if (coreType == ECoreType.sing_box)
{
profileItem.Remarks = ResUI.menuSetDefaultMultipleServerSingBoxLeastPing;
}
profileItem.Address = Global.CoreMultipleLoadConfigFileName; profileItem.Address = Global.CoreMultipleLoadConfigFileName;
profileItem.ConfigType = EConfigType.Custom; profileItem.ConfigType = EConfigType.Custom;
profileItem.CoreType = coreType; profileItem.CoreType = coreType;
@@ -1024,6 +1157,14 @@ namespace ServiceLib.Handler
return result; return result;
} }
/// <summary>
/// Get a SOCKS server profile for pre-SOCKS functionality
/// Used when TUN mode is enabled or when a custom config has a pre-SOCKS port
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="node">Server node that might need pre-SOCKS</param>
/// <param name="coreType">Core type being used</param>
/// <returns>A SOCKS profile item or null if not needed</returns>
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType) public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
{ {
ProfileItem? itemSocks = null; ProfileItem? itemSocks = null;
@@ -1053,6 +1194,13 @@ namespace ServiceLib.Handler
return itemSocks; return itemSocks;
} }
/// <summary>
/// Remove servers with invalid test results (timeout)
/// Useful for cleaning up subscription lists
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="subid">Subscription ID to filter servers</param>
/// <returns>Number of removed servers or -1 if failed</returns>
public static async Task<int> RemoveInvalidServerResult(Config config, string subid) public static async Task<int> RemoveInvalidServerResult(Config config, string subid)
{ {
var lstModel = await AppHandler.Instance.ProfileItems(subid, ""); var lstModel = await AppHandler.Instance.ProfileItems(subid, "");
@@ -1076,12 +1224,14 @@ namespace ServiceLib.Handler
#region Batch add servers #region Batch add servers
/// <summary> /// <summary>
/// 批量添加服务器 /// Add multiple servers from string data (common protocols)
/// Parses the string data into server profiles
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="strData"></param> /// <param name="strData">String data containing server information</param>
/// <param name="subid"></param> /// <param name="subid">Subscription ID to associate with the servers</param>
/// <returns>成功导入的数量</returns> /// <param name="isSub">Whether this is from a subscription</param>
/// <returns>Number of successfully imported servers or -1 if failed</returns>
private static async Task<int> AddBatchServersCommon(Config config, string strData, string subid, bool isSub) private static async Task<int> AddBatchServersCommon(Config config, string strData, string subid, bool isSub)
{ {
if (strData.IsNullOrEmpty()) if (strData.IsNullOrEmpty())
@@ -1161,6 +1311,15 @@ namespace ServiceLib.Handler
return countServers; return countServers;
} }
/// <summary>
/// Add servers from custom configuration formats (sing-box, v2ray, etc.)
/// Handles various configuration formats and imports them as custom configs
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="strData">String data containing server information</param>
/// <param name="subid">Subscription ID to associate with the servers</param>
/// <param name="isSub">Whether this is from a subscription</param>
/// <returns>Number of successfully imported servers or -1 if failed</returns>
private static async Task<int> AddBatchServers4Custom(Config config, string strData, string subid, bool isSub) private static async Task<int> AddBatchServers4Custom(Config config, string strData, string subid, bool isSub)
{ {
if (strData.IsNullOrEmpty()) if (strData.IsNullOrEmpty())
@@ -1259,6 +1418,15 @@ namespace ServiceLib.Handler
} }
} }
/// <summary>
/// Add Shadowsocks servers from SIP008 format
/// SIP008 is a JSON-based format for Shadowsocks servers
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="strData">String data in SIP008 format</param>
/// <param name="subid">Subscription ID to associate with the servers</param>
/// <param name="isSub">Whether this is from a subscription</param>
/// <returns>Number of successfully imported servers or -1 if failed</returns>
private static async Task<int> AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub) private static async Task<int> AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub)
{ {
if (strData.IsNullOrEmpty()) if (strData.IsNullOrEmpty())
@@ -1291,6 +1459,15 @@ namespace ServiceLib.Handler
return -1; return -1;
} }
/// <summary>
/// Main entry point for adding batch servers from various formats
/// Tries different parsing methods to import as many servers as possible
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="strData">String data containing server information</param>
/// <param name="subid">Subscription ID to associate with the servers</param>
/// <param name="isSub">Whether this is from a subscription</param>
/// <returns>Number of successfully imported servers or -1 if failed</returns>
public static async Task<int> AddBatchServers(Config config, string strData, string subid, bool isSub) public static async Task<int> AddBatchServers(Config config, string strData, string subid, bool isSub)
{ {
if (strData.IsNullOrEmpty()) if (strData.IsNullOrEmpty())
@@ -1363,11 +1540,12 @@ namespace ServiceLib.Handler
#region Sub & Group #region Sub & Group
/// <summary> /// <summary>
/// add sub /// Add a subscription item from URL
/// Creates a new subscription with default settings
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="url"></param> /// <param name="url">Subscription URL</param>
/// <returns></returns> /// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddSubItem(Config config, string url) public static async Task<int> AddSubItem(Config config, string url)
{ {
//already exists //already exists
@@ -1399,6 +1577,12 @@ namespace ServiceLib.Handler
return await AddSubItem(config, subItem); return await AddSubItem(config, subItem);
} }
/// <summary>
/// Add or update a subscription item
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="subItem">Subscription item to add or update</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddSubItem(Config config, SubItem subItem) public static async Task<int> AddSubItem(Config config, SubItem subItem)
{ {
var item = await AppHandler.Instance.GetSubItem(subItem.Id); var item = await AppHandler.Instance.GetSubItem(subItem.Id);
@@ -1450,11 +1634,12 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// 移除服务器 /// Remove servers associated with a subscription ID
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="config">Current configuration</param>
/// <param name="subid"></param> /// <param name="subid">Subscription ID</param>
/// <returns></returns> /// <param name="isSub">Whether to only remove servers marked as subscription items</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> RemoveServersViaSubid(Config config, string subid, bool isSub) public static async Task<int> RemoveServersViaSubid(Config config, string subid, bool isSub)
{ {
if (subid.IsNullOrEmpty()) if (subid.IsNullOrEmpty())
@@ -1478,6 +1663,12 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Delete a subscription item and all its associated servers
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="id">Subscription ID to delete</param>
/// <returns>0 if successful</returns>
public static async Task<int> DeleteSubItem(Config config, string id) public static async Task<int> DeleteSubItem(Config config, string id)
{ {
var item = await AppHandler.Instance.GetSubItem(id); var item = await AppHandler.Instance.GetSubItem(id);
@@ -1491,6 +1682,13 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Move servers to a different group (subscription)
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="lstProfile">List of profiles to move</param>
/// <param name="subid">Target subscription ID</param>
/// <returns>0 if successful</returns>
public static async Task<int> MoveToGroup(Config config, List<ProfileItem> lstProfile, string subid) public static async Task<int> MoveToGroup(Config config, List<ProfileItem> lstProfile, string subid)
{ {
foreach (var item in lstProfile) foreach (var item in lstProfile)
@@ -1506,6 +1704,12 @@ namespace ServiceLib.Handler
#region Routing #region Routing
/// <summary>
/// Save a routing item to the database
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="item">Routing item to save</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> SaveRoutingItem(Config config, RoutingItem item) public static async Task<int> SaveRoutingItem(Config config, RoutingItem item)
{ {
if (item.Id.IsNullOrEmpty()) if (item.Id.IsNullOrEmpty())
@@ -1524,11 +1728,11 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// AddBatchRoutingRules /// Add multiple routing rules to a routing item
/// </summary> /// </summary>
/// <param name="config"></param> /// <param name="routingItem">Routing item to add rules to</param>
/// <param name="strData"></param> /// <param name="strData">JSON string containing rules data</param>
/// <returns></returns> /// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddBatchRoutingRules(RoutingItem routingItem, string strData) public static async Task<int> AddBatchRoutingRules(RoutingItem routingItem, string strData)
{ {
if (strData.IsNullOrEmpty()) if (strData.IsNullOrEmpty())
@@ -1565,12 +1769,14 @@ namespace ServiceLib.Handler
} }
/// <summary> /// <summary>
/// MoveRoutingRule /// Move a routing rule within a rules list
/// Supports moving to top, up, down, bottom or specific position
/// </summary> /// </summary>
/// <param name="routingItem"></param> /// <param name="rules">List of routing rules</param>
/// <param name="index"></param> /// <param name="index">Index of the rule to move</param>
/// <param name="eMove"></param> /// <param name="eMove">Direction to move the rule</param>
/// <returns></returns> /// <param name="pos">Target position when using EMove.Position</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> MoveRoutingRule(List<RulesItem> rules, int index, EMove eMove, int pos = -1) public static async Task<int> MoveRoutingRule(List<RulesItem> rules, int index, EMove eMove, int pos = -1)
{ {
int count = rules.Count; int count = rules.Count;
@@ -1641,6 +1847,12 @@ namespace ServiceLib.Handler
return await Task.FromResult(0); return await Task.FromResult(0);
} }
/// <summary>
/// Set the default routing configuration
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="routingItem">Routing item to set as default</param>
/// <returns>0 if successful</returns>
public static async Task<int> SetDefaultRouting(Config config, RoutingItem routingItem) public static async Task<int> SetDefaultRouting(Config config, RoutingItem routingItem)
{ {
if (await SQLiteHelper.Instance.TableAsync<RoutingItem>().Where(t => t.Id == routingItem.Id).CountAsync() > 0) if (await SQLiteHelper.Instance.TableAsync<RoutingItem>().Where(t => t.Id == routingItem.Id).CountAsync() > 0)
@@ -1653,6 +1865,12 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Get the current default routing configuration
/// If no default is set, selects the first available routing item
/// </summary>
/// <param name="config">Current configuration</param>
/// <returns>The default routing item</returns>
public static async Task<RoutingItem> GetDefaultRouting(Config config) public static async Task<RoutingItem> GetDefaultRouting(Config config)
{ {
var item = await AppHandler.Instance.GetRoutingItem(config.RoutingBasicItem.RoutingIndexId); var item = await AppHandler.Instance.GetRoutingItem(config.RoutingBasicItem.RoutingIndexId);
@@ -1666,6 +1884,12 @@ namespace ServiceLib.Handler
return item; return item;
} }
/// <summary>
/// Initialize routing rules from built-in or external templates
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="blImportAdvancedRules">Whether to import advanced rules</param>
/// <returns>0 if successful</returns>
public static async Task<int> InitRouting(Config config, bool blImportAdvancedRules = false) public static async Task<int> InitRouting(Config config, bool blImportAdvancedRules = false)
{ {
if (config.ConstItem.RouteRulesTemplateSourceUrl.IsNullOrEmpty()) if (config.ConstItem.RouteRulesTemplateSourceUrl.IsNullOrEmpty())
@@ -1680,6 +1904,13 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Initialize routing rules from external templates
/// Downloads and processes routing templates from a URL
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="blImportAdvancedRules">Whether to import advanced rules</param>
/// <returns>0 if successful</returns>
public static async Task<int> InitExternalRouting(Config config, bool blImportAdvancedRules = false) public static async Task<int> InitExternalRouting(Config config, bool blImportAdvancedRules = false)
{ {
var downloadHandle = new DownloadService(); var downloadHandle = new DownloadService();
@@ -1728,6 +1959,13 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Initialize built-in routing rules
/// Creates default routing configurations (whitelist, blacklist, global)
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="blImportAdvancedRules">Whether to import advanced rules</param>
/// <returns>0 if successful</returns>
public static async Task<int> InitBuiltinRouting(Config config, bool blImportAdvancedRules = false) public static async Task<int> InitBuiltinRouting(Config config, bool blImportAdvancedRules = false)
{ {
var ver = "V3-"; var ver = "V3-";
@@ -1781,6 +2019,10 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Remove a routing item from the database
/// </summary>
/// <param name="routingItem">Routing item to remove</param>
public static async Task RemoveRoutingItem(RoutingItem routingItem) public static async Task RemoveRoutingItem(RoutingItem routingItem)
{ {
await SQLiteHelper.Instance.DeleteAsync(routingItem); await SQLiteHelper.Instance.DeleteAsync(routingItem);
@@ -1790,6 +2032,12 @@ namespace ServiceLib.Handler
#region DNS #region DNS
/// <summary>
/// Initialize built-in DNS configurations
/// Creates default DNS items for V2Ray and sing-box
/// </summary>
/// <param name="config">Current configuration</param>
/// <returns>0 if successful</returns>
public static async Task<int> InitBuiltinDNS(Config config) public static async Task<int> InitBuiltinDNS(Config config)
{ {
var items = await AppHandler.Instance.DNSItems(); var items = await AppHandler.Instance.DNSItems();
@@ -1813,6 +2061,12 @@ namespace ServiceLib.Handler
return 0; return 0;
} }
/// <summary>
/// Save a DNS item to the database
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="item">DNS item to save</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> SaveDNSItems(Config config, DNSItem item) public static async Task<int> SaveDNSItems(Config config, DNSItem item)
{ {
if (item == null) if (item == null)
@@ -1835,6 +2089,13 @@ namespace ServiceLib.Handler
} }
} }
/// <summary>
/// Get an external DNS configuration from URL
/// Downloads and processes DNS templates
/// </summary>
/// <param name="type">Core type (Xray or sing-box)</param>
/// <param name="url">URL of the DNS template</param>
/// <returns>DNS item with configuration from the URL</returns>
public static async Task<DNSItem> GetExternalDNSItem(ECoreType type, string url) public static async Task<DNSItem> GetExternalDNSItem(ECoreType type, string url)
{ {
var currentItem = await AppHandler.Instance.GetDNSItem(type); var currentItem = await AppHandler.Instance.GetDNSItem(type);
@@ -1866,6 +2127,13 @@ namespace ServiceLib.Handler
#region Regional Presets #region Regional Presets
/// <summary>
/// Apply regional presets for geo-specific configurations
/// Sets up geo files, routing rules, and DNS for specific regions
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="type">Type of preset (Default, Russia, Iran)</param>
/// <returns>True if successful</returns>
public static async Task<bool> ApplyRegionalPreset(Config config, EPresetType type) public static async Task<bool> ApplyRegionalPreset(Config config, EPresetType type)
{ {
switch (type) switch (type)
@@ -1906,4 +2174,3 @@ namespace ServiceLib.Handler
#endregion Regional Presets #endregion Regional Presets
} }
}

View File

@@ -0,0 +1,42 @@
namespace ServiceLib.Handler;
public class ConnectionHandler
{
private static readonly Lazy<ConnectionHandler> _instance = new(() => new());
public static ConnectionHandler Instance => _instance.Value;
public async Task<string> RunAvailabilityCheck()
{
var downloadHandle = new DownloadService();
var time = await downloadHandle.RunAvailabilityCheck(null);
var ip = time > 0 ? await GetIPInfo(downloadHandle) ?? Global.None : Global.None;
return string.Format(ResUI.TestMeOutput, time, ip);
}
private async Task<string?> GetIPInfo(DownloadService downloadHandle)
{
var url = AppHandler.Instance.Config.SpeedTestItem.IPAPIUrl;
if (url.IsNullOrEmpty())
{
return null;
}
var result = await downloadHandle.TryDownloadString(url, true, "");
if (result == null)
{
return null;
}
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
if (ipInfo == null)
{
return null;
}
var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query;
var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code;
return $"({country ?? "unknown"}) {ip}";
}
}

View File

@@ -0,0 +1,126 @@
using System.Diagnostics;
using System.Text;
using CliWrap;
namespace ServiceLib.Handler;
public class CoreAdminHandler
{
private static readonly Lazy<CoreAdminHandler> _instance = new(() => new());
public static CoreAdminHandler Instance => _instance.Value;
private Config _config;
private Action<bool, string>? _updateFunc;
private int _linuxSudoPid = -1;
public async Task Init(Config config, Action<bool, string> updateFunc)
{
if (_config != null)
{
return;
}
_config = config;
_updateFunc = updateFunc;
}
private void UpdateFunc(bool notify, string msg)
{
_updateFunc?.Invoke(notify, msg);
}
public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
{
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
Process proc = new()
{
StartInfo = new()
{
FileName = shFilePath,
Arguments = "",
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8,
}
};
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
await Task.Delay(100);
if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
_linuxSudoPid = proc.Id;
return proc;
}
public async Task KillProcessAsLinuxSudo()
{
if (_linuxSudoPid < 0)
{
return;
}
var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
await Cli.Wrap(shFilePath)
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
.ExecuteAsync();
_linuxSudoPid = -1;
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
var shFilePath = Utils.GetBinConfigPath(fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (Utils.IsAdministrator())
{
sb.AppendLine($"{cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
return shFilePath;
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
/// <summary> /// <summary>
/// Core configuration file processing class /// Core configuration file processing class
/// </summary> /// </summary>
@@ -133,16 +133,16 @@ namespace ServiceLib.Handler
return result; return result;
} }
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType) public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
{ {
var result = new RetResult(); var result = new RetResult();
if (coreType == ECoreType.sing_box) if (coreType == ECoreType.sing_box)
{ {
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds); result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
} }
else if (coreType == ECoreType.Xray) else
{ {
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds); result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad);
} }
if (result.Success != true) if (result.Success != true)
@@ -153,4 +153,3 @@ namespace ServiceLib.Handler
return result; return result;
} }
} }
}

View File

@@ -1,8 +1,8 @@
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
/// <summary> /// <summary>
/// Core process processing class /// Core process processing class
/// </summary> /// </summary>
@@ -13,7 +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 bool _linuxSudo = false;
private Action<bool, string>? _updateFunc; private Action<bool, string>? _updateFunc;
private const string _tag = "CoreHandler"; private const string _tag = "CoreHandler";
@@ -24,6 +24,7 @@ namespace ServiceLib.Handler
Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalCert, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
//Copy the bin folder to the storage location (for init) //Copy the bin folder to the storage location (for init)
if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1") if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1")
@@ -100,7 +101,7 @@ namespace ServiceLib.Handler
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds) public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{ {
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray; var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC) ? ECoreType.sing_box : ECoreType.Xray;
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName); var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
@@ -154,23 +155,23 @@ namespace ServiceLib.Handler
{ {
try try
{ {
if (_linuxSudo)
{
await CoreAdminHandler.Instance.KillProcessAsLinuxSudo();
_linuxSudo = false;
}
if (_process != null) if (_process != null)
{ {
await ProcUtils.ProcessKill(_process, true); await ProcUtils.ProcessKill(_process, Utils.IsWindows());
_process = null; _process = null;
} }
if (_processPre != null) if (_processPre != null)
{ {
await ProcUtils.ProcessKill(_processPre, true); await ProcUtils.ProcessKill(_processPre, Utils.IsWindows());
_processPre = null; _processPre = null;
} }
if (_linuxSudoPid > 0)
{
await KillProcessAsLinuxSudo();
}
_linuxSudoPid = -1;
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -224,15 +225,6 @@ namespace ServiceLib.Handler
_updateFunc?.Invoke(notify, msg); _updateFunc?.Invoke(notify, msg);
} }
private bool IsNeedSudo(ECoreType eCoreType)
{
return _config.TunModeItem.EnableTun
&& eCoreType == ECoreType.sing_box
&& (Utils.IsNonWindows())
//&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
;
}
#endregion Private #endregion Private
#region Process #region Process
@@ -247,6 +239,28 @@ namespace ServiceLib.Handler
} }
try try
{
if (mayNeedSudo
&& _config.TunModeItem.EnableTun
&& coreInfo.CoreType == ECoreType.sing_box
&& Utils.IsNonWindows())
{
_linuxSudo = true;
await CoreAdminHandler.Instance.Init(_config, _updateFunc);
return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath);
}
return await RunProcessNormal(fileName, coreInfo, configPath, displayLog);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
UpdateFunc(mayNeedSudo, ex.Message);
return null;
}
}
private async Task<Process?> RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog)
{ {
Process proc = new() Process proc = new()
{ {
@@ -264,47 +278,32 @@ namespace ServiceLib.Handler
} }
}; };
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
if (isNeedSudo)
{
await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
}
if (displayLog) if (displayLog)
{ {
proc.OutputDataReceived += (sender, e) => proc.OutputDataReceived += (sender, e) =>
{ {
if (e.Data.IsNullOrEmpty()) if (e.Data.IsNotEmpty())
return; {
UpdateFunc(false, e.Data + Environment.NewLine); UpdateFunc(false, e.Data + Environment.NewLine);
}
}; };
proc.ErrorDataReceived += (sender, e) => proc.ErrorDataReceived += (sender, e) =>
{ {
if (e.Data.IsNullOrEmpty()) if (e.Data.IsNotEmpty())
return; {
UpdateFunc(false, e.Data + Environment.NewLine); UpdateFunc(false, e.Data + Environment.NewLine);
}
}; };
} }
proc.Start(); proc.Start();
if (isNeedSudo && _config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
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);
}
if (isNeedSudo)
_linuxSudoPid = proc.Id;
if (displayLog) if (displayLog)
{ {
proc.BeginOutputReadLine(); proc.BeginOutputReadLine();
proc.BeginErrorReadLine(); proc.BeginErrorReadLine();
} }
await Task.Delay(500); await Task.Delay(100);
AppHandler.Instance.AddProcess(proc.Handle); AppHandler.Instance.AddProcess(proc.Handle);
if (proc is null or { HasExited: true }) if (proc is null or { HasExited: true })
{ {
@@ -312,98 +311,6 @@ namespace ServiceLib.Handler
} }
return proc; return proc;
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
UpdateFunc(mayNeedSudo, ex.Message);
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.GetBinConfigPath(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.GetBinConfigPath(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

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public sealed class CoreInfoHandler public sealed class CoreInfoHandler
{ {
private static readonly Lazy<CoreInfoHandler> _instance = new(() => new()); private static readonly Lazy<CoreInfoHandler> _instance = new(() => new());
@@ -215,4 +215,3 @@ namespace ServiceLib.Handler
return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases"; return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases";
} }
} }
}

View File

@@ -1,17 +1,20 @@
using System.Collections.Specialized; using System.Collections.Specialized;
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class BaseFmt public class BaseFmt
{ {
protected static string GetIpv6(string address) protected static string GetIpv6(string address)
{ {
if (Utils.IsIpv6(address)) if (Utils.IsIpv6(address))
{ {
// 检查地址是否已经被方括号包围,如果没有,则添加方括号 // Check if the address is already surrounded by square brackets, if not, add square brackets
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]"; return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
} }
return address; // 如果不是IPv6地址直接返回原地址 else
{
return address;
}
} }
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery) protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
@@ -239,4 +242,3 @@ namespace ServiceLib.Handler.Fmt
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}"; return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
} }
} }
}

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class FmtHandler public class FmtHandler
{ {
private static readonly string _tag = "FmtHandler"; private static readonly string _tag = "FmtHandler";
@@ -89,4 +89,3 @@
} }
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class Hysteria2Fmt : BaseFmt public class Hysteria2Fmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -99,4 +99,3 @@ namespace ServiceLib.Handler.Fmt
return null; return null;
} }
} }
}

View File

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

View File

@@ -1,7 +1,7 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class ShadowsocksFmt : BaseFmt public class ShadowsocksFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -177,4 +177,3 @@ namespace ServiceLib.Handler.Fmt
return null; return null;
} }
} }
}

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class SocksFmt : BaseFmt public class SocksFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -112,4 +112,3 @@ namespace ServiceLib.Handler.Fmt
return item; return item;
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class TrojanFmt : BaseFmt public class TrojanFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -45,4 +45,3 @@ namespace ServiceLib.Handler.Fmt
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark);
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class TuicFmt : BaseFmt public class TuicFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -61,4 +61,3 @@ namespace ServiceLib.Handler.Fmt
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark); return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
} }
} }
}

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class VLESSFmt : BaseFmt public class VLESSFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -57,4 +57,3 @@ namespace ServiceLib.Handler.Fmt
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark);
} }
} }
}

View File

@@ -1,14 +1,14 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class VmessFmt : BaseFmt public class VmessFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
{ {
msg = ResUI.ConfigurationFormatIncorrect; msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item; ProfileItem? item;
if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0) if (str.IndexOf('@') > 0)
{ {
item = ResolveStdVmess(str); item = ResolveStdVmess(str) ?? ResolveVmess(str, out msg);
} }
else else
{ {
@@ -123,4 +123,3 @@ namespace ServiceLib.Handler.Fmt
return item; return item;
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
{
public class WireguardFmt : BaseFmt public class WireguardFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public static ProfileItem? Resolve(string str, out string msg)
@@ -65,4 +65,3 @@ namespace ServiceLib.Handler.Fmt
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
} }
} }
}

View File

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

View File

@@ -1,8 +1,8 @@
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public class PacHandler public class PacHandler
{ {
private static string _configPath; private static string _configPath;
@@ -33,6 +33,13 @@ namespace ServiceLib.Handler
private static async Task InitText() private static async Task InitText()
{ {
var path = Path.Combine(_configPath, "pac.txt"); var path = Path.Combine(_configPath, "pac.txt");
// Delete the old pac file
if (File.Exists(path) && Utils.GetFileHash(path).Equals("b590c07280f058ef05d5394aa2f927fe"))
{
File.Delete(path);
}
if (!File.Exists(path)) if (!File.Exists(path))
{ {
var pac = EmbedUtils.GetEmbedText(Global.PacFileName); var pac = EmbedUtils.GetEmbedText(Global.PacFileName);
@@ -105,4 +112,3 @@ namespace ServiceLib.Handler
} }
} }
} }
}

View File

@@ -2,8 +2,8 @@ using System.Collections.Concurrent;
//using System.Reactive.Linq; //using System.Reactive.Linq;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public class ProfileExHandler public class ProfileExHandler
{ {
private static readonly Lazy<ProfileExHandler> _instance = new(() => new()); private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
@@ -179,4 +179,3 @@ namespace ServiceLib.Handler
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort); return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
} }
} }
}

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
{
public class ProxySettingLinux public class ProxySettingLinux
{ {
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh"; private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
@@ -30,4 +30,3 @@ namespace ServiceLib.Handler.SysProxy
await Utils.GetCliWrapOutput(fileName, args); await Utils.GetCliWrapOutput(fileName, args);
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
{
public class ProxySettingOSX public class ProxySettingOSX
{ {
private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh"; private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
@@ -35,4 +35,3 @@ namespace ServiceLib.Handler.SysProxy
await Utils.GetCliWrapOutput(fileName, args); await Utils.GetCliWrapOutput(fileName, args);
} }
} }
}

View File

@@ -1,8 +1,8 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption; using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
{
public class ProxySettingWindows public class ProxySettingWindows
{ {
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings"; private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
@@ -357,4 +357,3 @@ namespace ServiceLib.Handler.SysProxy
); );
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
{
public static class SysProxyHandler public static class SysProxyHandler
{ {
private static readonly string _tag = "SysProxyHandler"; private static readonly string _tag = "SysProxyHandler";
@@ -96,4 +96,3 @@
ProxySettingWindows.SetProxy(strProxy, "", 4); ProxySettingWindows.SetProxy(strProxy, "", 4);
} }
} }
}

View File

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

View File

@@ -1,8 +1,8 @@
using System.Net; using System.Net;
using WebDav; using WebDav;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
{
public sealed class WebDavHandler public sealed class WebDavHandler
{ {
private static readonly Lazy<WebDavHandler> _instance = new(() => new()); private static readonly Lazy<WebDavHandler> _instance = new(() => new());
@@ -179,4 +179,3 @@ namespace ServiceLib.Handler
public string GetLastError() => _lastDescription ?? string.Empty; public string GetLastError() => _lastDescription ?? string.Empty;
} }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class CoreBasicItem public class CoreBasicItem
{ {
@@ -147,7 +147,6 @@ namespace ServiceLib.Models
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? LinuxSudoPwd { get; set; }
} }
[Serializable] [Serializable]
@@ -157,6 +156,7 @@ namespace ServiceLib.Models
public string SpeedTestUrl { get; set; } public string SpeedTestUrl { get; set; }
public string SpeedPingTestUrl { get; set; } public string SpeedPingTestUrl { get; set; }
public int MixedConcurrencyCount { get; set; } public int MixedConcurrencyCount { get; set; }
public string IPAPIUrl { get; set; }
} }
[Serializable] [Serializable]
@@ -245,4 +245,3 @@ namespace ServiceLib.Models
public string? Length { get; set; } public string? Length { get; set; }
public string? Interval { get; set; } public string? Interval { get; set; }
} }
}

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,19 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
internal class IPAPIInfo internal class IPAPIInfo
{ {
public string? ip { get; set; } public string? ip { get; set; }
public string? city { get; set; } public string? clientIp { get; set; }
public string? region { get; set; } public string? ip_addr { get; set; }
public string? region_code { get; set; } public string? query { get; set; }
public string? country { get; set; } public string? country { get; set; }
public string? country_name { get; set; } public string? country_name { get; set; }
public string? country_code { get; set; } public string? country_code { get; set; }
public string? countryCode { get; set; }
public LocationInfo? location { get; set; }
} }
public class LocationInfo
{
public string? country_code { get; set; }
} }

View File

@@ -1,7 +1,7 @@
using SQLite; using SQLite;
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class ProfileExItem public class ProfileExItem
{ {
@@ -13,4 +13,3 @@ namespace ServiceLib.Models
public int Sort { get; set; } public int Sort { get; set; }
public string? Message { get; set; } public string? Message { get; set; }
} }
}

View File

@@ -1,7 +1,7 @@
using SQLite; using SQLite;
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class ProfileItem public class ProfileItem
{ {
@@ -64,6 +64,7 @@ namespace ServiceLib.Models
[PrimaryKey] [PrimaryKey]
public string IndexId { get; set; } public string IndexId { get; set; }
public EConfigType ConfigType { get; set; } public EConfigType ConfigType { get; set; }
public int ConfigVersion { get; set; } public int ConfigVersion { get; set; }
public string Address { get; set; } public string Address { get; set; }
@@ -93,4 +94,3 @@ namespace ServiceLib.Models
public string SpiderX { get; set; } public string SpiderX { get; set; }
public string Extra { get; set; } public string Extra { get; set; }
} }
}

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,7 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class RoutingItemModel : RoutingItem public class RoutingItemModel : RoutingItem
{ {
public bool IsActive { get; set; } public bool IsActive { get; set; }
} }
}

View File

@@ -1,9 +1,8 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class RoutingTemplate public class RoutingTemplate
{ {
public string Version { get; set; } public string Version { get; set; }
public RoutingItem[] RoutingItems { get; set; } public RoutingItem[] RoutingItems { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class RulesItem public class RulesItem
{ {
@@ -16,4 +16,3 @@
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
public string? Remarks { get; set; } public string? Remarks { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class RulesItemModel : RulesItem public class RulesItemModel : RulesItem
{ {
@@ -8,4 +8,3 @@
public string Domains { get; set; } public string Domains { get; set; }
public string Protocols { get; set; } public string Protocols { get; set; }
} }
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable] [Serializable]
public class ServerSpeedItem : ServerStatItem public class ServerSpeedItem : ServerStatItem
{ {
@@ -19,4 +19,3 @@
public ulong Down { get; set; } public ulong Down { get; set; }
} }
}

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