- Introduced a new support ticket system allowing users to submit bug reports and role requests. - Implemented endpoints for creating, updating, and managing support tickets, including file attachments. - Enhanced Telegram bot integration to handle role requests directly within the bot, enabling admins to approve or reject requests without accessing the website. - Updated database schema to include support ticket entities and their relationships. - Improved API documentation to reflect new support ticket endpoints and their usage. - Added necessary localization for support ticket features in both Russian and English.
80 lines
3.5 KiB
C#
80 lines
3.5 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using PnvPanel.Api.Common;
|
|
using PnvPanel.Application.Admin.Support;
|
|
using PnvPanel.Application.Common.Messaging;
|
|
using PnvPanel.Application.Common.Models;
|
|
using PnvPanel.Application.Support;
|
|
using PnvPanel.Application.Support.AddComment;
|
|
using PnvPanel.Infrastructure.Identity;
|
|
|
|
namespace PnvPanel.Api.Endpoints;
|
|
|
|
public static class AdminSupportEndpoints
|
|
{
|
|
public static IEndpointRouteBuilder MapAdminSupportEndpoints(this IEndpointRouteBuilder app)
|
|
{
|
|
var admin = app.MapGroup("/api/admin/support")
|
|
.WithTags("Admin.Support")
|
|
.RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin));
|
|
|
|
admin.MapGet("/tickets", ListTickets).Produces<PagedList<TicketSummaryDto>>();
|
|
admin.MapGet("/tickets/{id:guid}", GetTicket).Produces<TicketDetailDto>();
|
|
admin.MapPost("/tickets/{id:guid}/comments", AddComment).DisableAntiforgery().Produces<TicketCommentDto>();
|
|
admin.MapPost("/tickets/{id:guid}/resolve", Resolve).Produces(StatusCodes.Status204NoContent);
|
|
admin.MapPost("/tickets/{id:guid}/close", Close).Produces(StatusCodes.Status204NoContent);
|
|
admin.MapPost("/tickets/{id:guid}/approve", ApproveRoleRequest).Produces(StatusCodes.Status204NoContent);
|
|
admin.MapPost("/tickets/{id:guid}/reject", RejectRoleRequest).Produces(StatusCodes.Status204NoContent);
|
|
|
|
return app;
|
|
}
|
|
|
|
private static async Task<IResult> ListTickets(
|
|
[AsParameters] ListTicketsRequest request, ISender sender, CancellationToken cancellationToken)
|
|
{
|
|
var query = new ListAllTicketsQuery(request.Type, request.Status, request.Page, request.PageSize);
|
|
var result = await sender.Send(query, cancellationToken);
|
|
return result.ToHttpResult();
|
|
}
|
|
|
|
private static async Task<IResult> GetTicket(Guid id, ISender sender, CancellationToken cancellationToken)
|
|
{
|
|
var result = await sender.Send(new GetTicketAdminQuery(id), cancellationToken);
|
|
return result.ToHttpResult();
|
|
}
|
|
|
|
private static async Task<IResult> AddComment(
|
|
Guid id, [FromForm] string body, IFormFileCollection? files, ISender sender, CancellationToken cancellationToken)
|
|
{
|
|
var command = new AddTicketCommentCommand(id, body, SupportEndpoints.ToUploads(files));
|
|
var result = await sender.Send(command, cancellationToken);
|
|
return result.ToHttpResult();
|
|
}
|
|
|
|
private static async Task<IResult> Resolve(Guid id, ISender sender, CancellationToken cancellationToken)
|
|
{
|
|
var result = await sender.Send(new ResolveTicketCommand(id), cancellationToken);
|
|
return result.ToHttpResult();
|
|
}
|
|
|
|
private static async Task<IResult> Close(Guid id, ISender sender, CancellationToken cancellationToken)
|
|
{
|
|
var result = await sender.Send(new CloseTicketCommand(id), cancellationToken);
|
|
return result.ToHttpResult();
|
|
}
|
|
|
|
private static async Task<IResult> ApproveRoleRequest(Guid id, ISender sender, CancellationToken cancellationToken)
|
|
{
|
|
var result = await sender.Send(new ApproveRoleRequestCommand(id), cancellationToken);
|
|
return result.ToHttpResult();
|
|
}
|
|
|
|
private static async Task<IResult> RejectRoleRequest(
|
|
Guid id, RejectRoleRequestBody body, ISender sender, CancellationToken cancellationToken)
|
|
{
|
|
var result = await sender.Send(new RejectRoleRequestCommand(id, body.Reason), cancellationToken);
|
|
return result.ToHttpResult();
|
|
}
|
|
}
|
|
|
|
public sealed record RejectRoleRequestBody(string? Reason);
|