From 081d3f8e5f93b519cd639c1e2fc054f6da0d8060 Mon Sep 17 00:00:00 2001 From: taiphanvan2k3 Date: Mon, 3 Nov 2025 23:38:23 +0700 Subject: [PATCH] feat: Implement verify email --- .vscode/launch.json | 2 +- ...andler.cs => EmailVerifiedEventHandler.cs} | 13 +- .../EventHandlers/UserCreatedEventHandler.cs | 58 +++++ .../Auth/VerifyEmail/VerifyEmailCommand.cs | 13 ++ .../VerifyEmail/VerifyEmailCommandHandler.cs | 58 +++++ src/Application/Interfaces/IEmailService.cs | 11 +- .../Interfaces/IEmailTemplateService.cs | 22 ++ .../Interfaces/ITokenGenerationService.cs | 17 ++ src/Application/Interfaces/IUserRepository.cs | 10 + src/Domain/Events/User/EmailVerifiedEvent.cs | 14 ++ src/Domain/Events/User/UserCreatedEvent.cs | 4 +- .../Events/User/UserDeactivatedEvent.cs | 18 -- src/Domain/Events/User/UserUpdatedEvent.cs | 22 -- .../Extensions/ServiceCollectionExtensions.cs | 5 +- src/Infrastructure/Infrastructure.csproj | 9 + .../Repositories/UserRepository.cs | 31 ++- src/Infrastructure/Services/Email/.gitkeep | 0 .../Services/Email/EmailService.cs | 82 +++++++ .../Services/Email/EmailTemplateService.cs | 58 +++++ src/Infrastructure/Services/EmailService.cs | 140 ------------ .../Services/TokenGenerationService.cs | 48 ++++ .../Email/EmailVerificationEmail.html | 175 +++++++++++++++ .../Templates/Email/PasswordResetEmail.html | 174 +++++++++++++++ .../Templates/Email/WelcomeEmail.html | 207 ++++++++++++++++++ src/Web.Api/Controllers/V1/AuthController.cs | 33 +++ src/Web.Api/Program.cs | 6 + src/Web.Api/appsettings.Development.json | 3 + src/Web.Api/appsettings.json | 3 + src/Web.Api/wwwroot/images/email/banner.webp | Bin 0 -> 22296 bytes src/Web.Api/wwwroot/images/email/logo.png | Bin 0 -> 37308 bytes 30 files changed, 1044 insertions(+), 192 deletions(-) rename src/Application/EventHandlers/{UserCreatedSendEmailEventHandler.cs => EmailVerifiedEventHandler.cs} (64%) create mode 100644 src/Application/EventHandlers/UserCreatedEventHandler.cs create mode 100644 src/Application/Features/Auth/VerifyEmail/VerifyEmailCommand.cs create mode 100644 src/Application/Features/Auth/VerifyEmail/VerifyEmailCommandHandler.cs create mode 100644 src/Application/Interfaces/IEmailTemplateService.cs create mode 100644 src/Application/Interfaces/ITokenGenerationService.cs create mode 100644 src/Domain/Events/User/EmailVerifiedEvent.cs delete mode 100644 src/Domain/Events/User/UserDeactivatedEvent.cs delete mode 100644 src/Domain/Events/User/UserUpdatedEvent.cs delete mode 100644 src/Infrastructure/Services/Email/.gitkeep create mode 100644 src/Infrastructure/Services/Email/EmailService.cs create mode 100644 src/Infrastructure/Services/Email/EmailTemplateService.cs delete mode 100644 src/Infrastructure/Services/EmailService.cs create mode 100644 src/Infrastructure/Services/TokenGenerationService.cs create mode 100644 src/Infrastructure/Templates/Email/EmailVerificationEmail.html create mode 100644 src/Infrastructure/Templates/Email/PasswordResetEmail.html create mode 100644 src/Infrastructure/Templates/Email/WelcomeEmail.html create mode 100644 src/Web.Api/wwwroot/images/email/banner.webp create mode 100644 src/Web.Api/wwwroot/images/email/logo.png diff --git a/.vscode/launch.json b/.vscode/launch.json index b66e968..8e0e147 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,7 +17,7 @@ }, "env": { "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_URLS": "https://localhost:7001;http://localhost:5001" + "ASPNETCORE_URLS": "http://localhost:10000" }, "sourceFileMap": { "/Views": "${workspaceFolder}/Views" diff --git a/src/Application/EventHandlers/UserCreatedSendEmailEventHandler.cs b/src/Application/EventHandlers/EmailVerifiedEventHandler.cs similarity index 64% rename from src/Application/EventHandlers/UserCreatedSendEmailEventHandler.cs rename to src/Application/EventHandlers/EmailVerifiedEventHandler.cs index 8463c17..cd916fa 100644 --- a/src/Application/EventHandlers/UserCreatedSendEmailEventHandler.cs +++ b/src/Application/EventHandlers/EmailVerifiedEventHandler.cs @@ -6,17 +6,17 @@ namespace Application.EventHandlers; /// -/// Handler for sending welcome email when user is created +/// Handler for sending welcome email when user verifies their email /// -public sealed class UserCreatedSendEmailEventHandler( +public sealed class EmailVerifiedEventHandler( IEmailService emailService, - ILogger logger) - : IDomainEventHandler + ILogger logger) + : IDomainEventHandler { - public async Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken) + public async Task Handle(EmailVerifiedEvent notification, CancellationToken cancellationToken) { logger.LogInformation( - "Sending welcome email to user {UserId} at {Email}", + "Sending welcome email to verified user {UserId} at {Email}", notification.UserId, notification.Email); @@ -36,7 +36,6 @@ await emailService.SendWelcomeEmailAsync( logger.LogError(ex, "Failed to send welcome email to {Email}", notification.Email); - // Don't throw - we don't want email failures to break the registration flow } } } diff --git a/src/Application/EventHandlers/UserCreatedEventHandler.cs b/src/Application/EventHandlers/UserCreatedEventHandler.cs new file mode 100644 index 0000000..d7ff131 --- /dev/null +++ b/src/Application/EventHandlers/UserCreatedEventHandler.cs @@ -0,0 +1,58 @@ +using Application.Common; +using Application.Interfaces; +using Domain.Events.User; +using Microsoft.Extensions.Logging; + +namespace Application.EventHandlers; + +/// +/// Handler for sending email verification when user is created +/// +public sealed class UserCreatedEventHandler( + IEmailService emailService, + ITokenGenerationService tokenService, + ILogger logger) + : IDomainEventHandler +{ + public async Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken) + { + // If user registered with social provider (Google, etc.), email is already verified + // Skip sending verification email + if (notification.IsEmailVerified) + { + logger.LogInformation( + "User {UserId} registered with verified email (social provider). Skipping verification email.", + notification.UserId); + return; + } + + logger.LogInformation( + "Sending email verification to user {UserId} at {Email}", + notification.UserId, + notification.Email); + + try + { + // Generate email confirmation token using Identity + var token = await tokenService.GenerateEmailConfirmationTokenAsync(notification.UserId); + var verificationLink = $"http://localhost:10000/api/v1/auth/verify-email?userId={notification.UserId}&token={Uri.EscapeDataString(token)}"; + + await emailService.SendEmailVerificationAsync( + notification.Email, + notification.FullName, + verificationLink, + cancellationToken); + + logger.LogInformation( + "Verification email sent successfully to {Email}", + notification.Email); + } + catch (Exception ex) + { + logger.LogError(ex, + "Failed to send verification email to {Email}", + notification.Email); + // Don't throw - we don't want email failures to break the registration flow + } + } +} diff --git a/src/Application/Features/Auth/VerifyEmail/VerifyEmailCommand.cs b/src/Application/Features/Auth/VerifyEmail/VerifyEmailCommand.cs new file mode 100644 index 0000000..2775e87 --- /dev/null +++ b/src/Application/Features/Auth/VerifyEmail/VerifyEmailCommand.cs @@ -0,0 +1,13 @@ +using Application.Common; +using Domain.Common; + +namespace Application.Features.Auth.VerifyEmail; + +/// +/// Command to verify user's email address +/// +public sealed record VerifyEmailCommand : ICommand +{ + public required Guid UserId { get; init; } + public required string Token { get; init; } +} diff --git a/src/Application/Features/Auth/VerifyEmail/VerifyEmailCommandHandler.cs b/src/Application/Features/Auth/VerifyEmail/VerifyEmailCommandHandler.cs new file mode 100644 index 0000000..95ec393 --- /dev/null +++ b/src/Application/Features/Auth/VerifyEmail/VerifyEmailCommandHandler.cs @@ -0,0 +1,58 @@ +using Application.Common; +using Application.Interfaces; +using Domain.Common; +using Domain.Events.User; +using Microsoft.Extensions.Logging; + +namespace Application.Features.Auth.VerifyEmail; + +/// +/// Handler for verifying user's email address +/// +public sealed class VerifyEmailCommandHandler( + IUserRepository userRepository, + ITokenGenerationService tokenService, + ILogger logger) + : ICommandHandler +{ + public async Task Handle(VerifyEmailCommand request, CancellationToken cancellationToken) + { + // Find user by ID + var user = await userRepository.GetByIdAsync(request.UserId, cancellationToken); + if (user == null) + { + logger.LogWarning("User not found: {UserId}", request.UserId); + return Result.Failure(Error.NotFound("User.NotFound", "User not found")); + } + + // Check if email is already verified + var isAlreadyVerified = await userRepository.IsEmailVerifiedAsync(request.UserId, cancellationToken); + if (isAlreadyVerified) + { + logger.LogInformation("Email already confirmed for user {UserId}", request.UserId); + return Result.Success(); + } + + // Validate token using Identity's built-in token provider + // Token is stored in AspNetUserTokens table and validated automatically + var verifySuccess = await tokenService.ConfirmEmailAsync(request.UserId, request.Token); + if (!verifySuccess) + { + logger.LogError("Invalid or expired token for user {UserId}", request.UserId); + return Result.Failure(Error.Failure("User.InvalidToken", "Invalid or expired verification token")); + } + + // Raise EmailVerifiedEvent to send welcome email + user.AddDomainEvent(new EmailVerifiedEvent + { + UserId = user.Id, + Email = user.Email, + FullName = user.FullName + }); + + await userRepository.UpdateAsync(user, cancellationToken); + + logger.LogInformation("Email verified successfully for user {UserId}", request.UserId); + return Result.Success(); + } +} diff --git a/src/Application/Interfaces/IEmailService.cs b/src/Application/Interfaces/IEmailService.cs index 0c9fd2e..b9f5b91 100644 --- a/src/Application/Interfaces/IEmailService.cs +++ b/src/Application/Interfaces/IEmailService.cs @@ -6,7 +6,16 @@ namespace Application.Interfaces; public interface IEmailService { /// - /// Send welcome email to new user + /// Send email verification to new user + /// + /// User's email address + /// User's full name + /// Email verification link + /// Cancellation token + Task SendEmailVerificationAsync(string email, string fullName, string verificationLink, CancellationToken cancellationToken = default); + + /// + /// Send welcome email to verified user /// /// User's email address /// User's full name diff --git a/src/Application/Interfaces/IEmailTemplateService.cs b/src/Application/Interfaces/IEmailTemplateService.cs new file mode 100644 index 0000000..e1caf6c --- /dev/null +++ b/src/Application/Interfaces/IEmailTemplateService.cs @@ -0,0 +1,22 @@ +namespace Application.Interfaces; + +/// +/// Service for generating email templates +/// +public interface IEmailTemplateService +{ + /// + /// Generate welcome email template + /// + string GetWelcomeEmailTemplate(string fullName); + + /// + /// Generate password reset email template + /// + string GetPasswordResetEmailTemplate(string fullName, string resetLink); + + /// + /// Generate email verification template + /// + string GetEmailVerificationTemplate(string fullName, string verificationLink); +} diff --git a/src/Application/Interfaces/ITokenGenerationService.cs b/src/Application/Interfaces/ITokenGenerationService.cs new file mode 100644 index 0000000..6ffee6e --- /dev/null +++ b/src/Application/Interfaces/ITokenGenerationService.cs @@ -0,0 +1,17 @@ +namespace Application.Interfaces; + +/// +/// Service for generating verification tokens +/// +public interface ITokenGenerationService +{ + /// + /// Generate email confirmation token for user + /// + Task GenerateEmailConfirmationTokenAsync(Guid userId); + + /// + /// Confirm email with token + /// + Task ConfirmEmailAsync(Guid userId, string token); +} diff --git a/src/Application/Interfaces/IUserRepository.cs b/src/Application/Interfaces/IUserRepository.cs index c3ab403..665f837 100644 --- a/src/Application/Interfaces/IUserRepository.cs +++ b/src/Application/Interfaces/IUserRepository.cs @@ -26,4 +26,14 @@ public interface IUserRepository /// Create user /// Task CreateAsync(User user, CancellationToken cancellationToken = default); + + /// + /// Verify user's email + /// + Task VerifyEmailAsync(Guid userId, CancellationToken cancellationToken = default); + + /// + /// Check if user's email is verified + /// + Task IsEmailVerifiedAsync(Guid userId, CancellationToken cancellationToken = default); } diff --git a/src/Domain/Events/User/EmailVerifiedEvent.cs b/src/Domain/Events/User/EmailVerifiedEvent.cs new file mode 100644 index 0000000..9a8ff8a --- /dev/null +++ b/src/Domain/Events/User/EmailVerifiedEvent.cs @@ -0,0 +1,14 @@ +using Domain.Common; + +namespace Domain.Events.User; + +/// +/// Event raised when user verifies their email +/// +public sealed record EmailVerifiedEvent : IDomainEvent +{ + public required Guid UserId { get; init; } + public required string Email { get; init; } + public required string FullName { get; init; } + public DateTime OccurredOn { get; init; } = DateTime.UtcNow; +} diff --git a/src/Domain/Events/User/UserCreatedEvent.cs b/src/Domain/Events/User/UserCreatedEvent.cs index 014ed81..79cfb00 100644 --- a/src/Domain/Events/User/UserCreatedEvent.cs +++ b/src/Domain/Events/User/UserCreatedEvent.cs @@ -11,14 +11,16 @@ public sealed record UserCreatedEvent : IDomainEvent public string Email { get; } public string FullName { get; } public List Roles { get; } + public bool IsEmailVerified { get; } public DateTime OccurredOn { get; } - public UserCreatedEvent(Guid userId, string email, string fullName, List roles) + public UserCreatedEvent(Guid userId, string email, string fullName, List roles, bool isEmailVerified = false) { UserId = userId; Email = email; FullName = fullName; Roles = roles; + IsEmailVerified = isEmailVerified; OccurredOn = DateTime.UtcNow; } } diff --git a/src/Domain/Events/User/UserDeactivatedEvent.cs b/src/Domain/Events/User/UserDeactivatedEvent.cs deleted file mode 100644 index c75f62d..0000000 --- a/src/Domain/Events/User/UserDeactivatedEvent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Domain.Common; - -namespace Domain.Events.User; - -/// -/// Domain event raised when a user is deactivated -/// -public sealed record UserDeactivatedEvent : IDomainEvent -{ - public Guid UserId { get; } - public DateTime OccurredOn { get; } - - public UserDeactivatedEvent(Guid userId) - { - UserId = userId; - OccurredOn = DateTime.UtcNow; - } -} diff --git a/src/Domain/Events/User/UserUpdatedEvent.cs b/src/Domain/Events/User/UserUpdatedEvent.cs deleted file mode 100644 index c391460..0000000 --- a/src/Domain/Events/User/UserUpdatedEvent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Domain.Common; - -namespace Domain.Events.User; - -/// -/// Domain event raised when user information is updated -/// -public sealed record UserUpdatedEvent : IDomainEvent -{ - public Guid UserId { get; } - public string FullName { get; } - public string? PhoneNumber { get; } - public DateTime OccurredOn { get; } - - public UserUpdatedEvent(Guid userId, string fullName, string? phoneNumber) - { - UserId = userId; - FullName = fullName; - PhoneNumber = phoneNumber; - OccurredOn = DateTime.UtcNow; - } -} diff --git a/src/Infrastructure/Extensions/ServiceCollectionExtensions.cs b/src/Infrastructure/Extensions/ServiceCollectionExtensions.cs index 903312d..c56364b 100644 --- a/src/Infrastructure/Extensions/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/Extensions/ServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using Infrastructure.Data.Contexts; using Infrastructure.Repositories; using Infrastructure.Services; +using Infrastructure.Services.Email; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -96,9 +97,11 @@ private static IServiceCollection AddInfrastructureApplicationServices(this ISer // Authentication & Security services.AddScoped(); services.AddScoped(); + services.AddScoped(); - // Email Service + // Email Services services.AddScoped(); + services.AddScoped(); // Domain Events services.AddScoped(); diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index 7e9c0d2..ca9462c 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -31,4 +31,13 @@ + + + PreserveNewest + + + PreserveNewest + + + diff --git a/src/Infrastructure/Repositories/UserRepository.cs b/src/Infrastructure/Repositories/UserRepository.cs index a382003..0886270 100644 --- a/src/Infrastructure/Repositories/UserRepository.cs +++ b/src/Infrastructure/Repositories/UserRepository.cs @@ -1,6 +1,8 @@ using Application.Interfaces; using Domain.Entities; using Infrastructure.Data.Contexts; +using Infrastructure.Identity; +using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; namespace Infrastructure.Repositories; @@ -11,10 +13,12 @@ namespace Infrastructure.Repositories; public sealed class UserRepository : IUserRepository { private readonly DataContext _context; + private readonly UserManager _userManager; - public UserRepository(DataContext context) + public UserRepository(DataContext context, UserManager userManager) { _context = context; + _userManager = userManager; } /// @@ -53,4 +57,29 @@ public async Task CreateAsync(User user, CancellationToken cancellationTok await _context.SaveChangesAsync(cancellationToken); return entry.Entity; } + + /// + /// Verify user's email + /// + public async Task VerifyEmailAsync(Guid userId, CancellationToken cancellationToken = default) + { + var identityUser = await _userManager.FindByIdAsync(userId.ToString()); + if (identityUser == null) + { + return false; + } + + identityUser.EmailConfirmed = true; + var result = await _userManager.UpdateAsync(identityUser); + return result.Succeeded; + } + + /// + /// Check if user's email is verified + /// + public async Task IsEmailVerifiedAsync(Guid userId, CancellationToken cancellationToken = default) + { + var identityUser = await _userManager.FindByIdAsync(userId.ToString()); + return identityUser?.EmailConfirmed ?? false; + } } diff --git a/src/Infrastructure/Services/Email/.gitkeep b/src/Infrastructure/Services/Email/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/Infrastructure/Services/Email/EmailService.cs b/src/Infrastructure/Services/Email/EmailService.cs new file mode 100644 index 0000000..9080960 --- /dev/null +++ b/src/Infrastructure/Services/Email/EmailService.cs @@ -0,0 +1,82 @@ +using System.Net; +using System.Net.Mail; +using Application.Interfaces; +using Infrastructure.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Infrastructure.Services.Email; + +/// +/// Email service implementation using SMTP +/// +public sealed class EmailService : IEmailService +{ + private readonly EmailSettings _emailSettings; + private readonly IEmailTemplateService _templateService; + private readonly ILogger _logger; + + public EmailService( + IOptions emailSettings, + IEmailTemplateService templateService, + ILogger logger) + { + _emailSettings = emailSettings.Value; + _templateService = templateService; + _logger = logger; + } + + public async Task SendEmailVerificationAsync(string email, string fullName, string verificationLink, CancellationToken cancellationToken = default) + { + var subject = "Verify Your Email - Legal Assistant"; + var body = _templateService.GetEmailVerificationTemplate(fullName, verificationLink); + + await SendEmailAsync(email, subject, body, cancellationToken); + } + + public async Task SendWelcomeEmailAsync(string email, string fullName, CancellationToken cancellationToken = default) + { + var subject = "Welcome to Legal Assistant!"; + var body = _templateService.GetWelcomeEmailTemplate(fullName); + + await SendEmailAsync(email, subject, body, cancellationToken); + } + + public async Task SendEmailAsync(string to, string subject, string body, CancellationToken cancellationToken = default) + { + try + { + // Check if email is enabled + if (!_emailSettings.Enabled) + { + _logger.LogWarning("Email service is disabled. Email not sent to {Email}", to); + return; + } + + using var client = new SmtpClient(_emailSettings.SmtpHost, _emailSettings.SmtpPort) + { + Credentials = new NetworkCredential(_emailSettings.SmtpUsername, _emailSettings.SmtpPassword), + EnableSsl = _emailSettings.EnableSsl + }; + + using var mailMessage = new MailMessage + { + From = new MailAddress(_emailSettings.FromEmail, _emailSettings.FromName), + Subject = subject, + Body = body, + IsBodyHtml = true + }; + + mailMessage.To.Add(to); + + await client.SendMailAsync(mailMessage, cancellationToken); + + _logger.LogInformation("Email sent successfully to {Email}", to); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to send email to {Email}", to); + // Don't throw - we don't want email failures to break the registration flow + } + } +} diff --git a/src/Infrastructure/Services/Email/EmailTemplateService.cs b/src/Infrastructure/Services/Email/EmailTemplateService.cs new file mode 100644 index 0000000..421950c --- /dev/null +++ b/src/Infrastructure/Services/Email/EmailTemplateService.cs @@ -0,0 +1,58 @@ +using Application.Interfaces; +using Microsoft.Extensions.Configuration; + +namespace Infrastructure.Services.Email; + +/// +/// Service for generating email templates from HTML files +/// +public sealed class EmailTemplateService : IEmailTemplateService +{ + private readonly string _templateBasePath; + private readonly string _baseUrl; + + public EmailTemplateService(IConfiguration configuration) + { + // Get the base path for templates (Infrastructure/Templates/Email) + _templateBasePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Templates", "Email"); + _baseUrl = configuration["AppSettings:BaseUrl"] ?? "http://localhost:10000"; + } + + public string GetWelcomeEmailTemplate(string fullName) + { + var template = LoadTemplate("WelcomeEmail.html"); + return template + .Replace("{{FullName}}", fullName) + .Replace("{{BaseUrl}}", _baseUrl); + } + + public string GetPasswordResetEmailTemplate(string fullName, string resetLink) + { + var template = LoadTemplate("PasswordResetEmail.html"); + return template + .Replace("{{FullName}}", fullName) + .Replace("{{ResetLink}}", resetLink) + .Replace("{{BaseUrl}}", _baseUrl); + } + + public string GetEmailVerificationTemplate(string fullName, string verificationLink) + { + var template = LoadTemplate("EmailVerificationEmail.html"); + return template + .Replace("{{FullName}}", fullName) + .Replace("{{VerificationLink}}", verificationLink) + .Replace("{{BaseUrl}}", _baseUrl); + } + + private string LoadTemplate(string templateName) + { + var templatePath = Path.Combine(_templateBasePath, templateName); + + if (!File.Exists(templatePath)) + { + throw new FileNotFoundException($"Email template not found: {templateName}", templatePath); + } + + return File.ReadAllText(templatePath); + } +} diff --git a/src/Infrastructure/Services/EmailService.cs b/src/Infrastructure/Services/EmailService.cs deleted file mode 100644 index 37a1319..0000000 --- a/src/Infrastructure/Services/EmailService.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System.Net; -using System.Net.Mail; -using Application.Interfaces; -using Infrastructure.Configuration; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace Infrastructure.Services; - -/// -/// Email service implementation using SMTP -/// -public sealed class EmailService : IEmailService -{ - private readonly EmailSettings _emailSettings; - private readonly ILogger _logger; - - public EmailService(IOptions emailSettings, ILogger logger) - { - _emailSettings = emailSettings.Value; - _logger = logger; - } - - public async Task SendWelcomeEmailAsync(string email, string fullName, CancellationToken cancellationToken = default) - { - var subject = "Welcome to Legal Assistant!"; - var body = GetWelcomeEmailTemplate(fullName); - - await SendEmailAsync(email, subject, body, cancellationToken); - } - - public async Task SendEmailAsync(string to, string subject, string body, CancellationToken cancellationToken = default) - { - try - { - // Check if email is enabled - if (!_emailSettings.Enabled) - { - _logger.LogWarning("Email service is disabled. Email not sent to {Email}", to); - return; - } - - using var client = new SmtpClient(_emailSettings.SmtpHost, _emailSettings.SmtpPort) - { - Credentials = new NetworkCredential(_emailSettings.SmtpUsername, _emailSettings.SmtpPassword), - EnableSsl = _emailSettings.EnableSsl - }; - - using var mailMessage = new MailMessage - { - From = new MailAddress(_emailSettings.FromEmail, _emailSettings.FromName), - Subject = subject, - Body = body, - IsBodyHtml = true - }; - - mailMessage.To.Add(to); - - await client.SendMailAsync(mailMessage, cancellationToken); - - _logger.LogInformation("Email sent successfully to {Email}", to); - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to send email to {Email}", to); - // Don't throw - we don't want email failures to break the registration flow - } - } - - private string GetWelcomeEmailTemplate(string fullName) - { - return $@" - - - - - - -
-
-

Welcome to Legal Assistant!

-
-
-

Hello {fullName},

-

Thank you for registering with Legal Assistant!

-

We're excited to have you on board. Your account has been successfully created and you can now start using our services.

-

Here's what you can do:

-
    -
  • Start conversations with our AI legal assistant
  • -
  • Get instant answers to your legal questions
  • -
  • Access your conversation history anytime
  • -
-

If you have any questions or need assistance, feel free to reach out to our support team.

-

Best regards,
The Legal Assistant Team

-
- -
- -"; - } -} diff --git a/src/Infrastructure/Services/TokenGenerationService.cs b/src/Infrastructure/Services/TokenGenerationService.cs new file mode 100644 index 0000000..b14ae7c --- /dev/null +++ b/src/Infrastructure/Services/TokenGenerationService.cs @@ -0,0 +1,48 @@ +using Application.Interfaces; +using Infrastructure.Identity; +using Microsoft.AspNetCore.Identity; + +namespace Infrastructure.Services; + +/// +/// Service for generating verification tokens using ASP.NET Identity +/// +public sealed class TokenGenerationService : ITokenGenerationService +{ + private readonly UserManager _userManager; + + public TokenGenerationService(UserManager userManager) + { + _userManager = userManager; + } + + /// + /// Generate email confirmation token for user + /// Token is automatically stored in AspNetUserTokens table + /// + public async Task GenerateEmailConfirmationTokenAsync(Guid userId) + { + var user = await _userManager.FindByIdAsync(userId.ToString()) + ?? throw new InvalidOperationException($"User not found: {userId}"); + + // Identity automatically stores this token in AspNetUserTokens table + // Token includes: UserId, LoginProvider, Name, Value + return await _userManager.GenerateEmailConfirmationTokenAsync(user); + } + + /// + /// Confirm email with token + /// Identity automatically validates token from AspNetUserTokens table + /// + public async Task ConfirmEmailAsync(Guid userId, string token) + { + var user = await _userManager.FindByIdAsync(userId.ToString()); + if (user == null) + { + return false; + } + + var result = await _userManager.ConfirmEmailAsync(user, token); + return result.Succeeded; + } +} diff --git a/src/Infrastructure/Templates/Email/EmailVerificationEmail.html b/src/Infrastructure/Templates/Email/EmailVerificationEmail.html new file mode 100644 index 0000000..c24a0f3 --- /dev/null +++ b/src/Infrastructure/Templates/Email/EmailVerificationEmail.html @@ -0,0 +1,175 @@ + + + + + + + + + + + diff --git a/src/Infrastructure/Templates/Email/PasswordResetEmail.html b/src/Infrastructure/Templates/Email/PasswordResetEmail.html new file mode 100644 index 0000000..a07454c --- /dev/null +++ b/src/Infrastructure/Templates/Email/PasswordResetEmail.html @@ -0,0 +1,174 @@ + + + + + + + + + + + diff --git a/src/Infrastructure/Templates/Email/WelcomeEmail.html b/src/Infrastructure/Templates/Email/WelcomeEmail.html new file mode 100644 index 0000000..144ba1d --- /dev/null +++ b/src/Infrastructure/Templates/Email/WelcomeEmail.html @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Web.Api/Controllers/V1/AuthController.cs b/src/Web.Api/Controllers/V1/AuthController.cs index 1fc3111..9932f07 100644 --- a/src/Web.Api/Controllers/V1/AuthController.cs +++ b/src/Web.Api/Controllers/V1/AuthController.cs @@ -1,5 +1,6 @@ using Application.Features.Auth.Login; using Application.Features.Auth.Register; +using Application.Features.Auth.VerifyEmail; using Domain.Common; using MediatR; using Microsoft.AspNetCore.Mvc; @@ -58,4 +59,36 @@ public async Task Register([FromBody] RegisterCommand command) _ => BadRequest(ApiResponse.CreateFailure(result.Error.Description)) }; } + + /// + /// Verify user's email address + /// + /// User ID + /// Verification token + /// Verification result + [HttpGet("verify-email")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)] + public async Task VerifyEmail([FromQuery] Guid userId, [FromQuery] string token) + { + var command = new VerifyEmailCommand + { + UserId = userId, + Token = token + }; + + var result = await mediator.Send(command); + + if (result.IsSuccess) + { + return Ok(ApiResponse.CreateSuccess("Email verified successfully! You can now log in.")); + } + + return result.Error!.Type switch + { + ErrorType.NotFound => NotFound(ApiResponse.CreateFailure(result.Error.Description)), + _ => BadRequest(ApiResponse.CreateFailure(result.Error.Description)) + }; + } } diff --git a/src/Web.Api/Program.cs b/src/Web.Api/Program.cs index a38fa16..33b5656 100644 --- a/src/Web.Api/Program.cs +++ b/src/Web.Api/Program.cs @@ -19,6 +19,9 @@ // Add Serilog builder.Host.AddSerilog(); + // Add HttpContextAccessor for accessing HTTP request context in services + builder.Services.AddHttpContextAccessor(); + // Add services to the container. builder.Services.AddControllers();// Add Infrastructure services (Database, Repositories, Services) builder.Services.AddInfrastructureServices(builder.Configuration); @@ -50,6 +53,9 @@ // Add Global Exception Middleware app.UseMiddleware(); + // Serve static files (images, css, js, etc.) + app.UseStaticFiles(); + // Only use HTTPS redirection in production or when HTTPS is properly configured if (app.Environment.IsProduction() || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ASPNETCORE_HTTPS_PORT"))) { diff --git a/src/Web.Api/appsettings.Development.json b/src/Web.Api/appsettings.Development.json index 0d57799..1374c08 100644 --- a/src/Web.Api/appsettings.Development.json +++ b/src/Web.Api/appsettings.Development.json @@ -5,6 +5,9 @@ "Microsoft.AspNetCore": "Warning" } }, + "AppSettings": { + "BaseUrl": "http://localhost:10000" + }, "Email": { "Enabled": true, "FromEmail": "legalassistant.dut@gmail.com", diff --git a/src/Web.Api/appsettings.json b/src/Web.Api/appsettings.json index 299d248..078b2ad 100644 --- a/src/Web.Api/appsettings.json +++ b/src/Web.Api/appsettings.json @@ -6,6 +6,9 @@ } }, "AllowedHosts": "*", + "AppSettings": { + "BaseUrl": "https://localhost:7001" + }, "ConnectionStrings": { "DefaultConnection": "Host=69.164.244.36;Database=law_chatbot;Username=postgres;Password=Admin@123;Port=5555" }, diff --git a/src/Web.Api/wwwroot/images/email/banner.webp b/src/Web.Api/wwwroot/images/email/banner.webp new file mode 100644 index 0000000000000000000000000000000000000000..5208e82763ae78ff7bd2aecf30c45f905be4c38a GIT binary patch literal 22296 zcmV(tKBodGH# z1o;6zbvl_zq@yCLqH>veP!5S@Zu|xNAO$|K?5lJ?$&qlH#BHQm1n%c{8?pa6`K$g1 z%J)*fQ~Vd}U+;f7Kiu<=wEWgNpY(sV&-vb|KU06?_XYh)|E>M&{GVh0F(1=E<$A<= zUH=d2AN{-j*ITclhfEjm7p4xYXQ+S7cJ2Oq{CAXoeEsk4SF@IE`)SnI=0AZyqy5*~ z-}oK+{M`Ca*cb9|=l|t?(oFR&-(=d~UBKCSEt{R8~3 z{@?U|&Hwy;(tEc00C@1-_DhC^XUBZx8~Fiz<$$hNQNGOAq*_ z?RJPZvPNYMMq2Bcl{A*!%Boki7bhmfqLvBz<_8oV+F0>M9`XW{f&q6x0(&2z!kkO=bMw+f9`SxIYesu#1LFjOd z7P!Sxf3ZyK@c>K#*mE8D9k7oF#8&TN|L&rNFs5m_H_h(n*bj~uOST2vRBp=VVObbv z2{x-^qMYwp{mlfl#xk6?)y$%ql2PQISY6mw|5Q6s{R4EF1wXrSI%2V*WRJjSd6x*h zU4MCV{w>TyuTxMiu-Tl}AKEBsNrXg?u~9+cD*-;=%a22)`)u}emKxXo2j`_g0+6PM zW*{StN{9Rl9}`+K*gbXanqOH4Xom%oew>YKar9g7gI6j~v zg*#PBuN9n{(>TVrQvQELvJe8A|ceQrx=bZS&o0Ja(o~ zdD^=_`Tg(APdD?qh0<=>LV@>8Yn~)IpT78aYo}2gj)_7TSjiAw#syeR_y$3oPz;JM zZb1Q$K^_wJZ^16n0<1sIk|>pj(tvRjD}DP}&{7~fbbiNF&TsxBXN>2Hn5xigvzKWQ zD3$7lCREImif;uHh3tTxsF)>|&IOl&q)}4Xc3-eGSx6H?@E`#v#)BXSmz)Zz1*~Q9 zp7q1EEiE6w5s-PM(qxEyVp-5{MI9Mq-bg~8d^sAH@uf3Q(sj?R7$2(rN>5ll~oPXVtI z$q+N|qe_8OK&vUyP2hz$rnaW^!0}uSoU&y#!zi=5^G~2iIcO6!5 zdb9X*WflbYGgn}_Wzl>lJ@2(YyP>f!iel&4tp^JlEqBZ}_>KqEiX~z%J}sfQ>C#4RqnbqZrK3_UbLb+b*K5hov^b`Et$DB>BIYv z@D5W)&`45E-w{fQEjL$NNn-WKD0bN9&wEf{y~z8yD>P?;45)&zugVa5br<8Mce}2< z@W=T5Z7^t`LSAEZVv_T&U^ zFfo0B4JYq%z&5+=IDRhm`%AvyZ{m?^1|>AVVPE-xjAtl7g5v(m)BY7Zr6mOV_U3n- zLeDK7c#Ge~F$pRi@%>$jw?8T42!9qSf`!uTyMEwR)uYZpOMr`Y5$1q58`FZv9~TG= z+Sacd8=MwPzZ`pcOrT&^yob&XnrWQt`AV& z)xfEdSs(cU#l>Xg7J1^YOv@Gh@}&dOX~@(aiAuia(KJQM8w-8L!xk;l$2O_TYXzXF zGz!Ibm3zoAskGf9MulUI8-}5}YYYU@-L@(w8+eh&lX?kngDsEgA>7sq$0(wtBT6To zct#73CK_>$ZgUHS9#z{RPyf+^1k2-Ez1b(jk+a$nIt+43D7gETBV~_^@$%pZy{qTG zY|FHhz&Kx@`V>CrNI|g}QKWJ@SSscSNmF1?t!x1DF%fQ#J`QD(^mSc+OP}w+`gB0~ zOPM*bW2P_BbF7vz2&iTRkTX%B)qlItKymL6K~a(1{NAV4B>kY@U=klkuW{zL3;IIO z0%UR)Hi0t|0BebNjgX*#=#`IKte)8Y(bRoMP3}DPALotmkdHY(TOEU7SL^Vs5}z+T zI2HN6_LANs=H+?S$$}+gVS!)@?wLE`iMMv<@7X+0|8Z^!hK9bRtU)kEv$#qbGVM*e zujZo*?0B!K`%aNsd0Y1F#1}C-h*el9R_vzsRGPpC+Y~>ZRv21L4dW)lm0IEp70Nip zR(YD9)t~9pfR+FC{<%6o?ol;p$`ANFrkRri_J6SbXP^H-h%jv8ymF*Irz}`=K-+#h z9Bxz_Rpd;t*YC5!-f)Hm0aBu~+(>|EhH9i`}5d5=o-*SsxenW<>;ExUu%h0_< zvQ$2K8Rhi4ukm6b$mgEe2Zk{P`yjDawA2AUu8+&~|OO5`am)q15T+mk&ei z_z(@uK%WOnlRy5=kp<<^$n=j|>%O>$zE=ghsek&Hb;5fLtx14z zXQN2NL}~jGeyY6}0qCPgfBJ!msBm^|iM*oe_U3#y|3#|OIpIzO0~KN!^Lh9rAsHu% z#ipYQSiWo~*^Ta~3Q<%T%V&D4!@2Z-pCX)Bb6x-cwxHJk_(Xr|1<qsCSHs3RyeYON|`-ql_m>YL~COXNT{ zSmsse4g2!=;rZYw4=*7YE%pkwKl~|PjwCR>nSv21Qw)5x}!-@;pRbP zse1ZAUa_gLryA-$m1h2jMBh$~g?&Z-$C0Gl`D}jvJTsD9Lm}!$`~)4ftcL}7?Zv{LCa63UWYZZL3(RCq|gfusY6Om`7eH(UH1iNH)hSC{=G9|pM( z!g}coRrGjv6el_lBFQm0Uu9u5@{KG3=9T z#NmCFgwx72udHhnTugoCad%_1l^QZ69SwszLy62+4~6fDN|od^?EKaaklnZ9i)H?@ zqT4y6)PVONO0vhzGImtKqOr<=^*nlgd8r``S8N zqMUt}m&8xH#b3C#FaSMD6ogAVa`%WNkb*DofB^pa^i8LQ-lmLxt=G7?c2Y~FCwR&M zsGQZfgNwa5ax}ReXo?HAfWe3CGUBD_sIo(0`k!GUF|4Akc(WuFzY--B!4te^Yau6% zBfr)Cs2Mq&a}x0ImNdkn$*)|l;nw?oR=H&%*V}5*ucO`t>WY{0O5#`xt+12L)`&zk zOci_bM1tYu>gPp~C{o1+5^Nx6MsK;456zJW2O@aE`$o;ytKWTWT#auqz6|+5qt!Zf zFb+DW`6X^`s$AF!SNte!vWakFdy+wVJ;vipLDrWs(Z-;KxcYvkY1BQ_lODUVR>JOF z9$#}tao!+L2(=G5Jv7&{sS2hky$AdmAN32*Gp;}oKvLoDE8>T)SpKb!F|*mUex{?G z)0S#Kt{zsTYS}4rXDiB4{zbsu#T`+|K~;_x(*=9-m88h%=5aDlApL9uBM*|mRrZz` zRYlHszVLYqaRM2U1MA5SqBI0XY*uN%83A;U(xd%tUkU^=)3AAXyg`Rw(twUy1CycD zM)14;|AYhZC{vf~vfO&~&z`IOqs8|u+dXm98EZH`%HB|Ws@n3v715P9Y~Re6?mvB( zzzHC|6t_a#rWkEF{4a>|9%n-F;WV?n>urCGLx9y@rO#p7nH+*ue}}J>E#g9Aa3Z+G z!R7&pZ4sL@q2 zV#57j@!w(eiK3JBpF6JSI^U>of5}PkVCar|${;T8OXeSXPvl4gh`OM=rvP}4ca>zO z1U5=HR7~?XDAi_eg}siT&w_)_1?h_=xfENxjK|}Vn9;iWK{-Z`=ynj^dfej{#V{QT zO}=N4Drrd4z-)U(;CgBHFoNmDeAhqd7^LY>pS~L{RXpW(I!(@5Bn}eL^{6!!Ov*GE zD26dz`iftra5Url<;cWZ$%;)Kye|Ow)f5TcG589i2hXi~E#FFfnq7h2Gbrw^33k$( ztiV-boCzd`^ydDOfJ@zx4=;)bg9?Q!lL@ujhQfV_5)E(n7{9sLm%8nZ&D>$9*%279 z=}UN5`qj>#raa#-13$4fGs%hi8Sj{Gf6@xIy1H_tG2-HdjL0u&CDTo?2MYs=OEB@d?(8sT@N%ca|?CCK(^XG&k=X5poMMZ5H}M*39WOF6z_BhMt#t z;A`&baTAW}6ek;aNIrWe)@ZYfGYa1l1syxbcnWIhJoG*l72wWf;};JpAL=I=lYa6(Fr?+T7a4? zFjJ>9Zg}+7=HYQd<}09u8JD6tUF&tHKPg`?Fg@NJ605gilD3tq_fR6aGYVdOM0`AF z%LnbB6xS(CQzNQN+Jme-=W{ zc~~%QOdk;bdEF7*m@(wiryUKjOm0C+_4EOJ-DQxHx$jTeEKAy$tED&ii>QK09mPKz zZ3EMU4`PaNJ0Uyg)ApQXHZr-CYgGSq<~S-k(rnVq;Kr%K4&}Nn)?=Bq{e_mToGM1T^*mes6Y-%=o%h^~Pmt`>O!fUEcm1YY)>Lwa z{oU(dY)D?ZiI)Ndg7L)l(Uj@r*=qpbnh|%y9Et;h^58Zr9fl%7pkDCvhEN>V3x6|V zk=b`dv2^!qCCzjOFLr`XAETN#G;mn%q1*XnFSF5SX(fhhdG7)WIM zPb0_Cen5z=k@<%7%5pV{y*axk={oZ0V^ajfTnx|g-o8^zKuZ69uC}__YYdbak_xEC zQ2Y?qP+O}r+j-(Ut+pc_q#t{y`NU%-X7b%txukU%4<8%YmEuvMa5s*u z{p@r<g6M=y$j z)dxtW926S9$2@(m>8gK`8ZVStVGp73$Ou~m^Y}_A*FQ5fcGc01B3zM1Pz>0hQO&pc zgIwTQKx@#T3fyF+nX$eXi_l-FSE-rqMV$q#T+c-QWU19PP!OGrohu+b5jMZNztvA3 z7%KvH9`^yP^>26IRyGeLVEz@imiqT5C<`NYyVhDK^e)>Za`C9yrYe6X1O|k><)npc zG)|(!Ifk&BXXQrJw5bRL;PICLk(ZxY+|^7Ph0)KM*-co>nue*hYpp9L{2dO7bm0ev zju2F?q1wgH%rxT8m9pbX1P7L!j!fmgFM)Kw@Vhzb;?v;$&yND6v4K4dJ|PkJiay#x zoJB6oCiL`^r8~O%IR+cr>kWcji#)x^F`w-``q%zq6FM92zwk;YL^y>pFB#tN;`@lq z^~`9<51y4Hw_n`^PyWgesOg~mZIv{(yC$>spC}9w zSVV}zDj|G7C6H87CTvZ=YN6pEQDVJoChc=8Oz)(&2PVBOs@Ztpi;aBK?i=M;v$KB&?RNwFkZhEK2dcV2dtak z@h~Q74V3Iq!Z`&Bh(;(hb7pX@?83g-o&m0YIXk__Hq++IML&*THw9~l_&ThHlu#%f zw&wzaK-jgTg+qUqAFyqroOqjJpSL5vydF-=M)w+F7x$sgWBn_6g3J-&Mv06)mWsDvn+ZPvBL!(mvcU>~TM~!gE7AR{yj(REoTW zbh!qL)K&F>u!geDDvL9sOPzd7oxW%o*|oMO!f@M?jvJw(o$Lb0;8s3v_z)LjW(HVD1XmHNh3=a%@)5mAuD)} z=l_(+4-JXq^GSiI2;S2eTn@oF%cEEk_p7HTKe)3gqCKB%NlORro10F z3qJOJq-^R~))1m03jwr4;}WI?P}i5qA);gcG(kM>A2b+?W!3l$>b!1-L`^L6VVqi3 z=u3GZ&#SE8NjbxR*#OLu(#x@$8&}@y`BgU5KTyA#%%66m{{C&3eb#ZcF!jvS>p(c3 z=M(budhiOG_`pC0%hx$Y(r#%k^q6NF@&gO25``uwb9OSm=PYd)7GOKscwlCz>yz%k zVG_Uij(KSwh~2<}tmQCzFpe``XoYt7{m)^D{xifWR{Vuf;Fkg1F;*ThHNjSfkn&7II$2MFq9^d~5w$x)^x$i>)$;0KFT$h2SaU*&E z%GX!O78WnWm`)p*ibf6F(wQ|SBt1Qt5pLEBECjVBaH17&Z!!E~x+t^-Vlig=tUc)x zWCb3Q2JWK@S}Gdr+Jnw%Z7i?vf9Wfl=$CwAL=DCVw2QBMZ?21vi^;LZvyaWNddu*@ z@m6E#LlT&_J>jn7kjS~Xaf&7-hf=yn(Mjd+s@KyZzxK z_*43)cVM)ddH70_2uaR(;v*u7o6(Nhle;I#P3+L3KKb^U8os3HwQ?@EJ6*}g+Y)CY zb)@M%V+%Z8yJ<;>8Z+tDjXMak-g-lTgr(q@dhDnTj{{bdANXl)d(75taY_-i@o-LD z8z$ZKj0JS`1~akcNU!*x^oNOG#$8rk;Lj~Xx?3f8(PhW!-sD!p z^su+h4n?Ji4|ziAqm@0*h-#O~74EWUL3S7G8Ekcrpa6>o8~y z)zrp@ojH!6^INB{OQi-{(z-{v_(3>L9?lTd52b>+_wqJ1Uj%Qo{2rhJx9v#F`lBh1%$~bg5E%t#0G4_@b3zi_nQ#%@QJ-& zZ|p~)*`qDwI=ewrO;cy29Ap44rd21frH+TOg|(bEn;posuP7n?#Rwsg0N!R13uZBE zL}Dc4tT#qHV-J<+JE2cKdqdGrH=Cke2YKl}pTP1rT1pEzVoiV#euy>xeiI-|ahP!{ znvqAb4!>mKcibnNT}XkzUrC|^ys;k1mQp)AQJS9}+*hDco_g>dx^q|Py z18v(%g3C^+rkP*dz5#}GJeG2-pxlxa&~TNIF(|%j-gs;Lk8=xlQw%kAfgN!QHMCU7 zJ4jMRykY>E7Q)TjUeU;UvOt9y^_rl@xM@QEJtBz}_4vvVh5x$2e(6ShaKz*gdt43j z&_M{O&_!!)g>G6P$$lw7Z0cIn-%9t=?nLfBX3WZGPnKpi?cJ=Psy}{PUN1-Hr@#f$ zwUk?DlQdG-cyL@zq~+6eyATQ)2RX|;I4;l{vG+u#*4A*&a^VWK;%2u&|q zN4u>n1_LZBGW%t2G%Di#%^Z!t0lIb&NVvhxt1E*VzWF%unF13B_4|1R8p2#LogX*d zLxLrQl#vuE!#{XU!%W?kggV1$EWw~X95V9z6s&ERKN`goxYSS)St{~J=uS@}d}=x$ zHG)}sEsq^{Vf}H$Hqqs*o=2%8cz{p7zm}Gh&GESGt^=T>B>~eqy-J)p2`bdY+pHV&iycdfSSsEd&|igh4omG#j^kY zsvw?f8p--A^9lyGFiZif&{?urql-X@w3j?~ccuUSZTd#&G=OMWNEahxXpa0$TOQ|S zkO9z?Xs^40b^aiJv=)oDUE2u|qHK&j742vn1?E|N>XYgz1z96njWHB-=}T)v08IzI zz<=TCM8Hh7POdBMw z9-|9a{?&xUniXCT(9*e2IY^Yzv7I7LyJ#~f{SIi2khL6ef%-knRM7NeHrR@32R!5h zk6ubx#)#+;zn7QX{9!~8$~ez}U3m_m_g$Uf8Pw(rQokAJ*( z4Sw{P`FVOXhg@Qe3u%`HMo&jpkN_+A=Y3(F=VOQ7h(BE@P0>qK1$<#94$912rHJ*( zH;^e)xrui+2$yE&VO?kdhU=YmIkx3f;nho*lR{C_lWWP!_UZ`Y*tXgV1IZRAtr6y(=%GJdRRoUbtiKntntK7Gy${{VXYPXcHO6zIdW3f^-qr-*g2Td_0yatkBz$TIu* zcM$TPC1MVj8w(sxu4^=cMhBERsUxnlSyH3W&1&mUS-gt)f3c~GUzwvV z6usU46-rpdD$Cmf=IuOu%^nBgjpWU-A6?iys|z_JPnoRc5NG3;45?c&`3dhtp;WmJ z1$myQKU1leTuDvG{0sSd^s1@vstyB>_GIXQNVmX&ZdD%D>(Y`oC`>hZfO~&aX28R^ zliCa|x1t60^A~Cs=O1R+f3xuAF;7t6{b3@)g?e{FWQA%ZeZ0M=fTodAv+SD^#l(3k zO&#Ndpsqn`?nF#UnGdhpM$Qx(q%mwSxAiP&Gl>jgiF@hjhL7oJE<129P$#9?XJ- za(peD!xO+$$2NhYMwxH%0tgZ%YXG-Em@|8lifWnF#5035l(l!+9&nYz)%||8%vERZ zwihbO&b|JhuEZt^6T!`F(vv#1pd8W zzmVfr5zP})pto+U)-nSie^}xF+{=!r>{!`dkM`+Y+S2jxUdI3k5HqM{8qImdIeRM4 z2VzMO{W}Vw6E`U&>hG@PhgLnrVOLn%Tt*WvQZlI;U{+7XeK7c2N&7U_=z_2rDPVw) zyix&!PxWMJjf0h}BS*V?p5PHLl80Y8{3hkA@#ut{b1mDZNn^K7{+=X@lbF-b1Lg}v z7H9OM@~LM0H*wwTiBZR4yM}j6q1>E`!n-!(gi|NPY7ibyD}i}m@BNM~>R>@O9YqF3 zi;38&KUrFA(@Ia`q*D{dsB+b!XA?<~GU4S1y36W*Ue|9idSS4cVy=7cnK60qrNIbf zAJO@hHZQ)w0u3~%*fxAH zE=|OerO56eV$68WgelD)+k(9Tqq4raeN58bG;RBnC=L6Gl$RxkFWDPuL{Jmn@E+X> z;^Tcs@OMFoIr*b8?GyYNj0d1dXB`yudN&ryR>C;-3XT5NVqHbq3rlwrty*ZkRSYb?vQKIrOTe2p?Z<;U-X9}G zFUAny0>H@-|;NiD!FvhQoS-m-2rN0?tVq=ZZtk@=`sXmfwVhJu|t z52>*|kv22!9bPpF@tOs#z%SaGT0wBlNari#a2&A4A7li}ys=0GOb*IcoO;l;YgUW8 zXN3qV>JGaj0wyneo0_E^?PM=dJm6q|6Uf0}!H}+B{MPtBwr(P2ZK8RmuH^Y|(=1H}|?rwDN zn&bg*u?UBOgDUQjP?7s7267SVq;%rT)O*H9Ot}EjR^t=>nawYm%N1}zqbMmVa=^r_ z_MlP-AO-SP!uWpuuyzN!c4GG)TZ=}BSr>IHmVelh7=zk#5>O)n;EeYG&7wVE_22PB z3gCDBH8Oib+Z&2oxf4k_{+3oE%PSH8X@)zuICN{lV`PbnoPhT;ADEj@g(w zXS8-`k~!hm*c(QKR~T{5Y83jN%pw`jz$)#DP4;3y!YUstbIQwB)1D?BT5ZMp1ziEn zSq`qd6(vYUB7{4vQs{9QHRksY!<5Qj>q|xq!de9TJg&s=EQ;%-p6!Z~*R>b{S-TvR zSxF`6Q%u#C|0)YYTP8>?R)e{j0{LS7`5Ttnn&+|*C#I^h;Q1liD`T}jk6iQbM2dp1Ffk{e(6FOO+;$Wl41MqYN!ET0HVdS+M@-r67SHBBBIL__$s$rT`|Dgcc7*!Zu0KWS_upo+52VtBWG>OZ_t)wnHR!OqUDlXSR zjwIRhguiqz8LAdz(yJFfl8Mpl3r?xR(rm-Vt2Ahjdj%Z>F6Db+&I!wu>Qr7t?5%?4 z8;^L|TVIWmhZRQ|=BgnsAQr#2cmV)8#wD@05PV?Zgs@+}CLz591Ic5rQUEe z!9UxZh-5PTrL)G8O1p@Qnpm(0P3^>e(5w^e)cPO3Iw*(ja_NLq?^s2m|5fjm?!aS2 zZCi7Y#auWnXcy#!HiT4lc+Ju5y85>&?CJbi3KaEaDbC+gO>Zm_V~k@v3~@K2&hhcEv`jMqbK=~6Q%WDIax z)E_fBOw!K@3G#?=V=IXU$_g%@f&;rj99r# z5`JFo`t1XpSXyNU z%MegL4cA#5(Ox6CBOC|Q4t=OhEdjGw2q^t1|k`z5&aF%a&w zaDXNeR5_7h?W5QR6F3jUxZcWec>ufEH(bty9l3CBZxi*JckLJY*EZI4A4u0 zA*`0I|DPOSG{#bdHeO0&z4H28B+6iM(l`FQJi~c3sn5-$LR}6LXFN2S7YW|AP!A#b zE|CGzL#4q^GF2?x#9#Cg#Ri~AI1%~VmK92XF0|c{?$CnH z9?92s2Bdy3kCkJtUg@lRQb<~f+K6j2$20qKbmng*AuC8xc9oTIJJ5ZuE2osmo@>90z<*TW1z7qy20KeRVz0JW?SJV zO*;m=9g&N?L*A0g0TtUSGo?Wvb@ZhyB!&DDKKyNY?TMTm(hf9!3y+x7)h>8-{ zcxY7T7=C+2kBj3szzpCT+PeLQAdD!-f(49l&=q#P&YG(~XnwLk<4Ibvy_{ z+Qvqncj-L=0E3oQ&#c53yCBC-Xud{_O(6(zFaW-U4$XGN7Tax(bp*~k++sCYgt1GY z{h_j*1}M?2>LUv`oEo>+%!#-zJ>FxX)kiuJYX>9L6VGxcxdf%Q{pT`&(n{#8QUpJj z8w(vx0SdmVvpaA^N&Ph{D&@l8?9`}?kqYa!RRSXzji*-3i}=2KtGb^QJVusXx~L7V z|LUHhp_X>1(*4(p*M@0Zs8Xm*jvAmm1MvQ8MQ>&h)l=7oV#Nj`~aUTh_7x6{{oCpu6~Xc=3l_xjYQDi5Vkh z0)QX@0Sz@#dIwE=QNYl$wd%*uDBuo08*Y?(1t$I2JHR+MYBQt*B)Gc&0=|V$qOuaI zX#>y~6sNqe04SJOaWfKK*!w>u;W=sMr?R8Sgl^lDCfx`|vWHWe^tk1pL3(rka#7Ys zCYl@cD(mj8C_0DbHGXe&%Mf&tSgpT#Z`CCqH9sOb%GUN{jzMQ>YgVUy>A6m3&XjYm z5v^-{Y1d=C7>FT;#A@9_WD@65;#ufb0xOCiosE0k0pr-8@f%5&o!IVxJ);^NM|PD)tDgbp)jbPCrx zs{X^>dw7w)@`@mH&5##Jd~1#ln;0#p`$VI;9)Ju$<#6xS@r(Cr2^7O?oR@ic#ELG3 zVVUF{9TGXNH0*4Q91CtFf7U;6T;MP*#ReK%p}`a!kw1UwhcHGH z?H2w;T$#V6j&1CT=l}tE1?A`$6jS%#N$X*q?%c{3bnQ@HVOrXlvE)q&?UtORGFLGX z?zV6YZalXx#yXY@!hpp?H>J$#vo36XA#IUS?}F8dB%PtJ!F5?PA(FbNg?;yKm<6Jo zT>tRYNR&4VnuZQ!OW$bd6MXYvTbk3;{vqbE-4Kx49IUtS>nu#OL+ z0Z9qKU=5yMwxS@234t7bgt)j#$^0+`FbKgIC&UJvshYm_11pkfD)@pppi;h7U)D{w zc)*3Ff@Y?ywnC?a$ouMp8ys=**T7*%)z|zxb{@|T^1Vlyr~2uh)c4u(JEKyv9FxcY z#HE`2=By&i_WJoEROApO4y_yA59|U(U6i$##?_fdB?Bb72J&nvo)ISD8qeL126BQd zAs=t6n4THJe10aMFO$YPYs%eY_$25GzG8wL=AH)|VuIX-W2c+=9#)E}`qGtT&KY)2 zkxxEbO|gY-gd9;qMx-2uE9-r1-VAXHyd1h_S(7P7CZG`W{F%G7E>oxWj1jM*=wPB< zZY5a&9};9z6vRXyl=tT7V4((txwU9#?FeWK5crq%X}jGmQ&lL^1s(ljIi)IevA96( zZ}f4b)jol71&5<$i(9ELT=&t;o~&xkh6M`-?K7so>e;U6>LQXTAiUKuWZKZ^IP zLNii5J`T*-os4w^n^*RCy;|yj&7MooxXZ1hHxtJJOd=D6y7vGz(tOKnygyI9xd3~k zi^Wz;WJILmwz6yY)V9G4;FmKX^3PCb!)~EbX%VMu^be|ku|=ASP4s1Rr5RAh!z?*@ zWkWZ2gc7RZOaUxn{zdH4Xa%EbR02DXA9K&b{Ls)Y?A7R z8f?q&RmAc*0#oZ@258gKjR`n`6XXy6um4U$dF{;zVn_gOGH&;GF)5(5G^6FB zQH=-cZD+6>9~p9!Qx%k2SZJ%_+&P$C-un;=u76cn25ub;0Om!llQo&5OZM?k{Emw~ zz<8*5@fcI+LkTFRGQH^IEr@Ls>oy2MvfnR{Cmh810HI~egaR5kpe8mC&$rDjsxEem zwq23h$4J3X3warwtGt=tj<&{L@E%1Q(<{-7CKL7n zhgwI2MAy^Z!kg_@@Tw3qK3Gn*(9>{@(iLzuim^nRz>hw{fNgk6 z2j=LU!7)Q*(~#hR3?K}@lV(5Y=^TcO!koWl%ZAOj9^^&^<0(FO4|@0xKC5F}IDZ7pN)v(7>h9%M=@J z{u6xM8qjVVlaGW#csi-s)$S7P0`^2)Ja{wUy>&W^7#)TtB!bfBXbxwWorZ$iKjJY_T`b<59rC$5I6!LrNX z>DN|v)rc;YI*T>2VMR8&xDzv? ztUog=+$?|1^S2YnnsG?dqb9T3#uGY@O zF>S0Bw|S_*TzJY6gx=3cJ9C0Gz@PWf4XNx*yX)!X%032$&s!LWnnAr6@K z)zb8Wte7RjA?n7x&RH^OGM^mwn{v?q!X1@)EQ_gOr9gI5xVu9mYaGR%g3?-pc#}3p z*;#LQpdVL|x{iS6Zwe4s_8_wP-R?@FrxZt*HU7w+$Fu5!EbG>ThI0`fF+r<&MAkmM znTzX>k$olC6xm7;I-o#J|4a`OTz3@IcHC1n$TtOPu+aZx?=_BI+s}YY_3Fqg!>6C3 zlJ`bvJbp}SI1%RzJ~~!6Up^5TI9YY?b|3uOs?Mt2>~bfUQJqo;5(Hf68=B3fNvw*x zy7B{cy`M}?ceZ9ecWYP;J}dwF$%=kOIMMMR&OngoV;zpLt~~bG_T}YECQVqwF?Vd* z3!ls+%KaOSDjtD!d$~Ry08SyQ2+cnKw@nBAdv7Yk zw*1B#tLQh;pe_TSEG?D=?umlIZZ%*&VHlsvg3 z5=2SvKxuuYZjyN?ANdb~yL|Rr&KPx=Z96r|Z*rM{T9=uG69sb&>Y+tbS%N5X-jJuW zFX$%nN#KoZP($*Y4y@&p-gKyj!@UV|wfKJazo;kN$gtFoNMd7bZ72eSb_(38I~I{A z@xFzJmy0|+HWAG?s>2%p6|%icx>^sK{4?C65t9vw>W$b{_pPeY_iN|Q$SIE-AC71b zbtIhf7p)pfe)HHl1MszoyI0MghV?($`c}Q?_k=g72?w_ZtUEA>TnqZ&eRiHr^&#dA zu!ya(!=WBS*#nFFOlAm;QSm1K39J$u#&3UEqyOcwQx∾3@IVp#44BvKb5%fwY{r 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%0Dj5mN`$Pf@9H+KgHx{H0E@A2h1cND;wfZ0jw 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 literal 0 HcmV?d00001