Enhance admin endpoints and queries for improved filtering and management
CI / Backend (build + test) (push) Successful in 1m22s
CI / Frontend (lint + typecheck + build) (push) Successful in 34s

- Updated `ListPaymentRequestsQuery` to include `Kind` and `Search` parameters for better filtering of payment requests.
- Enhanced `ListAuditLogsQuery` to support additional filters: `Source`, `TargetType`, and `Action`, improving audit log retrieval.
- Modified `ListUsersQuery` to accept new filters: `RoleId`, `IsActivated`, `IsBlocked`, and `BillingExpired`, allowing for more granular user management.
- Introduced `DeleteInbound` endpoint to allow deletion of inbounds that are not currently available, enhancing inbound management capabilities.
- Updated frontend API calls to reflect new query parameters and support for additional filtering options in the admin interface.
- Revised API documentation to include new parameters and endpoint functionalities for better clarity and usage guidance.
This commit is contained in:
Leonid Pershin
2026-07-20 10:35:48 +03:00
parent e19860ba46
commit 33ad98cf62
37 changed files with 1115 additions and 124 deletions
@@ -4,13 +4,15 @@ using Microsoft.EntityFrameworkCore;
using PnvPanel.Application.Auth;
using PnvPanel.Application.Common.Interfaces;
using PnvPanel.Application.Common.Models;
using PnvPanel.Infrastructure.Persistence;
namespace PnvPanel.Infrastructure.Identity;
internal sealed class IdentityService(
UserManager<AppUser> userManager,
SignInManager<AppUser> signInManager,
RoleManager<AppRole> roleManager
RoleManager<AppRole> roleManager,
AppDbContext dbContext
) : IIdentityService
{
public async Task<Result<Guid>> CreateUserAsync(
@@ -176,6 +178,17 @@ internal sealed class IdentityService(
.ToDictionaryAsync(u => u.Id, u => u.UserName!, cancellationToken);
}
public async Task<IReadOnlyList<Guid>> FindUserIdsByUserNameAsync(
string search,
CancellationToken cancellationToken
)
{
return await userManager
.Users.Where(u => u.UserName!.Contains(search))
.Select(u => u.Id)
.ToListAsync(cancellationToken);
}
public async Task<Result> DeleteUserAsync(Guid userId, CancellationToken cancellationToken)
{
var user = await userManager.FindByIdAsync(userId.ToString());
@@ -252,12 +265,36 @@ internal sealed class IdentityService(
int page,
int pageSize,
string? search,
Guid? roleId,
bool? isActivated,
bool? isBlocked,
bool? billingExpired,
CancellationToken cancellationToken
)
{
var query = userManager.Users.AsNoTracking();
if (!string.IsNullOrWhiteSpace(search))
query = query.Where(u => u.UserName!.Contains(search));
if (isActivated is { } activated)
query = query.Where(u => u.IsActivated == activated);
if (isBlocked is { } blocked)
query = query.Where(u => u.IsBlocked == blocked);
if (roleId is { } roleIdFilter)
query = query.Where(u => dbContext.UserRoles.Any(ur => ur.UserId == u.Id && ur.RoleId == roleIdFilter));
if (billingExpired is { } expired)
{
// Только среди пользователей с billing-ролью — иначе сюда попали бы и те, у кого
// биллинг вообще не включён (BillingPaidUntil у них тоже null, но это не "просрочено").
var now = DateTimeOffset.UtcNow;
query = query.Where(u =>
dbContext.UserRoles.Any(ur =>
ur.UserId == u.Id && dbContext.Roles.Any(r => r.Id == ur.RoleId && r.BillingEnabled)
)
);
query = expired
? query.Where(u => u.BillingPaidUntil == null || u.BillingPaidUntil < now)
: query.Where(u => u.BillingPaidUntil != null && u.BillingPaidUntil >= now);
}
var total = await query.CountAsync(cancellationToken);
var users = await query