Convert Documents module to MediatR commands/queries

Replaces IDocumentService/DocumentService. The permission-check and
descendant-deletion logic moves to a shared DocumentAccess static
helper used by all seven handlers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 21:03:16 +07:00
co-authored by Claude Sonnet 5
parent 798637c6c1
commit c3384d76d9
12 changed files with 318 additions and 255 deletions
@@ -0,0 +1,71 @@
using Mws.Application.Common;
using Mws.Application.Permissions;
using Mws.Domain.Documents;
using Mws.Domain.Projects;
namespace Mws.Application.Documents;
internal static class DocumentAccess
{
public static async Task EnsureDocumentPermissionAsync(
IUnitOfWork uow, Guid userId, Guid projectId, PermissionAction action, CancellationToken ct)
{
var member = await uow.Projects.GetMemberAsync(projectId, userId, ct)
?? throw new NotFoundException("Project not found");
if (!await HasDocumentPermissionAsync(uow, member, action, ct))
{
throw new ForbiddenException("You do not have permission to access documents in this project");
}
}
public static async Task<bool> HasDocumentPermissionAsync(
IUnitOfWork uow, ProjectMember member, PermissionAction action, CancellationToken ct)
{
if (member.Role == MemberRole.Owner)
{
return true;
}
var permission = await uow.Projects.GetMemberPermissionAsync(member.ProjectId, member.UserId, ProjectPermissionScreens.Documents, ct);
return action switch
{
PermissionAction.View => permission?.CanView ?? false,
PermissionAction.Create => permission?.CanCreate ?? false,
PermissionAction.Edit => permission?.CanEdit ?? false,
PermissionAction.Delete => permission?.CanDelete ?? false,
_ => false,
};
}
public static async Task<Document> GetDocumentForUserAsync(IUnitOfWork uow, Guid userId, Guid documentId, CancellationToken ct)
{
var doc = await uow.Documents.GetByIdAsync(documentId, ct)
?? throw new NotFoundException("Document not found");
var member = await uow.Projects.GetMemberAsync(doc.ProjectId, userId, ct);
if (member is null)
{
throw new NotFoundException("Document not found");
}
if (!await HasDocumentPermissionAsync(uow, member, PermissionAction.View, ct))
{
throw new ForbiddenException("You do not have permission to view this document");
}
return doc;
}
public static async Task DeleteDescendantsAsync(IUnitOfWork uow, Guid parentId, CancellationToken ct)
{
var children = await uow.Documents.GetChildrenAsync(parentId, ct);
foreach (var child in children)
{
await DeleteDescendantsAsync(uow, child.Id, ct);
uow.Documents.Remove(child);
}
}
}