Rename auth feature to Features.Users (#281)

This commit is contained in:
Aaron Po
2026-09-06 01:23:51 -04:00
committed by GitHub
parent d958933250
commit 9d5a463d93
79 changed files with 190 additions and 192 deletions

View File

@@ -45,8 +45,6 @@ see the notes for what else each of those foundation projects pulls in.
commands/queries + handlers, validators, and Dapper repository commands/queries + handlers, validators, and Dapper repository
- `Features.Users` covers auth (`Commands/Authentication`), account management - `Features.Users` covers auth (`Commands/Authentication`), account management
(`Commands/Account`), and profile/avatar (`Commands/Profile`) (`Commands/Account`), and profile/avatar (`Commands/Profile`)
- internally namespaced `Features.Auth`, despite project name
`Features.Users`
- `Features.Emails` and `Features.PhotoUpload` have no controller — invoked - `Features.Emails` and `Features.PhotoUpload` have no controller — invoked
only via MediatR commands sent from other slices only via MediatR commands sent from other slices
- `Features.Locations` has no controller; one MediatR handler - `Features.Locations` has no controller; one MediatR handler
@@ -65,7 +63,7 @@ see the notes for what else each of those foundation projects pulls in.
`RegisterUserHandler`) and `ConfirmationEmailResendRequestedNotification` `RegisterUserHandler`) and `ConfirmationEmailResendRequestedNotification`
(published from `ResendConfirmationEmailHandler`) (published from `ResendConfirmationEmailHandler`)
- both notification types live in `Features.Users` - both notification types live in `Features.Users`
(`Features.Auth.Notifications`), not in `Shared.Application` (`Features.Users.Notifications`), not in `Shared.Application`
- **Shared.Application**: - **Shared.Application**:
- `ValidationBehavior` (MediatR pipeline) - `ValidationBehavior` (MediatR pipeline)

View File

@@ -1,6 +1,5 @@
using API.Core.Authentication; using API.Core.Authentication;
using Database.Connection.DependencyInjection; using Database.Connection.DependencyInjection;
using Features.Auth.Controllers;
using Features.Breweries.Controllers; using Features.Breweries.Controllers;
using Features.Breweries.DependencyInjection; using Features.Breweries.DependencyInjection;
using Features.Emails.DependencyInjection; using Features.Emails.DependencyInjection;
@@ -9,6 +8,7 @@ using Features.ImageUploads.Commands.UploadPhoto;
using Features.ImageUploads.DependencyInjection; using Features.ImageUploads.DependencyInjection;
using Features.Locations.Controllers; using Features.Locations.Controllers;
using Features.Locations.DependencyInjection; using Features.Locations.DependencyInjection;
using Features.Users.Controllers;
using Features.Users.DependencyInjection; using Features.Users.DependencyInjection;
using FluentValidation; using FluentValidation;
using FluentValidation.AspNetCore; using FluentValidation.AspNetCore;

View File

@@ -2,10 +2,6 @@ using Database.Seed.DatabaseHelpers;
using Database.Seed.Sqlite; using Database.Seed.Sqlite;
using Database.Seed.SourceDataModels; using Database.Seed.SourceDataModels;
using Domain.Entities; using Domain.Entities;
using Features.Auth.Commands.Authentication.RegisterUser;
using Features.Auth.Commands.Profile.UpdateBiography;
using Features.Auth.Commands.Profile.UploadAvatar;
using Features.Auth.Dtos;
using Features.Breweries.Commands.CreateBrewery; using Features.Breweries.Commands.CreateBrewery;
using Features.Locations.Commands.CreateCity; using Features.Locations.Commands.CreateCity;
using Features.Locations.Commands.CreateCountry; using Features.Locations.Commands.CreateCountry;
@@ -13,6 +9,10 @@ using Features.Locations.Commands.CreateStateProvince;
using Features.Locations.Queries.GetCity; using Features.Locations.Queries.GetCity;
using Features.Locations.Queries.GetCountry; using Features.Locations.Queries.GetCountry;
using Features.Locations.Queries.GetStateProvince; using Features.Locations.Queries.GetStateProvince;
using Features.Users.Commands.Authentication.RegisterUser;
using Features.Users.Commands.Profile.UpdateBiography;
using Features.Users.Commands.Profile.UploadAvatar;
using Features.Users.Dtos;
using idunno.Password; using idunno.Password;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;

View File

@@ -1,4 +1,4 @@
using Features.Auth.Services; using Features.Users.Services;
namespace Database.Seed.DatabaseHelpers; namespace Database.Seed.DatabaseHelpers;

View File

@@ -1,15 +1,15 @@
using Database.Connection.DependencyInjection; using Database.Connection.DependencyInjection;
using Database.Seed.DatabaseHelpers; using Database.Seed.DatabaseHelpers;
using Database.Seed.Sqlite; using Database.Seed.Sqlite;
using Features.Auth.Commands.Profile.UploadAvatar;
using Features.Auth.Services;
using Features.Breweries.Commands.CreateBrewery; using Features.Breweries.Commands.CreateBrewery;
using Features.Breweries.DependencyInjection; using Features.Breweries.DependencyInjection;
using Features.ImageUploads.Commands.UploadPhoto; using Features.ImageUploads.Commands.UploadPhoto;
using Features.ImageUploads.DependencyInjection; using Features.ImageUploads.DependencyInjection;
using Features.Locations.DependencyInjection; using Features.Locations.DependencyInjection;
using Features.Locations.Queries.GetCountry; using Features.Locations.Queries.GetCountry;
using Features.Users.Commands.Profile.UploadAvatar;
using Features.Users.DependencyInjection; using Features.Users.DependencyInjection;
using Features.Users.Services;
using Infrastructure.FileUpload; using Infrastructure.FileUpload;
using MediatR; using MediatR;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;

View File

@@ -1,6 +1,6 @@
using Features.Auth.Notifications;
using Features.Emails.Notifications; using Features.Emails.Notifications;
using Features.Emails.Services; using Features.Emails.Services;
using Features.Users.Notifications;
using FluentAssertions; using FluentAssertions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Moq; using Moq;

View File

@@ -1,6 +1,6 @@
using Features.Auth.Notifications;
using Features.Emails.Notifications; using Features.Emails.Notifications;
using Features.Emails.Services; using Features.Emails.Services;
using Features.Users.Notifications;
using FluentAssertions; using FluentAssertions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Moq; using Moq;

View File

@@ -1,5 +1,5 @@
using Features.Auth.Notifications;
using Features.Emails.Services; using Features.Emails.Services;
using Features.Users.Notifications;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@@ -1,5 +1,5 @@
using Features.Auth.Notifications;
using Features.Emails.Services; using Features.Emails.Services;
using Features.Users.Notifications;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@@ -1,16 +1,16 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Commands.Authentication.ConfirmUser; using Features.Users.Commands.Authentication.ConfirmUser;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Services; using Features.Users.Services;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.Commands; namespace Features.Users.Tests.Commands;
public class ConfirmUserHandlerTests public class ConfirmUserHandlerTests
{ {

View File

@@ -1,12 +1,12 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Commands.Account.DeleteAccount; using Features.Users.Commands.Account.DeleteAccount;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.Commands; namespace Features.Users.Tests.Commands;
public class DeleteAccountHandlerTests public class DeleteAccountHandlerTests
{ {

View File

@@ -1,10 +1,10 @@
using Features.Auth.Commands.Authentication.RefreshToken; using Features.Users.Commands.Authentication.RefreshToken;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Services; using Features.Users.Services;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
namespace Features.Auth.Tests.Commands; namespace Features.Users.Tests.Commands;
public class RefreshTokenHandlerTests public class RefreshTokenHandlerTests
{ {

View File

@@ -1,16 +1,16 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Commands.Authentication.RegisterUser; using Features.Users.Commands.Authentication.RegisterUser;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Notifications; using Features.Users.Notifications;
using Features.Auth.Services; using Features.Users.Services;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.Commands; namespace Features.Users.Tests.Commands;
public class RegisterUserHandlerTests public class RegisterUserHandlerTests
{ {

View File

@@ -1,13 +1,13 @@
using Features.Auth.Commands.Authentication.ResendConfirmationEmail; using Features.Users.Commands.Authentication.ResendConfirmationEmail;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Notifications; using Features.Users.Notifications;
using Features.Auth.Services; using Features.Users.Services;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.Commands; namespace Features.Users.Tests.Commands;
public class ResendConfirmationEmailHandlerTests public class ResendConfirmationEmailHandlerTests
{ {

View File

@@ -1,13 +1,13 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Commands.Account.UpdateEmail; using Features.Users.Commands.Account.UpdateEmail;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.Commands; namespace Features.Users.Tests.Commands;
public class UpdateEmailHandlerTests public class UpdateEmailHandlerTests
{ {

View File

@@ -1,13 +1,13 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Commands.Account.UpdatePassword; using Features.Users.Commands.Account.UpdatePassword;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.Commands; namespace Features.Users.Tests.Commands;
public class UpdatePasswordHandlerTests public class UpdatePasswordHandlerTests
{ {

View File

@@ -1,13 +1,13 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Commands.Profile.UpdateProfile; using Features.Users.Commands.Profile.UpdateProfile;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.Commands; namespace Features.Users.Tests.Commands;
public class UpdateProfileHandlerTests public class UpdateProfileHandlerTests
{ {

View File

@@ -1,13 +1,13 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Commands.Account.UpdateUsername; using Features.Users.Commands.Account.UpdateUsername;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.Commands; namespace Features.Users.Tests.Commands;
public class UpdateUsernameHandlerTests public class UpdateUsernameHandlerTests
{ {

View File

@@ -4,7 +4,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<RootNamespace>Features.Auth.Tests</RootNamespace> <RootNamespace>Features.Users.Tests</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,10 +1,10 @@
using Domain.Entities; using Domain.Entities;
using Features.Auth.Queries.GetAllUsers; using Features.Users.Queries.GetAllUsers;
using Features.Auth.Repository; using Features.Users.Repository;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
namespace Features.Auth.Tests.Queries; namespace Features.Users.Tests.Queries;
public class GetAllUsersHandlerTests public class GetAllUsersHandlerTests
{ {

View File

@@ -1,11 +1,11 @@
using Domain.Entities; using Domain.Entities;
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Queries.GetUserById; using Features.Users.Queries.GetUserById;
using Features.Auth.Repository; using Features.Users.Repository;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
namespace Features.Auth.Tests.Queries; namespace Features.Users.Tests.Queries;
public class GetUserByIdHandlerTests public class GetUserByIdHandlerTests
{ {

View File

@@ -1,14 +1,14 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Commands.Authentication.Login; using Features.Users.Commands.Authentication.Login;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Services; using Features.Users.Services;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.Queries; namespace Features.Users.Tests.Queries;
public class LoginHandlerTests public class LoginHandlerTests
{ {

View File

@@ -1,9 +1,9 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Services; using Features.Users.Services;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using Infrastructure.Configuration; using Infrastructure.Configuration;
using Infrastructure.Jwt; using Infrastructure.Jwt;
@@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Moq; using Moq;
namespace Features.Auth.Tests.Services; namespace Features.Users.Tests.Services;
public class TokenServiceRefreshTests public class TokenServiceRefreshTests
{ {

View File

@@ -1,15 +1,15 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Services; using Features.Users.Services;
using Features.Auth.Tests.TestSupport; using Features.Users.Tests.TestSupport;
using FluentAssertions; using FluentAssertions;
using Infrastructure.Configuration; using Infrastructure.Configuration;
using Infrastructure.Jwt; using Infrastructure.Jwt;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Moq; using Moq;
namespace Features.Auth.Tests.Services; namespace Features.Users.Tests.Services;
public class TokenServiceValidationTests public class TokenServiceValidationTests
{ {

View File

@@ -1,8 +1,8 @@
using Features.Auth.Identity; using Features.Users.Identity;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Moq; using Moq;
namespace Features.Auth.Tests.TestSupport; namespace Features.Users.Tests.TestSupport;
public static class UserManagerMockFactory public static class UserManagerMockFactory
{ {

View File

@@ -1,6 +1,6 @@
using MediatR; using MediatR;
namespace Features.Auth.Commands.Account.DeleteAccount; namespace Features.Users.Commands.Account.DeleteAccount;
/// <summary>Permanently deletes the given user account.</summary> /// <summary>Permanently deletes the given user account.</summary>
/// <param name="UserAccountId"> /// <param name="UserAccountId">

View File

@@ -1,9 +1,9 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Identity; using Features.Users.Identity;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Commands.Account.DeleteAccount; namespace Features.Users.Commands.Account.DeleteAccount;
/// <summary>Handles <see cref="DeleteAccountCommand" /> by deleting the user via <see cref="UserManager{TUser}" />.</summary> /// <summary>Handles <see cref="DeleteAccountCommand" /> by deleting the user via <see cref="UserManager{TUser}" />.</summary>
public class DeleteAccountHandler(UserManager<ApplicationUser> userManager) public class DeleteAccountHandler(UserManager<ApplicationUser> userManager)

View File

@@ -1,7 +1,7 @@
using Features.Auth.Dtos; using Features.Users.Dtos;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Account.UpdateEmail; namespace Features.Users.Commands.Account.UpdateEmail;
/// <summary> /// <summary>
/// Changes the email address of the given user account. The account's <c>EmailConfirmed</c> status /// Changes the email address of the given user account. The account's <c>EmailConfirmed</c> status

View File

@@ -1,10 +1,10 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Commands.Account.UpdateEmail; namespace Features.Users.Commands.Account.UpdateEmail;
/// <summary> /// <summary>
/// Handles <see cref="UpdateEmailCommand" /> via <see cref="UserManager{TUser}" />. /// Handles <see cref="UpdateEmailCommand" /> via <see cref="UserManager{TUser}" />.

View File

@@ -1,6 +1,6 @@
using FluentValidation; using FluentValidation;
namespace Features.Auth.Commands.Account.UpdateEmail; namespace Features.Users.Commands.Account.UpdateEmail;
/// <summary>Validates <see cref="UpdateEmailCommand" />, mirroring the email rules in <c>RegisterUserValidator</c>.</summary> /// <summary>Validates <see cref="UpdateEmailCommand" />, mirroring the email rules in <c>RegisterUserValidator</c>.</summary>
public class UpdateEmailValidator : AbstractValidator<UpdateEmailCommand> public class UpdateEmailValidator : AbstractValidator<UpdateEmailCommand>

View File

@@ -1,7 +1,7 @@
using Features.Auth.Dtos; using Features.Users.Dtos;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Account.UpdatePassword; namespace Features.Users.Commands.Account.UpdatePassword;
/// <summary>Changes the password of the given user account, verifying the current password first.</summary> /// <summary>Changes the password of the given user account, verifying the current password first.</summary>
/// <param name="UserAccountId"> /// <param name="UserAccountId">

View File

@@ -1,10 +1,10 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Commands.Account.UpdatePassword; namespace Features.Users.Commands.Account.UpdatePassword;
/// <summary> /// <summary>
/// Handles <see cref="UpdatePasswordCommand" /> via <see cref="UserManager{TUser}.ChangePasswordAsync" />, /// Handles <see cref="UpdatePasswordCommand" /> via <see cref="UserManager{TUser}.ChangePasswordAsync" />,

View File

@@ -1,6 +1,6 @@
using FluentValidation; using FluentValidation;
namespace Features.Auth.Commands.Account.UpdatePassword; namespace Features.Users.Commands.Account.UpdatePassword;
/// <summary>Validates <see cref="UpdatePasswordCommand" />, mirroring the password rules in <c>RegisterUserValidator</c>.</summary> /// <summary>Validates <see cref="UpdatePasswordCommand" />, mirroring the password rules in <c>RegisterUserValidator</c>.</summary>
public class UpdatePasswordValidator : AbstractValidator<UpdatePasswordCommand> public class UpdatePasswordValidator : AbstractValidator<UpdatePasswordCommand>

View File

@@ -1,7 +1,7 @@
using Features.Auth.Dtos; using Features.Users.Dtos;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Account.UpdateUsername; namespace Features.Users.Commands.Account.UpdateUsername;
/// <summary>Changes the username of the given user account.</summary> /// <summary>Changes the username of the given user account.</summary>
/// <param name="UserAccountId"> /// <param name="UserAccountId">

View File

@@ -1,10 +1,10 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Commands.Account.UpdateUsername; namespace Features.Users.Commands.Account.UpdateUsername;
/// <summary>Handles <see cref="UpdateUsernameCommand" /> via <see cref="UserManager{TUser}" />.</summary> /// <summary>Handles <see cref="UpdateUsernameCommand" /> via <see cref="UserManager{TUser}" />.</summary>
public class UpdateUsernameHandler(UserManager<ApplicationUser> userManager) public class UpdateUsernameHandler(UserManager<ApplicationUser> userManager)

View File

@@ -1,6 +1,6 @@
using FluentValidation; using FluentValidation;
namespace Features.Auth.Commands.Account.UpdateUsername; namespace Features.Users.Commands.Account.UpdateUsername;
/// <summary>Validates <see cref="UpdateUsernameCommand" />, mirroring the username rules in <c>RegisterUserValidator</c>.</summary> /// <summary>Validates <see cref="UpdateUsernameCommand" />, mirroring the username rules in <c>RegisterUserValidator</c>.</summary>
public class UpdateUsernameValidator : AbstractValidator<UpdateUsernameCommand> public class UpdateUsernameValidator : AbstractValidator<UpdateUsernameCommand>

View File

@@ -1,7 +1,7 @@
using Features.Auth.Dtos; using Features.Users.Dtos;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Authentication.ConfirmUser; namespace Features.Users.Commands.Authentication.ConfirmUser;
/// <summary>Validates a confirmation token and confirms the corresponding user account.</summary> /// <summary>Validates a confirmation token and confirms the corresponding user account.</summary>
public record ConfirmUserCommand(string Token) : IRequest<ConfirmationPayload>; public record ConfirmUserCommand(string Token) : IRequest<ConfirmationPayload>;

View File

@@ -1,11 +1,11 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Services; using Features.Users.Services;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Commands.Authentication.ConfirmUser; namespace Features.Users.Commands.Authentication.ConfirmUser;
/// <summary> /// <summary>
/// Handles <see cref="ConfirmUserCommand" /> by validating the confirmation token and marking the /// Handles <see cref="ConfirmUserCommand" /> by validating the confirmation token and marking the

View File

@@ -1,7 +1,7 @@
using Features.Auth.Dtos; using Features.Users.Dtos;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Authentication.Login; namespace Features.Users.Commands.Authentication.Login;
/// <summary> /// <summary>
/// Authenticates a user using their username and password and issues new tokens. Bound directly /// Authenticates a user using their username and password and issues new tokens. Bound directly

View File

@@ -1,11 +1,11 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Services; using Features.Users.Services;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Commands.Authentication.Login; namespace Features.Users.Commands.Authentication.Login;
/// <summary> /// <summary>
/// Handles <see cref="LoginCommand" /> by verifying credentials via /// Handles <see cref="LoginCommand" /> by verifying credentials via

View File

@@ -1,6 +1,6 @@
using FluentValidation; using FluentValidation;
namespace Features.Auth.Commands.Authentication.Login; namespace Features.Users.Commands.Authentication.Login;
/// <summary>Validates that <see cref="LoginCommand.Username" /> and <see cref="LoginCommand.Password" /> are present.</summary> /// <summary>Validates that <see cref="LoginCommand.Username" /> and <see cref="LoginCommand.Password" /> are present.</summary>
public class LoginValidator : AbstractValidator<LoginCommand> public class LoginValidator : AbstractValidator<LoginCommand>

View File

@@ -1,7 +1,7 @@
using Features.Auth.Dtos; using Features.Users.Dtos;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Authentication.RefreshToken; namespace Features.Users.Commands.Authentication.RefreshToken;
/// <summary> /// <summary>
/// Exchanges a valid refresh token for a new access/refresh token pair. Constructed from the /// Exchanges a valid refresh token for a new access/refresh token pair. Constructed from the

View File

@@ -1,8 +1,8 @@
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Services; using Features.Users.Services;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Authentication.RefreshToken; namespace Features.Users.Commands.Authentication.RefreshToken;
/// <summary> /// <summary>
/// Handles <see cref="RefreshTokenCommand" /> by validating the refresh token and issuing a new /// Handles <see cref="RefreshTokenCommand" /> by validating the refresh token and issuing a new

View File

@@ -1,6 +1,6 @@
using FluentValidation; using FluentValidation;
namespace Features.Auth.Commands.Authentication.RefreshToken; namespace Features.Users.Commands.Authentication.RefreshToken;
/// <summary>Validates that <see cref="RefreshTokenCommand.RefreshToken" /> is present.</summary> /// <summary>Validates that <see cref="RefreshTokenCommand.RefreshToken" /> is present.</summary>
public class RefreshTokenValidator : AbstractValidator<RefreshTokenCommand> public class RefreshTokenValidator : AbstractValidator<RefreshTokenCommand>

View File

@@ -1,7 +1,7 @@
using Features.Auth.Dtos; using Features.Users.Dtos;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Authentication.RegisterUser; namespace Features.Users.Commands.Authentication.RegisterUser;
/// <summary> /// <summary>
/// Registers a new user account. Bound directly from the request body of <c>POST /api/auth/register</c>. /// Registers a new user account. Bound directly from the request body of <c>POST /api/auth/register</c>.

View File

@@ -1,14 +1,14 @@
using Domain.Entities; using Domain.Entities;
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Commands.Profile.CreateUserProfile; using Features.Users.Commands.Profile.CreateUserProfile;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Notifications; using Features.Users.Notifications;
using Features.Auth.Services; using Features.Users.Services;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Commands.Authentication.RegisterUser; namespace Features.Users.Commands.Authentication.RegisterUser;
/// <summary> /// <summary>
/// Handles <see cref="RegisterUserCommand" />: creates the user via <see cref="UserManager{TUser}" /> /// Handles <see cref="RegisterUserCommand" />: creates the user via <see cref="UserManager{TUser}" />

View File

@@ -1,6 +1,6 @@
using FluentValidation; using FluentValidation;
namespace Features.Auth.Commands.Authentication.RegisterUser; namespace Features.Users.Commands.Authentication.RegisterUser;
/// <summary> /// <summary>
/// Validates the field-level constraints on <see cref="RegisterUserCommand" />: username format and /// Validates the field-level constraints on <see cref="RegisterUserCommand" />: username format and

View File

@@ -1,6 +1,6 @@
using MediatR; using MediatR;
namespace Features.Auth.Commands.Authentication.ResendConfirmationEmail; namespace Features.Users.Commands.Authentication.ResendConfirmationEmail;
/// <summary>Resends the account confirmation email to a user, generating a fresh confirmation token.</summary> /// <summary>Resends the account confirmation email to a user, generating a fresh confirmation token.</summary>
public record ResendConfirmationEmailCommand(Guid UserId) : IRequest; public record ResendConfirmationEmailCommand(Guid UserId) : IRequest;

View File

@@ -1,10 +1,10 @@
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Notifications; using Features.Users.Notifications;
using Features.Auth.Services; using Features.Users.Services;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Commands.Authentication.ResendConfirmationEmail; namespace Features.Users.Commands.Authentication.ResendConfirmationEmail;
/// <summary> /// <summary>
/// Handles <see cref="ResendConfirmationEmailCommand" /> by generating a fresh confirmation token and /// Handles <see cref="ResendConfirmationEmailCommand" /> by generating a fresh confirmation token and

View File

@@ -1,6 +1,6 @@
using MediatR; using MediatR;
namespace Features.Auth.Commands.Profile.CreateUserProfile; namespace Features.Users.Commands.Profile.CreateUserProfile;
/// <summary>Creates a new user profile for a user account.</summary> /// <summary>Creates a new user profile for a user account.</summary>
public record CreateUserProfileCommand(Guid UserAccountId, string Biography) : IRequest<Guid>; public record CreateUserProfileCommand(Guid UserAccountId, string Biography) : IRequest<Guid>;

View File

@@ -1,7 +1,7 @@
using Features.Auth.Repository; using Features.Users.Repository;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Profile.CreateUserProfile; namespace Features.Users.Commands.Profile.CreateUserProfile;
/// <summary>Handles <see cref="CreateUserProfileCommand" /> by inserting the new profile record.</summary> /// <summary>Handles <see cref="CreateUserProfileCommand" /> by inserting the new profile record.</summary>
public class CreateUserProfileHandler(IUserProfileRepository userProfileRepository) public class CreateUserProfileHandler(IUserProfileRepository userProfileRepository)

View File

@@ -1,6 +1,6 @@
using MediatR; using MediatR;
namespace Features.Auth.Commands.Profile.UpdateBiography; namespace Features.Users.Commands.Profile.UpdateBiography;
/// <summary>Updates the biography of the given user account's profile.</summary> /// <summary>Updates the biography of the given user account's profile.</summary>
/// <param name="UserAccountId"> /// <param name="UserAccountId">

View File

@@ -1,7 +1,7 @@
using Features.Auth.Repository; using Features.Users.Repository;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Profile.UpdateBiography; namespace Features.Users.Commands.Profile.UpdateBiography;
/// <summary>Handles <see cref="UpdateBiographyCommand" /> by updating the stored biography.</summary> /// <summary>Handles <see cref="UpdateBiographyCommand" /> by updating the stored biography.</summary>
public class UpdateBiographyHandler(IUserProfileRepository userProfileRepository) public class UpdateBiographyHandler(IUserProfileRepository userProfileRepository)

View File

@@ -1,7 +1,7 @@
using Features.Auth.Dtos; using Features.Users.Dtos;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Profile.UpdateProfile; namespace Features.Users.Commands.Profile.UpdateProfile;
/// <summary> /// <summary>
/// Updates the non-credential profile fields (first name, last name, date of birth) of the given /// Updates the non-credential profile fields (first name, last name, date of birth) of the given

View File

@@ -1,10 +1,10 @@
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Dtos; using Features.Users.Dtos;
using Features.Auth.Identity; using Features.Users.Identity;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Commands.Profile.UpdateProfile; namespace Features.Users.Commands.Profile.UpdateProfile;
/// <summary> /// <summary>
/// Handles <see cref="UpdateProfileCommand" /> via <see cref="UserManager{TUser}" />. First/last name /// Handles <see cref="UpdateProfileCommand" /> via <see cref="UserManager{TUser}" />. First/last name

View File

@@ -1,6 +1,6 @@
using FluentValidation; using FluentValidation;
namespace Features.Auth.Commands.Profile.UpdateProfile; namespace Features.Users.Commands.Profile.UpdateProfile;
/// <summary>Validates <see cref="UpdateProfileCommand" />, mirroring the profile rules in <c>RegisterUserValidator</c>.</summary> /// <summary>Validates <see cref="UpdateProfileCommand" />, mirroring the profile rules in <c>RegisterUserValidator</c>.</summary>
public class UpdateProfileValidator : AbstractValidator<UpdateProfileCommand> public class UpdateProfileValidator : AbstractValidator<UpdateProfileCommand>

View File

@@ -1,7 +1,7 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
namespace Features.Auth.Commands.Profile.UploadAvatar; namespace Features.Users.Commands.Profile.UploadAvatar;
/// <summary>Uploads a new avatar photo for a user account, replacing any existing avatar.</summary> /// <summary>Uploads a new avatar photo for a user account, replacing any existing avatar.</summary>
public record UploadAvatarCommand(Guid UserAccountId, IFormFile File) : IRequest<Guid>; public record UploadAvatarCommand(Guid UserAccountId, IFormFile File) : IRequest<Guid>;

View File

@@ -1,9 +1,9 @@
using Domain.Entities; using Domain.Entities;
using Features.Auth.Repository;
using Features.ImageUploads.Commands.UploadPhoto; using Features.ImageUploads.Commands.UploadPhoto;
using Features.Users.Repository;
using MediatR; using MediatR;
namespace Features.Auth.Commands.Profile.UploadAvatar; namespace Features.Users.Commands.Profile.UploadAvatar;
/// <summary> /// <summary>
/// Handles <see cref="UploadAvatarCommand" />: uploads the photo via <see cref="UploadPhotoCommand" />, /// Handles <see cref="UploadAvatarCommand" />: uploads the photo via <see cref="UploadPhotoCommand" />,

View File

@@ -1,20 +1,20 @@
using Features.Auth.Commands.Account.DeleteAccount; using Features.Users.Commands.Account.DeleteAccount;
using Features.Auth.Commands.Account.UpdateEmail; using Features.Users.Commands.Account.UpdateEmail;
using Features.Auth.Commands.Account.UpdatePassword; using Features.Users.Commands.Account.UpdatePassword;
using Features.Auth.Commands.Account.UpdateUsername; using Features.Users.Commands.Account.UpdateUsername;
using Features.Auth.Commands.Authentication.ConfirmUser; using Features.Users.Commands.Authentication.ConfirmUser;
using Features.Auth.Commands.Authentication.Login; using Features.Users.Commands.Authentication.Login;
using Features.Auth.Commands.Authentication.RefreshToken; using Features.Users.Commands.Authentication.RefreshToken;
using Features.Auth.Commands.Authentication.RegisterUser; using Features.Users.Commands.Authentication.RegisterUser;
using Features.Auth.Commands.Authentication.ResendConfirmationEmail; using Features.Users.Commands.Authentication.ResendConfirmationEmail;
using Features.Auth.Commands.Profile.UpdateProfile; using Features.Users.Commands.Profile.UpdateProfile;
using Features.Auth.Dtos; using Features.Users.Dtos;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Shared.Contracts; using Shared.Contracts;
namespace Features.Auth.Controllers; namespace Features.Users.Controllers;
/// <summary> /// <summary>
/// Handles user authentication concerns: registration, login, email confirmation, token refresh, and /// Handles user authentication concerns: registration, login, email confirmation, token refresh, and

View File

@@ -1,11 +1,11 @@
using Domain.Entities; using Domain.Entities;
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Queries.GetAllUsers; using Features.Users.Queries.GetAllUsers;
using Features.Auth.Queries.GetUserById; using Features.Users.Queries.GetUserById;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Features.Auth.Controllers; namespace Features.Users.Controllers;
/// <summary> /// <summary>
/// Exposes read endpoints for user accounts. /// Exposes read endpoints for user accounts.

View File

@@ -1,6 +1,6 @@
using Features.Auth.Identity; using Features.Users.Identity;
using Features.Auth.Repository; using Features.Users.Repository;
using Features.Auth.Services; using Features.Users.Services;
using Infrastructure.PasswordHashing; using Infrastructure.PasswordHashing;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

View File

@@ -1,4 +1,4 @@
namespace Features.Auth.Dtos; namespace Features.Users.Dtos;
/// <summary>Payload returned to the client after a successful login or token refresh.</summary> /// <summary>Payload returned to the client after a successful login or token refresh.</summary>
public record LoginPayload( public record LoginPayload(

View File

@@ -4,7 +4,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RootNamespace>Features.Auth</RootNamespace> <RootNamespace>Features.Users</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,4 +1,4 @@
namespace Features.Auth.Identity; namespace Features.Users.Identity;
/// <summary> /// <summary>
/// The user type driving <see cref="Microsoft.AspNetCore.Identity.UserManager{TUser}" /> for /// The user type driving <see cref="Microsoft.AspNetCore.Identity.UserManager{TUser}" /> for

View File

@@ -1,7 +1,7 @@
using Infrastructure.PasswordHashing; using Infrastructure.PasswordHashing;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
namespace Features.Auth.Identity; namespace Features.Users.Identity;
/// <summary>Adapts <see cref="IPasswordInfrastructure" />'s Argon2 hashing to <see cref="IPasswordHasher{TUser}" />.</summary> /// <summary>Adapts <see cref="IPasswordInfrastructure" />'s Argon2 hashing to <see cref="IPasswordHasher{TUser}" />.</summary>
public sealed class Argon2PasswordHasher(IPasswordInfrastructure passwordInfrastructure) public sealed class Argon2PasswordHasher(IPasswordInfrastructure passwordInfrastructure)

View File

@@ -4,7 +4,7 @@ using Database.Connection;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
namespace Features.Auth.Identity; namespace Features.Users.Identity;
/// <summary> /// <summary>
/// Dapper-based implementation of the ASP.NET Core Identity store interfaces consumed by /// Dapper-based implementation of the ASP.NET Core Identity store interfaces consumed by

View File

@@ -1,6 +1,6 @@
using Domain.Entities; using Domain.Entities;
namespace Features.Auth.Identity; namespace Features.Users.Identity;
/// <summary> /// <summary>
/// Converts the persistence-agnostic <see cref="UserAccount" /> domain entity into the /// Converts the persistence-agnostic <see cref="UserAccount" /> domain entity into the

View File

@@ -1,6 +1,6 @@
using MediatR; using MediatR;
namespace Features.Auth.Notifications; namespace Features.Users.Notifications;
/// <summary> /// <summary>
/// Published after a user requests a fresh confirmation link. Handled by Features.Emails to send the /// Published after a user requests a fresh confirmation link. Handled by Features.Emails to send the

View File

@@ -1,6 +1,6 @@
using MediatR; using MediatR;
namespace Features.Auth.Notifications; namespace Features.Users.Notifications;
/// <summary> /// <summary>
/// Published after a new user registers. Handled by Features.Emails to send the registration /// Published after a new user registers. Handled by Features.Emails to send the registration

View File

@@ -1,8 +1,8 @@
using Domain.Entities; using Domain.Entities;
using Features.Auth.Repository; using Features.Users.Repository;
using MediatR; using MediatR;
namespace Features.Auth.Queries.GetAllUsers; namespace Features.Users.Queries.GetAllUsers;
public class GetAllUsersHandler(IUserListRepository repository) public class GetAllUsersHandler(IUserListRepository repository)
: IRequestHandler<GetAllUsersQuery, IEnumerable<UserAccount>> : IRequestHandler<GetAllUsersQuery, IEnumerable<UserAccount>>

View File

@@ -1,7 +1,7 @@
using Domain.Entities; using Domain.Entities;
using MediatR; using MediatR;
namespace Features.Auth.Queries.GetAllUsers; namespace Features.Users.Queries.GetAllUsers;
/// <summary> /// <summary>
/// Requests a page of user accounts, ordered by creation date descending. /// Requests a page of user accounts, ordered by creation date descending.

View File

@@ -1,9 +1,9 @@
using Domain.Entities; using Domain.Entities;
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Repository; using Features.Users.Repository;
using MediatR; using MediatR;
namespace Features.Auth.Queries.GetUserById; namespace Features.Users.Queries.GetUserById;
public class GetUserByIdHandler(IUserListRepository repository) public class GetUserByIdHandler(IUserListRepository repository)
: IRequestHandler<GetUserByIdQuery, UserAccount> : IRequestHandler<GetUserByIdQuery, UserAccount>

View File

@@ -1,6 +1,6 @@
using Domain.Entities; using Domain.Entities;
using MediatR; using MediatR;
namespace Features.Auth.Queries.GetUserById; namespace Features.Users.Queries.GetUserById;
public record GetUserByIdQuery(Guid UserAccountId) : IRequest<UserAccount>; public record GetUserByIdQuery(Guid UserAccountId) : IRequest<UserAccount>;

View File

@@ -1,6 +1,6 @@
using Domain.Entities; using Domain.Entities;
namespace Features.Auth.Repository; namespace Features.Users.Repository;
/// <summary> /// <summary>
/// Read access for user account listing. Everything else on user accounts (lookup by /// Read access for user account listing. Everything else on user accounts (lookup by

View File

@@ -1,6 +1,6 @@
using Domain.Entities; using Domain.Entities;
namespace Features.Auth.Repository; namespace Features.Users.Repository;
/// <summary> /// <summary>
/// Repository for user profile records and their avatars. /// Repository for user profile records and their avatars.

View File

@@ -3,7 +3,7 @@ using Dapper;
using Database.Connection; using Database.Connection;
using Domain.Entities; using Domain.Entities;
namespace Features.Auth.Repository; namespace Features.Users.Repository;
/// <summary> /// <summary>
/// Dapper-based implementation of <see cref="IUserListRepository" />. /// Dapper-based implementation of <see cref="IUserListRepository" />.

View File

@@ -4,7 +4,7 @@ using Database.Connection;
using Domain.Entities; using Domain.Entities;
using Domain.Exceptions; using Domain.Exceptions;
namespace Features.Auth.Repository; namespace Features.Users.Repository;
/// <summary> /// <summary>
/// Dapper-based implementation of <see cref="IUserProfileRepository" />. /// Dapper-based implementation of <see cref="IUserProfileRepository" />.

View File

@@ -1,6 +1,6 @@
using System.Security.Claims; using System.Security.Claims;
namespace Features.Auth.Services; namespace Features.Users.Services;
/// <summary> /// <summary>
/// Identifies the kind of token being generated or validated. /// Identifies the kind of token being generated or validated.

View File

@@ -1,13 +1,13 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
using Domain.Exceptions; using Domain.Exceptions;
using Features.Auth.Identity; using Features.Users.Identity;
using Infrastructure.Configuration; using Infrastructure.Configuration;
using Infrastructure.Jwt; using Infrastructure.Jwt;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
namespace Features.Auth.Services; namespace Features.Users.Services;
/// <summary> /// <summary>
/// Default implementation of <see cref="ITokenService" /> that generates and validates JWTs /// Default implementation of <see cref="ITokenService" /> that generates and validates JWTs