first commit
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SequenceAuth.Example.Domain;
|
||||
using SequenceAuth.Example.Infrastructure;
|
||||
using SequenceAuth.Lib;
|
||||
|
||||
namespace SequenceAuth.Example.Features.Auth;
|
||||
|
||||
public record LoginCommand(string Username) : IRequest<LoginResult>;
|
||||
public record LoginResult(User User, string InitialSequence);
|
||||
|
||||
public class LoginCommandHandler(AppDbContext dbContext, ISequenceStore sequenceStore, Microsoft.AspNetCore.Http.IHttpContextAccessor httpContextAccessor, Microsoft.Extensions.Options.IOptions<SequenceAuth.Lib.SequenceAuthOptions> options) : IRequestHandler<LoginCommand, LoginResult>
|
||||
{
|
||||
public async Task<LoginResult> Handle(LoginCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var userOpt = await dbContext.Users.FirstOrDefaultAsync(u => u.Username == request.Username, cancellationToken);
|
||||
|
||||
var user = userOpt switch
|
||||
{
|
||||
null => await CreateUser(request.Username, cancellationToken),
|
||||
_ => userOpt
|
||||
};
|
||||
|
||||
var initialSequenceId = Guid.NewGuid().ToString("N");
|
||||
var sequenceData = new SequenceData(UserId: user.Id.ToString(), RequestsRemaining: 1000, State: SequenceState.Initialized, NextSequenceId: initialSequenceId);
|
||||
|
||||
await sequenceStore.SaveSequenceAsync(initialSequenceId, sequenceData);
|
||||
|
||||
httpContextAccessor.HttpContext?.Response.Headers.Append(options.Value.NextHeaderName, initialSequenceId);
|
||||
httpContextAccessor.HttpContext?.Response.Headers.Append(options.Value.RequestsRemainingHeaderName, "1000");
|
||||
|
||||
return new LoginResult(user, initialSequenceId);
|
||||
}
|
||||
|
||||
private async Task<User> CreateUser(string username, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = new User(Guid.NewGuid(), username);
|
||||
dbContext.Users.Add(user);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using SequenceAuth.Lib;
|
||||
|
||||
namespace SequenceAuth.Example.Features.Auth;
|
||||
|
||||
public record LogoutCommand() : IRequest;
|
||||
|
||||
public class LogoutCommandHandler(ISequenceStore sequenceStore, IHttpContextAccessor httpContextAccessor, Microsoft.Extensions.Options.IOptions<SequenceAuthOptions> options) : IRequestHandler<LogoutCommand>
|
||||
{
|
||||
public async Task Handle(LogoutCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var userIdStr = httpContextAccessor.HttpContext?.Items[options.Value.UserIdItemKey]?.ToString();
|
||||
var isUserIdPresent = string.IsNullOrEmpty(userIdStr);
|
||||
|
||||
_ = isUserIdPresent switch
|
||||
{
|
||||
false => await ProcessLogout(userIdStr!),
|
||||
true => 0
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<int> ProcessLogout(string userId)
|
||||
{
|
||||
await sequenceStore.InvalidateUserSessionsAsync(userId);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SequenceAuth.Example.Domain;
|
||||
using SequenceAuth.Example.Infrastructure;
|
||||
|
||||
namespace SequenceAuth.Example.Features.Todos;
|
||||
|
||||
public enum ChangeTodoStatusOutcome { Success, NotFound, Unauthorized }
|
||||
|
||||
public record ChangeTodoStatusResult(ChangeTodoStatusOutcome Outcome, TodoItem? Item);
|
||||
|
||||
public record ChangeTodoStatusCommand(Guid TodoId, Guid UserId, TodoStatus NewStatus) : IRequest<ChangeTodoStatusResult>;
|
||||
|
||||
public class ChangeTodoStatusCommandHandler(AppDbContext dbContext) : IRequestHandler<ChangeTodoStatusCommand, ChangeTodoStatusResult>
|
||||
{
|
||||
public async Task<ChangeTodoStatusResult> Handle(ChangeTodoStatusCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var todo = await dbContext.Todos.FirstOrDefaultAsync(t => t.Id == request.TodoId, cancellationToken);
|
||||
|
||||
return todo switch
|
||||
{
|
||||
null => new ChangeTodoStatusResult(ChangeTodoStatusOutcome.NotFound, null),
|
||||
{ UserId: var userId } when userId != request.UserId => new ChangeTodoStatusResult(ChangeTodoStatusOutcome.Unauthorized, null),
|
||||
_ => await UpdateStatusAsync(todo, request.NewStatus, cancellationToken)
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<ChangeTodoStatusResult> UpdateStatusAsync(TodoItem todo, TodoStatus newStatus, CancellationToken cancellationToken)
|
||||
{
|
||||
var updatedTodo = todo with { Status = newStatus };
|
||||
|
||||
dbContext.Entry(todo).CurrentValues.SetValues(updatedTodo);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new ChangeTodoStatusResult(ChangeTodoStatusOutcome.Success, updatedTodo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using MediatR;
|
||||
using SequenceAuth.Example.Domain;
|
||||
using SequenceAuth.Example.Infrastructure;
|
||||
|
||||
namespace SequenceAuth.Example.Features.Todos;
|
||||
|
||||
public record CreateTodoCommand(Guid UserId, string Title) : IRequest<TodoItem>;
|
||||
|
||||
public class CreateTodoCommandHandler(AppDbContext dbContext) : IRequestHandler<CreateTodoCommand, TodoItem>
|
||||
{
|
||||
public async Task<TodoItem> Handle(CreateTodoCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var todo = new TodoItem(Guid.NewGuid(), request.UserId, request.Title, TodoStatus.Pending);
|
||||
dbContext.Todos.Add(todo);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
return todo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SequenceAuth.Example.Domain;
|
||||
using SequenceAuth.Example.Infrastructure;
|
||||
|
||||
namespace SequenceAuth.Example.Features.Todos;
|
||||
|
||||
public record GetTodosQuery(Guid UserId, TodoStatus? StatusFilter) : IRequest<List<TodoItem>>;
|
||||
|
||||
public class GetTodosQueryHandler(AppDbContext dbContext) : IRequestHandler<GetTodosQuery, List<TodoItem>>
|
||||
{
|
||||
public async Task<List<TodoItem>> Handle(GetTodosQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = dbContext.Todos.Where(t => t.UserId == request.UserId);
|
||||
|
||||
query = request.StatusFilter switch
|
||||
{
|
||||
null => query,
|
||||
var status => query.Where(t => t.Status == status)
|
||||
};
|
||||
|
||||
return await query.ToListAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user