using Socialize.Infrastructure.Security; using Socialize.Modules.Identity.Contracts; using Socialize.Modules.Workspaces.Data; namespace Socialize.Modules.Workspaces.Handlers; public record CreateWorkspaceInviteRequest( string Email, string Role); public class CreateWorkspaceInviteRequestValidator : Validator { private static readonly string[] AllowedRoles = [ KnownRoles.Client, KnownRoles.Provider, KnownRoles.WorkspaceMember, ]; public CreateWorkspaceInviteRequestValidator() { RuleFor(x => x.Email).NotEmpty().MaximumLength(256).EmailAddress(); RuleFor(x => x.Role).NotEmpty().Must(role => AllowedRoles.Contains(role)); } } public class CreateWorkspaceInviteHandler( AppDbContext dbContext, AccessScopeService accessScopeService) : Endpoint { public override void Configure() { Post("/api/workspaces/{workspaceId:guid}/invites"); Options(o => o.WithTags("Workspaces")); } public override async Task HandleAsync(CreateWorkspaceInviteRequest request, CancellationToken ct) { Guid workspaceId = Route("workspaceId"); if (!accessScopeService.CanManageWorkspace(User, workspaceId)) { await SendForbiddenAsync(ct); return; } bool workspaceExists = await dbContext.Workspaces .AnyAsync(workspace => workspace.Id == workspaceId, ct); if (!workspaceExists) { AddError("workspaceId", "The selected workspace does not exist."); await SendErrorsAsync(StatusCodes.Status400BadRequest, ct); return; } string normalizedEmail = request.Email.Trim().ToLowerInvariant(); string normalizedRole = request.Role.Trim(); bool duplicateInvite = await dbContext.WorkspaceInvites.AnyAsync( invite => invite.WorkspaceId == workspaceId && invite.Email == normalizedEmail && invite.Status == "Pending", ct); if (duplicateInvite) { AddError(request => request.Email, "A pending invite already exists for this email in the selected workspace."); await SendErrorsAsync(StatusCodes.Status409Conflict, ct); return; } WorkspaceInvite invite = new() { Id = Guid.NewGuid(), WorkspaceId = workspaceId, Email = normalizedEmail, Role = normalizedRole, Status = "Pending", InvitedByUserId = User.GetUserId(), CreatedAt = DateTimeOffset.UtcNow, }; dbContext.WorkspaceInvites.Add(invite); await dbContext.SaveChangesAsync(ct); await SendAsync( new WorkspaceInviteDto( invite.Id, invite.WorkspaceId, invite.Email, invite.Role, invite.Status, invite.CreatedAt), StatusCodes.Status201Created, ct); } }