Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/ClientManager.Application/AuthApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class AuthApplication : IAuthApplication
{
private readonly IUserService _userService;
private readonly ITokenService _tokenService;
private readonly IEmailService _emailService;
private readonly IValidator<Dtos.User.CreateUserDto> _createUserValidator;
private readonly IValidator<Dtos.User.LoginDto> _loginValidator;

Expand All @@ -17,11 +18,13 @@ public class AuthApplication : IAuthApplication
public AuthApplication(
IUserService userService,
ITokenService tokenService,
IEmailService emailService,
IValidator<Dtos.User.CreateUserDto> createUserValidator,
IValidator<Dtos.User.LoginDto> loginValidator)
{
_userService = userService;
_tokenService = tokenService;
_emailService = emailService;
_createUserValidator = createUserValidator;
_loginValidator = loginValidator;
}
Expand Down Expand Up @@ -61,6 +64,19 @@ public AuthApplication(
ExpiresAt = DateTimeOffset.UtcNow.AddMinutes(60)
};

// Fire-and-forget: send welcome email without blocking the response
_ = Task.Run(async () =>
{
try
{
await _emailService.SendWelcomeEmailToUserAsync(user.Email, user.Username);
}
catch
{
// Silently ignore errors to not affect the registration response
}
});

return ServiceResponse<Dtos.User.AuthResponseDto>.Ok(authResponse, "UserRegistered");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ namespace ClientManager.Domain.Core.Interfaces.Services;
public interface IEmailService
{
Task SendWelcomeEmailAsync(string email, string name, byte[]? attachment = null, string? attachmentName = null);

Task SendWelcomeEmailToUserAsync(string email, string username);
}
64 changes: 64 additions & 0 deletions src/ClientManager.Infrastructure/Services/ResendEmailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,68 @@ public async Task SendWelcomeEmailAsync(string email, string name, byte[]? attac
logger.LogError(ex, "Exception while sending email to {Email} via Resend", email);
}
}

public async Task SendWelcomeEmailToUserAsync(string email, string username)
{
var apiKey = configuration["Resend:ApiKey"];
var fromEmail = configuration["Resend:FromEmail"] ?? "onboarding@resend.dev";
var fromName = configuration["Resend:FromName"] ?? "ClientManager";

if (string.IsNullOrEmpty(apiKey))
{
logger.LogWarning("Resend API key not configured. Skipping welcome email to {Email}", email);
return;
}

try
{
using var client = new SmtpClient("smtp.resend.com", 465)
{
Credentials = new NetworkCredential("resend", apiKey),
EnableSsl = true
};

var htmlContent = $@"
<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; background-color: #ffffff;'>
<div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; text-align: center; border-radius: 10px 10px 0 0;'>
<h1 style='color: #ffffff; margin: 0; font-size: 28px;'>Welcome to ClientManager!</h1>
</div>
<div style='padding: 30px; border: 1px solid #e0e0e0; border-top: none; border-radius: 0 0 10px 10px;'>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>Hello <strong>{username}</strong>,</p>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>Congratulations! Your registration at <strong>ClientManager</strong> has been completed successfully.</p>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>You now have full access to our client and document management platform.</p>
<div style='background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin: 25px 0; border-left: 4px solid #667eea;'>
<p style='margin: 0 0 10px 0; color: #333333; font-weight: bold; font-size: 16px;'>Next steps:</p>
<ul style='color: #555555; font-size: 14px; line-height: 1.8; padding-left: 20px; margin: 0;'>
<li>Access your account and complete your profile</li>
<li>Start registering your clients</li>
<li>Upload and organize your documents</li>
<li>Explore all available features</li>
</ul>
</div>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>If you have any questions, our team is ready to help.</p>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>Best regards,<br><strong>ClientManager Team</strong></p>
</div>
<div style='text-align: center; padding: 20px; background-color: #f8f9fa; border-radius: 0 0 10px 10px;'>
<p style='color: #999999; font-size: 12px; margin: 0;'>&copy; {DateTime.UtcNow.Year} ClientManager. All rights reserved.</p>
</div>
</div>";

var mailMessage = new MailMessage
{
From = new MailAddress(fromEmail, fromName),
Subject = "Welcome to ClientManager!",
Body = htmlContent,
IsBodyHtml = true
};
mailMessage.To.Add(new MailAddress(email, username));

await client.SendMailAsync(mailMessage);
logger.LogInformation("Welcome email sent successfully to {Email} via Resend", email);
}
catch (Exception ex)
{
logger.LogError(ex, "Exception while sending welcome email to {Email} via Resend", email);
}
}
}
67 changes: 67 additions & 0 deletions src/ClientManager.Infrastructure/Services/SendGridEmailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,71 @@ public async Task SendWelcomeEmailAsync(string email, string name, byte[]? attac
logger.LogError(ex, "Exception while sending email to {Email} via SendGrid", email);
}
}

public async Task SendWelcomeEmailToUserAsync(string email, string username)
{
var apiKey = configuration["SendGrid:ApiKey"];
var fromEmail = configuration["SendGrid:FromEmail"] ?? "no-reply@clientmanager.com";
var fromName = configuration["SendGrid:FromName"] ?? "ClientManager Team";

if (string.IsNullOrEmpty(apiKey) || apiKey == "YOUR_SENDGRID_API_KEY")
{
logger.LogWarning("SendGrid ApiKey not configured. Skipping welcome email to {Email}", email);
return;
}

var client = new SendGridClient(apiKey);
var from = new EmailAddress(fromEmail, fromName);
var subject = "Welcome to ClientManager!";
var to = new EmailAddress(email, username);

var plainTextContent = $"Hello {username}, welcome to ClientManager! Your registration has been completed successfully.";
var htmlContent = $@"
<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; background-color: #ffffff;'>
<div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; text-align: center; border-radius: 10px 10px 0 0;'>
<h1 style='color: #ffffff; margin: 0; font-size: 28px;'>Welcome to ClientManager!</h1>
</div>
<div style='padding: 30px; border: 1px solid #e0e0e0; border-top: none; border-radius: 0 0 10px 10px;'>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>Hello <strong>{username}</strong>,</p>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>Congratulations! Your registration at <strong>ClientManager</strong> has been completed successfully.</p>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>You now have full access to our client and document management platform.</p>
<div style='background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin: 25px 0; border-left: 4px solid #667eea;'>
<p style='margin: 0 0 10px 0; color: #333333; font-weight: bold; font-size: 16px;'>Next steps:</p>
<ul style='color: #555555; font-size: 14px; line-height: 1.8; padding-left: 20px; margin: 0;'>
<li>Access your account and complete your profile</li>
<li>Start registering your clients</li>
<li>Upload and organize your documents</li>
<li>Explore all available features</li>
</ul>
</div>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>If you have any questions, our team is ready to help.</p>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>Best regards,<br><strong>ClientManager Team</strong></p>
</div>
<div style='text-align: center; padding: 20px; background-color: #f8f9fa; border-radius: 0 0 10px 10px;'>
<p style='color: #999999; font-size: 12px; margin: 0;'>© {DateTime.UtcNow.Year} ClientManager. All rights reserved.</p>
</div>
</div>";

var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);

try
{
var response = await client.SendEmailAsync(msg);

if (response.IsSuccessStatusCode)
{
logger.LogInformation("Welcome email sent successfully to {Email} via SendGrid", email);
}
else
{
var body = await response.Body.ReadAsStringAsync();
logger.LogError("Failed to send welcome email to {Email}. Status: {Status}. Error: {Error}",
email, response.StatusCode, body);
}
}
catch (Exception ex)
{
logger.LogError(ex, "Exception while sending welcome email to {Email} via SendGrid", email);
}
}
}
67 changes: 67 additions & 0 deletions src/ClientManager.Infrastructure/Services/SmtpEmailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,71 @@ public async Task SendWelcomeEmailAsync(string email, string name, byte[]? attac
logger.LogError(ex, "Exception while sending email to {Email} via SMTP", email);
}
}

public async Task SendWelcomeEmailToUserAsync(string email, string username)
{
var host = configuration["Smtp:Host"];
var port = int.Parse(configuration["Smtp:Port"] ?? "587", CultureInfo.InvariantCulture);
var username2 = configuration["Smtp:Username"];
var password = configuration["Smtp:Password"];
var fromEmail = configuration["Smtp:FromEmail"] ?? "no-reply@clientmanager.com";
var fromName = configuration["Smtp:FromName"] ?? "ClientManager Team";

if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(username2))
{
logger.LogWarning("SMTP not configured. Skipping welcome email to {Email}", email);
return;
}

try
{
using var client = new SmtpClient(host, port)
{
Credentials = new NetworkCredential(username2, password),
EnableSsl = true
};

var htmlContent = $@"
<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; background-color: #ffffff;'>
<div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; text-align: center; border-radius: 10px 10px 0 0;'>
<h1 style='color: #ffffff; margin: 0; font-size: 28px;'>Welcome to ClientManager!</h1>
</div>
<div style='padding: 30px; border: 1px solid #e0e0e0; border-top: none; border-radius: 0 0 10px 10px;'>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>Hello <strong>{username}</strong>,</p>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>Congratulations! Your registration at <strong>ClientManager</strong> has been completed successfully.</p>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>You now have full access to our client and document management platform.</p>
<div style='background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin: 25px 0; border-left: 4px solid #667eea;'>
<p style='margin: 0 0 10px 0; color: #333333; font-weight: bold; font-size: 16px;'>Next steps:</p>
<ul style='color: #555555; font-size: 14px; line-height: 1.8; padding-left: 20px; margin: 0;'>
<li>Access your account and complete your profile</li>
<li>Start registering your clients</li>
<li>Upload and organize your documents</li>
<li>Explore all available features</li>
</ul>
</div>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>If you have any questions, our team is ready to help.</p>
<p style='color: #333333; font-size: 16px; line-height: 1.6;'>Best regards,<br><strong>ClientManager Team</strong></p>
</div>
<div style='text-align: center; padding: 20px; background-color: #f8f9fa; border-radius: 0 0 10px 10px;'>
<p style='color: #999999; font-size: 12px; margin: 0;'>© {DateTime.UtcNow.Year} ClientManager. All rights reserved.</p>
</div>
</div>";

var mailMessage = new MailMessage
{
From = new MailAddress(fromEmail, fromName),
Subject = "Welcome to ClientManager!",
Body = htmlContent,
IsBodyHtml = true
};
mailMessage.To.Add(new MailAddress(email, username));

await client.SendMailAsync(mailMessage);
logger.LogInformation("Welcome email sent successfully to {Email} via SMTP ({Host})", email, host);
}
catch (Exception ex)
{
logger.LogError(ex, "Exception while sending welcome email to {Email} via SMTP", email);
}
}
}
3 changes: 3 additions & 0 deletions tests/ClientManager.Application.Tests/AuthApplicationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class AuthApplicationTests
{
private readonly Mock<IUserService> _userServiceMock;
private readonly Mock<ITokenService> _tokenServiceMock;
private readonly Mock<IEmailService> _emailServiceMock;
private readonly Mock<IValidator<CreateUserDto>> _createUserValidatorMock;
private readonly Mock<IValidator<LoginDto>> _loginValidatorMock;
private readonly AuthApplication _authApplication;
Expand All @@ -22,12 +23,14 @@ public AuthApplicationTests()
{
_userServiceMock = new Mock<IUserService>();
_tokenServiceMock = new Mock<ITokenService>();
_emailServiceMock = new Mock<IEmailService>();
_createUserValidatorMock = new Mock<IValidator<CreateUserDto>>();
_loginValidatorMock = new Mock<IValidator<LoginDto>>();

_authApplication = new AuthApplication(
_userServiceMock.Object,
_tokenServiceMock.Object,
_emailServiceMock.Object,
_createUserValidatorMock.Object,
_loginValidatorMock.Object
);
Expand Down
Loading