/
va1eriy
/
taskwards
Обзор
Документация
Войти
/
va1eriy
/
taskwards
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Taskwards.Api/Features/Auth/Endpoints/ForgotPassword.cs
146 строк
5 KB
Valeriy Kokorev
feat: implement email confirmation functionality
09 фев 2026, 17:49
09 фев 2026, 17:49
4ada1c2
Код
Авторство
О чём код?
using FastEndpoints; using FluentValidation; using MediatR; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using Taskwards.Api.Infrastructure.Common; using Taskwards.Api.Infrastructure.Data.Entities; using Taskwards.Api.Infrastructure.Email; namespace Taskwards.Api.Features.Auth.Endpoints; /// <summary> /// Запрос на сброс пароля (отправка email с токеном) /// </summary> public static class ForgotPassword { // ===== FASTENDPOINTS ENDPOINT ===== public class Endpoint : Endpoint<Request, Response> { private readonly IMediator _mediator; public Endpoint(IMediator mediator) { _mediator = mediator; } public override void Configure() { Post("/auth/forgot-password"); AllowAnonymous(); Description(d => d .WithTags("Auth") .Produces<Response>(200) .ProducesProblemDetails(400)); } public override async Task HandleAsync(Request req, CancellationToken ct) { var command = new Command(req.Email); var result = await _mediator.Send(command, ct); if (!result.IsSuccess) { ThrowError(result.Error!, statusCode: 400); } var response = new Response(result.Value!); await Send.OkAsync(response, ct); } } // ===== REQUEST ===== /// <summary> /// Запрос на сброс пароля /// </summary> public record Request( string Email ); // ===== RESPONSE ===== /// <summary> /// Ответ на запрос сброса пароля /// </summary> public record Response( string Message ); // ===== VALIDATOR ===== public class Validator : Validator<Request> { public Validator() { RuleFor(x => x.Email) .NotEmpty().WithMessage("Email обязателен") .EmailAddress().WithMessage("Некорректный формат email"); } } // ===== MEDIATR HANDLER ===== /// <summary> /// Команда для генерации токена сброса пароля и отправки email /// </summary> public record Command(string Email) : IRequest<Result<string>>; public class Handler : IRequestHandler<Command, Result<string>> { private readonly UserManager<User> _userManager; private readonly IEmailService _emailService; private readonly IEmailTemplateService _emailTemplateService; private readonly AppSettings _appSettings; private readonly ILogger<Handler> _logger; public Handler( UserManager<User> userManager, IEmailService emailService, IEmailTemplateService emailTemplateService, IOptions<AppSettings> appSettings, ILogger<Handler> logger) { _userManager = userManager; _emailService = emailService; _emailTemplateService = emailTemplateService; _appSettings = appSettings.Value; _logger = logger; } public async Task<Result<string>> Handle(Command request, CancellationToken cancellationToken) { // 1. Найти пользователя по email var user = await _userManager.FindByEmailAsync(request.Email); // ВАЖНО: Из соображений безопасности всегда возвращаем успех, // даже если пользователь не найден (предотвращаем перебор email адресов) if (user is null) { _logger.LogWarning("Password reset requested for non-existent email: {Email}", request.Email); return Result<string>.Success("На указанный email отправлено письмо с инструкциями по сбросу пароля"); } // 2. Генерация токена сброса пароля var resetToken = await _userManager.GeneratePasswordResetTokenAsync(user); // 3. Создание ссылки для сброса пароля var frontendUrl = _appSettings.FrontendUrl; var resetLink = $"{frontendUrl}/reset-password?token={Uri.EscapeDataString(resetToken)}&email={Uri.EscapeDataString(user.Email!)}"; // 4. Подготовка email из шаблона var emailSubject = "Сброс пароля - Taskwards"; var emailBody = _emailTemplateService.GetPasswordResetHtml(user.UserName!, resetLink); // 5. Отправка email try { await _emailService.SendEmailAsync(user.Email!, emailSubject, emailBody, cancellationToken); _logger.LogInformation("Password reset email sent to {Email}", user.Email); } catch (Exception ex) { _logger.LogError(ex, "Failed to send password reset email to {Email}", user.Email); return Result<string>.Failure("Не удалось отправить email. Попробуйте позже."); } return Result<string>.Success("На указанный email отправлено письмо с инструкциями по сбросу пароля"); } } }