Add Telegram bot integration and enhance user management features

- Introduced Telegram.Bot package for bot functionality.
- Updated user management to include Telegram linking and blocking features.
- Enhanced activation request handling with notifications via Telegram.
- Added new database entities for Telegram link tokens and login requests.
- Implemented traffic synchronization for client stats in the XuiPanelGateway.
- Updated application structure to support new test projects and improved dependency injection for Telegram services.
This commit is contained in:
Leonid Pershin
2026-07-02 01:01:03 +03:00
parent 1a8d33efa3
commit 7b6fe9ad78
142 changed files with 7570 additions and 22 deletions
@@ -0,0 +1,84 @@
using System.Net;
using System.Net.Http.Json;
using PnvPanel.IntegrationTests.TestSupport;
using Xunit;
namespace PnvPanel.IntegrationTests.Auth;
[Collection(IntegrationTestCollection.Name)]
public class AuthFlowTests(PnvPanelWebApplicationFactory factory)
{
private sealed record RegisterResponse(Guid Id, string UserName);
private sealed record CurrentUserResponse(Guid Id, string UserName, string Role, bool IsActivated, bool TelegramLinked);
private sealed record LoginResponse(string AccessToken, DateTimeOffset ExpiresAt, CurrentUserResponse User);
[Fact]
public async Task RegisterLoginMeRefreshLogout_FullFlow_Succeeds()
{
using var client = factory.CreateClient();
var userName = $"alice_{Guid.NewGuid():N}"[..20];
const string password = "P@ssw0rd123";
var registerResponse = await client.PostJsonAsync("/api/auth/register", new { userName, password });
Assert.Equal(HttpStatusCode.OK, registerResponse.StatusCode);
var registered = await registerResponse.ReadAsAsync<RegisterResponse>();
Assert.NotNull(registered);
Assert.Equal(userName, registered!.UserName);
var loginResponse = await client.PostJsonAsync("/api/auth/login", new { userName, password });
Assert.Equal(HttpStatusCode.OK, loginResponse.StatusCode);
var login = await loginResponse.ReadAsAsync<LoginResponse>();
Assert.NotNull(login);
Assert.False(login!.User.IsActivated);
Assert.False(login.User.TelegramLinked);
Assert.Equal("user", login.User.Role);
client.UseBearerToken(login.AccessToken);
var meResponse = await client.GetAsync("/api/auth/me");
Assert.Equal(HttpStatusCode.OK, meResponse.StatusCode);
var me = await meResponse.ReadAsAsync<CurrentUserResponse>();
Assert.Equal(userName, me!.UserName);
var refreshResponse = await client.PostAsync("/api/auth/refresh", content: null);
Assert.Equal(HttpStatusCode.OK, refreshResponse.StatusCode);
var refreshed = await refreshResponse.ReadAsAsync<LoginResponse>();
Assert.NotNull(refreshed);
Assert.NotEqual(login.AccessToken, refreshed!.AccessToken);
var logoutResponse = await client.PostAsync("/api/auth/logout", content: null);
Assert.Equal(HttpStatusCode.NoContent, logoutResponse.StatusCode);
// Использованный refresh-токен отозван при logout — повторный refresh должен провалиться.
var refreshAfterLogout = await client.PostAsync("/api/auth/refresh", content: null);
Assert.Equal(HttpStatusCode.Unauthorized, refreshAfterLogout.StatusCode);
}
[Fact]
public async Task Login_WithWrongPassword_ReturnsUnauthorized()
{
using var client = factory.CreateClient();
var userName = $"bob_{Guid.NewGuid():N}"[..20];
await client.PostJsonAsync("/api/auth/register", new { userName, password = "CorrectPassword123" });
var loginResponse = await client.PostJsonAsync("/api/auth/login", new { userName, password = "WrongPassword123" });
Assert.Equal(HttpStatusCode.Unauthorized, loginResponse.StatusCode);
}
[Fact]
public async Task Register_WithDuplicateUserName_ReturnsConflict()
{
using var client = factory.CreateClient();
var userName = $"carol_{Guid.NewGuid():N}"[..20];
var first = await client.PostJsonAsync("/api/auth/register", new { userName, password = "P@ssw0rd123" });
Assert.Equal(HttpStatusCode.OK, first.StatusCode);
var second = await client.PostJsonAsync("/api/auth/register", new { userName, password = "AnotherPass123" });
Assert.Equal(HttpStatusCode.Conflict, second.StatusCode);
}
}