Фиксы

This commit is contained in:
Халимов Рустам
2026-03-19 01:22:28 +03:00
parent 3d00bacafb
commit 282b43d4c1
11 changed files with 58 additions and 19 deletions
@@ -11,8 +11,11 @@ using MediatR;
using Host.Application.Admin.Queries; using Host.Application.Admin.Queries;
using Host.Application.Admin.Commands; using Host.Application.Admin.Commands;
using Microsoft.AspNetCore.Authorization;
namespace Host.Controllers; namespace Host.Controllers;
[Authorize]
[ApiController] [ApiController]
[Route("api/[controller]")] [Route("api/[controller]")]
public class AdminController : ControllerBase public class AdminController : ControllerBase
@@ -52,7 +52,7 @@ public sealed class ChatsController : ControllerBase
var result = await _sender.Send(command, ct); var result = await _sender.Send(command, ct);
if (result.IsFailure) return BadRequest(result.Error.Description); if (result.IsFailure) return BadRequest(result.Error.Description);
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
@@ -63,7 +63,7 @@ public sealed class ChatsController : ControllerBase
var result = await _sender.Send(command, ct); var result = await _sender.Send(command, ct);
if (result.IsFailure) return BadRequest(result.Error.Description); if (result.IsFailure) return BadRequest(result.Error.Description);
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
@@ -81,7 +81,7 @@ public sealed class ChatsController : ControllerBase
var result = await _sender.Send(command, ct); var result = await _sender.Send(command, ct);
if (result.IsFailure) return BadRequest(result.Error.Description); if (result.IsFailure) return BadRequest(result.Error.Description);
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
@@ -91,7 +91,7 @@ public sealed class ChatsController : ControllerBase
var result = await _sender.Send(new GetOrCreateFavoritesCommand(_userContext.UserId), ct); var result = await _sender.Send(new GetOrCreateFavoritesCommand(_userContext.UserId), ct);
if (result.IsFailure) return BadRequest(result.Error.Description); if (result.IsFailure) return BadRequest(result.Error.Description);
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
@@ -101,7 +101,7 @@ public sealed class ChatsController : ControllerBase
var result = await _sender.Send(new UpdateChatCommand(id, _userContext.UserId, request.Name, request.Description), ct); var result = await _sender.Send(new UpdateChatCommand(id, _userContext.UserId, request.Name, request.Description), ct);
if (result.IsFailure) return NotFound(); if (result.IsFailure) return NotFound();
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
@@ -139,7 +139,7 @@ public sealed class ChatsController : ControllerBase
var result = await _sender.Send(new AddMembersCommand(id, _userContext.UserId, request.UserIds.ToList()), ct); var result = await _sender.Send(new AddMembersCommand(id, _userContext.UserId, request.UserIds.ToList()), ct);
if (result.IsFailure) return NotFound(); if (result.IsFailure) return NotFound();
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
@@ -149,7 +149,7 @@ public sealed class ChatsController : ControllerBase
var result = await _sender.Send(new RemoveMemberCommand(id, _userContext.UserId, userId), ct); var result = await _sender.Send(new RemoveMemberCommand(id, _userContext.UserId, userId), ct);
if (result.IsFailure) return NotFound(); if (result.IsFailure) return NotFound();
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
@@ -163,7 +163,7 @@ public sealed class ChatsController : ControllerBase
if (result.IsFailure) return NotFound(); if (result.IsFailure) return NotFound();
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
@@ -177,7 +177,7 @@ public sealed class ChatsController : ControllerBase
if (result.IsFailure) return NotFound(); if (result.IsFailure) return NotFound();
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
@@ -187,7 +187,7 @@ public sealed class ChatsController : ControllerBase
var result = await _sender.Send(new RemoveGroupAvatarCommand(id, _userContext.UserId), ct); var result = await _sender.Send(new RemoveGroupAvatarCommand(id, _userContext.UserId), ct);
if (result.IsFailure) return NotFound(); if (result.IsFailure) return NotFound();
var chatResult = await _sender.Send(new GetChatByIdQuery(result.Value), ct); var chatResult = await _sender.Send(new GetChatByIdQuery(_userContext.UserId, result.Value), ct);
return Ok(chatResult.Value); return Ok(chatResult.Value);
} }
} }
@@ -4,6 +4,7 @@ using Knot.Shared.Kernel.Storage;
namespace Host.Controllers; namespace Host.Controllers;
[Authorize]
[ApiController] [ApiController]
[Route("api/files")] [Route("api/files")]
public sealed class FilesController : ControllerBase public sealed class FilesController : ControllerBase
@@ -11,7 +11,7 @@ using Knot.Modules.Chats.Application.Abstractions;
namespace Knot.Modules.Chats.Application.Chats.GetChatById; namespace Knot.Modules.Chats.Application.Chats.GetChatById;
public record GetChatByIdQuery(Guid ChatId) : IQuery<ChatDto?>; public record GetChatByIdQuery(Guid UserId, Guid ChatId) : IQuery<ChatDto?>;
internal sealed class GetChatByIdQueryHandler : IQueryHandler<GetChatByIdQuery, ChatDto?> internal sealed class GetChatByIdQueryHandler : IQueryHandler<GetChatByIdQuery, ChatDto?>
{ {
@@ -31,6 +31,11 @@ internal sealed class GetChatByIdQueryHandler : IQueryHandler<GetChatByIdQuery,
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken); var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
if (chat == null) return Result.Success<ChatDto?>(null); if (chat == null) return Result.Success<ChatDto?>(null);
if (!chat.Members.Any(m => m.UserId == request.UserId))
{
return Result.Failure<ChatDto?>(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата."));
}
var userIdsToFetch = new HashSet<Guid>(); var userIdsToFetch = new HashSet<Guid>();
foreach (var m in chat.Members) userIdsToFetch.Add(m.UserId); foreach (var m in chat.Members) userIdsToFetch.Add(m.UserId);
@@ -8,6 +8,8 @@ using Knot.Shared.Kernel;
using Knot.Modules.Chats.Application.DTOs; using Knot.Modules.Chats.Application.DTOs;
using Knot.Modules.Chats.Domain; using Knot.Modules.Chats.Domain;
using Knot.Modules.Chats.Application.Abstractions;
namespace Knot.Modules.Chats.Application.Messages.GetMessages; namespace Knot.Modules.Chats.Application.Messages.GetMessages;
public record GetMessagesQuery(Guid UserId, Guid ChatId, string? Cursor) : IQuery<List<MessageDetailDto>>; public record GetMessagesQuery(Guid UserId, Guid ChatId, string? Cursor) : IQuery<List<MessageDetailDto>>;
@@ -16,15 +18,23 @@ internal sealed class GetMessagesQueryHandler : IQueryHandler<GetMessagesQuery,
{ {
private readonly IMessageRepository _messageRepository; private readonly IMessageRepository _messageRepository;
private readonly IUserDisplayNameProvider _userProvider; private readonly IUserDisplayNameProvider _userProvider;
private readonly IChatRepository _chatRepository;
public GetMessagesQueryHandler(IMessageRepository messageRepository, IUserDisplayNameProvider userProvider) public GetMessagesQueryHandler(IMessageRepository messageRepository, IUserDisplayNameProvider userProvider, IChatRepository chatRepository)
{ {
_messageRepository = messageRepository; _messageRepository = messageRepository;
_userProvider = userProvider; _userProvider = userProvider;
_chatRepository = chatRepository;
} }
public async Task<Result<List<MessageDetailDto>>> Handle(GetMessagesQuery request, CancellationToken cancellationToken) public async Task<Result<List<MessageDetailDto>>> Handle(GetMessagesQuery request, CancellationToken cancellationToken)
{ {
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
{
return Result.Failure<List<MessageDetailDto>>(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата."));
}
DateTime? cursorDate = null; DateTime? cursorDate = null;
if (!string.IsNullOrEmpty(request.Cursor) && DateTime.TryParse(request.Cursor, null, System.Globalization.DateTimeStyles.RoundtripKind, out var parsed)) if (!string.IsNullOrEmpty(request.Cursor) && DateTime.TryParse(request.Cursor, null, System.Globalization.DateTimeStyles.RoundtripKind, out var parsed))
{ {
@@ -9,6 +9,8 @@ using Knot.Shared.Kernel;
using Knot.Modules.Chats.Application.DTOs; using Knot.Modules.Chats.Application.DTOs;
using Knot.Modules.Chats.Domain; using Knot.Modules.Chats.Domain;
using Knot.Modules.Chats.Application.Abstractions;
namespace Knot.Modules.Chats.Application.Messages.GetSharedMedia; namespace Knot.Modules.Chats.Application.Messages.GetSharedMedia;
public record GetSharedMediaQuery(Guid UserId, Guid ChatId, string? Type) : IQuery<List<SharedMediaDto>>; public record GetSharedMediaQuery(Guid UserId, Guid ChatId, string? Type) : IQuery<List<SharedMediaDto>>;
@@ -17,15 +19,23 @@ internal sealed class GetSharedMediaQueryHandler : IQueryHandler<GetSharedMediaQ
{ {
private readonly IMessageRepository _messageRepository; private readonly IMessageRepository _messageRepository;
private readonly IUserDisplayNameProvider _userProvider; private readonly IUserDisplayNameProvider _userProvider;
private readonly IChatRepository _chatRepository;
public GetSharedMediaQueryHandler(IMessageRepository messageRepository, IUserDisplayNameProvider userProvider) public GetSharedMediaQueryHandler(IMessageRepository messageRepository, IUserDisplayNameProvider userProvider, IChatRepository chatRepository)
{ {
_messageRepository = messageRepository; _messageRepository = messageRepository;
_userProvider = userProvider; _userProvider = userProvider;
_chatRepository = chatRepository;
} }
public async Task<Result<List<SharedMediaDto>>> Handle(GetSharedMediaQuery request, CancellationToken cancellationToken) public async Task<Result<List<SharedMediaDto>>> Handle(GetSharedMediaQuery request, CancellationToken cancellationToken)
{ {
var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
if (chat == null || !chat.Members.Any(m => m.UserId == request.UserId))
{
return Result.Failure<List<SharedMediaDto>>(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата."));
}
var messages = await _messageRepository.GetChatMessagesAsync(request.ChatId, 300, 0, cancellationToken); var messages = await _messageRepository.GetChatMessagesAsync(request.ChatId, 300, 0, cancellationToken);
messages = messages.Where(m => !m.IsDeleted && !m.DeletedByUsers.Contains(request.UserId)).ToList(); messages = messages.Where(m => !m.IsDeleted && !m.DeletedByUsers.Contains(request.UserId)).ToList();
@@ -25,7 +25,7 @@ internal sealed class SearchMessagesQueryHandler : IQueryHandler<SearchMessagesQ
public async Task<Result<List<SearchMessageDto>>> Handle(SearchMessagesQuery request, CancellationToken cancellationToken) public async Task<Result<List<SearchMessageDto>>> Handle(SearchMessagesQuery request, CancellationToken cancellationToken)
{ {
var messages = await _messageRepository.SearchMessagesAsync(request.Query, request.ChatId, cancellationToken); var messages = await _messageRepository.SearchMessagesAsync(request.Query, request.ChatId, request.UserId, cancellationToken);
messages = messages.Where(m => !m.DeletedByUsers.Contains(request.UserId)).ToList(); messages = messages.Where(m => !m.DeletedByUsers.Contains(request.UserId)).ToList();
var userIds = messages.Select(m => m.SenderId).ToList(); var userIds = messages.Select(m => m.SenderId).ToList();
@@ -47,8 +47,11 @@ public sealed class SendMessageCommandHandler : ICommandHandler<SendMessageComma
return Result.Failure<Guid>(new Error("Chats.NotFound", "Чат не найден.")); return Result.Failure<Guid>(new Error("Chats.NotFound", "Чат не найден."));
} }
// 2. Проверяем, является ли отправитель участником (в будущем) // 2. Проверяем, является ли отправитель участником
// if (!chat.Members.Any(m => m.UserId == request.SenderId)) ... if (!chat.Members.Any(m => m.UserId == request.SenderId))
{
return Result.Failure<Guid>(new Error("Chats.Forbidden", "Вы не являетесь участником этого чата."));
}
// 3. Создаем сообщение // 3. Создаем сообщение
var message = Message.Create( var message = Message.Create(
@@ -7,7 +7,7 @@ public interface IMessageRepository
void Add(Message message); void Add(Message message);
Task<Message?> GetByIdAsync(Guid id, CancellationToken cancellationToken); Task<Message?> GetByIdAsync(Guid id, CancellationToken cancellationToken);
Task<List<Message>> GetChatMessagesAsync(Guid chatId, int limit, int offset, CancellationToken cancellationToken); Task<List<Message>> GetChatMessagesAsync(Guid chatId, int limit, int offset, CancellationToken cancellationToken);
Task<List<Message>> SearchMessagesAsync(string query, Guid? chatId, CancellationToken cancellationToken); Task<List<Message>> SearchMessagesAsync(string query, Guid? chatId, Guid requestingUserId, CancellationToken cancellationToken);
Task AddReadReceiptsAsync(Guid userId, List<Guid> messageIds, CancellationToken cancellationToken); Task AddReadReceiptsAsync(Guid userId, List<Guid> messageIds, CancellationToken cancellationToken);
Task<bool> AddReactionAsync(Guid messageId, Guid userId, string emoji, CancellationToken cancellationToken); Task<bool> AddReactionAsync(Guid messageId, Guid userId, string emoji, CancellationToken cancellationToken);
Task<bool> RemoveReactionAsync(Guid messageId, Guid userId, string emoji, CancellationToken cancellationToken); Task<bool> RemoveReactionAsync(Guid messageId, Guid userId, string emoji, CancellationToken cancellationToken);
@@ -58,6 +58,8 @@ public sealed class ChatsDbContext : DbContext, IChatsUnitOfWork
{ {
builder.ToTable("Messages"); builder.ToTable("Messages");
builder.HasKey(m => m.Id); builder.HasKey(m => m.Id);
builder.HasIndex(m => m.ChatId);
builder.HasIndex(m => new { m.ChatId, m.CreatedAt });
builder.Property(m => m.Content) builder.Property(m => m.Content)
.HasConversion( .HasConversion(
@@ -58,9 +58,14 @@ public sealed class MessageRepository : IMessageRepository
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
} }
public async Task<List<Message>> SearchMessagesAsync(string query, Guid? chatId, CancellationToken cancellationToken) public async Task<List<Message>> SearchMessagesAsync(string query, Guid? chatId, Guid requestingUserId, CancellationToken cancellationToken)
{ {
var q = _dbContext.Messages.AsQueryable(); var validChatIdsQuery = _dbContext.Chats
.Where(c => c.Members.Any(m => m.UserId == requestingUserId))
.Select(c => c.Id);
var q = _dbContext.Messages.Where(m => validChatIdsQuery.Contains(m.ChatId));
if (chatId.HasValue) if (chatId.HasValue)
{ {
q = q.Where(m => m.ChatId == chatId.Value); q = q.Where(m => m.ChatId == chatId.Value);