Add IEmailService interface + StubEmailService

- IEmailService in Domain/Interfaces for membership notifications
- StubEmailService in Infrastructure/Services logs instead of sending
- TODO comments for future SMTP implementation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
beo3000 2025-12-25 17:57:16 +01:00
parent 264119695f
commit a1bad43d88
3 changed files with 133 additions and 0 deletions

View File

@ -0,0 +1,49 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Koogle.Domain.Interfaces
{
/// <summary>
/// Service interface for email notifications.
/// </summary>
public interface IEmailService
{
/// <summary>
/// Sends notification to club admins about a new membership request.
/// </summary>
/// <param name="clubId">The club receiving the membership request.</param>
/// <param name="userDisplayName">Display name of the requesting user.</param>
/// <param name="userEmail">Email of the requesting user.</param>
/// <param name="ct">Cancellation token.</param>
Task SendMembershipRequestNotificationAsync(
Guid clubId,
string userDisplayName,
string userEmail,
CancellationToken ct = default);
/// <summary>
/// Sends notification to user that their membership was approved.
/// </summary>
/// <param name="userEmail">Email of the approved user.</param>
/// <param name="clubName">Name of the club.</param>
/// <param name="ct">Cancellation token.</param>
Task SendMembershipApprovedAsync(
string userEmail,
string clubName,
CancellationToken ct = default);
/// <summary>
/// Sends notification to user that their membership was rejected.
/// </summary>
/// <param name="userEmail">Email of the rejected user.</param>
/// <param name="clubName">Name of the club.</param>
/// <param name="reason">Optional rejection reason.</param>
/// <param name="ct">Cancellation token.</param>
Task SendMembershipRejectedAsync(
string userEmail,
string clubName,
string? reason,
CancellationToken ct = default);
}
}

View File

@ -4,6 +4,7 @@ using Koogle.Infrastructure.Data;
using Koogle.Infrastructure.Identity;
using Koogle.Infrastructure.Repositories;
using Koogle.Infrastructure.Security;
using Koogle.Infrastructure.Services;
using KoogleApp.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
@ -84,6 +85,9 @@ public static class DependencyInjection
services.AddScoped<IDayRepository, DayRepository>();
services.AddScoped<IPersonExpenseRepository, PersonExpenseRepository>();
// Services
services.AddScoped<IEmailService, StubEmailService>();
services.AddCascadingAuthenticationState();
return services;

View File

@ -0,0 +1,80 @@
using Koogle.Domain.Interfaces;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Koogle.Infrastructure.Services;
/// <summary>
/// Stub email service that logs instead of sending emails.
/// TODO: Replace with actual SMTP implementation.
/// </summary>
public class StubEmailService : IEmailService
{
private readonly ILogger<StubEmailService> _logger;
/// <summary>
/// Creates a new StubEmailService instance.
/// </summary>
public StubEmailService(ILogger<StubEmailService> logger)
{
_logger = logger;
}
/// <inheritdoc />
public Task SendMembershipRequestNotificationAsync(
Guid clubId,
string userDisplayName,
string userEmail,
CancellationToken ct = default)
{
_logger.LogInformation(
"[EMAIL STUB] Membership request notification - ClubId: {ClubId}, User: {UserDisplayName} ({UserEmail})",
clubId, userDisplayName, userEmail);
// TODO: Implement actual email sending via SMTP
// - Get club admin emails from database
// - Build notification template
// - Send via configured SMTP provider
return Task.CompletedTask;
}
/// <inheritdoc />
public Task SendMembershipApprovedAsync(
string userEmail,
string clubName,
CancellationToken ct = default)
{
_logger.LogInformation(
"[EMAIL STUB] Membership approved - To: {UserEmail}, Club: {ClubName}",
userEmail, clubName);
// TODO: Implement actual email sending via SMTP
// - Build approval notification template
// - Include link to dashboard
// - Send via configured SMTP provider
return Task.CompletedTask;
}
/// <inheritdoc />
public Task SendMembershipRejectedAsync(
string userEmail,
string clubName,
string? reason,
CancellationToken ct = default)
{
_logger.LogInformation(
"[EMAIL STUB] Membership rejected - To: {UserEmail}, Club: {ClubName}, Reason: {Reason}",
userEmail, clubName, reason ?? "(no reason provided)");
// TODO: Implement actual email sending via SMTP
// - Build rejection notification template
// - Include reason if provided
// - Send via configured SMTP provider
return Task.CompletedTask;
}
}