Update ThreeXui.Net package version and enhance IXuiPanelGateway implementation
- Updated the version of `ThreeXui.Net` to 1.0.3 in `Directory.Packages.props` for improved functionality. - Modified the `IXuiPanelGateway` interface documentation to reflect changes in client traffic retrieval, specifying the use of `GetInboundClientTrafficAsync` method for better data synchronization. - Refactored `XuiPanelGateway` to utilize the new method, ensuring accurate client traffic data retrieval and addressing issues with previous versions not loading client statistics correctly.
This commit is contained in:
@@ -28,7 +28,7 @@
|
|||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.10" />
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.10" />
|
||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.21.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.21.0" />
|
||||||
<PackageVersion Include="Telegram.Bot" Version="22.10.2" />
|
<PackageVersion Include="Telegram.Bot" Version="22.10.2" />
|
||||||
<PackageVersion Include="ThreeXui.Net" Version="1.0.2" />
|
<PackageVersion Include="ThreeXui.Net" Version="1.0.3" />
|
||||||
<!-- Тестирование (M8) -->
|
<!-- Тестирование (M8) -->
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
||||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||||
|
|||||||
@@ -112,8 +112,11 @@ public interface IXuiPanelGateway
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Трафик по клиентам инбаунда, ключ — ClientEmail. ThreeXui.Net не даёт типизированного метода
|
/// Трафик по клиентам инбаунда, ключ — ClientEmail. Реализация — через
|
||||||
/// для этого — извлекается из сырого clientStats[] в RawInboundJson (стандартное поле 3x-ui API).
|
/// <c>ThreeXui.Net</c>'s <c>IXuiClient.GetInboundClientTrafficAsync</c> (1.0.3+), а не
|
||||||
|
/// <c>GetInboundAsync</c>: на части версий 3x-ui (подтверждено на v2.8.11) одиночный
|
||||||
|
/// эндпоинт инбаунда не подгружает <c>clientStats</c>, из-за чего трафик молча не
|
||||||
|
/// синхронизировался бы никогда.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<Result<IReadOnlyDictionary<string, ClientTrafficInfo>>> GetClientTrafficAsync(
|
Task<Result<IReadOnlyDictionary<string, ClientTrafficInfo>>> GetClientTrafficAsync(
|
||||||
Node node,
|
Node node,
|
||||||
|
|||||||
@@ -294,15 +294,20 @@ internal sealed class XuiPanelGateway(
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var client = GetClient(node);
|
var client = GetClient(node);
|
||||||
var remoteInbound = await client.GetInboundAsync(inboundRemoteId, cancellationToken);
|
// GetInboundClientTrafficAsync (не GetInboundAsync!) — на 3x-ui v2.8.11 одиночный
|
||||||
if (remoteInbound is null)
|
// /panel/api/inbounds/get/{id} не подгружает clientStats (нет Preload на этом пути в
|
||||||
{
|
// самой панели), из-за чего трафик молча не синхронизировался бы вообще. Список
|
||||||
return Result.Failure<IReadOnlyDictionary<string, ClientTrafficInfo>>(
|
// инбаундов подгружает clientStats всегда — см. ThreeXui.Net CHANGELOG 1.0.3.
|
||||||
Error.Failure("Xui.InboundNotFound", "Inbound не найден на панели.")
|
var traffic = await client.GetInboundClientTrafficAsync(
|
||||||
);
|
inboundRemoteId,
|
||||||
}
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
return Result.Success(ParseClientStats(remoteInbound.RawInboundJson));
|
var result = new Dictionary<string, ClientTrafficInfo>(traffic.Count);
|
||||||
|
foreach (var entry in traffic)
|
||||||
|
result[entry.Email] = new ClientTrafficInfo(entry.UpBytes, entry.DownBytes);
|
||||||
|
|
||||||
|
return Result.Success<IReadOnlyDictionary<string, ClientTrafficInfo>>(result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -312,53 +317,6 @@ internal sealed class XuiPanelGateway(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ThreeXui.Net типизированно не отдаёт трафик по клиентам — достаём его из сырого JSON инбаунда:
|
|
||||||
/// стандартное поле 3x-ui API "clientStats": [{ "email": "...", "up": N, "down": N }, ...].
|
|
||||||
/// Формат форка может отличаться — при ошибке парсинга просто возвращаем пусто, не валим синхронизацию.
|
|
||||||
/// </summary>
|
|
||||||
private static IReadOnlyDictionary<string, ClientTrafficInfo> ParseClientStats(
|
|
||||||
string? rawInboundJson
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var result = new Dictionary<string, ClientTrafficInfo>();
|
|
||||||
if (string.IsNullOrWhiteSpace(rawInboundJson))
|
|
||||||
return result;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var doc = JsonDocument.Parse(rawInboundJson);
|
|
||||||
if (
|
|
||||||
!doc.RootElement.TryGetProperty("clientStats", out var clientStats)
|
|
||||||
|| clientStats.ValueKind != JsonValueKind.Array
|
|
||||||
)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
foreach (var stat in clientStats.EnumerateArray())
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
!stat.TryGetProperty("email", out var emailProp)
|
|
||||||
|| emailProp.ValueKind != JsonValueKind.String
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var email = emailProp.GetString();
|
|
||||||
if (string.IsNullOrEmpty(email))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var up = stat.TryGetProperty("up", out var upProp) ? upProp.GetInt64() : 0;
|
|
||||||
var down = stat.TryGetProperty("down", out var downProp) ? downProp.GetInt64() : 0;
|
|
||||||
result[email] = new ClientTrafficInfo(up, down);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (JsonException)
|
|
||||||
{
|
|
||||||
// Не удалось распарсить — возвращаем пусто, вызывающий код просто пропустит синк для этого инбаунда.
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvalidateClient(Guid nodeId)
|
public void InvalidateClient(Guid nodeId)
|
||||||
{
|
{
|
||||||
if (_clients.TryRemove(nodeId, out var lazy) && lazy.IsValueCreated)
|
if (_clients.TryRemove(nodeId, out var lazy) && lazy.IsValueCreated)
|
||||||
|
|||||||
Reference in New Issue
Block a user