Перепиливание под чистый DDD
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
using Carter;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Modules.Conversations.Application.DTOs;
|
||||
using Knot.Modules.Conversations.Application.Chats.GetChats;
|
||||
using Knot.Modules.Conversations.Application.Chats.GetChatById;
|
||||
using Knot.Modules.Conversations.Application.Chats.Create;
|
||||
using Knot.Modules.Conversations.Application.Chats.GetOrCreateFavorites;
|
||||
using Knot.Modules.Conversations.Application.Chats.Update;
|
||||
using Knot.Modules.Conversations.Application.Chats.LeaveOrDelete;
|
||||
using Knot.Modules.Conversations.Application.Chats.Clear;
|
||||
using Knot.Modules.Conversations.Application.Chats.TogglePin;
|
||||
using Knot.Modules.Conversations.Application.Chats.Members;
|
||||
using Knot.Modules.Conversations.Application.Chats.Avatar;
|
||||
using Knot.Modules.Conversations.Domain;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Knot.Modules.Conversations.Presentation.Endpoints;
|
||||
|
||||
public sealed class ChatsEndpoints : ICarterModule
|
||||
{
|
||||
public void AddRoutes(IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("api/chats").RequireAuthorization();
|
||||
|
||||
group.MapGet("", async (ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new GetChatsQuery(userContext.UserId), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
|
||||
});
|
||||
|
||||
group.MapPost("", async ([FromBody] CreateChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var command = new CreateChatCommand(request.Name, request.Type, request.MemberIds);
|
||||
var result = await sender.Send(command, ct);
|
||||
if (result.IsFailure) return Results.BadRequest(result.Error.Description);
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapPost("personal", async ([FromBody] CreatePersonalChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var command = new CreateChatCommand(string.Empty, ChatType.Personal, new List<Guid> { userContext.UserId, request.UserId });
|
||||
var result = await sender.Send(command, ct);
|
||||
if (result.IsFailure) return Results.BadRequest(result.Error.Description);
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapPost("group", async ([FromBody] CreateGroupChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var memberIds = request.MemberIds.ToList();
|
||||
if (memberIds.Contains(userContext.UserId)) memberIds.Remove(userContext.UserId);
|
||||
memberIds.Insert(0, userContext.UserId);
|
||||
|
||||
var command = new CreateChatCommand(request.Name, ChatType.Group, memberIds);
|
||||
var result = await sender.Send(command, ct);
|
||||
if (result.IsFailure) return Results.BadRequest(result.Error.Description);
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapPost("favorites", async (ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new GetOrCreateFavoritesCommand(userContext.UserId), ct);
|
||||
if (result.IsFailure) return Results.BadRequest(result.Error.Description);
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapPut("{id:guid}", async (Guid id, [FromBody] UpdateChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new UpdateChatCommand(id, userContext.UserId, request.Name, request.Description), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapDelete("{id:guid}", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new LeaveOrDeleteChatCommand(id, userContext.UserId), ct);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
if (result.Error.Code == "Unauthorized") return Results.Forbid();
|
||||
return Results.NotFound();
|
||||
}
|
||||
return Results.Ok(result.Value);
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/clear", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new ClearChatCommand(id, userContext.UserId), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound();
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/pin", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new TogglePinCommand(id, userContext.UserId), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound();
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/members", async (Guid id, [FromBody] AddMembersRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new AddMembersCommand(id, userContext.UserId, request.UserIds.ToList()), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapDelete("{id:guid}/members/{userId:guid}", async (Guid id, Guid userId, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new RemoveMemberCommand(id, userContext.UserId, userId), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/avatar", async (Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
if (!req.HasFormContentType) return Results.BadRequest("No file");
|
||||
var form = await req.ReadFormAsync(ct);
|
||||
var avatar = form.Files.FirstOrDefault();
|
||||
if (avatar == null || avatar.Length == 0) return Results.BadRequest("No file");
|
||||
|
||||
using var stream = avatar.OpenReadStream();
|
||||
var result = await sender.Send(new UploadGroupAvatarCommand(id, userContext.UserId, avatar.FileName, avatar.ContentType, stream), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
}).DisableAntiforgery();
|
||||
|
||||
group.MapPost("{id:guid}/avatar/crop", async (Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
if (!req.HasFormContentType) return Results.BadRequest("No file");
|
||||
var form = await req.ReadFormAsync(ct);
|
||||
var avatar = form.Files.FirstOrDefault();
|
||||
if (avatar == null || avatar.Length == 0) return Results.BadRequest("No file");
|
||||
|
||||
int.TryParse(form["x"], out int x);
|
||||
int.TryParse(form["y"], out int y);
|
||||
int.TryParse(form["width"], out int width);
|
||||
int.TryParse(form["height"], out int height);
|
||||
|
||||
using var stream = avatar.OpenReadStream();
|
||||
var result = await sender.Send(new CropGroupAvatarCommand(id, userContext.UserId, avatar.FileName ?? "avatar.jpg", avatar.ContentType, stream, x, y, width, height), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
}).DisableAntiforgery();
|
||||
|
||||
group.MapDelete("{id:guid}/avatar", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new RemoveGroupAvatarCommand(id, userContext.UserId), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
|
||||
var chatResult = await sender.Send(new GetChatByIdQuery(userContext.UserId, result.Value), ct);
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using Carter;
|
||||
using Knot.Shared.Kernel;
|
||||
using Knot.Modules.Conversations.Application.DTOs;
|
||||
using Knot.Modules.Conversations.Application.Messages.GetMessages;
|
||||
using Knot.Modules.Conversations.Application.Messages.SearchMessages;
|
||||
using Knot.Modules.Conversations.Application.Messages.UploadFile;
|
||||
using Knot.Modules.Conversations.Application.Messages.GetSharedMedia;
|
||||
using Knot.Modules.Conversations.Application.Messages.Send;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Knot.Modules.Conversations.Presentation.Endpoints;
|
||||
|
||||
public sealed class MessagesEndpoints : ICarterModule
|
||||
{
|
||||
public void AddRoutes(IEndpointRouteBuilder app)
|
||||
{
|
||||
var group = app.MapGroup("api/messages").RequireAuthorization();
|
||||
|
||||
group.MapGet("chat/{chatId:guid}", async (Guid chatId, [FromQuery] string? cursor, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new GetMessagesQuery(userContext.UserId, chatId, cursor), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
|
||||
});
|
||||
|
||||
group.MapGet("search", async ([FromQuery] string q, [FromQuery] Guid? chatId, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new SearchMessagesQuery(userContext.UserId, q, chatId), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
|
||||
});
|
||||
|
||||
group.MapPost("upload", async (HttpRequest req, ISender sender, CancellationToken ct) =>
|
||||
{
|
||||
if (!req.HasFormContentType) return Results.BadRequest("No file uploaded");
|
||||
var form = await req.ReadFormAsync(ct);
|
||||
var file = form.Files.FirstOrDefault();
|
||||
if (file == null || file.Length == 0) return Results.BadRequest("No file uploaded");
|
||||
|
||||
using var stream = file.OpenReadStream();
|
||||
var result = await sender.Send(new UploadFileCommand(file.FileName, file.ContentType, file.Length, stream), ct);
|
||||
|
||||
if (result.IsFailure)
|
||||
{
|
||||
if (result.Error.Code == "File.TooLarge") return Results.StatusCode(413);
|
||||
return Results.BadRequest(result.Error.Description);
|
||||
}
|
||||
return Results.Ok(result.Value);
|
||||
}).DisableAntiforgery();
|
||||
|
||||
group.MapGet("chat/{chatId:guid}/shared", async (Guid chatId, [FromQuery] string? type, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new GetSharedMediaQuery(userContext.UserId, chatId, type), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
|
||||
});
|
||||
|
||||
group.MapPost("chat/{chatId:guid}", async (Guid chatId, [FromBody] SendMessageRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var attachments = request.Attachments?.Select(a =>
|
||||
new AttachmentRequest(a.Type, a.Url, a.FileName, a.FileSize)).ToList();
|
||||
|
||||
var command = new SendMessageCommand(
|
||||
chatId,
|
||||
userContext.UserId,
|
||||
request.Content,
|
||||
request.Type,
|
||||
attachments,
|
||||
request.ReplyToId,
|
||||
request.Quote,
|
||||
request.ForwardedFromId);
|
||||
|
||||
var result = await sender.Send(command, ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user